* 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) {
XPathException err = new XPathException("Axis step " + toString(namePool) +
" cannot be used here: the context item is undefined");
err.setIsTypeError(true);
err.setErrorCode("XPDY0002");
err.setLocator(this);
throw err;
}
if (contextItemType.isAtomicType()) {
XPathException err = new XPathException("Axis step " + toString(namePool) +
" cannot be used here: the context item is an atomic value");
err.setIsTypeError(true);
err.setErrorCode("XPTY0020");
err.setLocator(this);
throw err;
}
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",
this);
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",
this);
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", this);
return Literal.makeEmptySequence();
}
if (axis==Axis.SELF) {
itemType = new CombinedNodeTest((NodeTest)contextItemType, Token.INTERSECT, test);
}
// If the content type of the context item is known, see whether the node test can select anything
if (contextItemType instanceof DocumentNodeTest && axis == Axis.CHILD && kind == Type.ELEMENT) {
NodeTest elementTest = ((DocumentNodeTest)contextItemType).getElementTest();
IntHashSet requiredNames = elementTest.getRequiredNodeNames();
if (requiredNames != null) {
// check that the name appearing in the step is one of the names allowed by the nodetest
IntHashSet selected = test.getRequiredNodeNames();
if (selected != null && selected.intersect(requiredNames).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", this);
return Literal.makeEmptySequence();
}
}
itemType = elementTest;
return this;
}
SchemaType contentType = ((NodeTest)contextItemType).getContentType();
if (contentType == AnyType.getInstance()) {
// fast exit in non-schema-aware case
return this;
}
int targetfp = test.getFingerprint();
if (contentType.isSimpleType()) {
if ((axis == Axis.CHILD || axis==Axis.DESCENDANT || axis==Axis.DESCENDANT_OR_SELF) &&
(kind==Type.ELEMENT || kind==Type.ATTRIBUTE || kind==Type.DOCUMENT)) {
env.issueWarning("The " + Axis.axisName[axis] + " axis will never select any " +
NodeKindTest.nodeKindName(kind) +
" nodes when starting at a node with simple type " +
contentType.getDescription(), this);
} else if (axis == Axis.CHILD && kind == Type.TEXT &&
(visitor.getParentExpression() instanceof Atomizer)) {
env.issueWarning("Selecting the text nodes of an element with simple content may give the " +
"wrong answer in the presence of comments or processing instructions. It is usually " +
"better to omit the '/text()' step", this);
} else if (axis == Axis.ATTRIBUTE) {
Iterator extensions = config.getExtensionsOfType(contentType);
boolean found = false;
if (targetfp == -1) {
while (extensions.hasNext()) {
ComplexType extension = (ComplexType)extensions.next();
if (extension.allowsAttributes()) {
found = true;
break;
}
}
} else {
while (extensions.hasNext()) {
ComplexType extension = (ComplexType)extensions.next();
try {
if (extension.getAttributeUseType(targetfp) != null) {
found = true;
break;
}
} catch (SchemaException e) {
// ignore the error
}
}
}
if (!found) {
env.issueWarning("The " + Axis.axisName[axis] + " axis will never select " +
(targetfp == -1 ?
"any attribute nodes" :
"an attribute node named " + env.getNamePool().getDisplayName(targetfp)) +
" when starting at a node with simple type " +
contentType.getDescription(), this);
// Despite the warning, leave the expression unchanged. This is because
// we don't necessarily know about all extended types at compile time:
// in particular, we don't seal the XML Schema namespace to block extensions
// of built-in types
}
}
} else if (((ComplexType)contentType).isSimpleContent() &&
(axis==Axis.CHILD || axis==Axis.DESCENDANT || axis==Axis.DESCENDANT_OR_SELF) &&
(kind==Type.ELEMENT || kind==Type.DOCUMENT)) {
// We don't need to consider extended types here, because a type with complex content
// can never be defined as an extension of a type with simple content
env.issueWarning("The " + Axis.axisName[axis] + " axis will never select any " +
NodeKindTest.nodeKindName(kind) +
" nodes when starting at a node with type " +
contentType.getDescription() +
", as this type requires simple content", this);
return new Literal(EmptySequence.getInstance());
} else if (((ComplexType)contentType).isEmptyContent() &&
(axis==Axis.CHILD || axis==Axis.DESCENDANT || axis==Axis.DESCENDANT_OR_SELF)) {
for (Iterator iter=config.getExtensionsOfType(contentType); iter.hasNext();) {
ComplexType extension = (ComplexType)iter.next();
if (!extension.isEmptyContent()) {
return this;
}
}