use of org.thymeleaf.standard.expression.FragmentExpression in project thymeleaf by thymeleaf.
the class StandardDefaultAttributesTagProcessor method processDefaultAttribute.
private static void processDefaultAttribute(final TemplateMode templateMode, final ITemplateContext context, final IProcessableElementTag tag, final IAttribute attribute, final IElementTagStructureHandler structureHandler) {
try {
final AttributeName attributeName = attribute.getAttributeDefinition().getAttributeName();
final String attributeValue = EscapedAttributeUtils.unescapeAttribute(context.getTemplateMode(), attribute.getValue());
/*
* Compute the new attribute name (i.e. the same, without the prefix)
*/
final String originalCompleteAttributeName = attribute.getAttributeCompleteName();
final String canonicalAttributeName = attributeName.getAttributeName();
final String newAttributeName;
if (TextUtil.endsWith(true, originalCompleteAttributeName, canonicalAttributeName)) {
// We avoid creating a new String instance
newAttributeName = canonicalAttributeName;
} else {
newAttributeName = originalCompleteAttributeName.substring(originalCompleteAttributeName.length() - canonicalAttributeName.length());
}
/*
* Obtain the parser
*/
final IStandardExpressionParser expressionParser = StandardExpressions.getExpressionParser(context.getConfiguration());
/*
* Execute the expression, handling nulls in a way consistent with the rest of the Standard Dialect
*/
final Object expressionResult;
if (attributeValue != null) {
final IStandardExpression expression = expressionParser.parseExpression(context, attributeValue);
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 {
// Default attributes will ALWAYS be executed in RESTRICTED mode, for safety reasons (they might
// create attributes involved in code execution)
expressionResult = expression.execute(context, StandardExpressionExecutionContext.RESTRICTED);
}
} else {
expressionResult = null;
}
/*
* If the result of this expression is NO-OP, there is nothing to execute
*/
if (expressionResult == NoOpToken.VALUE) {
structureHandler.removeAttribute(attributeName);
return;
}
/*
* Compute the new attribute value
*/
final String newAttributeValue = EscapedAttributeUtils.escapeAttribute(templateMode, expressionResult == null ? null : expressionResult.toString());
/*
* Set the new value, removing the attribute completely if the expression evaluated to null
*/
if (newAttributeValue == null || newAttributeValue.length() == 0) {
// We are removing the equivalent attribute name, without the prefix...
structureHandler.removeAttribute(newAttributeName);
structureHandler.removeAttribute(attributeName);
} else {
// We are setting the equivalent attribute name, without the prefix...
structureHandler.replaceAttribute(attributeName, newAttributeName, (newAttributeValue == null ? "" : newAttributeValue));
}
} catch (final TemplateProcessingException e) {
// specific because we know exactly what attribute was being executed and caused the error
if (!e.hasTemplateName()) {
e.setTemplateName(tag.getTemplateName());
}
if (!e.hasLineAndCol()) {
e.setLineAndCol(attribute.getLine(), attribute.getCol());
}
throw e;
} catch (final Exception e) {
throw new TemplateProcessingException("Error during execution of processor '" + StandardDefaultAttributesTagProcessor.class.getName() + "'", tag.getTemplateName(), attribute.getLine(), attribute.getCol(), e);
}
}
use of org.thymeleaf.standard.expression.FragmentExpression in project thymeleaf by thymeleaf.
the class EngineEventUtils method computeAttributeExpression.
/*
* The idea behind this method is to cache in the Attribute object itself the IStandardExpression object corresponding
* with the expression to be executed, so that we don't have to hit the expression cache at all
*/
public static IStandardExpression computeAttributeExpression(final ITemplateContext context, final IProcessableElementTag tag, final AttributeName attributeName, final String attributeValue) {
if (!(tag instanceof AbstractProcessableElementTag)) {
return parseAttributeExpression(context, attributeValue);
}
final AbstractProcessableElementTag processableElementTag = (AbstractProcessableElementTag) tag;
final Attribute attribute = (Attribute) processableElementTag.getAttribute(attributeName);
IStandardExpression expression = attribute.getCachedStandardExpression();
if (expression != null) {
return expression;
}
expression = parseAttributeExpression(context, attributeValue);
// If the expression has been correctly parsed AND it does not contain preprocessing marks (_), nor it is a FragmentExpression, cache it!
if (expression != null && !(expression instanceof FragmentExpression) && attributeValue.indexOf('_') < 0) {
attribute.setCachedStandardExpression(expression);
}
return expression;
}
use of org.thymeleaf.standard.expression.FragmentExpression in project thymeleaf by thymeleaf.
the class AbstractStandardExpressionAttributeTagProcessor method doProcess.
@Override
protected final void doProcess(final ITemplateContext context, final IProcessableElementTag tag, final AttributeName attributeName, final String attributeValue, final IElementTagStructureHandler structureHandler) {
final Object expressionResult;
if (attributeValue != null) {
final IStandardExpression expression = EngineEventUtils.computeAttributeExpression(context, tag, attributeName, attributeValue);
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 {
/*
* Some attributes will require the execution of the expressions contained in them in RESTRICTED
* mode, so that e.g. access to request parameters is forbidden.
*/
final StandardExpressionExecutionContext expCtx = (this.restrictedExpressionExecution ? StandardExpressionExecutionContext.RESTRICTED : StandardExpressionExecutionContext.NORMAL);
expressionResult = expression.execute(context, expCtx);
}
} else {
expressionResult = null;
}
// If the result of this expression is NO-OP, there is nothing to execute
if (expressionResult == NoOpToken.VALUE) {
if (this.removeIfNoop) {
structureHandler.removeAttribute(attributeName);
}
return;
}
doProcess(context, tag, attributeName, attributeValue, expressionResult, structureHandler);
}
use of org.thymeleaf.standard.expression.FragmentExpression in project thymeleaf by thymeleaf.
the class AbstractStandardFragmentInsertionTagProcessor method computeFragment.
/*
* This can return a Fragment, NoOpToken (if nothing should be done) or null
*/
private static Object computeFragment(final ITemplateContext context, final String input) {
final IStandardExpressionParser expressionParser = StandardExpressions.getExpressionParser(context.getConfiguration());
final String trimmedInput = input.trim();
if (shouldBeWrappedAsFragmentExpression(trimmedInput)) {
// We do not know for sure that this is a complete standard expression, so we will consider it the
// content of a FragmentExpression for legacy compatibility reasons.
// We will only reach this point if the expression does not contain any Fragment Expressions expressed
// as ~{...} (excluding parameters), nor the "::" fragment selector separator.
// NOTE we are using the generic parseExpression() and not directly calling a parse method in the
// FragmentExpression class because we want to take advantage of the expression cache.
final FragmentExpression fragmentExpression = (FragmentExpression) expressionParser.parseExpression(context, "~{" + trimmedInput + "}");
final FragmentExpression.ExecutedFragmentExpression executedFragmentExpression = FragmentExpression.createExecutedFragmentExpression(context, fragmentExpression);
if (executedFragmentExpression.getFragmentSelectorExpressionResult() == null && executedFragmentExpression.getFragmentParameters() == null) {
// We might be in the scenario that what we thought was a template name in fact was instead an expression
// returning a Fragment itself, so we should simply return it
final Object templateNameExpressionResult = executedFragmentExpression.getTemplateNameExpressionResult();
if (templateNameExpressionResult != null) {
if (templateNameExpressionResult instanceof Fragment) {
return templateNameExpressionResult;
}
if (templateNameExpressionResult == NoOpToken.VALUE) {
return NoOpToken.VALUE;
}
}
}
// have to execute a (potentially costly) resource.exists() call on the resolved resource.
return FragmentExpression.resolveExecutedFragmentExpression(context, executedFragmentExpression, true);
}
// If we reached this point, we know for sure this is a complete fragment expression, so we just parse it
// as such and execute it
final IStandardExpression fragmentExpression = expressionParser.parseExpression(context, trimmedInput);
final Object fragmentExpressionResult;
if (fragmentExpression != null && fragmentExpression instanceof FragmentExpression) {
// This is not a complex expression but merely a FragmentExpression, 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) fragmentExpression);
fragmentExpressionResult = FragmentExpression.resolveExecutedFragmentExpression(context, executedFragmentExpression, true);
} else {
fragmentExpressionResult = fragmentExpression.execute(context);
}
if (fragmentExpressionResult == null || fragmentExpressionResult == NoOpToken.VALUE) {
return fragmentExpressionResult;
}
if (!(fragmentExpressionResult instanceof Fragment)) {
throw new TemplateProcessingException("Invalid fragment specification: \"" + input + "\": " + "expression does not return a Fragment object");
}
return fragmentExpressionResult;
}
use of org.thymeleaf.standard.expression.FragmentExpression 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