use of org.thymeleaf.processor.element.IElementTagProcessor 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.processor.element.IElementTagProcessor in project thymeleaf by thymeleaf.
the class ConfigurationPrinterHelper method printProcessorsForTemplateMode.
private static void printProcessorsForTemplateMode(final ConfigLogBuilder logBuilder, final Set<IProcessor> processors, final TemplateMode templateMode) {
if (processors == null || processors.isEmpty()) {
return;
}
final List<ICDATASectionProcessor> cdataSectionProcessors = new ArrayList<ICDATASectionProcessor>();
final List<ICommentProcessor> commentProcessors = new ArrayList<ICommentProcessor>();
final List<IDocTypeProcessor> docTypeProcessors = new ArrayList<IDocTypeProcessor>();
final List<IElementTagProcessor> elementTagProcessors = new ArrayList<IElementTagProcessor>();
final List<IElementModelProcessor> elementModelProcessors = new ArrayList<IElementModelProcessor>();
final List<IProcessingInstructionProcessor> processingInstructionProcessors = new ArrayList<IProcessingInstructionProcessor>();
final List<ITextProcessor> textProcessors = new ArrayList<ITextProcessor>();
final List<IXMLDeclarationProcessor> xmlDeclarationProcessors = new ArrayList<IXMLDeclarationProcessor>();
boolean processorsForTemplateModeExist = false;
for (final IProcessor processor : processors) {
if (!templateMode.equals(processor.getTemplateMode())) {
continue;
}
processorsForTemplateModeExist = true;
if (processor instanceof ICDATASectionProcessor) {
cdataSectionProcessors.add((ICDATASectionProcessor) processor);
} else if (processor instanceof ICommentProcessor) {
commentProcessors.add((ICommentProcessor) processor);
} else if (processor instanceof IDocTypeProcessor) {
docTypeProcessors.add((IDocTypeProcessor) processor);
} else if (processor instanceof IElementTagProcessor) {
elementTagProcessors.add((IElementTagProcessor) processor);
} else if (processor instanceof IElementModelProcessor) {
elementModelProcessors.add((IElementModelProcessor) processor);
} else if (processor instanceof IProcessingInstructionProcessor) {
processingInstructionProcessors.add((IProcessingInstructionProcessor) processor);
} else if (processor instanceof ITextProcessor) {
textProcessors.add((ITextProcessor) processor);
} else if (processor instanceof IXMLDeclarationProcessor) {
xmlDeclarationProcessors.add((IXMLDeclarationProcessor) processor);
}
}
if (!processorsForTemplateModeExist) {
// Nothing to show, there are no processors for this template mode
return;
}
logBuilder.line("[THYMELEAF] * Processors for Template Mode: {}", templateMode);
Collections.sort(cdataSectionProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
Collections.sort(commentProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
Collections.sort(docTypeProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
Collections.sort(elementTagProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
Collections.sort(elementModelProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
Collections.sort(processingInstructionProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
Collections.sort(textProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
Collections.sort(xmlDeclarationProcessors, ProcessorComparators.PROCESSOR_COMPARATOR);
if (!elementTagProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * Element Tag Processors by [matching element and attribute name] [precedence]:");
for (final IElementTagProcessor processor : elementTagProcessors) {
final MatchingElementName matchingElementName = processor.getMatchingElementName();
final MatchingAttributeName matchingAttributeName = processor.getMatchingAttributeName();
final String elementName = (matchingElementName == null ? "*" : matchingElementName.toString());
final String attributeName = (matchingAttributeName == null ? "*" : matchingAttributeName.toString());
logBuilder.line("[THYMELEAF] * [{} {}] [{}]: {}", new Object[] { elementName, attributeName, Integer.valueOf(processor.getPrecedence()), processor.getClass().getName() });
}
}
if (!elementModelProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * Element Model Processors by [matching element and attribute name] [precedence]:");
for (final IElementModelProcessor processor : elementModelProcessors) {
final MatchingElementName matchingElementName = processor.getMatchingElementName();
final MatchingAttributeName matchingAttributeName = processor.getMatchingAttributeName();
final String elementName = (matchingElementName == null ? "*" : matchingElementName.toString());
final String attributeName = (matchingAttributeName == null ? "*" : matchingAttributeName.toString());
logBuilder.line("[THYMELEAF] * [{} {}] [{}]: {}", new Object[] { elementName, attributeName, Integer.valueOf(processor.getPrecedence()), processor.getClass().getName() });
}
}
if (!textProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * Text Processors by [precedence]:");
for (final ITextProcessor processor : textProcessors) {
logBuilder.line("[THYMELEAF] * [{}]: {}", new Object[] { Integer.valueOf(processor.getPrecedence()), processor.getClass().getName() });
}
}
if (!docTypeProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * DOCTYPE Processors by [precedence]:");
for (final IDocTypeProcessor processor : docTypeProcessors) {
logBuilder.line("[THYMELEAF] * [{}]: {}", new Object[] { Integer.valueOf(processor.getPrecedence()), processor.getClass().getName() });
}
}
if (!cdataSectionProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * CDATA Section Processors by [precedence]:");
for (final ICDATASectionProcessor processor : cdataSectionProcessors) {
logBuilder.line("[THYMELEAF] * [{}]: {}", new Object[] { Integer.valueOf(processor.getPrecedence()), processor.getClass().getName() });
}
}
if (!commentProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * Comment Processors by [precedence]:");
for (final ICommentProcessor processor : commentProcessors) {
logBuilder.line("[THYMELEAF] * [{}]: {}", new Object[] { Integer.valueOf(processor.getPrecedence()), processor.getClass().getName() });
}
}
if (!xmlDeclarationProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * XML Declaration Processors by [precedence]:");
for (final IXMLDeclarationProcessor processor : xmlDeclarationProcessors) {
logBuilder.line("[THYMELEAF] * [{}]: {}", new Object[] { Integer.valueOf(processor.getPrecedence()), processor.getClass().getName() });
}
}
if (!processingInstructionProcessors.isEmpty()) {
logBuilder.line("[THYMELEAF] * Processing Instruction Processors by [precedence]:");
for (final IProcessingInstructionProcessor processor : processingInstructionProcessors) {
logBuilder.line("[THYMELEAF] * [{}]: {}", new Object[] { Integer.valueOf(processor.getPrecedence()), processor.getClass().getName() });
}
}
}
use of org.thymeleaf.processor.element.IElementTagProcessor in project thymeleaf by thymeleaf.
the class ProcessorTemplateHandler method handleOpenElement.
@Override
public void handleOpenElement(final IOpenElementTag iopenElementTag) {
/*
* 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(iopenElementTag);
return;
}
/*
* CHECK WHETHER WE ARE GATHERING AN ELEMENT's MODEL
*/
if (!this.modelController.shouldProcessOpenElement(iopenElementTag)) {
return;
}
/*
* CAST TO ENGINE-SPECIFIC IMPLEMENTATION, which will ease the handling of the structure during processing
*/
OpenElementTag openElementTag = OpenElementTag.asEngineOpenElementTag(iopenElementTag);
/*
* 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 && !openElementTag.hasAssociatedProcessors()) {
this.next.handleOpenElement(openElementTag);
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(openElementTag)) != null) {
tagStructureHandler.reset();
modelStructureHandler.reset();
if (processor instanceof IElementTagProcessor) {
final IElementTagProcessor elementProcessor = ((IElementTagProcessor) processor);
elementProcessor.process(this.context, openElementTag, 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.
openElementTag = tagStructureHandler.applyAttributes(this.attributeDefinitions, openElementTag);
if (tagStructureHandler.iterateElement) {
// Initialize the gathering model
this.modelController.startGatheringIteratedModel(openElementTag, vars, tagStructureHandler.iterVariableName, tagStructureHandler.iterStatusVariableName, tagStructureHandler.iteratedObject);
// Nothing else to be done by this handler... let's just queue the rest of the events to be iterated
return;
} else if (tagStructureHandler.setBodyText) {
// 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.setBodyTextProcessable;
// Add the new Text to the queue
vars.modelAfter.add(new Text(tagStructureHandler.setBodyTextValue));
// All the body of the original open tag should be skipped (has just been replaced)
vars.skipBody = SkipBody.SKIP_ALL;
} else if (tagStructureHandler.setBodyModel) {
// 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.setBodyModelProcessable;
// Add the new body model to the queue
vars.modelAfter.addModel(tagStructureHandler.setBodyModelValue);
// All the body of the original open tag should be skipped (has just been replaced)
vars.skipBody = SkipBody.SKIP_ALL;
} 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);
// No intervention on the body flags - we will not be removing the body, just inserting before it
} 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, its body and its corresponding close tag have to be replaced.
vars.discardEvent = true;
vars.skipBody = SkipBody.SKIP_ALL;
vars.skipCloseTag = 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, its body and its corresponding close tag have to be replaced.
vars.discardEvent = true;
vars.skipBody = SkipBody.SKIP_ALL;
vars.skipCloseTag = true;
} else if (tagStructureHandler.removeElement) {
// Reset model, but only if it already exists
vars.modelAfter = resetModel(vars.modelAfter, false);
// We are removing the element (complete with body + close tag). No further processing will be allowed
vars.discardEvent = true;
vars.skipBody = SkipBody.SKIP_ALL;
vars.skipCloseTag = true;
} else if (tagStructureHandler.removeTags) {
// No modifications to the queue - it's just the tag that will be removed, not its possible body
vars.discardEvent = true;
vars.skipCloseTag = true;
} else if (tagStructureHandler.removeBody) {
// Reset model, but only if it already exists
vars.modelAfter = resetModel(vars.modelAfter, false);
// We will be only removing the body contents, not the tag itself
vars.skipBody = SkipBody.SKIP_ALL;
} else if (tagStructureHandler.removeAllButFirstChild) {
// Reset model, but only if it already exists
vars.modelAfter = resetModel(vars.modelAfter, false);
// This special SkipBody value will allow the first child element (open-body-close or standalone)
// to be processed, but only that. Once it has been processed, the eventModelController will change
// this value to SkipBody.SKIP_ELEMENTS.
//
// Note that all non-element child events before and after the first element child event will be
// processed normally.
vars.skipBody = SkipBody.PROCESS_ONE_ELEMENT;
}
} 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)", openElementTag.getTemplateName(), openElementTag.getLine(), openElementTag.getCol());
}
// Set the processor to be executed again, because this time we will just set the "model gathering" mechanism
vars.processorIterator.setLastToBeRepeated(openElementTag);
// Initialize the gathering model. This will be concluded (and the gathering model set for
// processing) at the corresponding close tag.
this.modelController.startGatheringDelayedModel(openElementTag, vars);
// 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 the 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;
// Given we are going to execute the modified model instead of the gathered one, we will set all body
// skipping flags just as if we had just executed a "replaceWithModel" operation.
vars.discardEvent = true;
vars.skipBody = SkipBody.SKIP_ALL;
vars.skipCloseTag = 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 OpenElementTagModelProcessable(openElementTag, vars, 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 and INCREASE THE MODEL LEVEL RIGHT AFTERWARDS
*/
if (!vars.discardEvent) {
this.next.handleOpenElement(openElementTag);
}
/*
* PROCESS THE QUEUE, launching all the queued events. Note executing the queue after increasing the model
* level makes sense even if what the queue contains is a replacement for the complete element (including open
* and close tags), because that way whatever comes in the queue will be encapsulated in a different model level
* and its internal open/close tags should not affect the correct delimitation of this block.
*/
if (vars.modelAfter != null) {
vars.modelAfter.process(vars.modelAfterProcessable ? this : this.next);
}
/*
* SET BODY TO BE SKIPPED, if required. Importantly, this has to be done AFTER executing the queue
*/
this.modelController.skip(vars.skipBody, vars.skipCloseTag);
}
Aggregations