Search in sources :

Example 41 with TemplateProcessingException

use of org.thymeleaf.exceptions.TemplateProcessingException in project thymeleaf by thymeleaf.

the class SubtractionExpression method executeSubtraction.

static Object executeSubtraction(final IExpressionContext context, final SubtractionExpression expression, final StandardExpressionExecutionContext expContext) {
    if (logger.isTraceEnabled()) {
        logger.trace("[THYMELEAF][{}] Evaluating subtraction expression: \"{}\"", TemplateEngine.threadIndex(), expression.getStringRepresentation());
    }
    Object leftValue = expression.getLeft().execute(context, expContext);
    Object rightValue = expression.getRight().execute(context, expContext);
    if (leftValue == null) {
        leftValue = "null";
    }
    if (rightValue == null) {
        rightValue = "null";
    }
    final BigDecimal leftNumberValue = EvaluationUtils.evaluateAsNumber(leftValue);
    final BigDecimal rightNumberValue = EvaluationUtils.evaluateAsNumber(rightValue);
    if (leftNumberValue != null && rightNumberValue != null) {
        // Addition will act as a mathematical 'plus'
        return leftNumberValue.subtract(rightNumberValue);
    }
    throw new TemplateProcessingException("Cannot execute subtraction: operands are \"" + LiteralValue.unwrap(leftValue) + "\" and \"" + LiteralValue.unwrap(rightValue) + "\"");
}
Also used : TemplateProcessingException(org.thymeleaf.exceptions.TemplateProcessingException) BigDecimal(java.math.BigDecimal)

Example 42 with TemplateProcessingException

use of org.thymeleaf.exceptions.TemplateProcessingException 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 43 with TemplateProcessingException

use of org.thymeleaf.exceptions.TemplateProcessingException 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)

Example 44 with TemplateProcessingException

use of org.thymeleaf.exceptions.TemplateProcessingException in project thymeleaf by thymeleaf.

the class MinusExpression method executeMinus.

static Object executeMinus(final IExpressionContext context, final MinusExpression expression, final StandardExpressionExecutionContext expContext) {
    if (logger.isTraceEnabled()) {
        logger.trace("[THYMELEAF][{}] Evaluating minus expression: \"{}\"", TemplateEngine.threadIndex(), expression.getStringRepresentation());
    }
    Object operandValue = expression.getOperand().execute(context, expContext);
    if (operandValue == null) {
        operandValue = "null";
    }
    final BigDecimal operandNumberValue = EvaluationUtils.evaluateAsNumber(operandValue);
    if (operandNumberValue != null) {
        // Addition will act as a mathematical 'plus'
        return operandNumberValue.multiply(BigDecimal.valueOf(-1));
    }
    throw new TemplateProcessingException("Cannot execute minus: operand is \"" + LiteralValue.unwrap(operandValue) + "\"");
}
Also used : TemplateProcessingException(org.thymeleaf.exceptions.TemplateProcessingException) BigDecimal(java.math.BigDecimal)

Example 45 with TemplateProcessingException

use of org.thymeleaf.exceptions.TemplateProcessingException in project thymeleaf by thymeleaf.

the class LazyEscapingCharSequence method produceEscapedOutput.

private void produceEscapedOutput(final Writer writer) {
    /*
         * Producing ESCAPED output is somewhat simple in HTML or XML modes, as it simply consists of converting
         * input into a String and HTML-or-XML-escaping it.
         *
         * But for JavaScript or CSS, it becomes a bit more complicated than that. JavaScript will output a complete
         * literal (incl. single quotes) if input is a String or a non-recognized value type, but will print input
         * as an object, number, boolean, etc. if it is recognized to be of one of these types. CSS will output
         * quoted literals.
         *
         * Note that, when in TEXT mode, HTML escaping will be used (as TEXT is many times used for
         * processing HTML templates)
         */
    try {
        switch(templateMode) {
            case TEXT:
            // fall-through
            case HTML:
                if (this.input != null) {
                    HtmlEscape.escapeHtml4Xml(this.input.toString(), writer);
                }
                return;
            case XML:
                if (this.input != null) {
                    // Note we are outputting a body content here, so it is important that we use the version
                    // of XML escaping meant for content, not attributes (slight differences)
                    XmlEscape.escapeXml10(this.input.toString(), writer);
                }
                return;
            case JAVASCRIPT:
                final IStandardJavaScriptSerializer javaScriptSerializer = StandardSerializers.getJavaScriptSerializer(this.configuration);
                javaScriptSerializer.serializeValue(this.input, writer);
                return;
            case CSS:
                final IStandardCSSSerializer cssSerializer = StandardSerializers.getCSSSerializer(this.configuration);
                cssSerializer.serializeValue(this.input, writer);
                return;
            case RAW:
                if (this.input != null) {
                    writer.write(this.input.toString());
                }
                return;
            default:
                throw new TemplateProcessingException("Unrecognized template mode " + templateMode + ". Cannot produce escaped output for " + "this template mode.");
        }
    } catch (final IOException e) {
        throw new TemplateProcessingException("An error happened while trying to produce escaped output", e);
    }
}
Also used : IStandardJavaScriptSerializer(org.thymeleaf.standard.serializer.IStandardJavaScriptSerializer) IStandardCSSSerializer(org.thymeleaf.standard.serializer.IStandardCSSSerializer) TemplateProcessingException(org.thymeleaf.exceptions.TemplateProcessingException) IOException(java.io.IOException)

Aggregations

TemplateProcessingException (org.thymeleaf.exceptions.TemplateProcessingException)45 BigDecimal (java.math.BigDecimal)12 IEngineConfiguration (org.thymeleaf.IEngineConfiguration)8 IStandardExpression (org.thymeleaf.standard.expression.IStandardExpression)6 IOException (java.io.IOException)5 ITemplateContext (org.thymeleaf.context.ITemplateContext)4 Writer (java.io.Writer)3 Test (org.junit.Test)3 Context (org.thymeleaf.context.Context)3 AttributeName (org.thymeleaf.engine.AttributeName)3 TemplateEngineException (org.thymeleaf.exceptions.TemplateEngineException)3 TemplateInputException (org.thymeleaf.exceptions.TemplateInputException)3 TemplateOutputException (org.thymeleaf.exceptions.TemplateOutputException)3 FastStringWriter (org.thymeleaf.util.FastStringWriter)3 DataBuffer (org.springframework.core.io.buffer.DataBuffer)2 IEngineContext (org.thymeleaf.context.IEngineContext)2 TemplateManager (org.thymeleaf.engine.TemplateManager)2 IAttribute (org.thymeleaf.model.IAttribute)2 IOpenElementTag (org.thymeleaf.model.IOpenElementTag)2 IProcessableElementTag (org.thymeleaf.model.IProcessableElementTag)2