// 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;
}
}
env.issueWarning("The " + Axis.axisName[axis] + " axis will never select any" +
" nodes when starting at a node with type " +
contentType.getDescription() +
", as this type requires empty content", this);
return new Literal(EmptySequence.getInstance());
} else if (axis==Axis.ATTRIBUTE && targetfp != -1) {
try {
SchemaType schemaType = ((ComplexType)contentType).getAttributeUseType(targetfp);
if (schemaType == null) {
String n = env.getNamePool().getDisplayName(targetfp);
env.issueWarning("The complex type " + contentType.getDescription() +
" does not allow an attribute named " + n, this);
return new Literal(EmptySequence.getInstance());
} else {
itemType = new CombinedNodeTest(
test,
Token.INTERSECT,
new ContentTypeTest(Type.ATTRIBUTE, schemaType, env.getConfiguration()));
}
} catch (SchemaException e) {
// ignore the exception
}
} else if (axis==Axis.CHILD && kind==Type.ELEMENT) {
try {
int childElement = targetfp;
if (targetfp == -1) {
// select="child::*"
if (((ComplexType)contentType).containsElementWildcard()) {
return this;
}
IntHashSet children = new IntHashSet();
((ComplexType)contentType).gatherAllPermittedChildren(children);
if (children.isEmpty()) {
env.issueWarning("The complex type " + contentType.getDescription() +
" does not allow children", this);
return new Literal(EmptySequence.getInstance());
}
// if (children.contains(-1)) {
// return this;
// }
if (children.size() == 1) {
IntIterator iter = children.iterator();
if (iter.hasNext()) {
childElement = iter.next();
}
} else {
return this;
}
}
SchemaType schemaType = ((ComplexType)contentType).getElementParticleType(childElement, true);
if (schemaType == null) {
String n = env.getNamePool().getDisplayName(childElement);
env.issueWarning("The complex type " + contentType.getDescription() +
" does not allow a child element named " + n, this);
return new Literal(EmptySequence.getInstance());
} else {
itemType = new CombinedNodeTest(
test,
Token.INTERSECT,
new ContentTypeTest(Type.ELEMENT, schemaType, env.getConfiguration()));
computedCardinality = ((ComplexType)contentType).getElementParticleCardinality(childElement);
visitor.resetStaticProperties();
if (computedCardinality == StaticProperty.ALLOWS_ZERO) {
// this shouldn't happen, because we've already checked for this a different way.
// but it's worth being safe (there was a bug involving an incorrect inference here)
String n = env.getNamePool().getDisplayName(childElement);
env.issueWarning("The complex type " + contentType.getDescription() +
" appears not to allow a child element named " + n, this);
return new Literal(EmptySequence.getInstance());
}
if (!Cardinality.allowsMany(computedCardinality)) {
// if there can be at most one child of this name, create a FirstItemExpression
// to stop the search after the first one is found
return new FirstItemExpression(this);
}
}
} catch (SchemaException e) {
// ignore the exception
}
} else if (axis==Axis.DESCENDANT && kind==Type.ELEMENT && targetfp != -1) {
// when searching for a specific element on the descendant axis, try to produce a more
// specific path that avoids searching branches of the tree where the element cannot occur
try {
IntHashSet descendants = new IntHashSet();
((ComplexType)contentType).gatherAllPermittedDescendants(descendants);
if (descendants.contains(-1)) {
return this;
}
if (descendants.contains(targetfp)) {
IntHashSet children = new IntHashSet();
((ComplexType)contentType).gatherAllPermittedChildren(children);
IntHashSet usefulChildren = new IntHashSet();
boolean considerSelf = false;
boolean considerDescendants = false;
for (IntIterator child = children.iterator(); child.hasNext();) {
int c = child.next();
if (c == targetfp) {
usefulChildren.add(c);
considerSelf = true;
}
SchemaType st = ((ComplexType)contentType).getElementParticleType(c, true);
if (st == null) {
throw new AssertionError("Can't find type for element " + c);
}
if (st instanceof ComplexType) {
IntHashSet subDescendants = new IntHashSet();
((ComplexType)st).gatherAllPermittedDescendants(subDescendants);
if (subDescendants.contains(targetfp)) {
usefulChildren.add(c);
considerDescendants = true;
}
}
}