Search in sources :

Example 1 with ITemplateContext

use of org.thymeleaf.context.ITemplateContext in project thymeleaf by thymeleaf.

the class MessageExpression method executeMessageExpression.

static Object executeMessageExpression(final IExpressionContext context, final MessageExpression expression, final StandardExpressionExecutionContext expContext) {
    if (logger.isTraceEnabled()) {
        logger.trace("[THYMELEAF][{}] Evaluating message: \"{}\"", TemplateEngine.threadIndex(), expression.getStringRepresentation());
    }
    if (!(context instanceof ITemplateContext)) {
        throw new TemplateProcessingException("Cannot evaluate expression \"" + expression + "\". Message externalization expressions " + "can only be evaluated in a template-processing environment (as a part of an in-template expression) " + "where processing context is an implementation of " + ITemplateContext.class.getClass() + ", which it isn't (" + context.getClass().getName() + ")");
    }
    final ITemplateContext templateContext = (ITemplateContext) context;
    final IStandardExpression baseExpression = expression.getBase();
    Object messageKey = baseExpression.execute(templateContext, expContext);
    messageKey = LiteralValue.unwrap(messageKey);
    if (messageKey != null && !(messageKey instanceof String)) {
        messageKey = messageKey.toString();
    }
    if (StringUtils.isEmptyOrWhitespace((String) messageKey)) {
        throw new TemplateProcessingException("Message key for message resolution must be a non-null and non-empty String");
    }
    final Object[] messageParameters;
    if (expression.hasParameters()) {
        final ExpressionSequence parameterExpressionSequence = expression.getParameters();
        final List<IStandardExpression> parameterExpressionValues = parameterExpressionSequence.getExpressions();
        final int parameterExpressionValuesLen = parameterExpressionValues.size();
        messageParameters = new Object[parameterExpressionValuesLen];
        for (int i = 0; i < parameterExpressionValuesLen; i++) {
            final IStandardExpression parameterExpression = parameterExpressionValues.get(i);
            final Object result = parameterExpression.execute(templateContext, expContext);
            messageParameters[i] = LiteralValue.unwrap(result);
        }
    } else {
        messageParameters = NO_PARAMETERS;
    }
    // Note message expressions will always return an absent representation if message does not exist
    return templateContext.getMessage(null, (String) messageKey, messageParameters, true);
}
Also used : TemplateProcessingException(org.thymeleaf.exceptions.TemplateProcessingException) ITemplateContext(org.thymeleaf.context.ITemplateContext)

Example 2 with ITemplateContext

use of org.thymeleaf.context.ITemplateContext in project thymeleaf by thymeleaf.

the class LinkExpression method executeLinkExpression.

static Object executeLinkExpression(final IExpressionContext context, final LinkExpression expression) {
    if (logger.isTraceEnabled()) {
        logger.trace("[THYMELEAF][{}] Evaluating link: \"{}\"", TemplateEngine.threadIndex(), expression.getStringRepresentation());
    }
    if (!(context instanceof ITemplateContext)) {
        throw new TemplateProcessingException("Cannot evaluate expression \"" + expression + "\". Link expressions " + "can only be evaluated in a template-processing environment (as a part of an in-template expression) " + "where processing context is an implementation of " + ITemplateContext.class.getClass() + ", which it isn't (" + context.getClass().getName() + ")");
    }
    final ITemplateContext templateContext = (ITemplateContext) context;
    final IStandardExpression baseExpression = expression.getBase();
    // The URL base in a link expression will always be executed in RESTRICTED mode, so we will forbid that
    // base URLs come directly from user input (request parameters). Note this restriction does not need to apply
    // to URL parameters.
    Object base = baseExpression.execute(templateContext, StandardExpressionExecutionContext.RESTRICTED);
    base = LiteralValue.unwrap(base);
    if (base != null && !(base instanceof String)) {
        base = base.toString();
    }
    if (base == null || StringUtils.isEmptyOrWhitespace((String) base)) {
        base = "";
    }
    /*
         * Resolve the parameters from the expression into a LinkParameters object.
         * Note the parameters variable might be null if there are no parameters
         *
         * Also note that link parameters, which should be correctly URL-encoded before being added to
         * the query string of the URL, will always be executed using UNRESTRICTED mode, so that request
         * params can be directly passed along to other generated URLs.
         */
    final Map<String, Object> parameters = resolveParameters(templateContext, expression, StandardExpressionExecutionContext.NORMAL);
    return templateContext.buildLink((String) base, parameters);
}
Also used : TemplateProcessingException(org.thymeleaf.exceptions.TemplateProcessingException) ITemplateContext(org.thymeleaf.context.ITemplateContext)

