use of org.thymeleaf.IEngineConfiguration in project thymeleaf-tests by thymeleaf.
the class DialectProcessWrappingTest method testDialectWrapping.
@Test
public void testDialectWrapping() throws Exception {
final Dialect01 dialect01 = new Dialect01();
final TemplateEngine templateEngine = new TemplateEngine();
templateEngine.setDialect(dialect01);
final IEngineConfiguration config = templateEngine.getConfiguration();
final List<IElementProcessor> elementProcessors = new ArrayList<IElementProcessor>(config.getElementProcessors(TemplateMode.HTML));
final List<ICDATASectionProcessor> cdataSectionProcessors = new ArrayList<ICDATASectionProcessor>(config.getCDATASectionProcessors(TemplateMode.HTML));
final List<ICommentProcessor> commentProcessors = new ArrayList<ICommentProcessor>(config.getCommentProcessors(TemplateMode.HTML));
final List<IDocTypeProcessor> docTypeProcessors = new ArrayList<IDocTypeProcessor>(config.getDocTypeProcessors(TemplateMode.HTML));
final List<IProcessingInstructionProcessor> processingInstructionProcessors = new ArrayList<IProcessingInstructionProcessor>(config.getProcessingInstructionProcessors(TemplateMode.HTML));
final List<ITemplateBoundariesProcessor> templateBoundariesProcessors = new ArrayList<ITemplateBoundariesProcessor>(config.getTemplateBoundariesProcessors(TemplateMode.HTML));
final List<ITextProcessor> textProcessors = new ArrayList<ITextProcessor>(config.getTextProcessors(TemplateMode.HTML));
final List<IXMLDeclarationProcessor> xmlDeclarationProcessors = new ArrayList<IXMLDeclarationProcessor>(config.getXMLDeclarationProcessors(TemplateMode.HTML));
Assert.assertEquals(2, elementProcessors.size());
Assert.assertEquals(1, cdataSectionProcessors.size());
Assert.assertEquals(1, commentProcessors.size());
Assert.assertEquals(1, docTypeProcessors.size());
Assert.assertEquals(1, processingInstructionProcessors.size());
Assert.assertEquals(1, templateBoundariesProcessors.size());
Assert.assertEquals(1, textProcessors.size());
Assert.assertEquals(1, xmlDeclarationProcessors.size());
// We will use the class names because the classes are package-protected
Assert.assertEquals("org.thymeleaf.util.ProcessorConfigurationUtils$ElementModelProcessorWrapper", elementProcessors.get(0).getClass().getName());
Assert.assertEquals("org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper", elementProcessors.get(1).getClass().getName());
Assert.assertEquals("org.thymeleaf.util.ProcessorConfigurationUtils$CDATASectionProcessorWrapper", cdataSectionProcessors.get(0).getClass().getName());
Assert.assertEquals("org.thymeleaf.util.ProcessorConfigurationUtils$CommentProcessorWrapper", commentProcessors.get(0).getClass().getName());
Assert.assertEquals("org.thymeleaf.util.ProcessorConfigurationUtils$DocTypeProcessorWrapper", docTypeProcessors.get(0).getClass().getName());
Assert.assertEquals("org.thymeleaf.util.ProcessorConfigurationUtils$ProcessingInstructionProcessorWrapper", processingInstructionProcessors.get(0).getClass().getName());
Assert.assertEquals("org.thymeleaf.util.ProcessorConfigurationUtils$TemplateBoundariesProcessorWrapper", templateBoundariesProcessors.get(0).getClass().getName());
Assert.assertEquals("org.thymeleaf.util.ProcessorConfigurationUtils$TextProcessorWrapper", textProcessors.get(0).getClass().getName());
Assert.assertEquals("org.thymeleaf.util.ProcessorConfigurationUtils$XMLDeclarationProcessorWrapper", xmlDeclarationProcessors.get(0).getClass().getName());
Assert.assertEquals(100, elementProcessors.get(0).getPrecedence());
Assert.assertEquals(110, elementProcessors.get(1).getPrecedence());
Assert.assertEquals(100, cdataSectionProcessors.get(0).getPrecedence());
Assert.assertEquals(100, commentProcessors.get(0).getPrecedence());
Assert.assertEquals(100, docTypeProcessors.get(0).getPrecedence());
Assert.assertEquals(100, processingInstructionProcessors.get(0).getPrecedence());
Assert.assertEquals(100, templateBoundariesProcessors.get(0).getPrecedence());
Assert.assertEquals(100, textProcessors.get(0).getPrecedence());
Assert.assertEquals(100, xmlDeclarationProcessors.get(0).getPrecedence());
}
use of org.thymeleaf.IEngineConfiguration in project thymeleaf by thymeleaf.
the class EachUtils method parseEach.
public static Each parseEach(final IExpressionContext context, final String input) {
Validate.notNull(context, "Context cannot be null");
Validate.notNull(input, "Input cannot be null");
final String preprocessedInput = StandardExpressionPreprocessor.preprocess(context, input);
final IEngineConfiguration configuration = context.getConfiguration();
if (configuration != null) {
final Each cachedEach = ExpressionCache.getEachFromCache(configuration, preprocessedInput);
if (cachedEach != null) {
return cachedEach;
}
}
final Each each = internalParseEach(preprocessedInput.trim());
if (each == null) {
throw new TemplateProcessingException("Could not parse as each: \"" + input + "\"");
}
if (configuration != null) {
ExpressionCache.putEachIntoCache(configuration, preprocessedInput, each);
}
return each;
}
use of org.thymeleaf.IEngineConfiguration in project thymeleaf by thymeleaf.
the class OGNLVariableExpressionEvaluator method evaluate.
private static Object evaluate(final IExpressionContext context, final IStandardVariableExpression expression, final StandardExpressionExecutionContext expContext, final boolean applyOGNLShortcuts) {
try {
if (logger.isTraceEnabled()) {
logger.trace("[THYMELEAF][{}] OGNL expression: evaluating expression \"{}\" on target", TemplateEngine.threadIndex(), expression.getExpression());
}
final IEngineConfiguration configuration = context.getConfiguration();
final String exp = expression.getExpression();
final boolean useSelectionAsRoot = expression.getUseSelectionAsRoot();
if (exp == null) {
throw new TemplateProcessingException("Expression content is null, which is not allowed");
}
final ComputedOGNLExpression parsedExpression = obtainComputedOGNLExpression(configuration, expression, exp, applyOGNLShortcuts);
final Map<String, Object> contextVariablesMap;
if (parsedExpression.mightNeedExpressionObjects) {
// The IExpressionObjects implementation returned by processing contexts that include the Standard
// Dialects will be lazy in the creation of expression objects (i.e. they won't be created until really
// needed). And in order for this behaviour to be accepted by OGNL, we will be wrapping this object
// inside an implementation of Map<String,Object>, which will afterwards be fed to the constructor
// of an OgnlContext object.
// Note this will never happen with shortcut expressions, as the '#' character with which all
// expression object names start is not allowed by the OGNLShortcutExpression parser.
final IExpressionObjects expressionObjects = context.getExpressionObjects();
contextVariablesMap = new OGNLExpressionObjectsWrapper(expressionObjects, expContext.getRestrictVariableAccess());
// can later lookup during evaluation.
if (expContext.getRestrictVariableAccess()) {
contextVariablesMap.put(OGNLContextPropertyAccessor.RESTRICT_REQUEST_PARAMETERS, OGNLContextPropertyAccessor.RESTRICT_REQUEST_PARAMETERS);
} else {
contextVariablesMap.remove(OGNLContextPropertyAccessor.RESTRICT_REQUEST_PARAMETERS);
}
} else {
if (expContext.getRestrictVariableAccess()) {
contextVariablesMap = CONTEXT_VARIABLES_MAP_NOEXPOBJECTS_RESTRICTIONS;
} else {
contextVariablesMap = Collections.EMPTY_MAP;
}
}
// The root object on which we will evaluate expressions will depend on whether a selection target is
// active or not...
final ITemplateContext templateContext = (context instanceof ITemplateContext ? (ITemplateContext) context : null);
final Object evaluationRoot = (useSelectionAsRoot && templateContext != null && templateContext.hasSelectionTarget() ? templateContext.getSelectionTarget() : templateContext);
// Execute the expression!
final Object result;
try {
result = executeExpression(configuration, parsedExpression.expression, contextVariablesMap, evaluationRoot);
} catch (final OGNLShortcutExpression.OGNLShortcutExpressionNotApplicableException notApplicable) {
// We tried to apply shortcuts, but it is not possible for this expression even if it parsed OK,
// so we need to empty the cache and try again disabling shortcuts. Once processed for the first time,
// an OGNL (non-shortcut) parsed expression will already be cached and this exception will not be
// thrown again
invalidateComputedOGNLExpression(configuration, expression, exp);
return evaluate(context, expression, expContext, false);
}
if (!expContext.getPerformTypeConversion()) {
return result;
}
final IStandardConversionService conversionService = StandardExpressions.getConversionService(configuration);
return conversionService.convert(context, result, String.class);
} catch (final Exception e) {
throw new TemplateProcessingException("Exception evaluating OGNL expression: \"" + expression.getExpression() + "\"", e);
}
}
use of org.thymeleaf.IEngineConfiguration in project thymeleaf by thymeleaf.
the class AbstractStandardFragmentInsertionTagProcessor method doProcess.
@Override
protected void doProcess(final ITemplateContext context, final IProcessableElementTag tag, final AttributeName attributeName, final String attributeValue, final IElementTagStructureHandler structureHandler) {
if (StringUtils.isEmptyOrWhitespace(attributeValue)) {
throw new TemplateProcessingException("Fragment specifications cannot be empty");
}
final IEngineConfiguration configuration = context.getConfiguration();
/*
* PARSE AND PROCESS THE FRAGMENT
*/
final Object fragmentObj = computeFragment(context, attributeValue);
if (fragmentObj == null) {
throw new TemplateInputException("Error resolving fragment: \"" + attributeValue + "\": " + "template or fragment could not be resolved");
} else if (fragmentObj == NoOpToken.VALUE) {
// If the Fragment result is NO-OP, we will just do nothing (apart from deleting the th:* attribute)
return;
} else if (fragmentObj == Fragment.EMPTY_FRAGMENT) {
// tag (th:insert) or remove it completely, tag included (th:replace)
if (this.replaceHost) {
structureHandler.removeElement();
} else {
structureHandler.removeBody();
}
return;
}
final Fragment fragment = (Fragment) fragmentObj;
final TemplateModel fragmentModel = fragment.getTemplateModel();
Map<String, Object> fragmentParameters = fragment.getParameters();
/*
* ONCE WE HAVE THE FRAGMENT MODEL (its events, in fact), CHECK THE FRAGMENT SIGNATURE
* Fragment signature is important because it might affect the way we apply the parameters to the fragment.
*
* Note this works whatever the template mode of the inserted fragment, given we are looking for an
* element containing a "th:fragment/data-th-fragment" in a generic, non-template-dependent way.
*/
// We will check types first instead of events in order to (many times) avoid creating an immutably-wrapped
// event object when calling "model.get(pos)"
boolean signatureApplied = false;
final ITemplateEvent firstEvent = (fragmentModel.size() > 2 ? fragmentModel.get(1) : null);
if (firstEvent != null && IProcessableElementTag.class.isAssignableFrom(firstEvent.getClass())) {
final String dialectPrefix = attributeName.getPrefix();
final IProcessableElementTag fragmentHolderEvent = (IProcessableElementTag) firstEvent;
if (fragmentHolderEvent.hasAttribute(dialectPrefix, FRAGMENT_ATTR_NAME)) {
// The selected fragment actually has a "th:fragment" attribute, so we should process its signature
final String fragmentSignatureSpec = EscapedAttributeUtils.unescapeAttribute(fragmentModel.getTemplateMode(), fragmentHolderEvent.getAttributeValue(dialectPrefix, FRAGMENT_ATTR_NAME));
if (!StringUtils.isEmptyOrWhitespace(fragmentSignatureSpec)) {
final FragmentSignature fragmentSignature = FragmentSignatureUtils.parseFragmentSignature(configuration, fragmentSignatureSpec);
if (fragmentSignature != null) {
// Reshape the fragment parameters into the ones that we will actually use, according to the signature
fragmentParameters = FragmentSignatureUtils.processParameters(fragmentSignature, fragmentParameters, fragment.hasSyntheticParameters());
signatureApplied = true;
}
}
}
}
// not being applied, maybe not realising there was no signature assignation involved.
if (!signatureApplied && fragment.hasSyntheticParameters()) {
throw new TemplateProcessingException("Fragment '" + attributeValue + "' specifies synthetic (unnamed) parameters, but the resolved fragment " + "does not match a fragment signature (th:fragment,data-th-fragment) which could apply names to " + "the specified parameters.");
}
/*
* CHECK WHETHER THIS IS A CROSS-TEMPLATE-MODE INSERTION. Only TemplateModels for the same template mode
* can be safely inserted into the template being executed and processed just like any other sequences of
* events. If the inserted template has a different template mode, we will need to process it aside and
* obtain a String result for it, then insert such String as mere text.
*
* Note inserting large templates with a different template mode could therefore have a negative effect
* on performance and memory usage, as their result needs to be completely stored in memory at some point
* before being handled to the following phases of template processing. It is therefore recommended that
* cross-template-mode fragment insertion is done only for small fragments, in which case it will work
* almost the same as inlining (with the exception that the content to be inlined will be retrieved from
* somewhere else by means of template resolution).
*/
if (context.getTemplateMode() != fragmentModel.getTemplateMode()) {
// Check if this is a th:include. If so, just don't allow
if (this.insertOnlyContents) {
throw new TemplateProcessingException("Template being processed uses template mode " + context.getTemplateMode() + ", " + "inserted fragment \"" + attributeValue + "\" uses template mode " + fragmentModel.getTemplateMode() + ". Cross-template-mode fragment insertion is not " + "allowed using the " + attributeName + " attribute, which is no longer recommended for use as " + "of Thymeleaf 3.0. Use {th:insert,data-th-insert} or {th:replace,data-th-replace} " + "instead, which do not remove the container element from the fragment being inserted.");
}
// doing it through the structure handler (we are going to perform a nested template processing operation)
if (fragmentParameters != null && fragmentParameters.size() > 0) {
if (!(context instanceof IEngineContext)) {
throw new TemplateProcessingException("Parameterized fragment insertion is not supported because local variable support is DISABLED. This is due to " + "the use of an implementation of the " + ITemplateContext.class.getName() + " interface that does " + "not provide local-variable support. In order to have local-variable support, the variables map " + "implementation should also implement the " + IEngineContext.class.getName() + " interface");
}
// NOTE this IEngineContext interface is internal and should not be used in users' code
((IEngineContext) context).setVariables(fragmentParameters);
}
// Once parameters are in order, just process the template in a nested template engine execution
final Writer stringWriter = new FastStringWriter(200);
configuration.getTemplateManager().process(fragmentModel, context, stringWriter);
// We will insert the result as NON-PROCESSABLE text (it's already been processed!)
if (this.replaceHost) {
structureHandler.replaceWith(stringWriter.toString(), false);
} else {
structureHandler.setBody(stringWriter.toString(), false);
}
return;
}
/*
* APPLY THE FRAGMENT'S TEMPLATE RESOLUTION so that all code inside the fragment is executed with its own
* template resolution info (working as if it were a local variable)
*/
final TemplateData fragmentTemplateData = fragmentModel.getTemplateData();
structureHandler.setTemplateData(fragmentTemplateData);
/*
* APPLY THE FRAGMENT PARAMETERS AS LOCAL VARIABLES, perhaps after reshaping it according to the fragment signature
*/
if (fragmentParameters != null && fragmentParameters.size() > 0) {
for (final Map.Entry<String, Object> fragmentParameterEntry : fragmentParameters.entrySet()) {
structureHandler.setLocalVariable(fragmentParameterEntry.getKey(), fragmentParameterEntry.getValue());
}
}
/*
* IF WE ARE ASKING ONLY FOR CONTENTS (th:include), THEN REMOVE THE CONTAINER BLOCK
*/
if (this.insertOnlyContents && fragmentTemplateData.hasTemplateSelectors()) {
/*
* In the case of th:include, things get a bit complicated because we need to remove the "element envelopes"
* that contain what we really want to include (these envelopes' contents). So we will need to traverse
* the entire returned model detecting those envelopes (open+close tags at model level == 0) and remove
* them, along with anything else that is also at that level 0.
*/
final IModel model = fragmentModel.cloneModel();
int modelLevel = 0;
int n = model.size();
while (n-- != 0) {
// We traverse backwards so that we can modify at the same time
final ITemplateEvent event = model.get(n);
if (event instanceof ICloseElementTag) {
if (((ICloseElementTag) event).isUnmatched()) {
// This is an unmatched close tag (no corresponding open), therefore should not affect our count
continue;
}
if (modelLevel <= 0) {
model.remove(n);
}
modelLevel++;
continue;
}
if (event instanceof IOpenElementTag) {
modelLevel--;
if (modelLevel <= 0) {
model.remove(n);
}
continue;
}
if (modelLevel <= 0) {
model.remove(n);
}
}
if (this.replaceHost) {
structureHandler.replaceWith(model, true);
} else {
structureHandler.setBody(model, true);
}
return;
}
if (this.replaceHost) {
structureHandler.replaceWith(fragmentModel, true);
} else {
structureHandler.setBody(fragmentModel, true);
}
}
use of org.thymeleaf.IEngineConfiguration in project thymeleaf by thymeleaf.
the class StandardUtextTagProcessor method doProcess.
@Override
protected void doProcess(final ITemplateContext context, final IProcessableElementTag tag, final AttributeName attributeName, final String attributeValue, final IElementTagStructureHandler structureHandler) {
final IEngineConfiguration configuration = context.getConfiguration();
final IStandardExpressionParser expressionParser = StandardExpressions.getExpressionParser(configuration);
final IStandardExpression expression = expressionParser.parseExpression(context, attributeValue);
final Object expressionResult;
if (expression != null && expression instanceof FragmentExpression) {
// This is merely a FragmentExpression (not complex, not combined with anything), so we can apply a shortcut
// so that we don't require a "null" result for this expression if the template does not exist. That will
// save a call to resource.exists() which might be costly.
final FragmentExpression.ExecutedFragmentExpression executedFragmentExpression = FragmentExpression.createExecutedFragmentExpression(context, (FragmentExpression) expression);
expressionResult = FragmentExpression.resolveExecutedFragmentExpression(context, executedFragmentExpression, true);
} else {
expressionResult = expression.execute(context, StandardExpressionExecutionContext.RESTRICTED);
}
// If result is no-op, there's nothing to execute
if (expressionResult == NoOpToken.VALUE) {
return;
}
/*
* First of all we should check whether the expression result is a Fragment so that, in such case, we can
* avoid creating a String in memory for it and just append its model.
*/
if (expressionResult != null && expressionResult instanceof Fragment) {
if (expressionResult == Fragment.EMPTY_FRAGMENT) {
structureHandler.removeBody();
return;
}
structureHandler.setBody(((Fragment) expressionResult).getTemplateModel(), false);
return;
}
final String unescapedTextStr = (expressionResult == null ? "" : expressionResult.toString());
/*
* We will check if there are configured post processors or not. The reason we do this is because output
* inserted as a result of a th:utext attribute, even if it might be markup, will never be considered as
* 'processable', i.e. no other processors/inliner will ever be able to act on it. The main reason for this
* is to protect against code injection.
*
* So the only other agents that would be able to modify these th:utext results are POST-PROCESSORS. And
* they will indeed need markup to have been parsed in order to separate text from structures, so that's why
* we check if there actually are any post-processors and, if not (most common case), simply output the
* expression result as if it were a mere (unescaped) text node.
*/
final Set<IPostProcessor> postProcessors = configuration.getPostProcessors(getTemplateMode());
if (postProcessors.isEmpty()) {
structureHandler.setBody(unescapedTextStr, false);
return;
}
/*
* We have post-processors, so from here one we will have to decide whether we need to parse the unescaped
* text or not...
*/
if (!mightContainStructures(unescapedTextStr)) {
// If this text contains no markup structures, there would be no need to parse it or treat it as markup!
structureHandler.setBody(unescapedTextStr, false);
return;
}
/*
* We have post-processors AND this text might contain structures, so there is no alternative but parsing
*/
final TemplateModel parsedFragment = configuration.getTemplateManager().parseString(context.getTemplateData(), unescapedTextStr, // we won't apply offset here because the inserted text does not really come from the template itself
0, // we won't apply offset here because the inserted text does not really come from the template itself
0, // No template mode forcing required
null, // useCache == false because we could potentially pollute the cache with too many entries (th:utext is too variable!)
false);
// Setting 'processable' to false avoiding text inliners processing already generated text,
// which in turn avoids code injection.
structureHandler.setBody(parsedFragment, false);
}
Aggregations