org.raml.parser.visitor.TemplateResolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of raml-parser Show documentation
Show all versions of raml-parser Show documentation
Java implementation of the raml parser taken from https://github.com/raml-org/raml-java-parser and adjusted
//Changed by hybris
/*
* Copyright 2013 (c) MuleSoft, Inc.
*
* 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.raml.parser.visitor;
import static org.raml.parser.rule.ValidationMessage.NON_SCALAR_KEY_MESSAGE;
import static org.raml.parser.rule.ValidationResult.createErrorResult;
import static org.raml.parser.tagresolver.IncludeResolver.INCLUDE_APPLIED_TAG;
import static org.raml.parser.tagresolver.IncludeResolver.INCLUDE_TAG;
import static org.yaml.snakeyaml.nodes.NodeId.mapping;
import static org.yaml.snakeyaml.nodes.NodeId.scalar;
import static org.yaml.snakeyaml.nodes.NodeId.sequence;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.raml.model.Action;
import org.raml.model.ActionType;
import org.raml.model.Resource;
import org.raml.parser.loader.ResourceLoader;
import org.raml.parser.rule.ValidationResult;
import org.raml.parser.tagresolver.ContextPathAware;
import org.raml.parser.tagresolver.IncludeResolver;
import org.raml.parser.utils.Inflector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeId;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;
public class TemplateResolver
{
public static final String OPTIONAL_MODIFIER = "?";
public static final String ALL_ACTIONS = "*";
public static final String TRAIT_USE_KEY = "is";
public static final String RESOURCE_TYPE_USE_KEY = "type";
public static final Pattern TEMPLATE_PARAMETER_PATTERN = Pattern.compile("<<[^>]+>>");
protected final Logger logger = LoggerFactory.getLogger(getClass());
private IncludeResolver includeResolver = new IncludeResolver();
private Map resourceTypesMap = new HashMap();
private Map traitsMap = new HashMap();
private ResourceLoader resourceLoader;
private NodeHandler nodeNandler;
private Set resolvedNodes = new HashSet();
private enum TemplateType
{
RESOURCE_TYPE, TRAIT
}
public TemplateResolver(ResourceLoader resourceLoader, NodeHandler nodeNandler)
{
this.resourceLoader = resourceLoader;
this.nodeNandler = nodeNandler;
this.includeResolver.setContextPath(((ContextPathAware) nodeNandler).getContextPath());
}
public Map getResourceTypesMap()
{
return resourceTypesMap;
}
public Map getTraitsMap()
{
return traitsMap;
}
public List init(MappingNode rootNode)
{
List validationResults = new ArrayList();
if (rootNode == null)
{
validationResults.add(createErrorResult("Invalid Root Node"));
return validationResults;
}
for (int i = 0; i < rootNode.getValue().size(); i++)
{
NodeTuple rootTuple = rootNode.getValue().get(i);
Node keyNode = rootTuple.getKeyNode();
if (keyNode.getNodeId() != scalar)
{
continue; //invalid key
}
String key = ((ScalarNode) keyNode).getValue();
if (key.equals("resourceTypes") || key.equals("traits"))
{
Node templateSequence = resolveInclude(rootTuple.getValueNode());
if (templateSequence != rootTuple.getValueNode())
{
rootNode.getValue().remove(i);
rootNode.getValue().add(i, new NodeTuple(keyNode, templateSequence));
}
if (templateSequence.getNodeId() != sequence)
{
validationResults.add(createErrorResult("Sequence expected", templateSequence));
rootNode.getValue().remove(i);
rootNode.getValue().add(i, new NodeTuple(keyNode, new SequenceNode(Tag.SEQ, new ArrayList(), false)));
break;
}
loopTemplateSequence((SequenceNode) templateSequence, key, validationResults);
}
}
return validationResults;
}
private void loopTemplateSequence(SequenceNode templateSequence, String templateType, List validationResults)
{
List prunedTemplates = new ArrayList();
for (int j = 0; j < templateSequence.getValue().size(); j++)
{
Node template = resolveInclude(templateSequence.getValue().get(j));
if (template.getNodeId() != mapping)
{
validationResults.add(createErrorResult("Mapping expected", templateSequence.getStartMark(), templateSequence.getEndMark()));
break;
}
for (NodeTuple tuple : ((MappingNode) template).getValue())
{
if (tuple.getKeyNode().getNodeId() != scalar)
{
validationResults.add(createErrorResult(NON_SCALAR_KEY_MESSAGE, tuple.getKeyNode()));
continue;
}
String templateKey = ((ScalarNode) tuple.getKeyNode()).getValue();
Node templateValue = resolveInclude(tuple.getValueNode());
if (templateValue.getNodeId() != mapping)
{
validationResults.add(createErrorResult("Mapping expected", templateValue.getStartMark(), templateValue.getEndMark()));
continue;
}
//HYBRIS start - prune only for resourceType or traits
if (templateType.equals("resourceTypes"))
{
resourceTypesMap.put(templateKey, (MappingNode) templateValue);
prunedTemplates.add(getFakeTemplateNode(tuple.getKeyNode()));
}
if (templateType.equals("traits"))
{
traitsMap.put(templateKey, (MappingNode) templateValue);
prunedTemplates.add(template);
}
//HYBRIS end
}
}
templateSequence.getValue().clear();
templateSequence.getValue().addAll(prunedTemplates);
}
private Node resolveInclude(Node node)
{
if (node.getNodeId() == scalar && node.getTag().equals(INCLUDE_TAG))
{
return includeResolver.resolve(node, resourceLoader, nodeNandler);
}
return node;
}
private Node getFakeTemplateNode(Node keyNode)
{
List innerTuples = new ArrayList();
innerTuples.add(new NodeTuple(new ScalarNode(Tag.STR, "displayName", null, null, null), keyNode));
MappingNode innerNode = new MappingNode(Tag.MAP, innerTuples, false);
List outerTuples = new ArrayList();
outerTuples.add(new NodeTuple(keyNode, innerNode));
return new MappingNode(Tag.MAP, outerTuples, false);
}
public List resolve(MappingNode resourceNode, String relativeUri, String fullUri)
{
List templateValidations = new ArrayList();
//avoid processing resources already processed (yaml references)
if (resolvedNodes.contains(resourceNode))
{
return templateValidations;
}
resolvedNodes.add(resourceNode);
return new ResourceTemplateMerger(templateValidations, resourceNode, relativeUri, fullUri).merge();
}
private static class TemplateReferences
{
private Node typeReference = null;
private Map traitsReference = new HashMap();
private Map actionNodes;
public TemplateReferences(Map globalActionNodes)
{
actionNodes = new HashMap(globalActionNodes);
}
}
private class ResourceTemplateMerger
{
private List templateValidations;
private MappingNode resourceNode;
private String relativeUri;
private String fullUri;
private String currentAction;
public ResourceTemplateMerger(List templateValidations, MappingNode resourceNode, String relativeUri, String fullUri)
{
this.templateValidations = templateValidations;
this.resourceNode = resourceNode;
this.relativeUri = relativeUri;
this.fullUri = fullUri;
}
public List merge()
{
if (mergeTemplatesIfNeeded(resourceNode, new HashMap()))
{
removeOptionalNodes(resourceNode, true);
}
return templateValidations;
}
private boolean mergeTemplatesIfNeeded(MappingNode resourceNode, Map globalActionNodes)
{
TemplateReferences references = extractTemplateReferences(resourceNode, globalActionNodes);
Node typeReference = references.typeReference;
Map traitsReference = references.traitsReference;
Map actionNodes = references.actionNodes;
if (!traitsReference.isEmpty())
{
//merge action level tratis
for (Map.Entry actionEntry : traitsReference.entrySet())
{
String actionName = actionEntry.getKey();
if (!actionName.equals(ALL_ACTIONS))
{
applyTraitsToActions(actionEntry.getValue(), actionNodes, actionName);
}
}
//merge resource level traits if there is no parent resource type
if (traitsReference.get(ALL_ACTIONS) != null && typeReference == null)
{
applyTraitsToActions(traitsReference.get(ALL_ACTIONS), actionNodes, null);
}
}
if (typeReference != null)
{
MappingNode clone = cloneTemplate(typeReference, TemplateType.RESOURCE_TYPE);
if (clone == null)
{
//template not found
return false;
}
//update global action map
for (Map.Entry entry : actionNodes.entrySet())
{
if (!globalActionNodes.containsKey(entry.getKey()))
{
globalActionNodes.put(entry.getKey(), entry.getValue());
}
}
//merge parent type if defined
mergeTemplatesIfNeeded(clone, actionNodes);
//merge resource level traits
if (traitsReference.get(ALL_ACTIONS) != null)
{
applyTraitsToActions(traitsReference.get(ALL_ACTIONS), actionNodes, null);
}
//merge type, no traits (action level traits could be merged)
mergeNodes(resourceNode, clone, new MergeContext(Resource.class, clone.getTag()));
}
return !traitsReference.isEmpty() || typeReference != null;
}
private TemplateReferences extractTemplateReferences(MappingNode resourceNode, Map globalActionNodes)
{
TemplateReferences templateReferences = new TemplateReferences(globalActionNodes);
for (int i = 0; i < resourceNode.getValue().size(); i++)
{
NodeTuple resourceTuple = resourceNode.getValue().get(i);
if (resourceTuple.getKeyNode().getNodeId() != scalar)
{
break; //invalid key
}
String key = ((ScalarNode) resourceTuple.getKeyNode()).getValue();
if (key.equals(RESOURCE_TYPE_USE_KEY))
{
templateReferences.typeReference = cloneNode(resourceTuple.getValueNode(), new HashMap());
removeParametersFromTemplateCall(resourceTuple);
}
else if (key.equals(TRAIT_USE_KEY) && expect(resourceTuple.getValueNode(), sequence))
{
SequenceNode sequence = cloneSequenceNode((SequenceNode) resourceTuple.getValueNode(), new HashMap());
templateReferences.traitsReference.put(ALL_ACTIONS, sequence);
removeParametersFromTraitsCall(resourceTuple);
}
else if (isAction(key))
{
Node actionNode = resourceTuple.getValueNode();
if (actionNode.getTag().equals(Tag.NULL))
{
actionNode = setTupleValueToEmptyMappingNode(resourceTuple);
}
else if (actionNode.getTag().equals(INCLUDE_TAG))
{
actionNode = includeResolver.resolve(actionNode, resourceLoader, nodeNandler);
resourceNode.getValue().remove(i);
resourceNode.getValue().add(i, new NodeTuple(resourceTuple.getKeyNode(), actionNode));
}
if (actionNode.getNodeId() != mapping)
{
break;
}
templateReferences.actionNodes.put(normalizeKey(key), actionNode);
for (NodeTuple actionTuple : ((MappingNode) actionNode).getValue())
{
String actionTupleKey = ((ScalarNode) actionTuple.getKeyNode()).getValue();
if (actionTupleKey.equals(TRAIT_USE_KEY) && expect(actionTuple.getValueNode(), sequence))
{
SequenceNode sequence = cloneSequenceNode((SequenceNode) actionTuple.getValueNode(), new HashMap());
templateReferences.traitsReference.put(normalizeKey(key), sequence);
removeParametersFromTraitsCall(actionTuple);
}
}
}
}
return templateReferences;
}
private boolean expect(Node node, NodeId nodeId)
{
if (node.getNodeId() != nodeId)
{
addError(nodeId + " node expected", node);
return false;
}
return true;
}
private void removeParametersFromTraitsCall(NodeTuple traitsNodeTuple)
{
if (traitsNodeTuple.getValueNode().getNodeId() == sequence)
{
List traitList = ((SequenceNode) traitsNodeTuple.getValueNode()).getValue();
for (int i = 0; i < traitList.size(); i++)
{
Node traitNode = traitList.get(i);
if (traitNode.getNodeId() == mapping)
{
Node keyNode = ((MappingNode) traitNode).getValue().get(0).getKeyNode();
if (keyNode.getNodeId() == scalar)
{
traitList.remove(i);
traitList.add(i, keyNode);
}
}
}
}
}
private void removeParametersFromTemplateCall(NodeTuple typeNodeTuple)
{
if (typeNodeTuple.getValueNode().getNodeId() == mapping)
{
NodeTuple typeParamTuple = ((MappingNode) typeNodeTuple.getValueNode()).getValue().get(0);
try
{
Field value = typeNodeTuple.getClass().getDeclaredField("valueNode");
value.setAccessible(true);
value.set(typeNodeTuple, typeParamTuple.getKeyNode());
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
private Node setTupleValueToEmptyMappingNode(NodeTuple tuple)
{
try
{
Field value = tuple.getClass().getDeclaredField("valueNode");
value.setAccessible(true);
Node valueNode = tuple.getValueNode();
MappingNode mappingNode = new MappingNode(Tag.MAP, false, new ArrayList(),
valueNode.getStartMark(), valueNode.getEndMark(), false);
value.set(tuple, mappingNode);
return mappingNode;
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
private void applyTraitsToActions(SequenceNode traits, Map actionNodes, String actionName)
{
for (Node ref : traits.getValue())
{
if (actionName == null)
{
for (Map.Entry actionEntry : actionNodes.entrySet())
{
currentAction = actionEntry.getKey();
MappingNode templateNode = cloneTemplate(ref, TemplateType.TRAIT);
if (templateNode != null)
{
mergeNodes(actionEntry.getValue(), templateNode, new MergeContext(Action.class, templateNode.getTag()));
}
}
}
else
{
currentAction = actionName;
MappingNode templateNode = cloneTemplate(ref, TemplateType.TRAIT);
if (templateNode != null)
{
mergeNodes(actionNodes.get(actionName), templateNode, new MergeContext(Action.class, templateNode.getTag()));
}
}
}
}
private MappingNode cloneTemplate(Node reference, TemplateType type)
{
String templateName = getTemplateName(reference);
if (templateName.isEmpty())
{
return null;
}
Map templateMap;
Map defaultParameters = new HashMap();
defaultParameters.put("resourcePath", relativeUri);
defaultParameters.put("resourcePathName", getResourcePathName(fullUri));
String label;
if (type == TemplateType.RESOURCE_TYPE)
{
templateMap = getResourceTypesMap();
label = "resource type";
}
else
{
templateMap = getTraitsMap();
label = "trait";
defaultParameters.put("methodName", currentAction);
}
MappingNode templateNode = templateMap.get(templateName);
if (templateNode == null)
{
addError(label + " not defined: " + templateName, reference);
return null;
}
return cloneMappingNode(templateNode, getTemplateParameters(reference, defaultParameters));
}
private String getResourcePathName(String fullUri)
{
String[] paths = fullUri.split("/");
for (int i = paths.length - 1; i >= 0; i--)
{
if (!paths[i].contains("{") && paths[i].length() > 0)
{
return paths[i];
}
}
return "";
}
private void addError(String message, Node node)
{
templateValidations.add(createErrorResult(message, node));
}
private Map getTemplateParameters(Node node, Map parameters)
{
if (node.getNodeId() == mapping)
{
List tuples = ((MappingNode) node).getValue();
Node params = tuples.get(0).getValueNode();
if (params.getTag() == Tag.NULL)
{
return parameters;
}
if (params.getNodeId() != mapping)
{
addError("Mapping node expected", params);
return parameters;
}
for (NodeTuple paramTuple : ((MappingNode) params).getValue())
{
if (paramTuple.getKeyNode().getNodeId() != scalar)
{
addError("Scalar node expected", paramTuple.getKeyNode());
break;
}
if (paramTuple.getValueNode().getNodeId() != scalar)
{
addError("Scalar node expected", paramTuple.getValueNode());
break;
}
String paramKey = ((ScalarNode) paramTuple.getKeyNode()).getValue();
ScalarNode valueNode = (ScalarNode) paramTuple.getValueNode();
parameters.put(paramKey, resolveParameterValueInclude(valueNode));
}
}
return parameters;
}
private String resolveParameterValueInclude(ScalarNode valueNode)
{
if (valueNode.getTag().equals(INCLUDE_TAG))
{
Node resolved = includeResolver.resolve(valueNode, resourceLoader, nodeNandler);
if (resolved.getNodeId() != scalar)
{
addError("Resource type and traits parameters must be scalars", valueNode);
return "";
}
valueNode = (ScalarNode) resolved;
}
return valueNode.getValue();
}
private String getTemplateName(Node templateReferenceNode)
{
Node templateNameNode = templateReferenceNode;
if (templateReferenceNode.getNodeId() == mapping)
{
templateNameNode = ((MappingNode) templateReferenceNode).getValue().get(0).getKeyNode();
}
return ((ScalarNode) templateNameNode).getValue();
}
private void removeOptionalNodes(MappingNode node, boolean isResourceNode)
{
for (NodeTuple tuple : new ArrayList(node.getValue()))
{
String keyValue = ((ScalarNode) tuple.getKeyNode()).getValue();
if (isResourceNode && keyValue.startsWith("/"))
{
continue;
}
if (isOptional(keyValue))
{
node.getValue().remove(tuple);
}
else
{
if (tuple.getValueNode().getNodeId() == mapping)
{
removeOptionalNodes((MappingNode) tuple.getValueNode(), false);
}
}
}
}
private MappingNode cloneMappingNode(MappingNode node, Map parameters)
{
List tuples = new ArrayList();
for (NodeTuple tuple : node.getValue())
{
if (tuple.getKeyNode().getNodeId() != scalar)
{
addError(NON_SCALAR_KEY_MESSAGE, tuple.getKeyNode());
break;
}
Node key = cloneScalarNode((ScalarNode) tuple.getKeyNode(), parameters);
Node value = cloneNode(tuple.getValueNode(), parameters);
tuples.add(new NodeTuple(key, value));
}
return new MappingNode(node.getTag(), tuples, node.getFlowStyle());
}
private Node cloneNode(Node valueNode, Map parameters)
{
if (valueNode.getNodeId() == mapping)
{
return cloneMappingNode((MappingNode) valueNode, parameters);
}
else if (valueNode.getNodeId() == sequence)
{
return cloneSequenceNode((SequenceNode) valueNode, parameters);
}
else if (valueNode.getNodeId() == scalar)
{
return cloneScalarNode((ScalarNode) valueNode, parameters);
}
addError("unsupported node type: " + valueNode.getNodeId(), valueNode);
return null;
}
private SequenceNode cloneSequenceNode(SequenceNode node, Map parameters)
{
List nodes = new ArrayList();
for (Node item : node.getValue())
{
nodes.add(cloneNode(item, parameters));
}
return new SequenceNode(node.getTag(), nodes, node.getFlowStyle());
}
private ScalarNode cloneScalarNode(ScalarNode node, Map parameters)
{
String value = node.getValue();
Matcher matcher = TEMPLATE_PARAMETER_PATTERN.matcher(value);
StringBuffer sb = new StringBuffer();
while (matcher.find())
{
matcher.appendReplacement(sb, "");
sb.append(resolveParameter(matcher.group(), parameters, node));
}
matcher.appendTail(sb);
return new ScalarNode(node.getTag(), sb.toString(), node.getStartMark(), node.getEndMark(), node.getStyle());
}
private String resolveParameter(String match, Map parameters, ScalarNode node)
{
String result = "";
String[] tokens = match.substring(2, match.length() - 2).split("\\|");
for (String token : tokens)
{
token = token.trim();
if (parameters.containsKey(token))
{
result = parameters.get(token);
}
else if (token.startsWith("!"))
{
try
{
Method method = Inflector.class.getMethod(token.substring(1), String.class);
result = (String) method.invoke(null, result);
}
catch (Exception e)
{
addError("Invalid parameter function: " + token, node);
}
}
else
{
addError("Invalid parameter definition: " + match, node);
}
}
return result;
}
private MappingNode mergeMappingNodes(MappingNode baseNode, MappingNode templateNode, MergeContext context)
{
Map baseTupleMap = getTupleMap(baseNode);
for (NodeTuple templateTuple : templateNode.getValue())
{
String templateKey = ((ScalarNode) templateTuple.getKeyNode()).getValue();
if (!nonMergeableFields(context.keyNodeType).contains(templateKey))
{
String baseKey = getMatchingKey(baseTupleMap, templateKey);
if (baseKey == null)
{
baseNode.getValue().add(context.tagInclude(templateTuple));
}
else
{
Node keyNode = baseTupleMap.get(baseKey).getKeyNode();
if (isOptional(baseKey) && !isOptional(templateKey))
{
keyNode = templateTuple.getKeyNode();
}
Node baseInnerNode = baseTupleMap.get(baseKey).getValueNode();
Node templateInnerNode = templateTuple.getValueNode();
Node valueNode = mergeNodes(baseInnerNode, templateInnerNode, new MergeContext(context, baseKey));
baseNode.getValue().remove(baseTupleMap.get(baseKey));
baseNode.getValue().add(new NodeTuple(keyNode, valueNode));
}
}
}
return baseNode;
}
private Class> pushMergeContext(Class> context, String key)
{
if (context.equals(Resource.class) && isAction(key))
{
return Action.class;
}
return Object.class;
}
private Node mergeNodes(Node baseNode, Node templateNode, MergeContext context)
{
if (baseNode.getNodeId() == mapping && templateNode.getNodeId() == mapping)
{
return mergeMappingNodes((MappingNode) baseNode, (MappingNode) templateNode, context);
}
if (templateNode.getNodeId() == mapping)
{
return cleanMergedTuples((MappingNode) templateNode, context);
}
return baseNode;
}
private MappingNode cleanMergedTuples(MappingNode templateNode, MergeContext context)
{
List tuples = new ArrayList(templateNode.getValue());
for (NodeTuple tuple : tuples)
{
String key = ((ScalarNode) tuple.getKeyNode()).getValue();
if (nonMergeableFields(context.keyNodeType).contains(key))
{
templateNode.getValue().remove(tuple);
}
else
{
context.tagInclude(tuple);
}
}
return templateNode;
}
private Set nonMergeableFields(Class> element)
{
String[] fields = {};
if (element.equals(Resource.class))
{
fields = new String[] {"usage", "summary", "displayName", "type", "is"};
}
else if (element.equals(Action.class))
{
fields = new String[] {"usage", "summary", "displayName", "is"};
}
return new HashSet(Arrays.asList(fields));
}
private boolean isOptional(String key)
{
return key.endsWith(OPTIONAL_MODIFIER);
}
private String getMatchingKey(Map tupleMap, String key)
{
key = normalizeKey(key);
for (String resourceKey : tupleMap.keySet())
{
if (normalizeKey(resourceKey).equals(key))
{
return resourceKey;
}
}
return null;
}
private Map getTupleMap(MappingNode mappingNode)
{
Map tupleMap = new HashMap();
for (NodeTuple tuple : mappingNode.getValue())
{
tupleMap.put(((ScalarNode) tuple.getKeyNode()).getValue(), tuple);
}
return tupleMap;
}
}
private static class MergeContext
{
Class> keyNodeType;
Tag templateInclude;
MergeContext(Class> keyNodeType, Tag templateInclude)
{
this.keyNodeType = keyNodeType;
if (templateInclude != null && templateInclude.startsWith(INCLUDE_APPLIED_TAG))
{
this.templateInclude = templateInclude;
}
}
MergeContext(MergeContext context, String baseKey)
{
this.templateInclude = context.templateInclude;
if (context.keyNodeType.equals(Resource.class) && isAction(baseKey))
{
this.keyNodeType = Action.class;
}
this.keyNodeType = Object.class;
}
NodeTuple tagInclude(NodeTuple tuple)
{
if (Tag.NULL.equals(tuple.getValueNode().getTag()))
{
return tuple;
}
if (templateInclude != null)
{
tuple.getValueNode().setTag(templateInclude);
}
return tuple;
}
}
private static boolean isAction(String key)
{
try
{
ActionType.valueOf(normalizeKey(key).toUpperCase());
return true;
}
catch (IllegalArgumentException e)
{
return false;
}
}
private static String normalizeKey(String key)
{
if (key.endsWith(OPTIONAL_MODIFIER))
{
return key.substring(0, key.length() - 1);
}
return key;
}
}