Package client.net.sf.saxon.ce.expr

Source Code of client.net.sf.saxon.ce.expr.AxisExpression

package client.net.sf.saxon.ce.expr;

import client.net.sf.saxon.ce.Configuration;
import client.net.sf.saxon.ce.expr.sort.SetUtils;
import client.net.sf.saxon.ce.om.*;
import client.net.sf.saxon.ce.pattern.*;
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;

import java.util.Set;


/**
* An AxisExpression is always obtained by simplifying a PathExpression.
* It represents a PathExpression that starts at the context node, and uses
* a simple node-test with no filters. For example "*", "title", "./item",
* "@*", or "ancestor::chapter*".
*
* <p>An AxisExpression delivers nodes in axis order (not in document order).
* To get nodes in document order, in the case of a reverse axis, the expression
* should be wrapped in a call on reverse().</p>
*/

public final class AxisExpression extends Expression {

    private byte axis;
    private NodeTest test;
    private ItemType itemType = null;
    private ItemType contextItemType = null;
    int computedCardinality = -1;
    private boolean doneWarnings = false;

    /**
     * Constructor
     * @param axis       The axis to be used in this AxisExpression: relevant constants are defined
     *                   in class client.net.sf.saxon.ce.om.Axis.
     * @param nodeTest   The conditions to be satisfied by selected nodes. May be null,
     *                   indicating that any node on the axis is acceptable
     * @see client.net.sf.saxon.ce.om.Axis
     */

    public AxisExpression(byte axis, NodeTest nodeTest) {
        this.axis = axis;
        test = nodeTest;
    }

    /**
     * Simplify an expression
     * @return the simplified expression
     * @param visitor an expression visitor
     */

    public Expression simplify(ExpressionVisitor visitor) {

        if (axis == Axis.PARENT && (test==null || test instanceof AnyNodeTest)) {
            ParentNodeExpression p = new ParentNodeExpression();
            ExpressionTool.copyLocationInfo(this, p);
            return p;
        }
        return this;
    }

    /**
    * Type-check the expression
    */

