use of org.thymeleaf.processor.element.IElementProcessor in project thymeleaf by thymeleaf.
the class DialectSetConfiguration method build.
public static DialectSetConfiguration build(final Set<DialectConfiguration> dialectConfigurations) {
Validate.notNull(dialectConfigurations, "Dialect configuration set cannot be null");
// This set will contain all the dialects - without any additional configuration information
final Set<IDialect> dialects = new LinkedHashSet<IDialect>(dialectConfigurations.size());
// If we find a standard dialect among the configured ones (Standard or SpringStandard), we will report its prefix
boolean standardDialectPresent = false;
String standardDialectPrefix = null;
// This map will be used for merging the execution attributes of all the dialects
final Map<String, Object> executionAttributes = new LinkedHashMap<String, Object>(10, 1.0f);
// This will aggregate all the expression object factories provided by the different dialects
final AggregateExpressionObjectFactory aggregateExpressionObjectFactory = new AggregateExpressionObjectFactory();
// EnumMaps for each type of processor (depending on the structures that they can be applied to)
final EnumMap<TemplateMode, List<ITemplateBoundariesProcessor>> templateBoundariesProcessorListsByTemplateMode = new EnumMap<TemplateMode, List<ITemplateBoundariesProcessor>>(TemplateMode.class);
final EnumMap<TemplateMode, List<ICDATASectionProcessor>> cdataSectionProcessorListsByTemplateMode = new EnumMap<TemplateMode, List<ICDATASectionProcessor>>(TemplateMode.class);
final EnumMap<TemplateMode, List<ICommentProcessor>> commentProcessorListsByTemplateMode = new EnumMap<TemplateMode, List<ICommentProcessor>>(TemplateMode.class);
final EnumMap<TemplateMode, List<IDocTypeProcessor>> docTypeProcessorListsByTemplateMode = new EnumMap<TemplateMode, List<IDocTypeProcessor>>(TemplateMode.class);
final EnumMap<TemplateMode, List<IElementProcessor>> elementProcessorListsByTemplateMode = new EnumMap<TemplateMode, List<IElementProcessor>>(TemplateMode.class);
final EnumMap<TemplateMode, List<IProcessingInstructionProcessor>> processingInstructionProcessorListsByTemplateMode = new EnumMap<TemplateMode, List<IProcessingInstructionProcessor>>(TemplateMode.class);
final EnumMap<TemplateMode, List<ITextProcessor>> textProcessorListsByTemplateMode = new EnumMap<TemplateMode, List<ITextProcessor>>(TemplateMode.class);
final EnumMap<TemplateMode, List<IXMLDeclarationProcessor>> xmlDeclarationProcessorListsByTemplateMode = new EnumMap<TemplateMode, List<IXMLDeclarationProcessor>>(TemplateMode.class);
// Lists for merging all pre and postprocessors from all dialects
final EnumMap<TemplateMode, List<IPreProcessor>> preProcessorListsByTemplateMode = new EnumMap<TemplateMode, List<IPreProcessor>>(TemplateMode.class);
final EnumMap<TemplateMode, List<IPostProcessor>> postProcessorListsByTemplateMode = new EnumMap<TemplateMode, List<IPostProcessor>>(TemplateMode.class);
/*
* ITERATE ALL DIALECTS, processing each one according to its features
*/
for (final DialectConfiguration dialectConfiguration : dialectConfigurations) {
// cannot be null -- ConfigurationDialect checks this
final IDialect dialect = dialectConfiguration.getDialect();
/*
* STEP ONE for each dialect: process, initialize and merge processors
*/
if (dialect instanceof IProcessorDialect) {
final IProcessorDialect processorDialect = (IProcessorDialect) dialect;
// Might be null if the dialect has been specified to use no prefix (or that is the default of such dialect)
final String dialectPrefix = (dialectConfiguration.isPrefixSpecified() ? dialectConfiguration.getPrefix() : processorDialect.getPrefix());
if (dialect instanceof StandardDialect) {
standardDialectPresent = true;
standardDialectPrefix = dialectPrefix;
}
final Set<IProcessor> dialectProcessors = processorDialect.getProcessors(dialectPrefix);
if (dialectProcessors == null) {
throw new ConfigurationException("Dialect should not return null processor set: " + dialect.getClass().getName());
}
for (final IProcessor dialectProcessor : dialectProcessors) {
if (dialectProcessor == null) {
throw new ConfigurationException("Dialect should not return null processor in processor set: " + dialect.getClass().getName());
}
// Obtain and check template mode
final TemplateMode templateMode = dialectProcessor.getTemplateMode();
if (templateMode == null) {
throw new ConfigurationException("Template mode cannot be null (processor: " + dialectProcessor.getClass().getName() + ")");
}
if (dialectProcessor instanceof IElementProcessor) {
// can be either a tag processor or a node one
List<IElementProcessor> processorsForTemplateMode = elementProcessorListsByTemplateMode.get(templateMode);
if (processorsForTemplateMode == null) {
processorsForTemplateMode = new ArrayList<IElementProcessor>(5);
elementProcessorListsByTemplateMode.put(templateMode, processorsForTemplateMode);
}
processorsForTemplateMode.add(ProcessorConfigurationUtils.wrap((IElementProcessor) dialectProcessor, processorDialect));
Collections.sort(processorsForTemplateMode, ProcessorComparators.PROCESSOR_COMPARATOR);
} else if (dialectProcessor instanceof ITemplateBoundariesProcessor) {
List<ITemplateBoundariesProcessor> processorsForTemplateMode = templateBoundariesProcessorListsByTemplateMode.get(templateMode);
if (processorsForTemplateMode == null) {
processorsForTemplateMode = new ArrayList<ITemplateBoundariesProcessor>(5);
templateBoundariesProcessorListsByTemplateMode.put(templateMode, processorsForTemplateMode);
}
processorsForTemplateMode.add(ProcessorConfigurationUtils.wrap((ITemplateBoundariesProcessor) dialectProcessor, processorDialect));
Collections.sort(processorsForTemplateMode, ProcessorComparators.PROCESSOR_COMPARATOR);
} else if (dialectProcessor instanceof ICDATASectionProcessor) {
List<ICDATASectionProcessor> processorsForTemplateMode = cdataSectionProcessorListsByTemplateMode.get(templateMode);
if (processorsForTemplateMode == null) {
processorsForTemplateMode = new ArrayList<ICDATASectionProcessor>(5);
cdataSectionProcessorListsByTemplateMode.put(templateMode, processorsForTemplateMode);
}
processorsForTemplateMode.add(ProcessorConfigurationUtils.wrap((ICDATASectionProcessor) dialectProcessor, processorDialect));
Collections.sort(processorsForTemplateMode, ProcessorComparators.PROCESSOR_COMPARATOR);
} else if (dialectProcessor instanceof ICommentProcessor) {
List<ICommentProcessor> processorsForTemplateMode = commentProcessorListsByTemplateMode.get(templateMode);
if (processorsForTemplateMode == null) {
processorsForTemplateMode = new ArrayList<ICommentProcessor>(5);
commentProcessorListsByTemplateMode.put(templateMode, processorsForTemplateMode);
}
processorsForTemplateMode.add(ProcessorConfigurationUtils.wrap((ICommentProcessor) dialectProcessor, processorDialect));
Collections.sort(processorsForTemplateMode, ProcessorComparators.PROCESSOR_COMPARATOR);
} else if (dialectProcessor instanceof IDocTypeProcessor) {
List<IDocTypeProcessor> processorsForTemplateMode = docTypeProcessorListsByTemplateMode.get(templateMode);
if (processorsForTemplateMode == null) {
processorsForTemplateMode = new ArrayList<IDocTypeProcessor>(5);
docTypeProcessorListsByTemplateMode.put(templateMode, processorsForTemplateMode);
}
processorsForTemplateMode.add(ProcessorConfigurationUtils.wrap((IDocTypeProcessor) dialectProcessor, processorDialect));
Collections.sort(processorsForTemplateMode, ProcessorComparators.PROCESSOR_COMPARATOR);
} else if (dialectProcessor instanceof IProcessingInstructionProcessor) {
List<IProcessingInstructionProcessor> processorsForTemplateMode = processingInstructionProcessorListsByTemplateMode.get(templateMode);
if (processorsForTemplateMode == null) {
processorsForTemplateMode = new ArrayList<IProcessingInstructionProcessor>(5);
processingInstructionProcessorListsByTemplateMode.put(templateMode, processorsForTemplateMode);
}
processorsForTemplateMode.add(ProcessorConfigurationUtils.wrap((IProcessingInstructionProcessor) dialectProcessor, processorDialect));
Collections.sort(processorsForTemplateMode, ProcessorComparators.PROCESSOR_COMPARATOR);
} else if (dialectProcessor instanceof ITextProcessor) {
List<ITextProcessor> processorsForTemplateMode = textProcessorListsByTemplateMode.get(templateMode);
if (processorsForTemplateMode == null) {
processorsForTemplateMode = new ArrayList<ITextProcessor>(5);
textProcessorListsByTemplateMode.put(templateMode, processorsForTemplateMode);
}
processorsForTemplateMode.add(ProcessorConfigurationUtils.wrap((ITextProcessor) dialectProcessor, processorDialect));
Collections.sort(processorsForTemplateMode, ProcessorComparators.PROCESSOR_COMPARATOR);
} else if (dialectProcessor instanceof IXMLDeclarationProcessor) {
List<IXMLDeclarationProcessor> processorsForTemplateMode = xmlDeclarationProcessorListsByTemplateMode.get(templateMode);
if (processorsForTemplateMode == null) {
processorsForTemplateMode = new ArrayList<IXMLDeclarationProcessor>(5);
xmlDeclarationProcessorListsByTemplateMode.put(templateMode, processorsForTemplateMode);
}
processorsForTemplateMode.add(ProcessorConfigurationUtils.wrap((IXMLDeclarationProcessor) dialectProcessor, processorDialect));
Collections.sort(processorsForTemplateMode, ProcessorComparators.PROCESSOR_COMPARATOR);
}
}
}
/*
* STEP TWO for each dialect: merge execution attributes
*/
if (dialect instanceof IExecutionAttributeDialect) {
final Map<String, Object> dialectExecutionAttributes = ((IExecutionAttributeDialect) dialect).getExecutionAttributes();
if (dialectExecutionAttributes != null) {
for (final Map.Entry<String, Object> entry : dialectExecutionAttributes.entrySet()) {
final String executionAttributeName = entry.getKey();
if (executionAttributes.containsKey(executionAttributeName)) {
throw new ConfigurationException("Conflicting execution attribute. Two or more dialects specify an execution " + "attribute with the same name \"" + executionAttributeName + "\".");
}
executionAttributes.put(entry.getKey(), entry.getValue());
}
}
}
/*
* STEP THREE for each dialect: aggregate all the expression object factories
*/
if (dialect instanceof IExpressionObjectDialect) {
final IExpressionObjectFactory factory = ((IExpressionObjectDialect) dialect).getExpressionObjectFactory();
if (factory != null) {
aggregateExpressionObjectFactory.add(factory);
}
}
/*
* STEP FOUR for each dialect: aggregate pre-processors (and check the correctness of the list)
*/
if (dialect instanceof IPreProcessorDialect) {
final Set<IPreProcessor> dialectPreProcessors = ((IPreProcessorDialect) dialect).getPreProcessors();
if (dialectPreProcessors != null) {
for (final IPreProcessor preProcessor : dialectPreProcessors) {
if (preProcessor == null) {
throw new ConfigurationException("Pre-Processor list for dialect " + dialect.getClass().getName() + " includes a " + "null entry, which is forbidden.");
}
// Obtain and check template mode
final TemplateMode templateMode = preProcessor.getTemplateMode();
if (templateMode == null) {
throw new ConfigurationException("Template mode cannot be null (pre-processor: " + preProcessor.getClass().getName() + ", dialect" + dialect.getClass().getName() + ")");
}
// Check the handler class: should extend ITemplateHandler and have an empty constructor
final Class<?> handlerClass = preProcessor.getHandlerClass();
if (handlerClass == null) {
throw new ConfigurationException("Pre-Processor " + preProcessor.getClass().getName() + " for dialect " + preProcessor.getClass().getName() + " returns a null handler class, which is forbidden.");
}
if (!ITemplateHandler.class.isAssignableFrom(handlerClass)) {
throw new ConfigurationException("Handler class " + handlerClass.getName() + " specified for " + "pre-processor " + preProcessor.getClass().getName() + " in dialect " + dialect.getClass().getName() + " does not implement required " + "interface " + ITemplateHandler.class.getName());
}
try {
// Check the empty constructor is present -- we will need to use it for creating new instances
handlerClass.getConstructor(new Class[0]);
} catch (final NoSuchMethodException e) {
throw new ConfigurationException("Pre-Processor class " + handlerClass.getName() + " specified for " + "pre-processor " + preProcessor.getClass().getName() + " in dialect " + dialect.getClass().getName() + " does not implement required " + "zero-argument constructor.", e);
}
// Add the pre-processor to its corresponding map and sort
List<IPreProcessor> preProcessorsForTemplateMode = preProcessorListsByTemplateMode.get(templateMode);
if (preProcessorsForTemplateMode == null) {
preProcessorsForTemplateMode = new ArrayList<IPreProcessor>(5);
preProcessorListsByTemplateMode.put(templateMode, preProcessorsForTemplateMode);
}
preProcessorsForTemplateMode.add(preProcessor);
Collections.sort(preProcessorsForTemplateMode, ProcessorComparators.PRE_PROCESSOR_COMPARATOR);
}
}
}
/*
* STEP FIVE for each dialect: aggregate post-processors (and check the correctness of the list)
*/
if (dialect instanceof IPostProcessorDialect) {
final Set<IPostProcessor> dialectPostProcessors = ((IPostProcessorDialect) dialect).getPostProcessors();
if (dialectPostProcessors != null) {
for (final IPostProcessor postProcessor : dialectPostProcessors) {
if (postProcessor == null) {
throw new ConfigurationException("Post-Processor list for dialect " + dialect.getClass().getName() + " includes a " + "null entry, which is forbidden.");
}
// Obtain and check template mode
final TemplateMode templateMode = postProcessor.getTemplateMode();
if (templateMode == null) {
throw new ConfigurationException("Template mode cannot be null (post-processor: " + postProcessor.getClass().getName() + ", dialect" + dialect.getClass().getName() + ")");
}
// Check the handler class: should extend ITemplateHandler and have an empty constructor
final Class<?> handlerClass = postProcessor.getHandlerClass();
if (handlerClass == null) {
throw new ConfigurationException("Post-Processor " + postProcessor.getClass().getName() + " for dialect " + postProcessor.getClass().getName() + " returns a null handler class, which is forbidden.");
}
if (!ITemplateHandler.class.isAssignableFrom(handlerClass)) {
throw new ConfigurationException("Handler class " + handlerClass.getName() + " specified for " + "post-processor " + postProcessor.getClass().getName() + " in dialect " + dialect.getClass().getName() + " does not implement required " + "interface " + ITemplateHandler.class.getName());
}
try {
// Check the empty constructor is present -- we will need to use it for creating new instances
handlerClass.getConstructor(new Class[0]);
} catch (final NoSuchMethodException e) {
throw new ConfigurationException("Post-Processor class " + handlerClass.getName() + " specified for " + "post-processor " + postProcessor.getClass().getName() + " in dialect " + dialect.getClass().getName() + " does not implement required " + "zero-argument constructor.", e);
}
// Add the pre-processor to its corresponding map and sort
List<IPostProcessor> postProcessorsForTemplateMode = postProcessorListsByTemplateMode.get(templateMode);
if (postProcessorsForTemplateMode == null) {
postProcessorsForTemplateMode = new ArrayList<IPostProcessor>(5);
postProcessorListsByTemplateMode.put(templateMode, postProcessorsForTemplateMode);
}
postProcessorsForTemplateMode.add(postProcessor);
Collections.sort(postProcessorsForTemplateMode, ProcessorComparators.POST_PROCESSOR_COMPARATOR);
}
}
}
/*
* LAST STEP for each dialect: add it to the dialects set
*/
dialects.add(dialect);
}
// Time to turn the list-based structures into sets -- we needed the lists because we needed a way to order them using Collections.sort()
final EnumMap<TemplateMode, Set<ITemplateBoundariesProcessor>> templateBoundariesProcessorsByTemplateMode = listMapToSetMap(templateBoundariesProcessorListsByTemplateMode);
final EnumMap<TemplateMode, Set<ICDATASectionProcessor>> cdataSectionProcessorsByTemplateMode = listMapToSetMap(cdataSectionProcessorListsByTemplateMode);
final EnumMap<TemplateMode, Set<ICommentProcessor>> commentProcessorsByTemplateMode = listMapToSetMap(commentProcessorListsByTemplateMode);
final EnumMap<TemplateMode, Set<IDocTypeProcessor>> docTypeProcessorsByTemplateMode = listMapToSetMap(docTypeProcessorListsByTemplateMode);
final EnumMap<TemplateMode, Set<IElementProcessor>> elementProcessorsByTemplateMode = listMapToSetMap(elementProcessorListsByTemplateMode);
final EnumMap<TemplateMode, Set<IProcessingInstructionProcessor>> processingInstructionProcessorsByTemplateMode = listMapToSetMap(processingInstructionProcessorListsByTemplateMode);
final EnumMap<TemplateMode, Set<ITextProcessor>> textProcessorsByTemplateMode = listMapToSetMap(textProcessorListsByTemplateMode);
final EnumMap<TemplateMode, Set<IXMLDeclarationProcessor>> xmlDeclarationProcessorsByTemplateMode = listMapToSetMap(xmlDeclarationProcessorListsByTemplateMode);
final EnumMap<TemplateMode, Set<IPreProcessor>> preProcessorsByTemplateMode = listMapToSetMap(preProcessorListsByTemplateMode);
final EnumMap<TemplateMode, Set<IPostProcessor>> postProcessorsByTemplateMode = listMapToSetMap(postProcessorListsByTemplateMode);
// Initialize the ElementDefinitions and AttributeDefinitions structures -- they need the element processors so that these
// are directly applied to the element/attribute definitions and therefore per element/attribute matching is not required
// during template processing.
final ElementDefinitions elementDefinitions = new ElementDefinitions(elementProcessorsByTemplateMode);
final AttributeDefinitions attributeDefinitions = new AttributeDefinitions(elementProcessorsByTemplateMode);
// Traverse the sets of processors in order to set the AttributeDefinitions and/or ElementDefinitions objects
// to those that need them in order to initialize and cache attribute/element definition-related structures
initializeDefinitionsForProcessors(templateBoundariesProcessorsByTemplateMode, elementDefinitions, attributeDefinitions);
initializeDefinitionsForProcessors(cdataSectionProcessorsByTemplateMode, elementDefinitions, attributeDefinitions);
initializeDefinitionsForProcessors(commentProcessorsByTemplateMode, elementDefinitions, attributeDefinitions);
initializeDefinitionsForProcessors(docTypeProcessorsByTemplateMode, elementDefinitions, attributeDefinitions);
initializeDefinitionsForProcessors(elementProcessorsByTemplateMode, elementDefinitions, attributeDefinitions);
initializeDefinitionsForProcessors(processingInstructionProcessorsByTemplateMode, elementDefinitions, attributeDefinitions);
initializeDefinitionsForProcessors(textProcessorsByTemplateMode, elementDefinitions, attributeDefinitions);
initializeDefinitionsForProcessors(xmlDeclarationProcessorsByTemplateMode, elementDefinitions, attributeDefinitions);
initializeDefinitionsForPreProcessors(preProcessorsByTemplateMode, elementDefinitions, attributeDefinitions);
initializeDefinitionsForPostProcessors(postProcessorsByTemplateMode, elementDefinitions, attributeDefinitions);
return new DialectSetConfiguration(new LinkedHashSet<DialectConfiguration>(dialectConfigurations), dialects, standardDialectPresent, standardDialectPrefix, executionAttributes, aggregateExpressionObjectFactory, elementDefinitions, attributeDefinitions, templateBoundariesProcessorsByTemplateMode, cdataSectionProcessorsByTemplateMode, commentProcessorsByTemplateMode, docTypeProcessorsByTemplateMode, elementProcessorsByTemplateMode, processingInstructionProcessorsByTemplateMode, textProcessorsByTemplateMode, xmlDeclarationProcessorsByTemplateMode, preProcessorsByTemplateMode, postProcessorsByTemplateMode);
}
use of org.thymeleaf.processor.element.IElementProcessor in project thymeleaf by thymeleaf.
the class AttributeDefinitions method buildTextAttributeDefinition.
private static TextAttributeDefinition buildTextAttributeDefinition(final TemplateMode templateMode, final TextAttributeName name, final Set<IElementProcessor> elementProcessors) {
// No need to use a list for sorting - the elementProcessors set has already been ordered
final Set<IElementProcessor> associatedProcessors = new LinkedHashSet<IElementProcessor>(2);
if (elementProcessors != null) {
for (final IElementProcessor processor : elementProcessors) {
if (processor.getTemplateMode() != templateMode) {
// We are creating a text element definition, therefore we are only interested on XML processors
continue;
}
final MatchingElementName matchingElementName = processor.getMatchingElementName();
final MatchingAttributeName matchingAttributeName = processor.getMatchingAttributeName();
if ((matchingElementName != null && matchingElementName.getTemplateMode() != templateMode) || (matchingAttributeName != null && matchingAttributeName.getTemplateMode() != templateMode)) {
throw new ConfigurationException(templateMode + " processors must return " + templateMode + "element names and " + templateMode + " attribute names (processor: " + processor.getClass().getName() + ")");
}
if (matchingAttributeName == null || matchingAttributeName.isMatchingAllAttributes()) {
// This processor does not relate to a specific attribute - surely an element processor
continue;
}
if (!matchingAttributeName.matches(name)) {
// Doesn't match. This processor is not associated with this attribute
continue;
}
associatedProcessors.add(processor);
}
}
// Build the final instance
return new TextAttributeDefinition(name, associatedProcessors);
}
use of org.thymeleaf.processor.element.IElementProcessor in project thymeleaf by thymeleaf.
the class AttributeDefinitions method buildHTMLAttributeDefinition.
private static HTMLAttributeDefinition buildHTMLAttributeDefinition(final HTMLAttributeName name, final Set<IElementProcessor> elementProcessors) {
// No need to use a list for sorting - the elementProcessors set has already been ordered
final Set<IElementProcessor> associatedProcessors = new LinkedHashSet<IElementProcessor>(2);
if (elementProcessors != null) {
for (final IElementProcessor processor : elementProcessors) {
// Cannot be null -- has been previously validated
final TemplateMode templateMode = processor.getTemplateMode();
if (templateMode != TemplateMode.HTML) {
// We are creating an HTML element definition, therefore we are only interested on HTML processors
continue;
}
final MatchingElementName matchingElementName = processor.getMatchingElementName();
final MatchingAttributeName matchingAttributeName = processor.getMatchingAttributeName();
if ((matchingElementName != null && matchingElementName.getTemplateMode() != TemplateMode.HTML) || (matchingAttributeName != null && matchingAttributeName.getTemplateMode() != TemplateMode.HTML)) {
throw new ConfigurationException("HTML processors must return HTML element names and HTML attribute names (processor: " + processor.getClass().getName() + ")");
}
if (matchingAttributeName == null || matchingAttributeName.isMatchingAllAttributes()) {
// This processor does not relate to a specific attribute - surely an element processor
continue;
}
if (!matchingAttributeName.matches(name)) {
// Doesn't match. This processor is not associated with this attribute
continue;
}
associatedProcessors.add(processor);
}
}
// Compute whether this attribute is to be considered boolean or not
boolean booleanAttribute = false;
for (final String completeAttributeName : name.getCompleteAttributeNames()) {
if (ALL_STANDARD_BOOLEAN_HTML_ATTRIBUTE_NAMES.contains(completeAttributeName)) {
booleanAttribute = true;
}
}
// Build the final instance
return new HTMLAttributeDefinition(name, booleanAttribute, associatedProcessors);
}
use of org.thymeleaf.processor.element.IElementProcessor in project thymeleaf by thymeleaf.
the class AttributeDefinitions method buildXMLAttributeDefinition.
private static XMLAttributeDefinition buildXMLAttributeDefinition(final XMLAttributeName name, final Set<IElementProcessor> elementProcessors) {
// No need to use a list for sorting - the elementProcessors set has already been ordered
final Set<IElementProcessor> associatedProcessors = new LinkedHashSet<IElementProcessor>(2);
if (elementProcessors != null) {
for (final IElementProcessor processor : elementProcessors) {
// Cannot be null -- has been previously validated
final TemplateMode templateMode = processor.getTemplateMode();
if (templateMode != TemplateMode.XML) {
// We are creating an XML element definition, therefore we are only interested on XML processors
continue;
}
final MatchingElementName matchingElementName = processor.getMatchingElementName();
final MatchingAttributeName matchingAttributeName = processor.getMatchingAttributeName();
if ((matchingElementName != null && matchingElementName.getTemplateMode() != TemplateMode.XML) || (matchingAttributeName != null && matchingAttributeName.getTemplateMode() != TemplateMode.XML)) {
throw new ConfigurationException("XML processors must return XML element names and XML attribute names (processor: " + processor.getClass().getName() + ")");
}
if (matchingAttributeName == null || matchingAttributeName.isMatchingAllAttributes()) {
// This processor does not relate to a specific attribute - surely an element processor
continue;
}
if (!matchingAttributeName.matches(name)) {
// Doesn't match. This processor is not associated with this attribute
continue;
}
associatedProcessors.add(processor);
}
}
// Build the final instance
return new XMLAttributeDefinition(name, associatedProcessors);
}
use of org.thymeleaf.processor.element.IElementProcessor 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