use of org.thymeleaf.exceptions.TemplateProcessingException in project thymeleaf by thymeleaf.
the class TemplateModel method toString.
@Override
public final String toString() {
try {
final Writer writer = new FastStringWriter();
write(writer);
return writer.toString();
} catch (final IOException e) {
throw new TemplateProcessingException("Error while creating String representation of model");
}
}
use of org.thymeleaf.exceptions.TemplateProcessingException in project thymeleaf by thymeleaf.
the class ProcessorTemplateHandler method handleStandaloneElement.
@Override
public void handleStandaloneElement(final IStandaloneElementTag istandaloneElementTag) {
/*
* If processing is stopped, we should queue this for later handling
* In theory, given the origin of events (parser or cache) should get stopped immediately, this should
* only happen if a pre-processor is producing additional events.
*/
if (this.throttleEngine && this.flowController.stopProcessing) {
queueEvent(istandaloneElementTag);
return;
}
/*
* CHECK WHETHER WE ARE GATHERING AN ELEMENT's MODEL
*/
if (!this.modelController.shouldProcessStandaloneElement(istandaloneElementTag)) {
return;
}
/*
* CAST TO ENGINE-SPECIFIC IMPLEMENTATION, which will ease the handling of the structure during processing
*/
StandaloneElementTag standaloneElementTag = StandaloneElementTag.asEngineStandaloneElementTag(istandaloneElementTag);
/*
* OBTAIN THE CURRENT SYNTHETIC MODEL (if any). This is needed in case this event was previously being handled,
* then a gathering process started (as a consequence of the execution of one of its processors), and then
* once the model was gathered the process started again by handling the first event, which was the one
* suspended. By obtaining the current gathering model here we can reinitialize all the handling variables and
* flags to their original state before being suspended.
*/
final IGatheringModelProcessable currentGatheringModel = obtainCurrentGatheringModel();
/*
* If we are resuming an execution after suspending it, we want to retire the register of the element tag
* that was added by the controller. The reason we want this is that the current tag was already registered
* by the controller when the execution was suspended, and we don't want it duplicated (nor altered).
*/
if (currentGatheringModel != null && this.engineContext != null) {
this.engineContext.setElementTag(null);
}
/*
* FAIL FAST in case this tag has no associated processors and we have no reason to pay attention to it
* anyway (because of having been suspended).
*/
if (currentGatheringModel == null && !standaloneElementTag.hasAssociatedProcessors()) {
this.next.handleStandaloneElement(standaloneElementTag);
if (!this.throttleEngine || !this.flowController.stopProcessing) {
if (this.engineContext != null) {
this.engineContext.decreaseLevel();
}
} else {
queueProcessable(this.decreaseContextLevelProcessable);
}
return;
}
/*
* DECLARE THE STATE VARS NEEDED FOR PROCESSOR EXECUTION. If we are executing the first event of a gathered
* model, we will just re-initialize to the original variables, the ones we had before suspending.
*/
final ProcessorExecutionVars vars = (currentGatheringModel == null ? new ProcessorExecutionVars() : currentGatheringModel.initializeProcessorExecutionVars());
/*
* GET THE STRUCTURE HANDLERS INTO LOCAL VARS
*/
final ElementTagStructureHandler tagStructureHandler = this.elementTagStructureHandler;
final ElementModelStructureHandler modelStructureHandler = this.elementModelStructureHandler;
/*
* EXECUTE PROCESSORS
*/
IElementProcessor processor;
while (!vars.discardEvent && (processor = vars.processorIterator.next(standaloneElementTag)) != null) {
tagStructureHandler.reset();
modelStructureHandler.reset();
if (processor instanceof IElementTagProcessor) {
final IElementTagProcessor elementProcessor = ((IElementTagProcessor) processor);
elementProcessor.process(this.context, standaloneElementTag, tagStructureHandler);
// Apply any context modifications made by the processor (local vars, inlining, etc.)
tagStructureHandler.applyContextModifications(this.engineContext);
// Apply any modifications to the tag itself: new/removed/replace attributes, etc. Note this
// creates a new tag object because tag objects are immutable.
standaloneElementTag = tagStructureHandler.applyAttributes(this.attributeDefinitions, standaloneElementTag);
if (tagStructureHandler.iterateElement) {
// Initialize a gathering model
this.modelController.startGatheringIteratedModel(standaloneElementTag, vars, tagStructureHandler.iterVariableName, tagStructureHandler.iterStatusVariableName, tagStructureHandler.iteratedObject);
// Obtain the gathered model (this is a standalone tag, so no additional events needed in iteration)
final IGatheringModelProcessable gatheredModel = this.modelController.getGatheredModel();
this.modelController.resetGathering();
// Process the gathering model, or queue for throttled execution
if (!this.throttleEngine) {
gatheredModel.process();
} else {
queueProcessable(gatheredModel);
}
// Complete exit of the handler method: no more processing to do from here
return;
} else if (tagStructureHandler.setBodyText) {
// Reset model, we need it clean
vars.modelAfter = resetModel(vars.modelAfter, true);
// Prepare the text node that will be added to the queue (which will be suspended)
final Text text = new Text(tagStructureHandler.setBodyTextValue);
vars.modelAfter.add(text);
// If processable, events will be executed by the ProcessorTemplateHandler. If not, by this.next
vars.modelAfterProcessable = tagStructureHandler.setBodyTextProcessable;
// Initialize the gathered model object (open+close equivalent to this standalone tag)
final GatheringModelProcessable equivalentSyntheticModel = this.modelController.createStandaloneEquivalentModel(standaloneElementTag, vars);
// Fire the now-equivalent events. Note the handleOpenElement event will take care of the suspended queue
if (!this.throttleEngine) {
equivalentSyntheticModel.process();
} else {
queueProcessable(equivalentSyntheticModel);
}
// Complete exit of the handler method: no more processing to do from here
return;
} else if (tagStructureHandler.setBodyModel) {
// Reset model, we need it clean
vars.modelAfter = resetModel(vars.modelAfter, true);
// Prepare the queue (that we will suspend)
vars.modelAfter.addModel(tagStructureHandler.setBodyModelValue);
// If processable, events will be executed by the ProcessorTemplateHandler. If not, by this.next
vars.modelAfterProcessable = tagStructureHandler.setBodyModelProcessable;
// Initialize the gathered model object (open+close equivalent to this standalone tag)
final GatheringModelProcessable equivalentSyntheticModel = this.modelController.createStandaloneEquivalentModel(standaloneElementTag, vars);
// Fire the now-equivalent events. Note the handleOpenElement event will take care of the suspended queue
if (!this.throttleEngine) {
equivalentSyntheticModel.process();
} else {
queueProcessable(equivalentSyntheticModel);
}
// Complete exit of the handler method: no more processing to do from here
return;
} else if (tagStructureHandler.insertBeforeModel) {
// Reset BEFORE model, we need it clean
vars.modelBefore = resetModel(vars.modelBefore, true);
// Add model to be passed to this.next BEFORE delegating the event. Note this cannot be processable.
vars.modelBefore.addModel(tagStructureHandler.insertBeforeModelValue);
} else if (tagStructureHandler.insertImmediatelyAfterModel) {
// will not be resetting it because we will be inserting our model at the very beginning of it.
if (vars.modelAfter == null) {
vars.modelAfter = resetModel(vars.modelAfter, true);
}
// If processable, events will be executed by the ProcessorTemplateHandler. If not, by this.next
vars.modelAfterProcessable = tagStructureHandler.insertImmediatelyAfterModelProcessable;
// Insert the new model
vars.modelAfter.insertModel(0, tagStructureHandler.insertImmediatelyAfterModelValue);
} else if (tagStructureHandler.replaceWithText) {
// Reset model, we need it clean
vars.modelAfter = resetModel(vars.modelAfter, true);
// If processable, events will be executed by the ProcessorTemplateHandler. If not, by this.next
vars.modelAfterProcessable = tagStructureHandler.replaceWithTextProcessable;
// Create the new replacement Text event and add it to the model
vars.modelAfter.add(new Text(tagStructureHandler.replaceWithTextValue));
// This tag, the standalone tag itself, will be replaced, so it has to be removed
vars.discardEvent = true;
} else if (tagStructureHandler.replaceWithModel) {
// Reset model, we need it clean
vars.modelAfter = resetModel(vars.modelAfter, true);
// If processable, events will be executed by the ProcessorTemplateHandler. If not, by this.next
vars.modelAfterProcessable = tagStructureHandler.replaceWithModelProcessable;
// Add the new replacement model
vars.modelAfter.addModel(tagStructureHandler.replaceWithModelValue);
// This tag, the standalone tag itself, will be replaced, so it has to be removed
vars.discardEvent = true;
} else if (tagStructureHandler.removeElement) {
// Reset model, but only if it already exists
vars.modelAfter = resetModel(vars.modelAfter, false);
// We are removing the element (the standalone tag), so no further processing will be allowed
vars.discardEvent = true;
} else if (tagStructureHandler.removeTags) {
// No modifications to the queue - it's just the tag that will be removed, not its possible contents
vars.discardEvent = true;
}
// --------------
// No way to process 'removeBody' or 'removeAllButFirstChild' on a standalone tag
// --------------
} else if (processor instanceof IElementModelProcessor) {
if (!vars.processorIterator.lastWasRepeated()) {
if ((vars.modelBefore != null && vars.modelBefore.size() > 0) || (vars.modelAfter != null && vars.modelAfter.size() > 0)) {
throw new TemplateProcessingException("Cannot execute model processor " + processor.getClass().getName() + " as the body " + "of the target element has already been modified by a previously executed processor " + "on the same tag. Model processors cannot execute on already-modified bodies as these " + "might contain unprocessable events (e.g. as a result of a 'th:text' or similar)", standaloneElementTag.getTemplateName(), standaloneElementTag.getLine(), standaloneElementTag.getCol());
}
// Set the processor to be executed again, because this time we will just set the "model gathering" mechanism
vars.processorIterator.setLastToBeRepeated(standaloneElementTag);
// Initialize the gathering model, and close it quickly because this is a standalone tag so there
// is only one event to be gathered.
this.modelController.startGatheringDelayedModel(standaloneElementTag, vars);
final IGatheringModelProcessable newModel = this.modelController.getGatheredModel();
this.modelController.resetGathering();
// Process the new gathering model (no need to wait for a "close" event, as this is a standalone)
if (!this.throttleEngine) {
newModel.process();
} else {
queueProcessable(newModel);
}
// Nothing else to be done by this handler... let's just queue the rest of the events in this element
return;
}
/*
* This is not the first time we try to execute this processor, which means the model gathering
* process has already taken place.
*/
// Create the actual Model instance (a clone) that will be passed to the processor to execute on
final Model gatheredModel = currentGatheringModel.getInnerModel();
final Model processedModel = new Model(gatheredModel);
// Execute the processor on the just-created Model
((IElementModelProcessor) processor).process(this.context, processedModel, modelStructureHandler);
// Apply any context modifications made by the processor (local vars, inlining, etc.)
modelStructureHandler.applyContextModifications(this.engineContext);
// Reset the skipbody flags so that the processed model can be executed in the same conditions as the original
currentGatheringModel.resetGatheredSkipFlags();
/*
* Before making any changes and queue the new model for execution, check that it actually is
* a "new" model (the processor might have been no-op on the tag and changes might have been
* only on the local variables, for example.)
*/
if (!gatheredModel.sameAs(processedModel)) {
/*
* Now we will do the exact equivalent to what is performed for an Element Tag processor, when this
* returns a result of type "replaceWithModel".
*/
// Reset model
vars.modelAfter = resetModel(vars.modelAfter, true);
// Set the model to be executed, and set it to be processable (that is a MUST in this case)
vars.modelAfter.addModel(processedModel);
vars.modelAfterProcessable = true;
// We will discard this event (the standalone one) because we are going to process the new, modified
// model instead. Note we do not need to set the body to skip or anything because we know this is a
// standalone tag.
vars.discardEvent = true;
}
} else {
throw new IllegalStateException("An element has been found with an associated processor of type " + processor.getClass().getName() + " which is neither a Tag Element Processor nor a Model Element Processor.");
}
}
/*
* QUEUE MODEL HANDLING (IF WE ARE THROTTLING)
*/
if (this.throttleEngine && ((vars.modelAfter != null && vars.modelAfter.size() > 0) || (vars.modelBefore != null && vars.modelBefore.size() > 0))) {
queueProcessable(new StandaloneElementTagModelProcessable(standaloneElementTag, vars, this.engineContext, this.modelController, this.flowController, this, this.next));
return;
}
/*
* PROCESS THE QUEUE BEFORE DELEGATING, if specified to do so
*/
if (vars.modelBefore != null) {
// This is never processable
vars.modelBefore.process(this.next);
}
/*
* PROCESS THE REST OF THE HANDLER CHAIN
*/
if (!vars.discardEvent) {
this.next.handleStandaloneElement(standaloneElementTag);
}
/*
* PROCESS THE QUEUE, launching all the queued events
*/
if (vars.modelAfter != null) {
vars.modelAfter.process(vars.modelAfterProcessable ? this : this.next);
}
/*
* DECREASE THE CONTEXT LEVEL once we have executed all the processors (and maybe a body if we added
* one to the tag converting it into an open tag)
*/
if (!this.throttleEngine || !this.flowController.stopProcessing) {
if (this.engineContext != null) {
this.engineContext.decreaseLevel();
}
} else {
queueProcessable(this.decreaseContextLevelProcessable);
}
}
use of org.thymeleaf.exceptions.TemplateProcessingException 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.exceptions.TemplateProcessingException in project thymeleaf by thymeleaf.
the class RemainderExpression method executeRemainder.
static Object executeRemainder(final IExpressionContext context, final RemainderExpression expression, final StandardExpressionExecutionContext expContext) {
if (logger.isTraceEnabled()) {
logger.trace("[THYMELEAF][{}] Evaluating remainder 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.remainder(rightNumberValue);
}
throw new TemplateProcessingException("Cannot execute division: operands are \"" + LiteralValue.unwrap(leftValue) + "\" and \"" + LiteralValue.unwrap(rightValue) + "\"");
}
use of org.thymeleaf.exceptions.TemplateProcessingException in project thymeleaf by thymeleaf.
the class AbstractStandardInliner method processExpression.
private String processExpression(final ITemplateContext context, final IStandardExpressionParser expressionParser, final String expression, final boolean escape, final String templateName, final int line, final int col) {
try {
final String unescapedExpression = EscapedAttributeUtils.unescapeAttribute(context.getTemplateMode(), expression);
final Object expressionResult;
if (unescapedExpression != null) {
final IStandardExpression expressionObj = expressionParser.parseExpression(context, unescapedExpression);
expressionResult = expressionObj.execute(context);
} else {
expressionResult = null;
}
if (escape) {
return produceEscapedOutput(expressionResult);
} else {
return (expressionResult == null ? "" : expressionResult.toString());
}
} catch (final TemplateProcessingException e) {
// We will add location info
if (!e.hasTemplateName()) {
e.setTemplateName(templateName);
}
if (!e.hasLineAndCol()) {
e.setLineAndCol(line, col);
}
throw e;
} catch (final Exception e) {
throw new TemplateProcessingException("Error during execution of inlined expression '" + expression + "'", templateName, line, col, e);
}
}
Aggregations