    public Expression typeCheck(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException {

        Configuration config = visitor.getConfiguration();
        NamePool namePool = config.getNamePool();
        StaticContext env = visitor.getStaticContext();
        if (contextItemType == null) {
            typeError(visitor, "Axis step " + toString(namePool) +
                    " cannot be used here: the context item is undefined", "XPDY0002", null);
        }
        if (contextItemType.isAtomicType()) {
            typeError(visitor, "Axis step " + toString(namePool) +
                    " cannot be used here: the context item is an atomic value", "XPTY0020", null);
        }

        if (this.contextItemType == contextItemType && doneWarnings) {
            return this;
        }

        this.contextItemType = contextItemType;
        doneWarnings = true;

        if (contextItemType instanceof NodeTest) {
            int origin = contextItemType.getPrimitiveType();
            if (origin != Type.NODE) {
                if (Axis.isAlwaysEmpty(axis, origin)) {
                    env.issueWarning("The " + Axis.axisName[axis] + " axis starting at " +
                            (origin==Type.ELEMENT || origin == Type.ATTRIBUTE ? "an " : "a ") +
                            NodeKindTest.nodeKindName(origin) + " node will never select anything",
                            getSourceLocator());
                    return Literal.makeEmptySequence();
                }
            }

            if (test != null) {
                int kind = test.getPrimitiveType();
                if (kind != Type.NODE) {
                    if (!Axis.containsNodeKind(axis, kind)) {
                        env.issueWarning("The " + Axis.axisName[axis] + " axis will never select any " +
                            NodeKindTest.nodeKindName(kind) + " nodes",
                            getSourceLocator());
                        return Literal.makeEmptySequence();
                    }
                }
                if (axis==Axis.SELF && kind!=Type.NODE && origin!=Type.NODE && kind!=origin) {
                    env.issueWarning("The self axis will never select any " +
                            NodeKindTest.nodeKindName(kind) +
                            " nodes when starting at " +
                            (origin==Type.ELEMENT || origin == Type.ATTRIBUTE ? "an " : "a "+
                            NodeKindTest.nodeKindName(origin) + " node", getSourceLocator());
                    return Literal.makeEmptySequence();
                }
                if (axis==Axis.SELF) {
                    itemType = new CombinedNodeTest(test,  Token.INTERSECT, (NodeTest)contextItemType);
                }

                // If the content type of the context item is known, see whether the node test can select anything

                if (contextItemType instanceof DocumentNodeTest && kind == Type.ELEMENT) {
                    NodeTest elementTest = ((DocumentNodeTest)contextItemType).getElementTest();
                    Set<Integer> outermostElementNames = elementTest.getRequiredNodeNames();
                    if (outermostElementNames != null) {
                        Set<Integer> selectedElementNames = test.getRequiredNodeNames();
                        if (selectedElementNames != null) {
                            if (axis == Axis.CHILD) {
                                // check that the name appearing in the step is one of the names allowed by the nodetest

                                if (SetUtils.intersect(selectedElementNames, outermostElementNames).isEmpty()) {
                                    env.issueWarning("Starting at a document node, the step is selecting an element whose name " +
                                            "is not among the names of child elements permitted for this document node type", getSourceLocator());

                                    return Literal.makeEmptySequence();
                                }
                                itemType = elementTest;
                                return this;

                            } else if (axis == Axis.DESCENDANT) {
                                // check that the name appearing in the step is one of the names allowed by the nodetest
                                boolean canMatchOutermost = !SetUtils.intersect(selectedElementNames, outermostElementNames).isEmpty();
                                if (!canMatchOutermost) {
                                    // The expression /descendant::x starting at the document node doesn't match the outermost
                                    // element, so replace it by child::*/descendant::x, and check that
                                    PathExpression path = new PathExpression(
                                            new AxisExpression(Axis.CHILD, elementTest),
                                            new AxisExpression(Axis.DESCENDANT, test));
                                    ExpressionTool.copyLocationInfo(this, path);
                                    return path.typeCheck(visitor, contextItemType);
                                }
                            }
                        }
                    }
                }
            }
        }

        return this;
    }

    /**
     * Get the static type of the context item for this AxisExpression. May be null if not known.
     * @return the statically-inferred type, or null if not known
     */

    public ItemType getContextItemType() {
        return contextItemType;
    }


    /**
     * Perform optimisation of an expression and its subexpressions.
     * <p/>
     * <p>This method is called after all references to functions and variables have been resolved
     * to the declaration of the function or variable, and after all type checking has been done.</p>
     *
     * @param visitor an expression visitor
     * @param contextItemType the static type of "." at the point where this expression is invoked.
     *                        The parameter is set to null if it is known statically that the context item will be undefined.
     *                        If the type of the context item is not known statically, the argument is set to
     *                        {@link client.net.sf.saxon.ce.type.Type#ITEM_TYPE}
     * @return the original expression, rewritten if appropriate to optimize execution
     */

    public Expression optimize(ExpressionVisitor visitor, ItemType contextItemType) {
        return this;
    }

    /**
    * Is this expression the same as another expression?
    */

    public boolean equals(Object other) {
        if (!(other instanceof AxisExpression)) {
            return false;
        }
        if (axis != ((AxisExpression)other).axis) {
            return false;
        }
        if (test==null) {
            return ((AxisExpression)other).test==null;
        }
        return test.toString().equals(((AxisExpression)other).test.toString());
    }

    /**
    * get HashCode for comparing two expressions
    */

    public int hashCode() {
        // generate an arbitrary hash code that depends on the axis and the node test
        int h = 9375162 + axis<<20;
        if (test != null) {
            h ^= test.getPrimitiveType()<<16;
            h ^= test.getFingerprint();
        }
        return h;
    }

    /**
    * Determine which aspects of the context the expression depends on. The result is
    * a bitwise-or'ed value composed from constants such as XPathContext.VARIABLES and
    * XPathContext.CURRENT_NODE
    */

    public int getIntrinsicDependencies() {
      return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
    }


    /**
     * Copy an expression. This makes a deep copy.
     *
     * @return the copy of the original expression
     */

    private Expression copy() {
        AxisExpression a2 = new AxisExpression(axis, test);
        a2.itemType = itemType;
        a2.contextItemType = contextItemType;
        a2.computedCardinality = computedCardinality;
        a2.doneWarnings = false;
        return a2;
    }

    /**
    * Get the static properties of this expression (other than its type). The result is
    * bit-signficant. These properties are used for optimizations. In general, if
    * property bit is set, it is true, but if it is unset, the value is unknown.
     */

    public int computeSpecialProperties() {
        return StaticProperty.CONTEXT_DOCUMENT_NODESET |
               StaticProperty.SINGLE_DOCUMENT_NODESET |
               StaticProperty.NON_CREATIVE |
               (Axis.isForwards[axis] ? StaticProperty.ORDERED_NODESET  : StaticProperty.REVERSE_DOCUMENT_ORDER) |
               (Axis.isPeerAxis[axis] ? StaticProperty.PEER_NODESET : 0) |
               (Axis.isSubtreeAxis[axis] ? StaticProperty.SUBTREE_NODESET : 0) |
               ((axis==Axis.ATTRIBUTE || axis==Axis.NAMESPACE) ? StaticProperty.ATTRIBUTE_NS_NODESET : 0);
    }

    /**
     * Determine the data type of the items returned by this expression
     * @return Type.NODE or a subtype, based on the NodeTest in the axis step, plus
     * information about the content type if this is known from schema analysis
     * @param th the type hierarchy cache
     */

    public final ItemType getItemType(TypeHierarchy th) {
        if (itemType != null) {
            return itemType;
        }
        int p = Axis.principalNodeType[axis];
        switch (p) {
        case Type.ATTRIBUTE:
        case Type.NAMESPACE:
            return NodeKindTest.makeNodeKindTest(p);
        default:
            if (test==null) {
                return AnyNodeTest.getInstance();
            } else {
                return test;
                //return NodeKindTest.makeNodeKindTest(test.getPrimitiveType());
            }
        }
    }

    /**
    * Determine the cardinality of the result of this expression
    */

    public final int computeCardinality() {
        if (computedCardinality != -1) {
            // This takes care of the case where cardinality was computed during type checking of the child axis
            return computedCardinality;
        }


        if (axis == Axis.ATTRIBUTE && test instanceof NameTest) {
            return StaticProperty.ALLOWS_ZERO_OR_ONE;
        } else if (axis == Axis.SELF) {
            return StaticProperty.ALLOWS_ZERO_OR_ONE;
        } else {
            return StaticProperty.ALLOWS_ZERO_OR_MORE;
        }
        // the parent axis isn't handled by this class
    }

    /**
     * Determine whether the expression can be evaluated without reference to the part of the context
     * document outside the subtree rooted at the context node.
     * @return true if the expression has no dependencies on the context node, or if the only dependencies
     *         on the context node are downward selections using the self, child, descendant, attribute, and namespace
     *         axes.
     */

    public boolean isSubtreeExpression() {
        return Axis.isSubtreeAxis[axis];
    }

    /**
     * Get the axis
     * @return the axis number, for example {@link Axis#CHILD}
    */

    public byte getAxis() {
        return axis;
    }

    /**
     * Get the NodeTest. Returns null if the AxisExpression can return any node.
     * @return the node test, or null if all nodes are returned
    */

    public NodeTest getNodeTest() {
        return test;
    }


    /**
    * Evaluate the path-expression in a given context to return a NodeSet
    * @param context the evaluation context
    */

    public SequenceIterator iterate(XPathContext context) throws XPathException {
        Item item = context.getContextItem();
        try {
            if (test==null) {
                return ((NodeInfo)item).iterateAxis(axis);
            } else {
                return ((NodeInfo)item).iterateAxis(axis, test);
            }
        } catch (Exception exe) {
            NamePool pool;
            try {
                pool = context.getConfiguration().getNamePool();
            } catch (Exception err) {
                pool = null;
            }
            String cName = (pool == null) ? toString() : toString(pool);
            boolean isCCE = (exe instanceof ClassCastException);
           if (exe instanceof NullPointerException || item == null || isCCE) {
                String appendText = " is " + ((isCCE)? "not a node" : "undefined");
                String code = (isCCE)? "XPTY0020" : "XPDY0002";
              XPathException err = new XPathException("The context item for axis step " +
                  cName + appendText);
              err.setErrorCode(code);
              err.setXPathContext(context);
              err.setLocator(getSourceLocator());
              err.setIsTypeError(true);
              throw err;
            } else { // if (exe instanceof UnsupportedOperationException) {
              if (exe.getCause() instanceof XPathException) {
                  XPathException ec = (XPathException)exe.getCause();
                  ec.maybeSetLocation(getSourceLocator());
                  ec.maybeSetContext(context);
                  throw ec;
              } else {
                  // the namespace axis is not supported for all tree implementations
                  dynamicError("Axis Expression Error on: " + cName + " " + exe.getMessage(), "XPST0010", context);
                  return null;
              }
            }
        }
    }

    /**
     * Represent the expression as a string for diagnostics
     */

    public String toString() {
        return Axis.axisName[axis] +
                "::" +
                (test==null ? "node()" : test.toString());
    }

    /**
     * Represent the expression as a string for diagnostics
     * @param pool the name pool, used for expanding names in the node test
     * @return a string representation of the expression
     */

    public String toString(NamePool pool) {
        return Axis.axisName[axis] +
                "::" +
                (test==null ? "node()" : test.toString(pool));
    }
}



// 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.
TOP

Related Classes of client.net.sf.saxon.ce.expr.AxisExpression

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.