org.thymeleaf.Arguments Maven / Gradle / Ivy
/*
* =============================================================================
*
* Copyright (c) 2011-2012, 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;
import java.util.HashMap;
import java.util.Map;
import org.thymeleaf.context.IContext;
import org.thymeleaf.context.VariablesMap;
import org.thymeleaf.dom.Document;
import org.thymeleaf.exceptions.TemplateProcessingException;
import org.thymeleaf.expression.ExpressionEvaluatorObjects;
import org.thymeleaf.templateresolver.TemplateResolution;
import org.thymeleaf.util.Validate;
/**
*
* Objects of this class contain all the required arguments for template
* processing.
*
*
* These objects are created internally by the Template Engine in order
* to provide processors with all the information
* they might need for performing their tasks.
*
*
* Arguments include many attributes, among which some should be used internally
* only, and never by developers of attribute/element processors. Public
* attributes are:
*
*
* - The Template Engine configuration ({@link Configuration}): {@link #getConfiguration()}
* - The template name: {@link #getTemplateName()}
* - The Context ({@link IContext}): {@link #getContext()}
* - The current map of ID Counts (used for adding a unique index to repeated id attributes): {@link #getIdCounts()}
* - The Expression roots:
*
* - For normal (non selected) evaluation (${...} in standard dialect): {@link #getExpressionEvaluationRoot()}
* - For selected evaluation (*{...} in standard dialect): {@link #getExpressionSelectionEvaluationRoot()}
*
*
* - Information about whether processing non-element nodes is allowed at a specific point of a template
* execution or not (default is to process only elements).
*
*
* @author Daniel Fernández
*
* @since 1.0
*
*/
public final class Arguments {
public static final String SELECTION_TARGET_LOCAL_VARIABLE_NAME = "%%{SELECTION_TARGET}%%";
private final TemplateProcessingParameters templateProcessingParameters;
private final Configuration configuration;
private final Document document;
private final TemplateResolution templateResolution;
private final TemplateRepository templateRepository;
private final IContext context;
private final HashMap localVariables;
private final Map idCounts;
private final Object evaluationRoot;
private final Object selectionEvaluationRoot;
private final boolean processOnlyElementNodes;
private final Map baseContextVariables;
/**
*
* Create a new Arguments instance.
*
*
* Mainly for internal use. Should not be called directly except
* when processing a template (e.g. a fragment) using the {@link TemplateEngine}
* from a element/attribute processor.
*
*
* @param templateProcessingParameters the template processing parameters
* @param templateResolution the template resolution object
* @param templateRepository the template repository in use
* @param templateParser the template parser
* @param context the context
*/
Arguments(
final TemplateProcessingParameters templateProcessingParameters,
final TemplateResolution templateResolution,
final TemplateRepository templateRepository,
final Document document) {
super();
Validate.notNull(templateProcessingParameters, "Template processing parameters cannot be null");
Validate.notNull(templateResolution, "Template resolution cannot be null");
Validate.notNull(templateRepository, "Template repository cannot be null");
Validate.notNull(document, "Document cannot be null");
this.templateProcessingParameters = templateProcessingParameters;
this.configuration = this.templateProcessingParameters.getConfiguration();
this.templateResolution = templateResolution;
this.templateRepository = templateRepository;
this.document = document;
this.context = this.templateProcessingParameters.getContext();
this.localVariables = null;
this.idCounts = new HashMap();
this.evaluationRoot = createEvaluationRoot();
this.selectionEvaluationRoot = createSelectedEvaluationRoot();
this.processOnlyElementNodes = true;
this.baseContextVariables =
ExpressionEvaluatorObjects.computeEvaluationVariablesForArguments(this);
}
private Arguments(
final TemplateProcessingParameters templateProcessingParameters,
final TemplateResolution templateResolution,
final TemplateRepository templateRepository,
final Document document,
final HashMap localVariables,
final Map idCounts,
final boolean processOnlyElementNodes) {
super();
this.templateProcessingParameters = templateProcessingParameters;
this.configuration = this.templateProcessingParameters.getConfiguration();
this.templateResolution = templateResolution;
this.templateRepository = templateRepository;
this.document = document;
this.context = this.templateProcessingParameters.getContext();
this.localVariables = localVariables;
this.idCounts = idCounts;
this.evaluationRoot = createEvaluationRoot();
this.selectionEvaluationRoot = createSelectedEvaluationRoot();
this.processOnlyElementNodes = processOnlyElementNodes;
this.baseContextVariables =
ExpressionEvaluatorObjects.computeEvaluationVariablesForArguments(this);
}
private Object createEvaluationRoot() {
final Map newEvaluationRoot = new VariablesMap();
newEvaluationRoot.putAll(this.context.getVariables());
if (hasLocalVariables()) {
newEvaluationRoot.putAll(this.localVariables);
}
return newEvaluationRoot;
}
private Object createSelectedEvaluationRoot() {
if (hasSelectionTarget()) {
return getSelectionTarget();
}
return this.evaluationRoot;
}
/**
*
* Returns the Template Processing Parameters used for resolving
* and parsing the template being processed.
*
*
* @return the template processing parameters
*/
public TemplateProcessingParameters getTemplateProcessingParameters() {
return this.templateProcessingParameters;
}
/**
*
* Returns the Template Engine configuration being used for
* processing templates.
*
*
* @return the configuration
*/
public Configuration getConfiguration() {
return this.configuration;
}
/**
*
* Returns the template resolution object corresponding to the
* template currently being processed.
*
*
* @return the Template Resolution object
*/
public TemplateResolution getTemplateResolution() {
return this.templateResolution;
}
/**
*
* Returns the template repository in use.
*
*
* @return the Template Repository object
*/
public TemplateRepository getTemplateRepository() {
return this.templateRepository;
}
/**
*
* Returns the parsed Document DOM object.
*
*
* @return the Document object
*/
public Document getDocument() {
return this.document;
}
/**
*
* Returns the name of the template currently being processed.
*
*
* @return the template name
*/
public String getTemplateName() {
return this.templateResolution.getTemplateName();
}
/**
*
* Returns the current context specified for template processing.
*
*
* @return the current context
*/
public IContext getContext() {
return this.context;
}
/**
*
* Returns whether local variables have currently been specified or not.
* (e.g. th:with in standard dialect).
*
*
* @return true if there are local variables, false if not
*/
public boolean hasLocalVariables() {
return this.localVariables != null && this.localVariables.size() > 0;
}
/**
*
* Returns the value of a local variable.
*
*
* @param variableName the name of the local variable to be returned
* @return the value of the variable, or null if the variable does not exist (or has null value)
*
*/
public Object getLocalVariable(final String variableName) {
if (this.localVariables == null) {
return null;
}
return this.localVariables.get(variableName);
}
/**
*
* Returns whether a specific local variable is defined or not.
*
*
* @return true if the variable is currently defined, false if not.
*/
public boolean hasLocalVariable(final String variableName) {
if (this.localVariables == null) {
return false;
}
return this.localVariables.containsKey(variableName);
}
/**
*
* Returns the real inner map of local variables. This
* method should not be called directly.
*
*
* @return the local variables map, which could be null if no variables are defined
*/
public HashMap unsafeGetLocalVariables() {
return this.localVariables;
}
/**
*
* Returns a safe copy of the map of local variables.
*
*
* @return the local variables
*/
public Map getLocalVariables() {
final HashMap vars = new HashMap();
if (this.localVariables != null) {
vars.putAll(this.localVariables);
}
return vars;
}
/**
*
* Returns whether only element nodes should be processed (as opposed
* to texts, CDATAs, comments, etc.). Default is true.
*
*
* @return whether only element nodes will be processed
*/
public boolean getProcessOnlyElementNodes() {
return this.processOnlyElementNodes;
}
/**
*
* Returns whether there currently is a selection going on
* (e.g. th:object in standard dialect).
*
*
* @return true if there is a selection currently established, false if not
*/
public boolean hasSelectionTarget() {
return hasLocalVariable(Arguments.SELECTION_TARGET_LOCAL_VARIABLE_NAME);
}
/**
*
* Returns the selection target object, and raises an
* exception if there isn't any.
*
*
* Meant for internal use.
*
*
* @return the selection target object
*/
public Object getSelectionTarget() {
if (hasSelectionTarget()) {
return getLocalVariable(Arguments.SELECTION_TARGET_LOCAL_VARIABLE_NAME);
}
throw new IllegalStateException(
"Cannot return selection target object, a selection target has not been set.");
}
/**
*
* Returns the map of ID counts.
*
*
* These numbers are used to avoid conflicts among elements with the same id
* attribute; for example elements being repeated as a part of a th:each iteration.
*
*
* @return the current map of ID counts
*/
public Map getIdCounts() {
return this.idCounts;
}
/**
*
* Returns the execution attributes.
*
*
* @return the current map of execution attributes
*/
public Map getExecutionAttributes() {
return this.configuration.getExecutionAttributes();
}
/**
*
* Returns the execution attribute with the specified name.
*
*
* @param attributeName the name of the attribute to be retrieved
* @return the value of the attribute
*/
public Object getExecutionAttribute(final String attributeName) {
return this.configuration.getExecutionAttributes().get(attributeName);
}
/**
*
* Returns the current evaluation root. This is the object on which expressions
* (normal expressions, like those specified in the standard dialect with
* ${...}) are executed.
*
*
* @return the expression evaluation root
*/
public Object getExpressionEvaluationRoot() {
return this.evaluationRoot;
}
/**
*
* Returns the current selection evaluation root. This is the object on which selection expressions
* (like those specified in the standard dialect with *{...}) are executed.
*
*
* @return the selection evaluation root
*/
public Object getExpressionSelectionEvaluationRoot() {
return this.selectionEvaluationRoot;
}
/**
*
* Returns the map of base variables that should be made available to every expression
* evaluation operation (whenever variable evaluation is available).
*
*
* @since 2.0.0
* @return the map of variables (a new object, mutable, safe to use as a context variables base)
*/
public Map getBaseContextVariables() {
final Map variables = new HashMap();
variables.putAll(this.baseContextVariables);
return variables;
}
/**
*
* Returns a new index (ID count) for a specific
* value of the id attribute, and increments
* the count.
*
*
* @param id the ID for which the count will be computed
* @return the new count, ready to be used
*/
public Integer getAndIncrementIDSeq(final String id) {
Validate.notNull(id, "ID cannot be null");
Integer count = this.idCounts.get(id);
if (count == null) {
count = Integer.valueOf(1);
}
this.idCounts.put(id, Integer.valueOf(count.intValue() + 1));
return count;
}
/**
*
* Returns the index (ID count) for a specific
* value of the id attribute without incrementing
* the count.
*
*
* @param id the ID for which the count will be retrieved
* @return the current count
*/
public Integer getNextIDSeq(final String id) {
Validate.notNull(id, "ID cannot be null");
Integer count = this.idCounts.get(id);
if (count == null) {
count = Integer.valueOf(1);
}
return count;
}
/**
*
* Returns the last index (ID count) returned for a specific
* value of the id attribute (without incrementing
* the count).
*
*
* @param id the ID for which the last count will be retrieved
* @return the count
*/
public Integer getPreviousIDSeq(final String id) {
Validate.notNull(id, "ID cannot be null");
Integer count = this.idCounts.get(id);
if (count == null) {
throw new TemplateProcessingException(
"Cannot obtain previous ID count for ID \"" + id + "\"");
}
return Integer.valueOf(count.intValue() - 1);
}
/**
*
* Creates a new Arguments object by adding some new local variables
* to the existing map (the rest of the attributes are copied verbatim).
*
*
* @param newVariables the new variables
* @return the new Arguments object
*/
public Arguments addLocalVariables(final Map newVariables) {
if (newVariables == null || newVariables.isEmpty()) {
return this;
}
final int localVariablesSize = (this.localVariables != null? this.localVariables.size() : 0);
final HashMap cloneLocalVariables =
new HashMap(localVariablesSize + newVariables.size() + 1, 1.0f);
if (this.localVariables != null) {
cloneLocalVariables.putAll(this.localVariables);
}
cloneLocalVariables.putAll(newVariables);
final Arguments arguments =
new Arguments(this.templateProcessingParameters, this.templateResolution, this.templateRepository, this.document, cloneLocalVariables, this.idCounts, this.processOnlyElementNodes);
return arguments;
}
/**
*
* Creates a new Arguments object by setting a new value for the processOnlyElementNodes flag.
*
*
* @param shouldProcessOnlyElementNodes whether only element nodes should be processed from this moment in template execution
* @return the new Arguments object
*/
public Arguments setProcessOnlyElementNodes(final boolean shouldProcessOnlyElementNodes) {
final Arguments arguments =
new Arguments(this.templateProcessingParameters, this.templateResolution, this.templateRepository, this.document, this.localVariables, this.idCounts, shouldProcessOnlyElementNodes);
return arguments;
}
/**
*
* Creates a new Arguments object by setting new local variables and a new value for the processOnlyElementNodes flag.
*
*
* @param newVariables the new local variables
* @param shouldProcessOnlyElementNodes whether only element nodes should be processed from this moment in template execution
* @return the new Arguments object
*/
public Arguments addLocalVariablesAndProcessOnlyElementNodes(final Map newVariables, final boolean shouldProcessOnlyElementNodes) {
if (newVariables == null || newVariables.isEmpty()) {
return this;
}
final int localVariablesSize = (this.localVariables != null? this.localVariables.size() : 0);
final HashMap cloneLocalVariables =
new HashMap(localVariablesSize + newVariables.size() + 1, 1.0f);
if (this.localVariables != null) {
cloneLocalVariables.putAll(this.localVariables);
}
cloneLocalVariables.putAll(newVariables);
final Arguments arguments =
new Arguments(this.templateProcessingParameters, this.templateResolution, this.templateRepository, this.document, cloneLocalVariables, this.idCounts, shouldProcessOnlyElementNodes);
return arguments;
}
}