/** 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; }
/** 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()); }
/** 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; }
/** * 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)); }