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