/*
* =============================================================================
*
* Copyright (c) 2011-2014, 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.standard.fragment;
import java.util.HashMap;
import java.util.Map;
import org.thymeleaf.Configuration;
import org.thymeleaf.context.IProcessingContext;
import org.thymeleaf.dom.DOMSelector;
import org.thymeleaf.exceptions.TemplateProcessingException;
import org.thymeleaf.fragment.DOMSelectorFragmentSpec;
import org.thymeleaf.fragment.IFragmentSpec;
import org.thymeleaf.fragment.WholeFragmentSpec;
import org.thymeleaf.standard.expression.Assignation;
import org.thymeleaf.standard.expression.AssignationSequence;
import org.thymeleaf.standard.expression.FragmentSelection;
import org.thymeleaf.standard.expression.FragmentSelectionUtils;
import org.thymeleaf.standard.expression.IStandardExpression;
import org.thymeleaf.util.Validate;
/**
*
* @author Daniel Fernández
*
* @since 2.0.9
*
*/
public final class StandardFragmentProcessor {
private static final String TEMPLATE_NAME_CURRENT_TEMPLATE = "this";
/**
* @since 2.1.0
*/
public static StandardFragment computeStandardFragmentSpec(
final Configuration configuration, final IProcessingContext processingContext,
final String standardFragmentSpec, final String dialectPrefix, final String fragmentSignatureAttributeName) {
Validate.notNull(processingContext, "Processing Context cannot be null");
Validate.notEmpty(standardFragmentSpec, "Fragment Spec cannot be null");
// Target element and attribute names can be null
final FragmentSelection fragmentSelection =
FragmentSelectionUtils.parseFragmentSelection(configuration, processingContext, standardFragmentSpec);
final IStandardExpression templateNameExpression = fragmentSelection.getTemplateName();
final String templateName;
if (templateNameExpression != null) {
final Object templateNameObject = templateNameExpression.execute(configuration, processingContext);
if (templateNameObject == null) {
throw new TemplateProcessingException(
"Evaluation of template name from spec \"" + standardFragmentSpec + "\" " +
"returned null.");
}
final String evaluatedTemplateName = templateNameObject.toString();
if (TEMPLATE_NAME_CURRENT_TEMPLATE.equals(evaluatedTemplateName)) {
// Template name is "this" and therefore we are including a fragment from the same template.
templateName = null;
} else {
templateName = templateNameObject.toString();
}
} else {
// If template name expression is null, we will execute the fragment on the "current" template
templateName = null;
}
// Resolve fragment parameters, if specified (null if not)
final Map<String,Object> fragmentParameters =
resolveFragmentParameters(configuration,processingContext,fragmentSelection.getParameters());
if (fragmentSelection.hasFragmentSelector()) {
final Object fragmentSelectorObject =
fragmentSelection.getFragmentSelector().execute(configuration, processingContext);
if (fragmentSelectorObject == null) {
throw new TemplateProcessingException(
"Evaluation of fragment selector from spec \"" + standardFragmentSpec + "\" " +
"returned null.");
}
String fragmentSelector = fragmentSelectorObject.toString();
if (fragmentSelector.length() > 3 &&
fragmentSelector.charAt(0) == '[' && fragmentSelector.charAt(fragmentSelector.length() - 1) == ']' &&
fragmentSelector.charAt(fragmentSelector.length() - 2) != '\'') {
// For legacy compatibility reasons, we allow fragment DOM Selector expressions to be specified
// between brackets. Just remove them.
fragmentSelector = fragmentSelector.substring(1, fragmentSelector.length() - 1);
}
final DOMSelector.INodeReferenceChecker nodeReferenceChecker =
new StandardFragmentSignatureNodeReferenceChecker(configuration, dialectPrefix, fragmentSignatureAttributeName);
final IFragmentSpec fragmentSpec = new DOMSelectorFragmentSpec(fragmentSelector, nodeReferenceChecker);
return new StandardFragment(templateName, fragmentSpec, fragmentParameters, dialectPrefix, fragmentSignatureAttributeName);
}
return new StandardFragment(templateName, WholeFragmentSpec.INSTANCE, fragmentParameters);
}
private static Map<String,Object> resolveFragmentParameters(
final Configuration configuration, final IProcessingContext processingContext,
final AssignationSequence parameters) {
if (parameters == null) {
return null;
}
final Map<String,Object> parameterValues = new HashMap<String, Object>(parameters.size() + 2);
for (final Assignation assignation : parameters.getAssignations()) {
final IStandardExpression parameterNameExpr = assignation.getLeft();
final Object parameterNameValue = parameterNameExpr.execute(configuration, processingContext);
final String parameterName = (parameterNameValue == null? null : parameterNameValue.toString());
final IStandardExpression parameterValueExpr = assignation.getRight();
final Object parameterValueValue = parameterValueExpr.execute(configuration, processingContext);
parameterValues.put(parameterName, parameterValueValue);
}
return parameterValues;
}
private StandardFragmentProcessor() {
super();
}
}