This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag 
org.apache.sling.scripting.thymeleaf-1.0.0
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-scripting-thymeleaf.git

commit 9a238ba9df0eab674df27a4b47ef5b0ec039be4f
Author: Oliver Lietz <[email protected]>
AuthorDate: Sun Mar 13 12:23:37 2016 +0000

    SLING-5075 Upgrade Thymeleaf to 3.0
    
    update to Thymeleaf 3.0.0.BETA02, AttoParser 2.0.0.BETA05 and unbescape 
1.1.2.RELEASE
    
    git-svn-id: 
https://svn.apache.org/repos/asf/sling/trunk/contrib/scripting/org.apache.sling.scripting.thymeleaf@1734795
 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            |   6 +-
 .../thymeleaf/internal/SlingTemplateResource.java  |   2 +-
 .../thymeleaf/internal/dialect/SlingDialect.java   |   2 +-
 .../SlingIncludeAttributeTagProcessor.java         |  24 +-
 .../java/org/thymeleaf/engine/TemplateManager.java | 251 +++++++++++++++------
 .../templateresolver/AbstractTemplateResolver.java |  92 +++++++-
 .../templateresolver/ITemplateResolver.java        |   3 +
 7 files changed, 292 insertions(+), 88 deletions(-)

diff --git a/pom.xml b/pom.xml
index 5402766..d6edcf7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,9 +39,9 @@
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
     <sling.java.version>7</sling.java.version>
-    <org.thymeleaf.version>3.0.0.BETA01</org.thymeleaf.version>
-    <org.attoparser.version>2.0.0.BETA04</org.attoparser.version>
-    <org.unbescape.version>1.1.1.RELEASE</org.unbescape.version>
+    <org.thymeleaf.version>3.0.0.BETA02</org.thymeleaf.version>
+    <org.attoparser.version>2.0.0.BETA05</org.attoparser.version>
+    <org.unbescape.version>1.1.2.RELEASE</org.unbescape.version>
     <ognl.version>3.1</ognl.version>
   </properties>
 
diff --git 
a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/SlingTemplateResource.java
 
b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/SlingTemplateResource.java
index 52a18b9..072014c 100644
--- 
a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/SlingTemplateResource.java
+++ 
b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/SlingTemplateResource.java
@@ -62,7 +62,7 @@ public class SlingTemplateResource implements 
ITemplateResource {
     }
 
     @Override