Example 3 with ITemplateContext

use of org.thymeleaf.context.ITemplateContext 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);
    }
}
Also used : IEngineConfiguration(org.thymeleaf.IEngineConfiguration) IExpressionObjects(org.thymeleaf.expression.IExpressionObjects) TemplateProcessingException(org.thymeleaf.exceptions.TemplateProcessingException) OgnlException(ognl.OgnlException) TemplateProcessingException(org.thymeleaf.exceptions.TemplateProcessingException) ITemplateContext(org.thymeleaf.context.ITemplateContext)

Example 4 with ITemplateContext

use of org.thymeleaf.context.ITemplateContext 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);
    }
}
Also used : ITemplateEvent(org.thymeleaf.model.ITemplateEvent) IModel(org.thymeleaf.model.IModel) IEngineConfiguration(org.thymeleaf.IEngineConfiguration) IOpenElementTag(org.thymeleaf.model.IOpenElementTag) FragmentSignature(org.thymeleaf.standard.expression.FragmentSignature) TemplateModel(org.thymeleaf.engine.TemplateModel) Fragment(org.thymeleaf.standard.expression.Fragment) TemplateInputException(org.thymeleaf.exceptions.TemplateInputException) IEngineContext(org.thymeleaf.context.IEngineContext) TemplateData(org.thymeleaf.engine.TemplateData) IProcessableElementTag(org.thymeleaf.model.IProcessableElementTag) FastStringWriter(org.thymeleaf.util.FastStringWriter) TemplateProcessingException(org.thymeleaf.exceptions.TemplateProcessingException) ITemplateContext(org.thymeleaf.context.ITemplateContext) Map(java.util.Map) FastStringWriter(org.thymeleaf.util.FastStringWriter) Writer(java.io.Writer) ICloseElementTag(org.thymeleaf.model.ICloseElementTag)

Aggregations

ITemplateContext (org.thymeleaf.context.ITemplateContext)4 TemplateProcessingException (org.thymeleaf.exceptions.TemplateProcessingException)4 IEngineConfiguration (org.thymeleaf.IEngineConfiguration)2 Writer (java.io.Writer)1 Map (java.util.Map)1 OgnlException (ognl.OgnlException)1 IEngineContext (org.thymeleaf.context.IEngineContext)1 TemplateData (org.thymeleaf.engine.TemplateData)1 TemplateModel (org.thymeleaf.engine.TemplateModel)1 TemplateInputException (org.thymeleaf.exceptions.TemplateInputException)1 IExpressionObjects (org.thymeleaf.expression.IExpressionObjects)1 ICloseElementTag (org.thymeleaf.model.ICloseElementTag)1 IModel (org.thymeleaf.model.IModel)1 IOpenElementTag (org.thymeleaf.model.IOpenElementTag)1 IProcessableElementTag (org.thymeleaf.model.IProcessableElementTag)1 ITemplateEvent (org.thymeleaf.model.ITemplateEvent)1 Fragment (org.thymeleaf.standard.expression.Fragment)1 FragmentSignature (org.thymeleaf.standard.expression.FragmentSignature)1 FastStringWriter (org.thymeleaf.util.FastStringWriter)1