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]>.
