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 2f5a44ca9f4bc7d50dcba5b82316392b72bcdd72
Author: Oliver Lietz <[email protected]>
AuthorDate: Mon Dec 7 15:54:18 2015 +0000

    SLING-5075 Upgrade Thymeleaf to 3.0
    
    * add (missing) patched Thymeleaf sources
    
    git-svn-id: 
https://svn.apache.org/repos/asf/sling/trunk/contrib/scripting/thymeleaf@1718412
 13f79535-47bb-0310-9956-ffa450edef68
---
 .../java/org/thymeleaf/engine/TemplateManager.java | 776 +++++++++++++++++++++
 .../templateresolver/AbstractTemplateResolver.java | 371 ++++++++++
 .../templateresolver/ITemplateResolver.java        | 156 +++++
 3 files changed, 1303 insertions(+)

diff --git a/src/main/java/org/thymeleaf/engine/TemplateManager.java 
b/src/main/java/org/thymeleaf/engine/TemplateManager.java
new file mode 100644
index 0000000..c9eb560
--- /dev/null
+++ b/src/main/java/org/thymeleaf/engine/TemplateManager.java
@@ -0,0 +1,776 @@
+/*
+ * 
=============================================================================
+ * 
+ *   Copyright (c) 2011-2014, The THYMELEAF team (http://www.thymeleaf.org)
+ * 
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ * 
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ * 
+ * 
=============================================================================
+ */
+package org.thymeleaf.engine;
+
+import java.io.Writer;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.thymeleaf.IEngineConfiguration;
+import org.thymeleaf.TemplateEngine;
+import org.thymeleaf.TemplateSpec;
+import org.thymeleaf.cache.AlwaysValidCacheEntryValidity;
+import org.thymeleaf.cache.ICache;
+import org.thymeleaf.cache.ICacheEntryValidity;
+import org.thymeleaf.cache.ICacheManager;
+import org.thymeleaf.cache.NonCacheableCacheEntryValidity;
+import org.thymeleaf.cache.TemplateCacheKey;
+import org.thymeleaf.context.IContext;
+import org.thymeleaf.context.IEngineContext;
+import org.thymeleaf.context.ITemplateContext;
+import org.thymeleaf.exceptions.TemplateInputException;
+import org.thymeleaf.exceptions.TemplateProcessingException;
+import org.thymeleaf.postprocessor.IPostProcessor;
+import org.thymeleaf.preprocessor.IPreProcessor;
+import org.thymeleaf.templatemode.TemplateMode;
+import org.thymeleaf.templateparser.ITemplateParser;
+import org.thymeleaf.templateparser.markup.HTMLTemplateParser;
+import org.thymeleaf.templateparser.markup.XMLTemplateParser;
+import org.thymeleaf.templateparser.raw.RawTemplateParser;
+import org.thymeleaf.templateparser.text.CSSTemplateParser;
+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.util.LoggingUtils;
+import org.thymeleaf.util.Validate;
+
+
+/**
+ *
+ * @author Daniel Fern&aacute;ndez
+ * 
+ * @since 3.0.0
+ *
+ */
+public final class TemplateManager {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(TemplateManager.class);
+
+    private static final int DEFAULT_PARSER_POOL_SIZE = 40;
+    private static final int DEFAULT_PARSER_BLOCK_SIZE = 2048;
+
+    private final IEngineConfiguration configuration;
+
+    private final ITemplateParser htmlParser;
+    private final ITemplateParser xmlParser;
+    private final ITemplateParser textParser;
+    private final ITemplateParser javascriptParser;
+    private final ITemplateParser cssParser;
+    private final ITemplateParser rawParser;
+
+
+    private final ICache<TemplateCacheKey,TemplateModel> templateCache; // 
might be null! (= no cache)
+
+
+
+
+    /**
+     * <p>
+     *   This constructor should only be called directly for <strong>testing 
purposes</strong>.
+     * </p>
+     *
+     * @param configuration the engine configuration
+     */
+    public TemplateManager(final IEngineConfiguration configuration) {
+        
+        super();
+
+        Validate.notNull(configuration, "Configuration cannot be null");
+
+        this.configuration = configuration;
+
+        final ICacheManager cacheManager = 
this.configuration.getCacheManager();
+
+        if (cacheManager == null) {
+            this.templateCache = null;
+        } else {
+            this.templateCache = cacheManager.getTemplateCache();
+        }
+
+        final boolean standardDialectPresent = 
this.configuration.isStandardDialectPresent();
+        final String standardDialectPrefix = 
this.configuration.getStandardDialectPrefix();
+
+        // TODO Make these parser implementations configurable: one parser per 
template mode, then make default implementations extensible/configurable (e.g. 
AttoParser config)
+        this.htmlParser = new 
HTMLTemplateParser(DEFAULT_PARSER_POOL_SIZE,DEFAULT_PARSER_BLOCK_SIZE);
+        this.xmlParser = new XMLTemplateParser(DEFAULT_PARSER_POOL_SIZE, 
DEFAULT_PARSER_BLOCK_SIZE);
+        this.textParser = new TextTemplateParser(DEFAULT_PARSER_POOL_SIZE, 
DEFAULT_PARSER_BLOCK_SIZE, standardDialectPresent, standardDialectPrefix);
+        this.javascriptParser = new 
JavaScriptTemplateParser(DEFAULT_PARSER_POOL_SIZE, DEFAULT_PARSER_BLOCK_SIZE, 
standardDialectPresent, standardDialectPrefix);
+        this.cssParser = new CSSTemplateParser(DEFAULT_PARSER_POOL_SIZE, 
DEFAULT_PARSER_BLOCK_SIZE, standardDialectPresent, standardDialectPrefix);
+        this.rawParser = new RawTemplateParser(DEFAULT_PARSER_POOL_SIZE, 
DEFAULT_PARSER_BLOCK_SIZE);
+
+    }
+    
+
+    
+    
+    
+    /**
+     * <p>
+     *   Clears the template cache.
+     * </p>
+     */
+    public void clearCaches() {
+        if (this.templateCache != null) {
+            this.templateCache.clear();
+        }
+    }
+
+    
+    /**
+     * <p>
+     *   Clears any existing entries for template of the specified
+     *   name at the template cache.
+     * </p>
+     * 
+     * @param template the name of the template whose entries have to be 
cleared.
+     */
+    public void clearCachesFor(final String template) {
+        Validate.notNull(template, "Cannot specify null template");
+        if (this.templateCache != null) {
+            final Set<TemplateCacheKey> keysToBeRemoved = new 
HashSet<TemplateCacheKey>(4);
+            final Set<TemplateCacheKey> templateCacheKeys = 
this.templateCache.keySet();
+            // We are iterating twice and creating a temporary set just in 
case the 'keySet' Set is still connected
+            // to the original cache store and we provoke 
ConcurrentModificationExceptions when removing entries
+            for (final TemplateCacheKey templateCacheKey : templateCacheKeys) {
+                final String ownerTemplate = 
templateCacheKey.getOwnerTemplate();
+                if (ownerTemplate != null) {
+                    // It's not a standalone template, so we are interested on 
the owner template
+                    if (ownerTemplate.equals(template)) {
+                        keysToBeRemoved.add(templateCacheKey);
+                    }
+                } else {
+                    if (templateCacheKey.getTemplate().equals(template)) {
+                        keysToBeRemoved.add(templateCacheKey);
+                    }
+                }
+            }
+            for (final TemplateCacheKey keyToBeRemoved : keysToBeRemoved) {
+                this.templateCache.clearKey(keyToBeRemoved);
+            }
+        }
+    }
+
+
+
+
+
+
+    /*
+     * -------------
+     * PARSE methods
+     * -------------
+     *
+     * Parse methods will create 'template models' that are basically 
collections of events in the form of an
+     * immutable IModel implementation.
+     */
+
+
+    public TemplateModel parseStandalone(
+            final ITemplateContext context, final String template, final 
Set<String> templateSelectors,
+            final TemplateMode templateMode, final boolean useCache) {
+
+        Validate.notNull(context, "Context cannot be null");
+        Validate.notNull(template, "Template cannot be null");
+        // templateSelectors CAN be null if we are going to render the entire 
template
+        // templateMode CAN be null if we are going to use the mode specified 
by the template resolver
+        // templateResolutionAttributes CAN be null
+
+
+        final String ownerTemplate = context.getTemplateData().getTemplate();
+        final Map<String,Object> templateResolutionAttributes = 
context.getTemplateResolutionAttributes();
+
+        final Set<String> cleanTemplateSelectors;
+        if (templateSelectors != null && !templateSelectors.isEmpty()) {
+            Validate.containsNoEmpties(
+                    templateSelectors, "If specified, the Template Selector 
set cannot contain any nulls or empties");
+            if (templateSelectors.size() == 1) {
+                cleanTemplateSelectors = 
Collections.singleton(templateSelectors.iterator().next());
+            } else {
+                // We will be using a TreeSet because we want the selectors to 
be ORDERED, so that comparison at the
+                // equals(...) method works alright
+                cleanTemplateSelectors = Collections.unmodifiableSet(new 
TreeSet<String>(templateSelectors));
+            }
+        } else {
+            cleanTemplateSelectors = null;
+        }
+
+
+        final TemplateCacheKey cacheKey =
+                useCache?
+                        new TemplateCacheKey(
+                                ownerTemplate,
+                                template, cleanTemplateSelectors,
+                                0, 0,
+                                templateMode,
+                                templateResolutionAttributes)
+                        : null;
+
+        /*
+         * First look at the cache - it might be already cached
+         */
+        if (useCache && this.templateCache != null) {
+            final TemplateModel cached =  this.templateCache.get(cacheKey);
+            if (cached != null) {
+                return cached;
+            }
+        }
+
+
+        /*
+         * Resolve the template
+         */
+        final TemplateResolution templateResolution =
+                resolveTemplate(this.configuration, context, ownerTemplate, 
template, templateResolutionAttributes);
+
+
+        /*
+         * Build the TemplateData object
+         */
+        final TemplateData templateData =
+                buildTemplateData(templateResolution, template, 
cleanTemplateSelectors, templateMode, useCache);
+
+
+        /*
+         * Build the TemplateModel that we will end up returning
+         */
+        final TemplateModel templateModel = new 
TemplateModel(this.configuration, templateData);
+
+
+        /*
+         *  Create the Template Handler that will be in charge of building the 
TemplateModel
+         */
+        final ModelBuilderTemplateHandler builderHandler = new 
ModelBuilderTemplateHandler(templateModel.getInternalModel());
+
+
+        /*
+         * PROCESS THE TEMPLATE
+         */
+        final ITemplateParser parser = 
getParserForTemplateMode(templateData.getTemplateMode());
+        parser.parseStandalone(
+                this.configuration,
+                ownerTemplate, template, cleanTemplateSelectors, 
templateData.getTemplateResource(),
+                templateData.getTemplateMode(), builderHandler);
+
+
+        /*
+         * Cache the template if it is cacheable
+         */
+        if (useCache && this.templateCache != null) {
+            if (templateResolution.getValidity().isCacheable()) {
+                this.templateCache.put(cacheKey, templateModel);
+            }
+        }
+
+        return templateModel;
+
+
+
+    }
+
+
+
+
+    public TemplateModel parseString(
+            final TemplateData ownerTemplateData, final String template,
+            final int lineOffset, final int colOffset,
+            final TemplateMode templateMode,
+            final boolean useCache) {
+
+        Validate.notNull(ownerTemplateData, "Owner template cannot be null");
+        Validate.notNull(template, "Template cannot be null");
+        // NOTE selectors cannot be specified when parsing a nested template
+        // templateMode CAN be null (if we are using the owner's)
+
+        final String ownerTemplate = ownerTemplateData.getTemplate();
+
+        final TemplateMode definitiveTemplateMode =
+                (templateMode != null? templateMode : 
ownerTemplateData.getTemplateMode());
+
+
+        final TemplateCacheKey cacheKey =
+                useCache?
+                        new TemplateCacheKey(
+                                ownerTemplate,
+                                template, null,
+                                lineOffset, colOffset,
+                                definitiveTemplateMode,
+                                null) // template resolution attributes do not 
affect string fragments: no resolution!
+                        : null;
+
+        /*
+         * First look at the cache - it might be already cached
+         */
+        if (useCache && this.templateCache != null) {
+            final TemplateModel cached =  this.templateCache.get(cacheKey);
+            if (cached != null) {
+                return cached;
+            }
+        }
+
+
+        /*
+         * Compute the cache validity. In order for a String fragment to be 
cacheable, we will have to have
+         * specified the 'useCache' parameter as true, and the owner template 
must be cacheable
+         */
+        final ICacheEntryValidity cacheValidity =
+                (useCache && ownerTemplateData.getValidity().isCacheable()?
+                        AlwaysValidCacheEntryValidity.INSTANCE : 
NonCacheableCacheEntryValidity.INSTANCE);
+
+
+        /*
+         * Build the TemplateData
+         *
+         * NOTE how, by default, we are using the owner's TemplateData. And 
even if the template mode changes
+         * and we need to create a new TemplateData object, we will keep the 
original name and resource.
+         * This is because we want the elements inside the fragment to me 
reported as belonging to the
+         * container template, not to the fragment String considered as a 
fragment in its own (which
+         * wouldn't make sense)
+         */
+        final TemplateData templateData =
+                (templateMode == null?
+                        // No change in Template Mode -> simply use the 
owner's template data
+                        ownerTemplateData :
+                        // Template Mode changed -> new TemplateData, very 
similar but different template mode
+                        new TemplateData(
+                                ownerTemplateData.getTemplate(), 
ownerTemplateData.getTemplateSelectors(),
+                                ownerTemplateData.getTemplateResource(), 
templateMode, cacheValidity));
+
+
+        /*
+         * Build the TemplateModel
+         *
+         * NOTE how we are using the owner's TemplateData and not a new one 
created for this fragment, because
+         * we want the elements inside the fragment to me reported as 
belonging to the container template,
+         * not to the fragment String considered as a fragment in its own 
(which wouldn't make sense)
+         */
+        final TemplateModel parsedTemplate = new 
TemplateModel(this.configuration, templateData);
+
+
+        /*
+         *  Create the Template Handler that will be in charge of building the 
TemplateModel
+         */
+        final ModelBuilderTemplateHandler builderHandler = new 
ModelBuilderTemplateHandler(parsedTemplate.getInternalModel());
+
+
+        /*
+         * PROCESS THE TEMPLATE
+         */
+        final ITemplateParser parser = 
getParserForTemplateMode(templateData.getTemplateMode());
+        // NO RESOURCE is sent to the parser, in this case. We simply pass the 
String template
+        parser.parseString(this.configuration, ownerTemplate, template, 
lineOffset, colOffset, definitiveTemplateMode, builderHandler);
+
+
+        /*
+         * Cache the template if it is cacheable
+         */
+        if (useCache && this.templateCache != null) {
+            if (cacheValidity.isCacheable()) {
+                this.templateCache.put(cacheKey, parsedTemplate);
+            }
+        }
+        
+        return parsedTemplate;
+        
+    }
+
+
+
+
+
+
+    /*
+     * ---------------
+     * PROCESS methods
+     * ---------------
+     *
+     * Processing means executing a template that has already been parsed into 
a TemplateModel object
+     */
+
+
+    public void process(
+            final TemplateModel template,
+            final ITemplateContext context,
+            final Writer writer) {
+
+        Validate.isTrue(
+                this.configuration == template.getConfiguration(),
+                "Specified template was built by a different Template Engine 
instance");
+
+        /*
+         * Create the context instance that corresponds to this execution of 
the template engine
+         */
+        final IEngineContext engineContext =
+                EngineContextManager.prepareEngineContext(this.configuration, 
template.getTemplateData(), context.getTemplateResolutionAttributes(), context);
+
+        /*
+         * Create the handler chain to process the data
+         */
+        final ITemplateHandler processingHandlerChain = 
createTemplateProcessingHandlerChain(engineContext, writer);
+
+        /*
+         *  Process the template
+         */
+        template.getInternalModel().process(processingHandlerChain);
+
+
+        /*
+         * Dispose the engine context now that processing has been done
+         */
+        EngineContextManager.disposeEngineContext(engineContext);
+
+    }
+
+
+
+
+
+
+    /*
+     * -------------------------
+     * PARSE-AND-PROCESS methods
+     * -------------------------
+     *
+     * These methods perform the whole cycle of a template's processing: 
resolving, parsing and processing.
+     * This is only meant to be called from the TemplateEngine
+     */
+
+
+    public void parseAndProcess(
+            final TemplateSpec templateSpec,
+            final IContext context,
+            final Writer writer) {
+
+        Validate.notNull(templateSpec, "Template Specification cannot be 
null");
+        Validate.notNull(context, "Context cannot be null");
+        Validate.notNull(writer, "Writer cannot be null");
+
+
+        // TemplateSpec will already have validated its contents, so need to 
do it here (template selectors,
+        // resolution attributes, etc.)
+
+        final String template = templateSpec.getTemplate();
+        final Set<String> templateSelectors = 
templateSpec.getTemplateSelectors();
+        final TemplateMode templateMode = templateSpec.getTemplateMode();
+        final Map<String, Object> templateResolutionAttributes = 
templateSpec.getTemplateResolutionAttributes();
+
+        final TemplateCacheKey cacheKey =
+                    new TemplateCacheKey(
+                            null, // ownerTemplate
+                            template, templateSelectors,
+                            0, 0, // lineOffset, colOffset
+                            templateMode,
+                            templateResolutionAttributes);
+
+
+        /*
+         * First look at the cache - it might be already cached
+         */
+        if (this.templateCache != null) {
+
+            final TemplateModel cached =  this.templateCache.get(cacheKey);
+
+            if (cached != null) {
+
+                final IEngineContext engineContext =
+                        
EngineContextManager.prepareEngineContext(this.configuration, 
cached.getTemplateData(), templateResolutionAttributes, context);
+
+                final ITemplateHandler processingHandlerChain = 
createTemplateProcessingHandlerChain(engineContext, writer);
+
+                cached.getInternalModel().process(processingHandlerChain);
+
+                EngineContextManager.disposeEngineContext(engineContext);
+
+                return;
+
+            }
+
+        }
+
+
+        /*
+         * Resolve the template
+         */
+        final TemplateResolution templateResolution =
+                resolveTemplate(this.configuration, context, null, template, 
templateResolutionAttributes);
+
+
+        /*
+         * Build the TemplateData object
+         */
+        final TemplateData templateData =
+                buildTemplateData(templateResolution, template, 
templateSelectors, templateMode, true);
+
+
+        /*
+         * Prepare the context instance that corresponds to this execution of 
the template engine
+         */
+        final IEngineContext engineContext =
+                EngineContextManager.prepareEngineContext(this.configuration, 
templateData, templateResolutionAttributes, context);
+
+
+        /*
+         * Create the handler chain to process the data
+         */
+        final ITemplateHandler processingHandlerChain = 
createTemplateProcessingHandlerChain(engineContext, writer);
+
+
+        /*
+         * Obtain the parser
+         */
+        final ITemplateParser parser = 
getParserForTemplateMode(engineContext.getTemplateMode());
+
+
+        /*
+         * If the resolved template is cacheable, so we will first read it as 
an object, cache it, and then process it
+         */
+        if (templateResolution.getValidity().isCacheable() && 
this.templateCache != null) {
+
+            // Build the TemplateModel
+            final TemplateModel templateModel = new 
TemplateModel(this.configuration, templateData);
+
+            // Create the handler chain to create the Template object
+            final ModelBuilderTemplateHandler builderHandler = new 
ModelBuilderTemplateHandler(templateModel.getInternalModel());
+
+            // Process the cached template itself
+            parser.parseStandalone(
+                    this.configuration,
+                    null, template, templateSelectors, 
templateData.getTemplateResource(),
+                    engineContext.getTemplateMode(), builderHandler);
+
+            // Put the new template into cache
+            this.templateCache.put(cacheKey, templateModel);
+
+            // Process the read (+cached) template itself
+            templateModel.getInternalModel().process(processingHandlerChain);
+
+        } else {
+
+            //  Process the template, which is not cacheable (so no worry 
about caching)
+            parser.parseStandalone(
+                    this.configuration,
+                    null, template, templateSelectors, 
templateData.getTemplateResource(),
+                    engineContext.getTemplateMode(), processingHandlerChain);
+
+        }
+
+
+        /*
+         * Dispose the engine context now that processing has been done
+         */
+        EngineContextManager.disposeEngineContext(engineContext);
+
+
+    }
+
+
+
+
+
+
+    private static TemplateResolution resolveTemplate(
+            final IEngineConfiguration configuration,
+            final IContext context,
+            final String ownerTemplate,
+            final String template,
+            final Map<String, Object> templateResolutionAttributes) {
+
+        // 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,
+        // not the template resolvers, and allowing the resolver to take any 
decisions based on markup selectors
+        // (like e.g. omitting some output from the resource) could harm the 
correctness of the selection operation
+        // performed by the parser.
+
+        for (final ITemplateResolver templateResolver : 
configuration.getTemplateResolvers()) {
+
+            final TemplateResolution templateResolution =
+                    templateResolver.resolveTemplate(configuration, context, 
ownerTemplate, template, templateResolutionAttributes);
+            if (templateResolution != null) {
+                if (logger.isTraceEnabled()) {
+                    logger.trace(
+                            "[THYMELEAF][{}] Template resolver match! Resolver 
\"{}\" will resolve template \"{}\"",
+                            new Object[] {TemplateEngine.threadIndex(), 
templateResolver.getName(), LoggingUtils.loggifyTemplateName(template)});
+                }
+                return templateResolution;
+            }
+
+            if (logger.isTraceEnabled()) {
+                    logger.trace(
+                            "[THYMELEAF][{}] Skipping template resolver \"{}\" 
for template \"{}\"",
+                            new Object[] {TemplateEngine.threadIndex(), 
templateResolver.getName(), LoggingUtils.loggifyTemplateName(template)});
+            }
+
+        }
+
+        throw new TemplateInputException(
+                "Error resolving template \"" + 
LoggingUtils.loggifyTemplateName(template) + "\", " +
+                "template might not exist or might not be accessible by " +
+                "any of the configured Template Resolvers");
+
+    }
+
+
+
+
+    private static TemplateData buildTemplateData(
+            final TemplateResolution templateResolution,
+            final String template,
+            final Set<String> templateSelectors,
+            final TemplateMode templateMode,
+            final boolean useCache) {
+
+        final TemplateMode definitiveTemplateMode =
+                (templateMode == null ? templateResolution.getTemplateMode() : 
templateMode);
+
+        final ICacheEntryValidity definitiveCacheEntryValidity =
+                (useCache? templateResolution.getValidity() : 
NonCacheableCacheEntryValidity.INSTANCE);
+
+        return new TemplateData(
+                template, templateSelectors, 
templateResolution.getTemplateResource(), definitiveTemplateMode, 
definitiveCacheEntryValidity);
+
+
+    }
+
+
+
+
+    private ITemplateParser getParserForTemplateMode(final TemplateMode 
templateMode) {
+        switch (templateMode) {
+            case HTML:       return this.htmlParser;
+            case XML:        return this.xmlParser;
+            case TEXT:       return this.textParser;
+            case JAVASCRIPT: return this.javascriptParser;
+            case CSS:        return this.cssParser;
+            case RAW:        return this.rawParser;
+            default:
+                throw new IllegalArgumentException("No parser exists for 
template mode: " + templateMode);
+        }
+    }
+
+
+
+
+
+    private static ITemplateHandler createTemplateProcessingHandlerChain(
+            final IEngineContext context,
+            final Writer writer) {
+
+        final IEngineConfiguration configuration = context.getConfiguration();
+
+        /*
+         * Declare the pair of pointers that will allow us to build the chain 
of template handlers
+         */
+        ITemplateHandler firstHandler = null;
+        ITemplateHandler lastHandler = null;
+
+        /*
+         * 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;
+                }
+            }
+        }
+
+
+        /*
+         * Initialize and add to the chain te Processor Handler itself, the 
central piece of the chain
+         */
+        final ProcessorTemplateHandler processorHandler = new 
ProcessorTemplateHandler();
+        processorHandler.setContext(context);
+        if (firstHandler == null) {
+            firstHandler = processorHandler;
+            lastHandler = processorHandler;
+        } else {
+            lastHandler.setNext(processorHandler);
+            lastHandler = processorHandler;
+        }
+
+
+        /*
+         * 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;
+                }
+            }
+        }
+
+
+        /*
+         * Last step: the OUTPUT HANDLER
+         */
+        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
new file mode 100755
index 0000000..f4805e6
--- /dev/null
+++ b/src/main/java/org/thymeleaf/templateresolver/AbstractTemplateResolver.java
@@ -0,0 +1,371 @@
+/*
+ * 
=============================================================================
+ * 
+ *   Copyright (c) 2011-2014, The THYMELEAF team (http://www.thymeleaf.org)
+ * 
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ * 
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ * 
+ * 
=============================================================================
+ */
+package org.thymeleaf.templateresolver;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.thymeleaf.IEngineConfiguration;
+import org.thymeleaf.cache.ICacheEntryValidity;
+import org.thymeleaf.context.IContext;
+import org.thymeleaf.templatemode.TemplateMode;
+import org.thymeleaf.templateresource.ITemplateResource;
+import org.thymeleaf.util.PatternSpec;
+import org.thymeleaf.util.Validate;
+
+
+/**
+ * <p>
+ *   Convenience base class for all Template Resolvers.
+ * </p>
+ * <p>
+ *   Note a class with this name existed since 1.0, but it was completely 
reimplemented
+ *   in Thymeleaf 3.0
+ * </p>
+ *
+ * @author Daniel Fern&aacute;ndez
+ * 
+ * @since 3.0.0
+ *
+ */
+public abstract class AbstractTemplateResolver implements ITemplateResolver {
+
+    /**
+     * <p>
+     *   By default, resources will not be checked their existence before 
being returned. This tries to
+     *   avoid a possible performance impact from performing a double access 
to the resource (one for checking
+     *   existence, another one for reading it).
+     * </p>
+     */
+    public static final boolean DEFAULT_EXISTENCE_CHECK = false;
+
+    
+    private String name = this.getClass().getName();
+    private Integer order = null;
+    private boolean checkExistence = DEFAULT_EXISTENCE_CHECK;
+
+    private final PatternSpec resolvablePatternSpec = new PatternSpec();
+    
+
+
+
+    protected AbstractTemplateResolver() {
+        super();
+    }
+
+
+
+    /**
+     * <p>
+     *   Returns the name of the template resolver
+     * </p>
+     * 
+     * @return the name of the template resolver
+     */
+    public final String getName() {
+        return this.name;
+    }
+
+
+    /**
+     * <p>
+     *   Sets a new name for the Template Resolver.
+     * </p>
+     * 
+     * @param name the new name
+     */
+    public final void setName(final String name) {
+        this.name = name;
+    }
+    
+    
+    /**
+     * <p>
+     *   Returns the order in which this template resolver will be asked to 
resolve
+     *   templates as a part of the chain of resolvers configured into the 
template engine.
+     * </p>
+     * <p>
+     *   Order should start with 1.
+     * </p>
+     * 
+     * @return the order in which this template resolver will be called in the 
chain.
+     */
+    public final Integer getOrder() {
+        return this.order;
+    }
+
+
+    /**
+     * <p>
+     *   Sets a new order for the template engine in the chain. Order should 
start with 1.
+     * </p>
+     * 
+     * @param order the new order.
+     */
+    public final void setOrder(final Integer order) {
+        this.order = order;
+    }
+    
+    
+
+    
+    /**
+     * <p>
+     *   Returns the <i>pattern spec</i> specified for establishing which
+     *   templates can be resolved by this template resolver. For those 
templates
+     *   which names do not match this patterns, the Template Resolver will 
return null.
+     * </p>
+     * <p>
+     *   This allows for a fast discard of those templates that the developer 
might
+     *   know for sure that will not be resolvable by the Resource Resolver 
used by
+     *   this Template Resolver, so that an execution of the resource resolver 
is not needed.
+     * </p>
+     * 
+     * @return the pattern spec
+     */
+    public final PatternSpec getResolvablePatternSpec() {
+        return this.resolvablePatternSpec;
+    }
+    
+    /**
+     * <p>
+     *   Returns the <i>patterns</i> (as String) specified for establishing 
which
+     *   templates can be resolved by this template resolver. For those 
templates
+     *   which names do not match this patterns, the Template Resolver will 
return null.
+     * </p>
+     * <p>
+     *   This allows for a fast discard of those templates that the developer 
might
+     *   know for sure that will not be resolvable by the Resource Resolver 
used by
+     *   this Template Resolver, so that an execution of the resource resolver 
is not needed.
+     * </p>
+     * <p>
+     *   This is a convenience method equivalent to {@link 
#getResolvablePatternSpec()}.getPatterns()
+     * </p>
+     * 
+     * @return the pattern spec
+     */
+    public final Set<String> getResolvablePatterns() {
+        return this.resolvablePatternSpec.getPatterns();
+    }
+
+    /**
+     * <p>
+     *   Sets the new <i>patterns</i> to be applied for establishing which
+     *   templates can be resolved by this template resolver. For those 
templates
+     *   which names do not match this patterns, the Template Resolver will 
return null.
+     * </p>
+     * <p>
+     *   This allows for a fast discard of those templates that the developer 
might
+     *   know for sure that will not be resolvable by the Resource Resolver 
used by
+     *   this Template Resolver, so that an execution of the resource resolver 
is not needed.
+     * </p>
+     * <p>
+     *   This is a convenience method equivalent to {@link 
#getResolvablePatternSpec()}.setPatterns(Set&lt;String&gt;)
+     * </p>
+     * 
+     * @param resolvablePatterns the new patterns
+     */
+    public final void setResolvablePatterns(final Set<String> 
resolvablePatterns) {
+        this.resolvablePatternSpec.setPatterns(resolvablePatterns);
+    }
+
+
+
+    /**
+     * <p>
+     *   Returns whether template resources will be checked for existence 
before being returned or not.
+     * </p>
+     * <p>
+     *   Default value is <tt>FALSE</tt>.
+     * </p>
+     * <p>
+     *   Checking resources for existence will make the template resolver 
execute {@link ITemplateResource#exists()}
+     *   for each resolved resource before returning a {@link 
TemplateResolution}, returning <tt>null</tt> if the
+     *   resource does not exist.
+     * </p>
+     * <p>
+     *   This allows resolvers to pass control to the next {@link 
ITemplateResolver} in
+     *   the chain based on real resource existence and not only on the 
matching performed by the <em>resolvable
+     *   patterns</em> specified at {@link #getResolvablePatterns()}. But at 
the same time, might pose a performance
+     *   issue on certain scenarios (e.g. HTTP URL resolution) that require 
actually accessing the resource in order
+     *   to determine its existence, being the resource accessed twice in 
those cases (once for determining its
+     *   existence, another time for reading it).
+     * </p>
+     * <p>
+     *   If this <em>existence check</em> is enabled and a resource is 
determined to not exist,
+     *   {@link ITemplateResolver#resolveTemplate(IEngineConfiguration, 
String, String, Map)} will return <tt>null</tt>.
+     * </p>
+     *
+     * @return <tt>true</tt> if resource existence will be checked, 
<tt>false</tt> if not
+     *
+     * @since 3.0.0
+     *
+     */
+    public final boolean getCheckExistence() {
+        return this.checkExistence;
+    }
+
+
+    /**
+     * <p>
+     *   Sets whether template resources will be checked for existence before 
being returned or not.
+     * </p>
+     * <p>
+     *   Default value is <tt>FALSE</tt>.
+     * </p>
+     * <p>
+     *   Checking resources for existence will make the template resolver 
execute {@link ITemplateResource#exists()}
+     *   for each resolved resource before returning a {@link 
TemplateResolution}, returning <tt>null</tt> if the
+     *   resource does not exist.
+     * </p>
+     * <p>
+     *   This allows resolvers to pass control to the next {@link 
ITemplateResolver} in
+     *   the chain based on real resource existence and not only on the 
matching performed by the <em>resolvable
+     *   patterns</em> specified at {@link #getResolvablePatterns()}. But at 
the same time, might pose a performance
+     *   issue on certain scenarios (e.g. HTTP URL resolution) that require 
actually accessing the resource in order
+     *   to determine its existence, being the resource accessed twice in 
those cases (once for determining its
+     *   existence, another time for reading it).
+     * </p>
+     * <p>
+     *   If this <em>existence check</em> is enabled and a resource is 
determined to not exist,
+     *   {@link ITemplateResolver#resolveTemplate(IEngineConfiguration, 
String, String, Map)} will return <tt>null</tt>.
+     * </p>
+     *
+     * @param checkExistence <tt>true</tt> if resource existence should be 
checked, <tt>false</tt> if not
+     *
+     * @since 3.0.0
+     *
+     */
+    public final void setCheckExistence(final boolean checkExistence) {
+        this.checkExistence = checkExistence;
+    }
+
+
+    public final TemplateResolution resolveTemplate(
+        final IEngineConfiguration configuration,
+        final IContext context,
+        final String ownerTemplate, final String template,
+        final Map<String, Object> templateResolutionAttributes) {
+
+        Validate.notNull(configuration, "Engine Configuration cannot be null");
+        // ownerTemplate CAN be null
+        Validate.notNull(template, "Template Name cannot be null");
+        // templateResolutionAttributes CAN be null
+
+        if (!computeResolvable(configuration, ownerTemplate, template, 
templateResolutionAttributes)) {
+            return null;
+        }
+
+        final ITemplateResource templateResource = 
computeTemplateResource(configuration, ownerTemplate, template, 
templateResolutionAttributes);
+        if (templateResource == null) {
+            return null;
+        }
+
+        if (this.checkExistence && !templateResource.exists()) { // will only 
check if flag set to true
+            return null;
+        }
+
+        return new TemplateResolution(
+                templateResource,
+                computeTemplateMode(configuration, ownerTemplate, template, 
templateResolutionAttributes),
+                computeValidity(configuration, ownerTemplate, template, 
templateResolutionAttributes));
+        
+    }
+    
+    
+    
+    
+    /**
+     * <p>
+     *   Computes whether a template can be resolved by this resolver or not, 
+     *   applying the corresponding patterns. Meant only for use or override 
by subclasses.
+     * </p>
+     *
+     * @param configuration the engine configuration.
+     * @param ownerTemplate the owner template, if the resource being computed 
is a fragment. Might be null.
+     * @param template the template to be resolved (usually its name).
+     * @param templateResolutionAttributes the template resolution attributes, 
if any. Might be null.
+     *
+     * @return whether the template is resolvable or not.
+     */
+    protected boolean computeResolvable(final IEngineConfiguration 
configuration, final String ownerTemplate, final String template, final 
Map<String, Object> templateResolutionAttributes) {
+        if (this.resolvablePatternSpec.isEmpty()) {
+            return true;
+        }
+        return this.resolvablePatternSpec.matches(template);
+    }
+
+
+
+
+    
+    
+    /**
+     * <p>
+     *   Computes the resolved template resource.
+     * </p>
+     *
+     * @param configuration the engine configuration.
+     * @param ownerTemplate the owner template, if the resource being computed 
is a fragment. Might be null.
+     * @param template the template to be resolved (usually its name).
+     * @param templateResolutionAttributes the template resolution attributes, 
if any. Might be null.
+     *
+     * @return the template resource, or null if this template cannot be 
resolved (or the resource does not exist).
+     */
+    protected abstract ITemplateResource computeTemplateResource(final 
IEngineConfiguration configuration, final String ownerTemplate, final String 
template, final Map<String, Object> templateResolutionAttributes);
+
+    
+    
+    /**
+     * <p>
+     *   Computes the template mode that should be applied to a template, 
according
+     *   to existing configuration.
+     * </p>
+     *
+     * @param configuration the engine configuration.
+     * @param ownerTemplate the owner template, if the resource being computed 
is a fragment. Might be null.
+     * @param template the template to be resolved (usually its name).
+     * @param templateResolutionAttributes the template resolution attributes, 
if any. Might be null.
+     *
+     * @return the template mode proposed by the template resolver for the 
resolved template.
+     */
+    protected abstract TemplateMode computeTemplateMode(final 
IEngineConfiguration configuration, final String ownerTemplate, final String 
template, final Map<String, Object> templateResolutionAttributes);
+    
+    
+    
+    /**
+     * <p>
+     *   Computes the validity to be applied to the template resolution. This
+     *   includes determining whether the template can be cached or not, and
+     *   also in what circumstances (for instance, for how much time) can
+     *   its cache entry be considered valid.
+     * </p>
+     *
+     * @param configuration the engine configuration.
+     * @param ownerTemplate the owner template, if the resource being computed 
is a fragment. Might be null.
+     * @param template the template to be resolved (usually its name).
+     * @param templateResolutionAttributes the template resolution attributes, 
if any. Might be null.
+     * @return the validity
+     */
+    protected abstract ICacheEntryValidity computeValidity(final 
IEngineConfiguration configuration, final String ownerTemplate, final String 
template, final Map<String, Object> templateResolutionAttributes);
+    
+    
+    
+}
diff --git 
a/src/main/java/org/thymeleaf/templateresolver/ITemplateResolver.java 
b/src/main/java/org/thymeleaf/templateresolver/ITemplateResolver.java
new file mode 100755
index 0000000..935b97f
--- /dev/null
+++ b/src/main/java/org/thymeleaf/templateresolver/ITemplateResolver.java
@@ -0,0 +1,156 @@
+/*
+ * 
=============================================================================
+ * 
+ *   Copyright (c) 2011-2014, The THYMELEAF team (http://www.thymeleaf.org)
+ * 
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ * 
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ * 
+ * 
=============================================================================
+ */
+package org.thymeleaf.templateresolver;
+
+import java.util.Map;
+
+import org.thymeleaf.IEngineConfiguration;
+import org.thymeleaf.context.IContext;
+import org.thymeleaf.templatemode.TemplateMode;
+import org.thymeleaf.templateresource.ITemplateResource;
+
+/**
+ * <p>
+ *   Interface for all Template Resolvers.
+ * </p>
+ * <p>
+ *   Template resolvers are in charge of resolving templates into
+ *   {@link TemplateResolution} objects that contain additional information 
related to
+ *   the template like:
+ * </p>
+ * <ul>
+ *   <li>Its corresponding <i>template resource</i> (see
+ *       {@link org.thymeleaf.templateresource.ITemplateResource}).</li>
+ *   <li>The Template Mode to be applied to this template: {@link 
TemplateMode}</li>
+ *   <li>Whether the template can be cached or not.</li>
+ *   <li>If the template can be cached, (optionally) the time it will live in 
cache.</li>
+ * </ul>
+ * <p>
+ *   The Template Resolver will usually get all this information from a set of 
configurations
+ *   like applicability patterns, template mode patterns, etc. Each {@link 
ITemplateResolver}
+ *   implementation will provide its own set of methods for specifying such 
configurations. 
+ * </p>
+ * <p>
+ *   The fact that a Template Resolver returns a {@link TemplateResolution} 
does not necessarily
+ *   mean that the resolved template resource exists. It might only be so if 
the template resolver
+ *   is configured to perform an <em>existence check</em> on the resource 
before returning a resolution
+ *   result (by means of calling {@link ITemplateResource#exists()}), which 
might be configurable on
+ *   a per-{@link ITemplateResolver}-implementation basis. Implementations 
might choose not to check
+ *   resource existance by default in order to avoid the possible performance 
impact of a double access
+ *   to the resource.
+ * </p>
+ * <p>
+ *   A Template Engine can be configured several template resolvers, and these 
will
+ *   be asked in order (according to the value returned by {@link 
#getOrder()}) to return
+ *   a {@link TemplateResolution} object for each template name. If a template 
resolver
+ *   returns null for a specific resolution, the next one in the chain is 
asked. Template Resolvers
+ *   that are not configured an order will be executed last in the chain.
+ * </p>
+ * <p>
+ *   Note a class with this name existed since 1.0, but it was completely 
reimplemented
+ *   in Thymeleaf 3.0
+ * </p>
+ * 
+ * @author Daniel Fern&aacute;ndez
+ *
+ * @see ITemplateResource
+ * @see ClassLoaderTemplateResolver
+ * @see FileTemplateResolver
+ * @see ServletContextTemplateResolver
+ * @see StringTemplateResolver
+ * @see UrlTemplateResolver
+ *
+ * @since 3.0.0
+ *
+ */
+public interface ITemplateResolver {
+
+    
+    /**
+     * <p>
+     *   Returns the name of this template resolver. Used in logs and 
configuration
+     *   details.
+     * </p>
+     * 
+     * @return the template resolver name.
+     */
+    public String getName();
+
+    
+    /**
+     * <p>
+     *   Return the order in which this template resolver will be executed in 
the
+     *   chain when several template resolvers are set for the same Template 
Engine.
+     * </p>
+     * 
+     * @return the order of this resolver in the chain.
+     */
+    public Integer getOrder();
+
+
+    /**
+     * <p>
+     *   Tries to resolve a template.
+     * </p>
+     * <p>
+     *   The method arguments contain all the info needed for trying to
+     *   resolve the template. The Template Resolver will apply its 
configuration
+     *   (prefixes/suffixes, template mode patterns, cache configurations, 
etc) and
+     *   return a {@link TemplateResolution} object.
+     * </p>
+     * <p>
+     *   The <tt>ownerTemplate</tt>, which might be null, will be specified 
when the template
+     *   is resolved in order to be used as a fragent to be inserted into a 
higher level
+     *   template (the <em>owner</em>). Most template resolver implementations 
will simply ignore
+     *   this argument, but others might change their resolution results 
depending on the
+     *   owner template that is inserting the resolved fragment.
+     * </p>
+     * <p>
+     *   The fact that a Template Resolver returns a {@link 
TemplateResolution} does not necessarily
+     *   mean that the resolved template resource exists. It might only be so 
if the template resolver
+     *   is configured to perform an <em>existence check</em> on the resource 
before returning a resolution
+     *   result (by means of calling {@link ITemplateResource#exists()}), 
which might be configurable on
+     *   a per-{@link ITemplateResolver}-implementation basis. Implementations 
might choose not to check
+     *   resource existance by default in order to avoid the possible 
performance impact of a double access
+     *   to the resource.
+     * </p>
+     * <p>
+     *   Note that the <em>template selectors</em> that might be used for a 
executing or inserting a template
+     *   are not specified to the template resolver. The reason is template 
selectors are applied by the parser,
+     *   not the template resolvers, and allowing the resolver to take any 
decisions based on template selectors
+     *   (like e.g. omitting some output from the resource) could harm the 
correctness of the selection operation
+     *   performed by the parser.
+     * </p>
+     * 
+     * @param configuration the engine configuration.
+     * @param ownerTemplate the containing template from which we want to 
resolve a new one as a fragment. Can be null.
+     * @param template the template to be resolved (usually its name).
+     * @param templateResolutionAttributes the template resolution attributes 
to be used (usually coming from a
+     *                                     {@link org.thymeleaf.TemplateSpec} 
instance. Can be null.
+     * @return a TemplateResolution object (which might represent an existing 
resource or not), or null if the
+     *         template could not be resolved.
+     */
+    public TemplateResolution resolveTemplate(
+        final IEngineConfiguration configuration,
+        final IContext context,
+        final String ownerTemplate, final String template,
+        final Map<String, Object> templateResolutionAttributes);
+
+}

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

Reply via email to