Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* =============================================================================
*
* Copyright (c) 2011, The THYMELEAF team (http://www.thymeleaf.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* =============================================================================
*/
package org.thymeleaf;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.thymeleaf.exceptions.NoAvailableProcessorException;
import org.thymeleaf.exceptions.ParsingException;
import org.thymeleaf.processor.SubstitutionTag;
import org.thymeleaf.processor.attr.AttrProcessResult;
import org.thymeleaf.processor.attr.IAttrProcessor;
import org.thymeleaf.processor.tag.ITagProcessor;
import org.thymeleaf.processor.tag.TagProcessResult;
import org.thymeleaf.templateresolver.TemplateResolution;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
/**
*
* @author Daniel Fernández
*
* @since 1.0
*
*/
final class DOMDocumentProcessor {
private static final Logger logger = LoggerFactory.getLogger(DOMDocumentProcessor.class);
static DocumentType transform(final Arguments arguments, final TemplateResolution templateResolution, final Document document) {
final TemplateMode templateMode = templateResolution.getTemplateMode();
final NodeList docChildren = document.getChildNodes();
DocumentType docType = null;
Element rootElement = null;
if (docChildren.getLength() < 1) {
throw new ParsingException(
"Invalid document structure: no root element found.");
}
if (docChildren.getLength() == 1) {
if (docChildren.item(0).getNodeType() != Node.ELEMENT_NODE) {
throw new ParsingException(
"Invalid document structure: If only one top-level node exists, this " +
"has to be the root element.");
}
if (templateMode.isXHTML() || templateMode.isHTML5()) {
throw new ParsingException(
"Invalid document structure: Web templates (XHTML/HTML5) must include a DOCTYPE declaration.");
}
rootElement = (Element) docChildren.item(0);
} else if (docChildren.getLength() == 2) {
if (docChildren.item(0).getNodeType() != Node.DOCUMENT_TYPE_NODE) {
throw new ParsingException(
"Invalid document structure: If two top-level nodes exist, the first one " +
"has to be the DOCTYPE.");
}
if (docChildren.item(1).getNodeType() != Node.ELEMENT_NODE) {
throw new ParsingException(
"Invalid document structure: If two top-level nodes exist, the second one " +
"has to be the root element.");
}
docType = (DocumentType) docChildren.item(0);
final String publicId = docType.getPublicId();
final String systemId = docType.getSystemId();
if (templateMode.isHTML5()) {
if (!((publicId == null || publicId.trim().equals("")) &&
(systemId == null || systemId.trim().equals("") || systemId.trim().equalsIgnoreCase(Standards.HTML_5_LEGACY_WILDCARD_SYSTEMID.getValue())))) {
throw new ParsingException(
"Template is being processed in " + templateMode + " mode. Only " +
"\"\" and \"\" " +
"are allowed.");
}
}
if (templateMode.isXHTML()) {
if (systemId == null || systemId.trim().equals("")) {
throw new ParsingException(
"Template is being processed in " + templateMode + " mode. A " +
"correct 'PUBLIC' or 'SYSTEM' DOCTYPE declaration is required.");
}
}
rootElement = (Element) docChildren.item(1);
} else {
throw new ParsingException(
"Invalid document structure: No more than two top-level elements are allowed " +
"(either root element, or doctype + root element).");
}
/*
* Start traversing the node tree and applying transformations
*/
transformNode(arguments, templateResolution, document, rootElement);
return docType;
}
private static void transformNode(final Arguments arguments,
final TemplateResolution templateResolution,
final Document document, final Node node) {
final Configuration configuration = arguments.getConfiguration();
final Set processedAttributeNames = configuration.getProcessedAttrNames();
if (node.getNodeType() == Node.ELEMENT_NODE) {
final Element element = (Element) node;
final String elementName = element.getNodeName().toLowerCase();
boolean tagIsRemoved = false;
boolean tagChildrenAreRemoved = false;
boolean tagIsSubstituted = false;
final Map localVariables = new LinkedHashMap();
final List substitutionTags = new ArrayList();
Object selectionTarget = null;
boolean selectionTargetSet = false;
if (configuration.getProcessedTagNames().contains(elementName)) {
final ITagProcessor elementProcessor = configuration.getTagProcessor(element);
if (elementProcessor == null) {
if (configuration.tagNameHasPrefix(elementName) && !configuration.isLenient()) {
throw new NoAvailableProcessorException(
"No processor in dialect found for tag \"" + elementName + "\"");
}
} else {
if (logger.isTraceEnabled()) {
logger.trace("[THYMELEAF][{}] TAG: Processing tag: \"{}\"", TemplateEngine.threadIndex(), elementName);
}
final TagProcessResult tagProcessResult =
elementProcessor.process(arguments, templateResolution, document, element);
tagIsRemoved = tagProcessResult.getAction().isTagRemoved();
tagChildrenAreRemoved = tagProcessResult.getAction().isChildrenRemoved();
tagIsSubstituted = tagProcessResult.getAction().isTagSubstituted();
localVariables.putAll(tagProcessResult.getLocalVariables());
substitutionTags.addAll(tagProcessResult.getSubstitutionTags());
if (tagProcessResult.isSelectionTargetSet()) {
selectionTargetSet = true;
selectionTarget = tagProcessResult.getSelectionTarget();
}
}
} else if (configuration.tagNameHasPrefix(elementName) && !configuration.isLenient()) {
throw new NoAvailableProcessorException(
"No processor in dialect found for tag \"" + elementName + "\"");
}
/*
* If tag is not removed after tag execution, we should process attributes
*/
if (!tagIsRemoved) {
final Map attrProcessorsToExecute = new LinkedHashMap();
/*
* First, we create a list with the attr processors that should be executed.
*/
final NamedNodeMap attributes = element.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
final Attr attribute = (Attr) attributes.item(i);
final String attributeName = attribute.getName().toLowerCase();
if (processedAttributeNames.contains(attributeName)) {
final IAttrProcessor attrProcessor = configuration.getAttrProcessor(element, attribute);
if (attrProcessor != null) {
attrProcessorsToExecute.put(attributeName, attrProcessor);
} else if (configuration.attrNameHasPrefix(attributeName) && !configuration.isLenient()) {
throw new NoAvailableProcessorException(
"No processor in dialect found for attribute \"" + attributeName + "\" with value = \"" + attribute.getValue() + "\"");
}
} else if (configuration.attrNameHasPrefix(attributeName) && !configuration.isLenient()) {
throw new NoAvailableProcessorException(
"No processor in dialect found for attribute \"" + attributeName + "\" with value = \"" + attribute.getValue() + "\"");
}
}
/*
* Attr processors are sorted according to their precedence
*/
final Map orderedAttrProcessorsToExecute =
new TreeMap(
new AttrProcessorMapComparator(attrProcessorsToExecute));
orderedAttrProcessorsToExecute.putAll(attrProcessorsToExecute);
/*
* Once ordered, attr processors are executed on their corresponding attribute nodes
*/
for (final Map.Entry attrProcessorEntry : orderedAttrProcessorsToExecute.entrySet()) {
if (tagIsRemoved) {
break;
}
final String attributeName = attrProcessorEntry.getKey();
final IAttrProcessor attrProcessor = attrProcessorEntry.getValue();
final Attr attribute = element.getAttributeNode(attributeName);
if (logger.isTraceEnabled()) {
logger.trace("[THYMELEAF][{}] ATTRIBUTE: Processing attribute: \"{}\" with value \"{}\"", new Object[] {TemplateEngine.threadIndex(), attributeName, attribute.getValue()});
}
/*
* Compute the Arguments object to be used for attribute evaluation
*/
Arguments attrArguments = null;
if (localVariables.isEmpty()) {
if (!selectionTargetSet) {
attrArguments = arguments;
} else {
attrArguments = arguments.setSelectionTarget(selectionTarget);
}
} else {
if (!selectionTargetSet) {
attrArguments = arguments.addLocalVariables(localVariables);
} else {
attrArguments = arguments.addLocalVariablesAndSetSelectionTarget(localVariables, selectionTarget);
}
}
final AttrProcessResult attrProcessResult =
attrProcessor.process(attrArguments, templateResolution, document, element, attribute);
tagIsRemoved = attrProcessResult.getAction().isTagRemoved();
tagChildrenAreRemoved = attrProcessResult.getAction().isChildrenRemoved();
tagIsSubstituted = attrProcessResult.getAction().isTagSubstituted();
localVariables.putAll(attrProcessResult.getLocalVariables());
substitutionTags.addAll(attrProcessResult.getSubstitutionTags());
if (attrProcessResult.isSelectionTargetSet()) {
selectionTargetSet = true;
selectionTarget = attrProcessResult.getSelectionTarget();
}
if (attrProcessResult.getAction().isAttrRemoved()) {
element.removeAttribute(attributeName);
}
}
}
/*
* Compute the Arguments object to be used for children evaluation
*/
Arguments childrenArguments = null;
if (localVariables.isEmpty()) {
if (!selectionTargetSet) {
childrenArguments = arguments;
} else {
childrenArguments = arguments.setSelectionTarget(selectionTarget);
}
} else {
if (!selectionTargetSet) {
childrenArguments = arguments.addLocalVariables(localVariables);
} else {
childrenArguments = arguments.addLocalVariablesAndSetSelectionTarget(localVariables, selectionTarget);
}
}
if (!tagIsRemoved) {
if (tagChildrenAreRemoved) {
final NodeList children = node.getChildNodes();
final List childNodes = new ArrayList();
for (int i = 0; i < children.getLength(); i++) {
// In case nodes are deleted along the way, we create a list in order not
// to have to rely on the NodeList object.
childNodes.add(children.item(i));
}
for (final Node child : childNodes) {
element.removeChild(child);
}
} else {
final NodeList children = node.getChildNodes();
final List childNodes = new ArrayList();
for (int i = 0; i < children.getLength(); i++) {
// In case nodes are deleted along the way, we create a list in order not
// to have to rely on the NodeList object.
childNodes.add(children.item(i));
}
for (final Node child : childNodes) {
transformNode(childrenArguments, templateResolution, document, child);
}
}
} else {
if (tagChildrenAreRemoved) {
final NodeList children = node.getChildNodes();
final List childNodes = new ArrayList();
for (int i = 0; i < children.getLength(); i++) {
// In case nodes are deleted along the way, we create a list in order not
// to have to rely on the NodeList object.
childNodes.add(children.item(i));
}
for (final Node child : childNodes) {
element.removeChild(child);
}
if (tagIsSubstituted) {
for (final SubstitutionTag substitutionTag : substitutionTags) {
element.getParentNode().insertBefore(substitutionTag.getTag(), element);
}
for (final SubstitutionTag substitutionTag : substitutionTags) {
final Map substitutionLocalVariables = new LinkedHashMap();
substitutionLocalVariables.putAll(localVariables);
substitutionLocalVariables.putAll(substitutionTag.getLocalVariables());
/*
* Compute the Arguments object to be used for substitution evaluation
*/
Arguments substitutionArguments = null;
if (substitutionLocalVariables.isEmpty()) {
if (!selectionTargetSet) {
substitutionArguments = arguments;
} else {
substitutionArguments = arguments.setSelectionTarget(selectionTarget);
}
} else {
if (!selectionTargetSet) {
substitutionArguments = arguments.addLocalVariables(substitutionLocalVariables);
} else {
substitutionArguments = arguments.addLocalVariablesAndSetSelectionTarget(substitutionLocalVariables, selectionTarget);
}
}
transformNode(substitutionArguments, templateResolution, document, substitutionTag.getTag());
}
}
element.getParentNode().removeChild(element);
} else {
final NodeList children = node.getChildNodes();
final List childNodes = new ArrayList();
for (int i = 0; i < children.getLength(); i++) {
// In case nodes are deleted along the way, we create a list in order not
// to have to rely on the NodeList object.
childNodes.add(children.item(i));
}
for (final Node child : childNodes) {
element.removeChild(child);
element.getParentNode().insertBefore(child, element);
}
element.getParentNode().removeChild(element);
for (final Node child : childNodes) {
transformNode(childrenArguments, templateResolution, document, child);
}
}
}
if (!tagIsRemoved && !configuration.isLenient() && configuration.xmlNsAttrName() != null) {
element.removeAttribute(configuration.xmlNsAttrName());
}
if (!tagIsRemoved &&
nodeIsEmpty(element) &&
(templateResolution.getTemplateMode().isXHTML() || templateResolution.getTemplateMode().isHTML5())) {
/*
* If we are processing an XHTML template, we have to make sure that
* only certain tags get minimized (because the XHTML specification forbids
* many tags like