Search in sources :

Example 1 with FragmentExpression

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);
    }
}
Also used : IStandardExpression(org.thymeleaf.standard.expression.IStandardExpression) FragmentExpression(org.thymeleaf.standard.expression.FragmentExpression) IStandardExpressionParser(org.thymeleaf.standard.expression.IStandardExpressionParser) TemplateProcessingException(org.thymeleaf.exceptions.TemplateProcessingException) MatchingAttributeName(org.thymeleaf.processor.element.MatchingAttributeName) AttributeName(org.thymeleaf.engine.AttributeName) TemplateProcessingException(org.thymeleaf.exceptions.TemplateProcessingException)

Example 2 with FragmentExpression

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;
}
Also used : IStandardExpression(org.thymeleaf.standard.expression.IStandardExpression) FragmentExpression(org.thymeleaf.standard.expression.FragmentExpression)

Example 3 with FragmentExpression

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);
}
Also used : IStandardExpression(org.thymeleaf.standard.expression.IStandardExpression) FragmentExpression(org.thymeleaf.standard.expression.FragmentExpression) StandardExpressionExecutionContext(org.thymeleaf.standard.expression.StandardExpressionExecutionContext)

Example 4 with FragmentExpression

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;
}
Also used : IStandardExpression(org.thymeleaf.standard.expression.IStandardExpression) FragmentExpression(org.thymeleaf.standard.expression.FragmentExpression) IStandardExpressionParser(org.thymeleaf.standard.expression.IStandardExpressionParser) TemplateProcessingException(org.thymeleaf.exceptions.TemplateProcessingException) Fragment(org.thymeleaf.standard.expression.Fragment)

Example 5 with FragmentExpression

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);
}
Also used : IStandardExpression(org.thymeleaf.standard.expression.IStandardExpression) FragmentExpression(org.thymeleaf.standard.expression.FragmentExpression) IEngineConfiguration(org.thymeleaf.IEngineConfiguration) IPostProcessor(org.thymeleaf.postprocessor.IPostProcessor) IStandardExpressionParser(org.thymeleaf.standard.expression.IStandardExpressionParser) TemplateModel(org.thymeleaf.engine.TemplateModel) Fragment(org.thymeleaf.standard.expression.Fragment)

Aggregations

FragmentExpression (org.thymeleaf.standard.expression.FragmentExpression)5 IStandardExpression (org.thymeleaf.standard.expression.IStandardExpression)5 IStandardExpressionParser (org.thymeleaf.standard.expression.IStandardExpressionParser)3 TemplateProcessingException (org.thymeleaf.exceptions.TemplateProcessingException)2 Fragment (org.thymeleaf.standard.expression.Fragment)2 IEngineConfiguration (org.thymeleaf.IEngineConfiguration)1 AttributeName (org.thymeleaf.engine.AttributeName)1 TemplateModel (org.thymeleaf.engine.TemplateModel)1 IPostProcessor (org.thymeleaf.postprocessor.IPostProcessor)1 MatchingAttributeName (org.thymeleaf.processor.element.MatchingAttributeName)1 StandardExpressionExecutionContext (org.thymeleaf.standard.expression.StandardExpressionExecutionContext)1