package client.net.sf.saxon.ce.pattern;
import client.net.sf.saxon.ce.Configuration;
import client.net.sf.saxon.ce.expr.*;
import client.net.sf.saxon.ce.expr.sort.DocumentSorter;
import client.net.sf.saxon.ce.js.IXSLFunction;
import client.net.sf.saxon.ce.om.Axis;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.type.ItemType;
import client.net.sf.saxon.ce.type.Type;
import client.net.sf.saxon.ce.type.TypeHierarchy;
/**
* This is a singleton class used to convert an expression to an equivalent pattern.
* This version of the class is used to generate conventional XSLT match patterns;
* there is another version used to generate patterns suitable for streamed evaluation
* in Saxon-EE.
*/
public class PatternMaker {
/**
* Static factory method to make a pattern by converting an expression. The supplied
* expression is the equivalent expression to the pattern, in the sense that it takes
* the same syntactic form.
*
* <p>Note that this method does NOT check all the rules for XSLT patterns; it deliberately allows
* a (slightly) wider class of expressions to be converted than XSLT allows.</p>
*
* <p>The expression root() at the start of the expression has a special meaning: it refers to
* the root of the subtree in which the pattern must match, which can be supplied at run-time
* during pattern matching. This is used for patterns that represent streamable path expressions.</p>
*
* @param expression the expression to be converted
* @param config the Saxon configuration
* @throws client.net.sf.saxon.ce.trans.XPathException if the expression cannot be converted
*/
public static Pattern fromExpression(Expression expression, Configuration config) throws XPathException {
Pattern result = null;
if (expression instanceof DocumentSorter) {
expression = ((DocumentSorter)expression).getBaseExpression();
}
if (expression instanceof VennExpression && ((VennExpression)expression).getOperator() == Token.UNION) {
result = new UnionPattern(
fromExpression(((VennExpression)expression).getOperands()[0], config),
fromExpression(((VennExpression)expression).getOperands()[1], config));
} else if (expression instanceof AxisExpression) {
int axis = ((AxisExpression)expression).getAxis();
NodeTest test = ((AxisExpression)expression).getNodeTest();
if (test == null) {
test = AnyNodeTest.getInstance();
}
if (test instanceof AnyNodeTest && (axis == Axis.CHILD || axis == Axis.DESCENDANT)) {
test = AnyChildNodeTest.getInstance();
}
int kind = test.getPrimitiveType();
if (axis == Axis.SELF && kind == Type.DOCUMENT) {
result = new NodeTestPattern(test);
} else if (axis == Axis.ATTRIBUTE) {
if (kind == Type.NODE) {
// attribute::node() matches any attribute, and only an attribute
result = new NodeTestPattern(NodeKindTest.ATTRIBUTE);
} else if (!Axis.containsNodeKind(axis, kind)) {
// for example, attribute::comment()
result = new NodeTestPattern(EmptySequenceTest.getInstance());
} else {
result = new NodeTestPattern(test);
}
} else if (axis == Axis.CHILD || axis == Axis.DESCENDANT || axis == Axis.DESCENDANT_OR_SELF) {
if (kind != Type.NODE && !Axis.containsNodeKind(axis, kind)) {
test = EmptySequenceTest.getInstance();
}
result = new NodeTestPattern(test);
} else {
throw new XPathException("Only downwards axes are allowed in a pattern", "XTSE0340");
}
// TODO: //A only matches an A element in a tree rooted at a document
} else if (expression instanceof FilterExpression) {
Expression base = ((FilterExpression)expression).getControllingExpression();
Expression filter = ((FilterExpression)expression).getFilter();
Pattern basePattern = fromExpression(base, config);
if (basePattern instanceof NodeTestPattern) {
LocationPathPattern path = new LocationPathPattern();
path.setNodeTest(basePattern.getNodeTest());
basePattern = path;
}
if (!(basePattern instanceof LocationPathPattern)) {
throw new XPathException("The filtered expression in a pattern must be a simple step");
}
((LocationPathPattern)basePattern).addFilter(filter);
result = basePattern;
} else if (expression instanceof SlashExpression) {
Expression head = ((SlashExpression)expression).getLeadingSteps();
Expression tail = ((SlashExpression)expression).getLastStep();
Pattern tailPattern = fromExpression(tail, config);
if (tailPattern instanceof NodeTestPattern) {
LocationPathPattern path = new LocationPathPattern();
path.setNodeTest(tailPattern.getNodeTest());
tailPattern = path;
}
if (!(tailPattern instanceof LocationPathPattern)) {
throw new XPathException("The path in a pattern must contain simple steps: found " + tailPattern.toString());
}
if (((LocationPathPattern)tailPattern).getUpperPattern() != null) {
throw new XPathException("The path in a pattern must contain simple steps");
}
byte axis = getAxisForPathStep(tail);
Pattern headPattern = fromExpression(head, config);
((LocationPathPattern)tailPattern).setUpperPattern(axis, headPattern);
result = tailPattern;
} else if (expression instanceof RootExpression) {
result = new NodeTestPattern(NodeKindTest.DOCUMENT);
} else if (expression instanceof IXSLFunction) {
result = new JSObjectPattern(expression, config);
} else {
TypeHierarchy th = config.getTypeHierarchy();
ItemType type = expression.getItemType(th);
if (((expression.getDependencies() & StaticProperty.DEPENDS_ON_NON_DOCUMENT_FOCUS) == 0) &&
(type instanceof NodeTest || expression instanceof VariableReference)) {
result = new NodeSetPattern(expression, config);
}
}
if (result == null) {
throw new XPathException("Cannot convert the expression {" + expression.toString() + "} to a pattern");
} else {
result.setOriginalText(expression.toString());
return result;
}
}
public static byte getAxisForPathStep(Expression step) throws XPathException {
if (step instanceof AxisExpression) {
return Axis.inverseAxis[((AxisExpression)step).getAxis()];
} else if (step instanceof FilterExpression) {
return getAxisForPathStep(((FilterExpression)step).getControllingExpression());
} else if (step instanceof PathExpression) {
return getAxisForPathStep(((PathExpression)step).getFirstStep());
} else if (step instanceof ContextItemExpression) {
return Axis.SELF;
} else {
throw new XPathException("The path in a pattern must contain simple steps");
}
}
}
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.