-    public ITemplateResource relative(final String relativeLocation) throws 
IOException {
+    public ITemplateResource relative(final String relativeLocation) {
         throw new UnsupportedOperationException("not yet implemented"); // TODO
     }
 
diff --git 
a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/dialect/SlingDialect.java
 
b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/dialect/SlingDialect.java
index 289586c..9c7b995 100644
--- 
a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/dialect/SlingDialect.java
+++ 
b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/dialect/SlingDialect.java
@@ -49,7 +49,7 @@ public final class SlingDialect extends 
AbstractProcessorDialect {
     @Override
     public Set<IProcessor> getProcessors(final String prefix) {
         final Set<IProcessor> processors = new HashSet<IProcessor>();
-        processors.add(new SlingIncludeAttributeTagProcessor(this, prefix));
+        processors.add(new SlingIncludeAttributeTagProcessor(prefix));
         return processors;
     }
 
diff --git 
a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/processor/SlingIncludeAttributeTagProcessor.java
 
b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/processor/SlingIncludeAttributeTagProcessor.java
index 4fe1d1d..1676cf7 100644
--- 
a/src/main/java/org/apache/sling/scripting/thymeleaf/internal/processor/SlingIncludeAttributeTagProcessor.java
+++ 
b/src/main/java/org/apache/sling/scripting/thymeleaf/internal/processor/SlingIncludeAttributeTagProcessor.java
@@ -35,7 +35,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.thymeleaf.IEngineConfiguration;
 import org.thymeleaf.context.ITemplateContext;
-import org.thymeleaf.dialect.IProcessorDialect;
 import org.thymeleaf.engine.AttributeName;
 import org.thymeleaf.model.IElementAttributes;
 import org.thymeleaf.model.IProcessableElementTag;
@@ -48,6 +47,8 @@ import org.thymeleaf.templatemode.TemplateMode;
 
 public class SlingIncludeAttributeTagProcessor extends 
AbstractAttributeTagProcessor {
 
+    private final String dialectPrefix; // TODO remove (use dialectPrefix from 
extended AbstractElementTagProcessor)
+
     public static final int ATTRIBUTE_PRECEDENCE = 100;
 
     public static final String ATTRIBUTE_NAME = "include";
@@ -64,12 +65,13 @@ public class SlingIncludeAttributeTagProcessor extends 
AbstractAttributeTagProce
 
     private final Logger logger = 
LoggerFactory.getLogger(SlingIncludeAttributeTagProcessor.class);
 
-    public SlingIncludeAttributeTagProcessor(final IProcessorDialect 
processorDialect, final String dialectPrefix) {
-        super(processorDialect, TemplateMode.HTML, dialectPrefix, null, true, 
ATTRIBUTE_NAME, true, ATTRIBUTE_PRECEDENCE, true);
+    public SlingIncludeAttributeTagProcessor(final String dialectPrefix) {
+        super(TemplateMode.HTML, dialectPrefix, null, true, ATTRIBUTE_NAME, 
true, ATTRIBUTE_PRECEDENCE, true);
+        this.dialectPrefix = dialectPrefix;
     }
 
     @Override
-    protected void doProcess(final ITemplateContext templateContext, final 
IProcessableElementTag processableElementTag, final AttributeName 
attributeName, final String attributeValue, final String tagTemplateName, final 
int tagLine, final int tagCol, final IElementTagStructureHandler 
elementTagStructureHandler) {
+    protected void doProcess(final ITemplateContext templateContext, final 
IProcessableElementTag processableElementTag, final AttributeName 
attributeName, final String attributeValue, final IElementTagStructureHandler 
elementTagStructureHandler) {
         try {
             final SlingHttpServletRequest slingHttpServletRequest = 
(SlingHttpServletRequest) templateContext.getVariable(SlingBindings.REQUEST);
             final SlingHttpServletResponse slingHttpServletResponse = 
(SlingHttpServletResponse) templateContext.getVariable(SlingBindings.RESPONSE);
@@ -105,30 +107,28 @@ public class SlingIncludeAttributeTagProcessor extends 
AbstractAttributeTagProce
 
     protected Object parseAttribute(final IStandardExpressionParser 
expressionParser, final ITemplateContext templateContext, final 
IProcessableElementTag processableElementTag, final String name) {
         final IElementAttributes attributes = 
processableElementTag.getAttributes();
-        final String value = attributes.getValue(getDialect().getPrefix(), 
name);
+        final String value = attributes.getValue(dialectPrefix, name);
         Object result = null;
         if (value != null) {
             final IStandardExpression expression = 
expressionParser.parseExpression(templateContext, value);
             result = expression.execute(templateContext);
         }
-        attributes.removeAttribute(getDialect().getPrefix(), name);
+        attributes.removeAttribute(dialectPrefix, name);
         return result;
     }
 
     protected RequestDispatcherOptions prepareRequestDispatcherOptions(final 
IStandardExpressionParser expressionParser, final ITemplateContext 
templateContext, final IProcessableElementTag processableElementTag) {
-        final String prefix = getDialect().getPrefix();
-
         final String resourceType = (String) parseAttribute(expressionParser, 
templateContext, processableElementTag, RESOURCE_TYPE_ATTRIBUTE_NAME);
-        processableElementTag.getAttributes().removeAttribute(prefix, 
RESOURCE_TYPE_ATTRIBUTE_NAME);
+        processableElementTag.getAttributes().removeAttribute(dialectPrefix, 
RESOURCE_TYPE_ATTRIBUTE_NAME);
 
         final String replaceSelectors = (String) 
parseAttribute(expressionParser, templateContext, processableElementTag, 
REPLACE_SELECTORS_ATTRIBUTE_NAME);
-        processableElementTag.getAttributes().removeAttribute(prefix, 
REPLACE_SELECTORS_ATTRIBUTE_NAME);
+        processableElementTag.getAttributes().removeAttribute(dialectPrefix, 
REPLACE_SELECTORS_ATTRIBUTE_NAME);
 
         final String addSelectors = (String) parseAttribute(expressionParser, 
templateContext, processableElementTag, ADD_SELECTORS_ATTRIBUTE_NAME);
-        processableElementTag.getAttributes().removeAttribute(prefix, 
ADD_SELECTORS_ATTRIBUTE_NAME);
+        processableElementTag.getAttributes().removeAttribute(dialectPrefix, 
ADD_SELECTORS_ATTRIBUTE_NAME);
 
         final String replaceSuffix = (String) parseAttribute(expressionParser, 
templateContext, processableElementTag, REPLACE_SUFFIX_ATTRIBUTE_NAME);
-        processableElementTag.getAttributes().removeAttribute(prefix, 
REPLACE_SUFFIX_ATTRIBUTE_NAME);
+        processableElementTag.getAttributes().removeAttribute(dialectPrefix, 
REPLACE_SUFFIX_ATTRIBUTE_NAME);
 
         final RequestDispatcherOptions options = new 
RequestDispatcherOptions();
         options.setForceResourceType(resourceType);
diff --git a/src/main/java/org/thymeleaf/engine/TemplateManager.java 
b/src/main/java/org/thymeleaf/engine/TemplateManager.java
index c9eb560..469ae95 100644
--- a/src/main/java/org/thymeleaf/engine/TemplateManager.java
+++ b/src/main/java/org/thymeleaf/engine/TemplateManager.java
@@ -54,6 +54,7 @@ import 
org.thymeleaf.templateparser.text.JavaScriptTemplateParser;
 import org.thymeleaf.templateparser.text.TextTemplateParser;
 import org.thymeleaf.templateresolver.ITemplateResolver;
 import org.thymeleaf.templateresolver.TemplateResolution;
+import org.thymeleaf.templateresource.ITemplateResource;
 import org.thymeleaf.util.LoggingUtils;
 import org.thymeleaf.util.Validate;
 
@@ -190,7 +191,7 @@ public final class TemplateManager {
 
     public TemplateModel parseStandalone(
             final ITemplateContext context, final String template, final 
Set<String> templateSelectors,
-            final TemplateMode templateMode, final boolean useCache) {
+            final TemplateMode templateMode, final boolean useCache, final 
boolean failIfNotExists) {
 
         Validate.notNull(context, "Context cannot be null");
         Validate.notNull(template, "Template cannot be null");
@@ -234,7 +235,11 @@ public final class TemplateManager {
         if (useCache && this.templateCache != null) {
             final TemplateModel cached =  this.templateCache.get(cacheKey);
             if (cached != null) {
-                return cached;
+                /*
+                 * Just at the end, and importantly AFTER CACHING, check if we 
need to apply any pre-processors
+                 * to this model before returning and letting the engine 
insert the model in any way it needs.
+                 */
+                return applyPreProcessorsIfNeeded(context, cached);
             }
         }
 
@@ -243,7 +248,39 @@ public final class TemplateManager {
          * Resolve the template
          */
         final TemplateResolution templateResolution =
-                resolveTemplate(this.configuration, context, ownerTemplate, 
template, templateResolutionAttributes);
+                resolveTemplate(this.configuration, context, ownerTemplate, 
template, templateResolutionAttributes, failIfNotExists);
+
+
+        /*
+         * Once the template has been resolved (or tried to), and depending on 
the value of our 'failIfNotExists'
+         * flag, we will check two conditions in which we will be returning 
null:
+         *
+         *    1. No template resolver has been able to resolve the template 
(this can happen if resolvers are
+         *       configured with the 'checkExistence' flag to true).
+         *    2. If the template was resolved, its existence should be checked 
in order to avoid exceptions during
+         *       the reading phase.
+         *
+         * NOTE we will not cache this "null" result because the fact that a 
template is cacheable or not is
+         * determined by template resolvers. And in this case there is no 
template resolver being applied
+         * (actually, we are here because no resolver had success).
+         */
+        if (!failIfNotExists) {
+
+            if (templateResolution == null) {
+                // No resolver could resolve this
+                return null;
+            }
+
+            if (!templateResolution.isTemplateResourceExistenceVerified()) {
+                final ITemplateResource resource = 
templateResolution.getTemplateResource();
+                if (resource == null || !resource.exists()) {
+                    // Calling resource.exists() each time is not great, but 
think this only happens if the resource
+                    // has not been cached (e.g. when it does not exist)
+                    return null;
+                }
+            }
+
+        }
 
 
         /*
@@ -272,7 +309,7 @@ public final class TemplateManager {
         parser.parseStandalone(
                 this.configuration,
                 ownerTemplate, template, cleanTemplateSelectors, 
templateData.getTemplateResource(),
-                templateData.getTemplateMode(), builderHandler);
+                templateData.getTemplateMode(), 
templateResolution.getUseDecoupledLogic(), builderHandler);
 
 
         /*
@@ -284,9 +321,53 @@ public final class TemplateManager {
             }
         }
 
-        return templateModel;
 
+        /*
+         * Last step: just at the end, and importantly AFTER CACHING, check if 
we need to apply any pre-processors
+         * to this model before returning and letting the engine insert the 
model in any way it needs.
+         */
+        return applyPreProcessorsIfNeeded(context, templateModel);
+
+    }
+
+
+
+
+    /*
+     * This method manually applies preprocessors to template models that have 
just been parsed or obtained from
+     * cache. This is needed for fragments, just before these fragments 
(coming from templates, not simply parsed
+     * text) are returned to whoever needs them (usually the fragment 
insertion mechanism).
+     *
+     * NOTE that PRE-PROCESSOR INSTANCES ARE NOT SHARED among the different 
fragments being inserted
+     *      in a template (or between fragments and the main template). The 
reason for this is that pre-processors are
+     *      implementations of ITemplateHandler and therefore instances are 
inserted into processing chains that cannot
+     *      be broken (if a pre-processor is used for the main template its 
"next" step in the chain cannot be
+     *      'momentarily' changed in order to be a fragment-building handler 
instead of the ProcessorTemplateHandler)
+     *
+     *      The only way therefore among pre-processor instances to actually 
share information is by setting it into
+     *      the context.
+     */
+    private TemplateModel applyPreProcessorsIfNeeded(final ITemplateContext 
context, final TemplateModel templateModel) {
+
+        final TemplateData templateData = templateModel.getTemplateData();
+
+        if 
(this.configuration.getPreProcessors(templateData.getTemplateMode()).isEmpty()) 
{
+            return templateModel;
+        }
+
+        final IEngineContext engineContext =
+                EngineContextManager.prepareEngineContext(this.configuration, 
templateData, context.getTemplateResolutionAttributes(), context);
+
+        final TemplateModel preProcessedTemplateModel = new 
TemplateModel(this.configuration, templateData);
+        final ModelBuilderTemplateHandler builderHandler = new 
ModelBuilderTemplateHandler(preProcessedTemplateModel.getInternalModel());
+        final ITemplateHandler processingHandlerChain =
+                createTemplateProcessingHandlerChain(engineContext, true, 
false, builderHandler, null);
 
+        templateModel.getInternalModel().process(processingHandlerChain);
+
+        EngineContextManager.disposeEngineContext(engineContext);
+
+        return preProcessedTemplateModel;
 
     }
 
@@ -426,9 +507,21 @@ public final class TemplateManager {
                 EngineContextManager.prepareEngineContext(this.configuration, 
template.getTemplateData(), context.getTemplateResolutionAttributes(), context);
 
         /*
-         * Create the handler chain to process the data
+         * Create the handler chain to process the data.
+         *
+         * In this case we are only processing an already existing model, 
which was created after some computation
+         * at template-processing time. So this does not come directly from a 
template, and therefore pre-processors
+         * should not be applied.
+         *
+         * As for post-processors, we know the result of this will not be 
directly written to output in most cases but
+         * instead used to create a String that is afterwards inserted into 
the model as a Text node. In the only cases
+         * in which this is not true is when this is used inside any kind of 
Lazy-processing CharSequence writer like
+         * LazyProcessingCharSequence, and in such case we know those 
CharSequences are only used when there are
+         * NO post-processors, so we are safe anyway.
          */
-        final ITemplateHandler processingHandlerChain = 
createTemplateProcessingHandlerChain(engineContext, writer);
+        final ProcessorTemplateHandler processorTemplateHandler = new 
ProcessorTemplateHandler();
+        final ITemplateHandler processingHandlerChain =
+                createTemplateProcessingHandlerChain(engineContext, false, 
false, processorTemplateHandler, writer);
 
         /*
          *  Process the template
@@ -497,7 +590,14 @@ public final class TemplateManager {
                 final IEngineContext engineContext =
                         
EngineContextManager.prepareEngineContext(this.configuration, 
cached.getTemplateData(), templateResolutionAttributes, context);
 
-                final ITemplateHandler processingHandlerChain = 
createTemplateProcessingHandlerChain(engineContext, writer);
+                /*
+                 * Create the handler chain to process the data.
+                 * This is PARSE + PROCESS, so its called from the 
TemplateEngine, and the only case in which we should apply
+                 * both pre-processors and post-processors (besides creating a 
last output-to-writer step)
+                 */
+                final ProcessorTemplateHandler processorTemplateHandler = new 
ProcessorTemplateHandler();
+                final ITemplateHandler processingHandlerChain =
+                        createTemplateProcessingHandlerChain(engineContext, 
true, true, processorTemplateHandler, writer);
 
                 cached.getInternalModel().process(processingHandlerChain);
 
@@ -514,7 +614,7 @@ public final class TemplateManager {
          * Resolve the template
          */
         final TemplateResolution templateResolution =
-                resolveTemplate(this.configuration, context, null, template, 
templateResolutionAttributes);
+                resolveTemplate(this.configuration, context, null, template, 
templateResolutionAttributes, true);
 
 
         /*
@@ -532,9 +632,13 @@ public final class TemplateManager {
 
 
         /*
-         * Create the handler chain to process the data
+         * Create the handler chain to process the data.
+         * This is PARSE + PROCESS, so its called from the TemplateEngine, and 
the only case in which we should apply
+         * both pre-processors and post-processors (besides creating a last 
output-to-writer step)
          */
-        final ITemplateHandler processingHandlerChain = 
createTemplateProcessingHandlerChain(engineContext, writer);
+        final ProcessorTemplateHandler processorTemplateHandler = new 
ProcessorTemplateHandler();
+        final ITemplateHandler processingHandlerChain =
+                createTemplateProcessingHandlerChain(engineContext, true, 
true, processorTemplateHandler, writer);
 
 
         /*
@@ -558,7 +662,7 @@ public final class TemplateManager {
             parser.parseStandalone(
                     this.configuration,
                     null, template, templateSelectors, 
templateData.getTemplateResource(),
-                    engineContext.getTemplateMode(), builderHandler);
+                    engineContext.getTemplateMode(), 
templateResolution.getUseDecoupledLogic(), builderHandler);
 
             // Put the new template into cache
             this.templateCache.put(cacheKey, templateModel);
@@ -572,7 +676,7 @@ public final class TemplateManager {
             parser.parseStandalone(
                     this.configuration,
                     null, template, templateSelectors, 
templateData.getTemplateResource(),
-                    engineContext.getTemplateMode(), processingHandlerChain);
+                    engineContext.getTemplateMode(), 
templateResolution.getUseDecoupledLogic(),  processingHandlerChain);
 
         }
 
@@ -595,7 +699,8 @@ public final class TemplateManager {
             final IContext context,
             final String ownerTemplate,
             final String template,
-            final Map<String, Object> templateResolutionAttributes) {
+            final Map<String, Object> templateResolutionAttributes,
+            final boolean failIfNotExists) {
 
         // Note that the MARKUP SELECTORS that might be used for a executing 
or inserting a template
         // are not specified to the template resolver. The reason is markup 
selectors are applied by the parser,
@@ -624,6 +729,12 @@ public final class TemplateManager {
 
         }
 
+        if (!failIfNotExists) {
+            // In this case we will not consider that a "not exists" means a 
failure. Maybe we are in a scenario
+            // (e.g. some types of operations with FragmentExpressions) in 
which we desire this.
+            return null;
+        }
+
         throw new TemplateInputException(
                 "Error resolving template \"" + 
LoggingUtils.loggifyTemplateName(template) + "\", " +
                 "template might not exist or might not be accessible by " +
@@ -675,7 +786,8 @@ public final class TemplateManager {
 
     private static ITemplateHandler createTemplateProcessingHandlerChain(
             final IEngineContext context,
-            final Writer writer) {
+            final boolean setPreProcessors, final boolean setPostProcessors,
+            final ITemplateHandler handler, final Writer writer) {
 
         final IEngineConfiguration configuration = context.getConfiguration();
 
@@ -688,26 +800,28 @@ public final class TemplateManager {
         /*
          * First type of handlers to be added: pre-processors (if any)
          */
-        final Set<IPreProcessor> preProcessors = 
configuration.getPreProcessors(context.getTemplateMode());
-        if (preProcessors != null) {
-            for (final IPreProcessor preProcessor : preProcessors) {
-                final Class<? extends ITemplateHandler> preProcessorClass = 
preProcessor.getHandlerClass();
-                final ITemplateHandler preProcessorHandler;
-                try {
-                    preProcessorHandler = preProcessorClass.newInstance();
-                } catch (final Exception e) {
-                    // This should never happen - class was already checked 
during configuration to contain a zero-arg constructor
-                    throw new TemplateProcessingException(
-                            "An exception happened during the creation of a 
new instance of pre-processor " + preProcessorClass.getClass().getName(), e);
-                }
-                // Initialize the pre-processor
-                preProcessorHandler.setContext(context);
-                if (firstHandler == null) {
-                    firstHandler = preProcessorHandler;
-                    lastHandler = preProcessorHandler;
-                } else {
-                    lastHandler.setNext(preProcessorHandler);
-                    lastHandler = preProcessorHandler;
+        if (setPreProcessors) {
+            final Set<IPreProcessor> preProcessors = 
configuration.getPreProcessors(context.getTemplateMode());
+            if (preProcessors != null) {
+                for (final IPreProcessor preProcessor : preProcessors) {
+                    final Class<? extends ITemplateHandler> preProcessorClass 
= preProcessor.getHandlerClass();
+                    final ITemplateHandler preProcessorHandler;
+                    try {
+                        preProcessorHandler = preProcessorClass.newInstance();
+                    } catch (final Exception e) {
+                        // This should never happen - class was already 
checked during configuration to contain a zero-arg constructor
+                        throw new TemplateProcessingException(
+                                "An exception happened during the creation of 
a new instance of pre-processor " + preProcessorClass.getClass().getName(), e);
+                    }
+                    // Initialize the pre-processor
+                    preProcessorHandler.setContext(context);
+                    if (firstHandler == null) {
+                        firstHandler = preProcessorHandler;
+                        lastHandler = preProcessorHandler;
+                    } else {
+                        lastHandler.setNext(preProcessorHandler);
+                        lastHandler = preProcessorHandler;
+                    }
                 }
             }
         }
@@ -716,40 +830,41 @@ public final class TemplateManager {
         /*
          * Initialize and add to the chain te Processor Handler itself, the 
central piece of the chain
          */
-        final ProcessorTemplateHandler processorHandler = new 
ProcessorTemplateHandler();
-        processorHandler.setContext(context);
+        handler.setContext(context);
         if (firstHandler == null) {
-            firstHandler = processorHandler;
-            lastHandler = processorHandler;
+            firstHandler = handler;
+            lastHandler = handler;
         } else {
-            lastHandler.setNext(processorHandler);
-            lastHandler = processorHandler;
+            lastHandler.setNext(handler);
+            lastHandler = handler;
         }
 
 
         /*
          * After the Processor Handler, we now must add the post-processors 
(if any)
          */
-        final Set<IPostProcessor> postProcessors = 
configuration.getPostProcessors(context.getTemplateMode());
-        if (postProcessors != null) {
-            for (final IPostProcessor postProcessor : postProcessors) {
-                final Class<? extends ITemplateHandler> postProcessorClass = 
postProcessor.getHandlerClass();
-                final ITemplateHandler postProcessorHandler;
-                try {
-                    postProcessorHandler = postProcessorClass.newInstance();
-                } catch (final Exception e) {
-                    // This should never happen - class was already checked 
during configuration to contain a zero-arg constructor
-                    throw new TemplateProcessingException(
-                            "An exception happened during the creation of a 
new instance of post-processor " + postProcessorClass.getClass().getName(), e);
-                }
-                // Initialize the pre-processor
-                postProcessorHandler.setContext(context);
-                if (firstHandler == null) {
-                    firstHandler = postProcessorHandler;
-                    lastHandler = postProcessorHandler;
-                } else {
-                    lastHandler.setNext(postProcessorHandler);
-                    lastHandler = postProcessorHandler;
+        if (setPostProcessors) {
+            final Set<IPostProcessor> postProcessors = 
configuration.getPostProcessors(context.getTemplateMode());
+            if (postProcessors != null) {
+                for (final IPostProcessor postProcessor : postProcessors) {
+                    final Class<? extends ITemplateHandler> postProcessorClass 
= postProcessor.getHandlerClass();
+                    final ITemplateHandler postProcessorHandler;
+                    try {
+                        postProcessorHandler = 
postProcessorClass.newInstance();
+                    } catch (final Exception e) {
+                        // This should never happen - class was already 
checked during configuration to contain a zero-arg constructor
+                        throw new TemplateProcessingException(
+                                "An exception happened during the creation of 
a new instance of post-processor " + postProcessorClass.getClass().getName(), 
e);
+                    }
+                    // Initialize the pre-processor
+                    postProcessorHandler.setContext(context);
+                    if (firstHandler == null) {
+                        firstHandler = postProcessorHandler;
+                        lastHandler = postProcessorHandler;
+                    } else {
+                        lastHandler.setNext(postProcessorHandler);
+                        lastHandler = postProcessorHandler;
+                    }
                 }
             }
         }
@@ -758,12 +873,14 @@ public final class TemplateManager {
         /*
          * Last step: the OUTPUT HANDLER
          */
-        final OutputTemplateHandler outputHandler = new 
OutputTemplateHandler(writer);
-        outputHandler.setContext(context);
-        if (firstHandler == null) {
-            firstHandler = outputHandler;
-        } else {
-            lastHandler.setNext(outputHandler);
+        if (writer != null) {
+            final OutputTemplateHandler outputHandler = new 
OutputTemplateHandler(writer);
+            outputHandler.setContext(context);
+            if (firstHandler == null) {
+                firstHandler = outputHandler;
+            } else {
+                lastHandler.setNext(outputHandler);
+            }
         }
 
         return firstHandler;
diff --git 
a/src/main/java/org/thymeleaf/templateresolver/AbstractTemplateResolver.java 
b/src/main/java/org/thymeleaf/templateresolver/AbstractTemplateResolver.java
index bdde094..8fc2092 100755
--- a/src/main/java/org/thymeleaf/templateresolver/AbstractTemplateResolver.java
+++ b/src/main/java/org/thymeleaf/templateresolver/AbstractTemplateResolver.java
@@ -56,10 +56,18 @@ public abstract class AbstractTemplateResolver implements 
ITemplateResolver {
      */
     public static final boolean DEFAULT_EXISTENCE_CHECK = false;
 
+    /**
+     * <p>
+     *   By default, resources will not be marked to look for decoupled logic.
+     * </p>
+     */
+    public static final boolean DEFAULT_USE_DECOUPLED_LOGIC = false;
+
     
     private String name = this.getClass().getName();
     private Integer order = null;
     private boolean checkExistence = DEFAULT_EXISTENCE_CHECK;
+    private boolean useDecoupledLogic = DEFAULT_USE_DECOUPLED_LOGIC;
 
     private final PatternSpec resolvablePatternSpec = new PatternSpec();
     
@@ -91,7 +99,7 @@ public abstract class AbstractTemplateResolver implements 
ITemplateResolver {
      * 
      * @param name the new name
      */
-    public final void setName(final String name) {
+    public void setName(final String name) {
         this.name = name;
     }
     
@@ -119,7 +127,7 @@ public abstract class AbstractTemplateResolver implements 
ITemplateResolver {
      * 
      * @param order the new order.
      */
-    public final void setOrder(final Integer order) {
+    public void setOrder(final Integer order) {
         this.order = order;
     }
     
@@ -182,7 +190,7 @@ public abstract class AbstractTemplateResolver implements 
ITemplateResolver {
      * 
      * @param resolvablePatterns the new patterns
      */
-    public final void setResolvablePatterns(final Set<String> 
resolvablePatterns) {
+    public void setResolvablePatterns(final Set<String> resolvablePatterns) {
         this.resolvablePatternSpec.setPatterns(resolvablePatterns);
     }
 
@@ -253,11 +261,85 @@ public abstract class AbstractTemplateResolver implements 
ITemplateResolver {
      * @since 3.0.0
      *
      */
-    public final void setCheckExistence(final boolean checkExistence) {
+    public void setCheckExistence(final boolean checkExistence) {
         this.checkExistence = checkExistence;
     }
 
 
+
+    /**
+     * <p>
+     *   Returns whether a separate (decoupled) resource containing template 
logic should be checked for existence
+     *   and its instructions included into the resolved template during 
parsing.
+     * </p>
+     * <p>
+     *   This mechanism allows the creation of <em>pure</em> HTML or XML 
markup templates, which acquire their logic
+     *   from an external resource. The way this decoupled resources are 
resolved is defined by a configured
+     *   implementation of the {@link 
org.thymeleaf.templateparser.markup.decoupled.IDecoupledTemplateLogicResolver}
+     *   interface.
+     * </p>
+     * <p>
+     *   Note this flag can only be <tt>true</tt> for the {@link 
TemplateMode#HTML} and {@link TemplateMode#XML}
+     *   template modes. Also, note that setting this flag to <tt>true</tt> 
does not mean that a resource with
+     *   decoupled logic must exist for the resolved template, only that it 
can exist.
+     * </p>
+     * <p>
+     *   Decoupled logic extracted from these additional resources is injected 
into the resolved templates in real-time
+     *   as the resolved templates are parsed and processed. This greatly 
reduces overhead caused by decoupled parsing
+     *   for non-cacheable templates, and completely removes any overhead for 
cached templates.
+     * </p>
+     * <p>
+     *   Default value is <tt>FALSE</tt>.
+     * </p>
+     *
+     * @return <tt>true</tt> if decoupled logic resources should be checked, 
<tt>false</tt> if not.
+     *
+     * @since 3.0.0
+     *
+     */
+    public final boolean getUseDecoupledLogic() {
+        return this.useDecoupledLogic;
+    }
+
+
+    /**
+     * <p>
+     *   Sets whether a separate (decoupled) resource containing template 
logic should be checked for existence
+     *   and its instructions included into the resolved template during 
parsing.
+     * </p>
+     * <p>
+     *   This mechanism allows the creation of <em>pure</em> HTML or XML 
markup templates, which acquire their logic
+     *   from an external resource. The way this decoupled resources are 
resolved is defined by a configured
+     *   implementation of the {@link 
org.thymeleaf.templateparser.markup.decoupled.IDecoupledTemplateLogicResolver}
+     *   interface.
+     * </p>
+     * <p>
+     *   Note this flag can only be <tt>true</tt> for the {@link 
TemplateMode#HTML} and {@link TemplateMode#XML}
+     *   template modes. Also, note that setting this flag to <tt>true</tt> 
does not mean that a resource with
+     *   decoupled logic must exist for the resolved template, only that it 
can exist and therefore it should be
+     *   checked.
+     * </p>
+     * <p>
+     *   Decoupled logic extracted from these additional resources is injected 
into the resolved templates in real-time
+     *   as the resolved templates are parsed and processed. This greatly 
reduces overhead caused by decoupled parsing
+     *   for non-cacheable templates, and completely removes any overhead for 
cached templates.
+     * </p>
+     * <p>
+     *   Default value is <tt>FALSE</tt>.
+     * </p>
+     *
+     * @param useDecoupledLogic <tt>true</tt> if resource existence should be 
checked, <tt>false</tt> if not
+     *
+     * @since 3.0.0
+     *
+     */
+    public void setUseDecoupledLogic(final boolean useDecoupledLogic) {
+        this.useDecoupledLogic = useDecoupledLogic;
+    }
+
+
+
+
     public final TemplateResolution resolveTemplate(
         final IEngineConfiguration configuration,
         final IContext context,
@@ -284,7 +366,9 @@ public abstract class AbstractTemplateResolver implements 
ITemplateResolver {
 
         return new TemplateResolution(
                 templateResource,
+                this.checkExistence,
                 computeTemplateMode(configuration, ownerTemplate, template, 
templateResolutionAttributes),
+                this.useDecoupledLogic,
                 computeValidity(configuration, ownerTemplate, template, 
templateResolutionAttributes));
         
     }
diff --git 
a/src/main/java/org/thymeleaf/templateresolver/ITemplateResolver.java 
b/src/main/java/org/thymeleaf/templateresolver/ITemplateResolver.java
index 16319c2..0bd62b9 100755
--- a/src/main/java/org/thymeleaf/templateresolver/ITemplateResolver.java
+++ b/src/main/java/org/thymeleaf/templateresolver/ITemplateResolver.java
@@ -64,6 +64,9 @@ import org.thymeleaf.templateresource.ITemplateResource;
  *   that are not configured an order will be executed last in the chain.
  * </p>
  * <p>
+ *   Implementations of this interface should be <strong>thread-safe</strong>.
+ * </p>
+ * <p>
  *   Note a class with this name existed since 1.0, but it was completely 
reimplemented
  *   in Thymeleaf 3.0
  * </p>

-- 
To stop receiving notification emails like this one, please contact
"[email protected]" <[email protected]>.

Reply via email to