org.thymeleaf.processor.attr.AbstractFragmentAttrProcessor Maven / Gradle / Ivy
/*
* =============================================================================
*
* Copyright (c) 2011-2012, 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.processor.attr;
import java.util.Collections;
import java.util.List;
import org.thymeleaf.Arguments;
import org.thymeleaf.Template;
import org.thymeleaf.TemplateProcessingParameters;
import org.thymeleaf.cache.ICache;
import org.thymeleaf.cache.ICacheManager;
import org.thymeleaf.dom.DOMSelector;
import org.thymeleaf.dom.NestableNode;
import org.thymeleaf.dom.Node;
import org.thymeleaf.dom.Element;
import org.thymeleaf.exceptions.TemplateProcessingException;
import org.thymeleaf.processor.IAttributeNameProcessorMatcher;
import org.thymeleaf.processor.ProcessorResult;
import org.thymeleaf.util.DOMUtils;
import org.thymeleaf.util.Validate;
/**
*
* @author Daniel Fernández
*
* @since 1.1
*
*/
public abstract class AbstractFragmentAttrProcessor
extends AbstractAttrProcessor {
private static final String DOM_SELECTOR_EXPRESSION_PREFIX = "{dom_selector}";
public AbstractFragmentAttrProcessor(final String attributeName) {
super(attributeName);
}
public AbstractFragmentAttrProcessor(final IAttributeNameProcessorMatcher matcher) {
super(matcher);
}
@Override
public final ProcessorResult processAttribute(final Arguments arguments, final Element element, final String attributeName) {
final String attributeValue = element.getAttributeValue(attributeName);
final boolean substituteInclusionNode =
getSubstituteInclusionNode(arguments, element, attributeName, attributeValue);
final List newNodes =
getNewNodes(arguments, element, attributeName, attributeValue, substituteInclusionNode);
element.clearChildren();
for (final Node newNode : newNodes) {
element.addChild(newNode);
}
element.removeAttribute(attributeName);
if (!substituteInclusionNode) {
return ProcessorResult.OK;
}
// Inclusion element will be substituted by the new nodes
element.getParent().extractChild(element);
return ProcessorResult.OK;
}
private final List getNewNodes(
final Arguments arguments, final Element element,
final String attributeName, final String attributeValue,
final boolean substituteInclusionNode) {
final AbstractFragmentSpec fragmentSpec =
getFragmentSpec(arguments, element, attributeName, attributeValue);
if (fragmentSpec == null) {
throw new TemplateProcessingException("Null value for \"" + attributeName + "\" fragment specification not allowed");
}
final Node fragmentNode = getFragment(arguments, attributeName, fragmentSpec);
if (fragmentNode == null) {
throw new TemplateProcessingException(
"An error happened during inclusion/substitution of \"" + attributeValue + "\": fragment node is null");
}
try {
if (substituteInclusionNode) {
return Collections.singletonList(fragmentNode);
}
if (!(fragmentNode instanceof NestableNode)) {
throw new TemplateProcessingException(
"An error happened during parsing of include: \"" + attributeValue + "\": selected fragment has no children " +
"and therefore is not suitable for use in an inclusion operation -- maybe a substitution operation should be used instead?");
}
return ((NestableNode)fragmentNode).getChildren();
} catch (final TemplateProcessingException e) {
throw e;
} catch (final Exception e) {
throw new TemplateProcessingException(
"An error happened during inclusion/substitution of \"" + attributeValue + "\"", e);
}
}
private static Node getFragment(
final Arguments arguments, final String attributeName, final AbstractFragmentSpec fragmentSpec) {
final String templateName = arguments.getTemplateResolution().getTemplateName();
final String fragmentTemplateName = fragmentSpec.getFragmentTemplateName();
if (templateName.equals(fragmentTemplateName)) {
throw new TemplateProcessingException(
"Template \"" + templateName +
"\" references itself from a " +
"\"" + attributeName + "\" attribute, which is forbidden.");
}
try {
final TemplateProcessingParameters fragmentTemplateProcessingParameters =
new TemplateProcessingParameters(
arguments.getConfiguration(), fragmentTemplateName, arguments.getContext());
final Template parsedTemplate = arguments.getTemplateRepository().getTemplate(fragmentTemplateProcessingParameters);
Node fragmentNode = null;
if (fragmentSpec instanceof NamedFragmentSpec) {
final NamedFragmentSpec namedFragmentSpec = (NamedFragmentSpec) fragmentSpec;
final String fragmentElementName = namedFragmentSpec.getFragmentElementName();
final String fragmentAttributeName = namedFragmentSpec.getFragmentAttributeName();
final String fragmentAttributeValue = namedFragmentSpec.getFragmentAttributeValue();
fragmentNode =
DOMUtils.extractFragmentByAttributeValue(parsedTemplate.getDocument(), fragmentElementName, fragmentAttributeName, fragmentAttributeValue);
if (fragmentNode == null) {
throw new TemplateProcessingException(
"Fragment \"" + fragmentAttributeValue + "\" in template \"" + fragmentTemplateName + "\" could not be found");
}
} else if (fragmentSpec instanceof CompleteTemplateFragmentSpec) {
fragmentNode = parsedTemplate.getDocument();
if (fragmentNode == null) {
throw new TemplateProcessingException(
"Root node in template \"" + fragmentTemplateName + "\" could not be found");
}
} else if (fragmentSpec instanceof DOMSelectorFragmentSpec) {
final DOMSelectorFragmentSpec domSelectorFragmentSpec = (DOMSelectorFragmentSpec) fragmentSpec;
final String domSelectorExpression = domSelectorFragmentSpec.getSelectorExpression();
DOMSelector selector = null;
ICache expressionCache = null;
final ICacheManager cacheManager = arguments.getConfiguration().getCacheManager();
if (cacheManager != null) {
expressionCache = cacheManager.getExpressionCache();
if (expressionCache != null) {
selector = (DOMSelector) expressionCache.get(DOM_SELECTOR_EXPRESSION_PREFIX + domSelectorExpression);
}
}
if (selector == null) {
selector = new DOMSelector(domSelectorExpression);
if (expressionCache != null) {
expressionCache.put(DOM_SELECTOR_EXPRESSION_PREFIX + domSelectorExpression, selector);
}
}
final List selectedNodes = selector.select(parsedTemplate.getDocument().getChildren());
if (selectedNodes == null || selectedNodes.size() == 0) {
throw new TemplateProcessingException(
"No result for DOM selector expression \"" + domSelectorExpression +"\" in template \"" + fragmentTemplateName + "\" could be found");
}
fragmentNode = selectedNodes.get(0);
}
return fragmentNode;
} catch (final TemplateProcessingException e) {
throw e;
} catch (final Exception e) {
throw new TemplateProcessingException(
"An error happened during parsing of template: \"" + fragmentTemplateName + "\"", e);
}
}
protected abstract boolean getSubstituteInclusionNode(
final Arguments arguments, final Element element,
final String attributeName, final String attributeValue);
protected abstract AbstractFragmentSpec getFragmentSpec(
final Arguments arguments, final Element element,
final String attributeName, final String attributeValue);
/**
*
* @author Daniel Fernández
*
* @since 1.0
*
*/
protected static abstract class AbstractFragmentSpec {
private final String fragmentTemplateName;
public AbstractFragmentSpec(final String fragmentTemplateName) {
super();
Validate.notEmpty(fragmentTemplateName, "Fragment template name cannot be null or empty");
this.fragmentTemplateName = fragmentTemplateName;
}
public String getFragmentTemplateName() {
return this.fragmentTemplateName;
}
}
/**
*
* @author Daniel Fernández
*
* @since 1.0
*
*/
protected static final class NamedFragmentSpec extends AbstractFragmentSpec {
private final String fragmentElementName;
private final String fragmentAttributeName;
private final String fragmentAttributeValue;
public NamedFragmentSpec(final String fragmentTemplateName,
final String fragmentAttributeName, final String fragmentAttributeValue) {
this(fragmentTemplateName, null, fragmentAttributeName, fragmentAttributeValue);
}
public NamedFragmentSpec(final String fragmentTemplateName,
final String fragmentElementName, final String fragmentAttributeName, final String fragmentAttributeValue) {
super(fragmentTemplateName);
// Fragment Element name CAN be null. In that case any element name will be applicable
Validate.notEmpty(fragmentAttributeName, "Fragment attribute name cannot be null or empty");
Validate.notEmpty(fragmentAttributeValue, "Fragment attribute value cannot be null or empty");
this.fragmentElementName = fragmentElementName;
this.fragmentAttributeName = fragmentAttributeName;
this.fragmentAttributeValue = fragmentAttributeValue;
}
public String getFragmentElementName() {
return this.fragmentElementName;
}
public String getFragmentAttributeName() {
return this.fragmentAttributeName;
}
public String getFragmentAttributeValue() {
return this.fragmentAttributeValue;
}
}
/**
*
* @author Daniel Fernández
*
* @since 1.0
*
*/
protected static final class CompleteTemplateFragmentSpec extends AbstractFragmentSpec {
public CompleteTemplateFragmentSpec(final String fragmentTemplateName) {
super(fragmentTemplateName);
}
}
/**
*
* @author Daniel Fernández
*
* @since 2.0.0
*
*/
protected static final class DOMSelectorFragmentSpec extends AbstractFragmentSpec {
private final String selectorExpression;
public DOMSelectorFragmentSpec(final String fragmentTemplateName,
final String selectorExpression) {
super(fragmentTemplateName);
Validate.notEmpty(selectorExpression, "DOM selector expression cannot be null or empty");
this.selectorExpression = selectorExpression;
}
public String getSelectorExpression() {
return this.selectorExpression;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy