Search in sources :

Example 6 with IElementProcessor

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);
}
Also used : LinkedHashSet(java.util.LinkedHashSet) IExecutionAttributeDialect(org.thymeleaf.dialect.IExecutionAttributeDialect) IExpressionObjectFactory(org.thymeleaf.expression.IExpressionObjectFactory) ITemplateBoundariesProcessor(org.thymeleaf.processor.templateboundaries.ITemplateBoundariesProcessor) ArrayList(java.util.ArrayList) IDialect(org.thymeleaf.dialect.IDialect) IPostProcessorDialect(org.thymeleaf.dialect.IPostProcessorDialect) LinkedHashMap(java.util.LinkedHashMap) ICommentProcessor(org.thymeleaf.processor.comment.ICommentProcessor) ConfigurationException(org.thymeleaf.exceptions.ConfigurationException) ArrayList(java.util.ArrayList) List(java.util.List) EnumMap(java.util.EnumMap) StandardDialect(org.thymeleaf.standard.StandardDialect) ITemplateHandler(org.thymeleaf.engine.ITemplateHandler) IProcessorDialect(org.thymeleaf.dialect.IProcessorDialect) IElementProcessor(org.thymeleaf.processor.element.IElementProcessor) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) EnumMap(java.util.EnumMap) ICDATASectionProcessor(org.thymeleaf.processor.cdatasection.ICDATASectionProcessor) TemplateMode(org.thymeleaf.templatemode.TemplateMode) LinkedHashSet(java.util.LinkedHashSet) Set(java.util.Set) ElementDefinitions(org.thymeleaf.engine.ElementDefinitions) IDocTypeProcessor(org.thymeleaf.processor.doctype.IDocTypeProcessor) IPreProcessorDialect(org.thymeleaf.dialect.IPreProcessorDialect) AttributeDefinitions(org.thymeleaf.engine.AttributeDefinitions) ITextProcessor(org.thymeleaf.processor.text.ITextProcessor) IPostProcessor(org.thymeleaf.postprocessor.IPostProcessor) IProcessingInstructionProcessor(org.thymeleaf.processor.processinginstruction.IProcessingInstructionProcessor) IProcessor(org.thymeleaf.processor.IProcessor) IExpressionObjectDialect(org.thymeleaf.dialect.IExpressionObjectDialect) IXMLDeclarationProcessor(org.thymeleaf.processor.xmldeclaration.IXMLDeclarationProcessor) IPreProcessor(org.thymeleaf.preprocessor.IPreProcessor)

Example 7 with IElementProcessor

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);
}
Also used : IElementProcessor(org.thymeleaf.processor.element.IElementProcessor) LinkedHashSet(java.util.LinkedHashSet) MatchingElementName(org.thymeleaf.processor.element.MatchingElementName) MatchingAttributeName(org.thymeleaf.processor.element.MatchingAttributeName) ConfigurationException(org.thymeleaf.exceptions.ConfigurationException)

Example 8 with IElementProcessor

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);
}
Also used : IElementProcessor(org.thymeleaf.processor.element.IElementProcessor) LinkedHashSet(java.util.LinkedHashSet) MatchingElementName(org.thymeleaf.processor.element.MatchingElementName) TemplateMode(org.thymeleaf.templatemode.TemplateMode) MatchingAttributeName(org.thymeleaf.processor.element.MatchingAttributeName) ConfigurationException(org.thymeleaf.exceptions.ConfigurationException)

Example 9 with IElementProcessor

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);
}
Also used : IElementProcessor(org.thymeleaf.processor.element.IElementProcessor) LinkedHashSet(java.util.LinkedHashSet) MatchingElementName(org.thymeleaf.processor.element.MatchingElementName) TemplateMode(org.thymeleaf.templatemode.TemplateMode) MatchingAttributeName(org.thymeleaf.processor.element.MatchingAttributeName) ConfigurationException(org.thymeleaf.exceptions.ConfigurationException)

Example 10 with IElementProcessor

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);
}
Also used : IElementTagProcessor(org.thymeleaf.processor.element.IElementTagProcessor) IText(org.thymeleaf.model.IText) IOpenElementTag(org.thymeleaf.model.IOpenElementTag) IElementProcessor(org.thymeleaf.processor.element.IElementProcessor) IElementModelProcessor(org.thymeleaf.processor.element.IElementModelProcessor) TemplateProcessingException(org.thymeleaf.exceptions.TemplateProcessingException)

Aggregations

IElementProcessor (org.thymeleaf.processor.element.IElementProcessor)13 LinkedHashSet (java.util.LinkedHashSet)7 ConfigurationException (org.thymeleaf.exceptions.ConfigurationException)7 MatchingElementName (org.thymeleaf.processor.element.MatchingElementName)7 MatchingAttributeName (org.thymeleaf.processor.element.MatchingAttributeName)6 TemplateMode (org.thymeleaf.templatemode.TemplateMode)5 ArrayList (java.util.ArrayList)3 Test (org.junit.Test)2 TemplateProcessingException (org.thymeleaf.exceptions.TemplateProcessingException)2 IText (org.thymeleaf.model.IText)2 IProcessor (org.thymeleaf.processor.IProcessor)2 ICDATASectionProcessor (org.thymeleaf.processor.cdatasection.ICDATASectionProcessor)2 ICommentProcessor (org.thymeleaf.processor.comment.ICommentProcessor)2 IDocTypeProcessor (org.thymeleaf.processor.doctype.IDocTypeProcessor)2 IElementModelProcessor (org.thymeleaf.processor.element.IElementModelProcessor)2 IProcessingInstructionProcessor (org.thymeleaf.processor.processinginstruction.IProcessingInstructionProcessor)2 ITemplateBoundariesProcessor (org.thymeleaf.processor.templateboundaries.ITemplateBoundariesProcessor)2 ITextProcessor (org.thymeleaf.processor.text.ITextProcessor)2 IXMLDeclarationProcessor (org.thymeleaf.processor.xmldeclaration.IXMLDeclarationProcessor)2 EnumMap (java.util.EnumMap)1