001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.om.*;
004: import net.sf.saxon.pattern.*;
005: import net.sf.saxon.trans.DynamicError;
006: import net.sf.saxon.trans.StaticError;
007: import net.sf.saxon.trans.XPathException;
008: import net.sf.saxon.type.*;
009: import net.sf.saxon.value.EmptySequence;
010:
011: import java.io.PrintStream;
012:
013: /**
014: * An AxisExpression is always obtained by simplifying a PathExpression.
015: * It represents a PathExpression that starts at the context node, and uses
016: * a simple node-test with no filters. For example "*", "title", "./item",
017: * "@*", or "ancestor::chapter*".
018: *
019: * <p>An AxisExpression delivers nodes in axis order (not in document order).
020: * To get nodes in document order, in the case of a reverse axis, the expression
021: * should be wrapped in a Reverser.</p>
022: */
023:
024: public final class AxisExpression extends ComputedExpression {
025:
026: private byte axis;
027: private NodeTest test;
028: private ItemType itemType = null;
029: int computedCardinality = -1;
030:
031: /**
032: * Constructor
033: * @param axis The axis to be used in this AxisExpression: relevant constants are defined
034: * in class net.sf.saxon.om.Axis.
035: * @param nodeTest The conditions to be satisfied by selected nodes. May be null,
036: * indicating that any node on the axis is acceptable
037: * @see net.sf.saxon.om.Axis
038: */
039:
040: public AxisExpression(byte axis, NodeTest nodeTest) {
041: this .axis = axis;
042: this .test = nodeTest;
043: }
044:
045: /**
046: * Simplify an expression
047: * @return the simplified expression
048: */
049:
050: public Expression simplify(StaticContext env) {
051:
052: Expression exp = this ;
053: if (axis == Axis.PARENT
054: && (test == null || test instanceof AnyNodeTest)) {
055: exp = new ParentNodeExpression();
056: ExpressionTool.copyLocationInfo(this , exp);
057: }
058:
059: return exp;
060: }
061:
062: /**
063: * Type-check the expression
064: */
065:
066: public Expression typeCheck(StaticContext env,
067: ItemType contextItemType) throws XPathException {
068: if (contextItemType == null) {
069: StaticError err = new StaticError(
070: "Axis step "
071: + toString()
072: + " cannot be used here: the context item is undefined");
073: err.setIsTypeError(true);
074: err.setErrorCode("XPDY0002");
075: err.setLocator(this );
076: throw err;
077: }
078: if (contextItemType instanceof AtomicType) {
079: StaticError err = new StaticError(
080: "Axis step "
081: + toString()
082: + " cannot be used here: the context item is an atomic value");
083: err.setIsTypeError(true);
084: err.setErrorCode("XPTY0020");
085: err.setLocator(this );
086: throw err;
087: }
088:
089: if (contextItemType instanceof NodeTest) {
090: int origin = contextItemType.getPrimitiveType();
091: if (origin != Type.NODE) {
092: if (Axis.isAlwaysEmpty(axis, origin)) {
093: env.issueWarning("The "
094: + Axis.axisName[axis]
095: + " axis starting at "
096: + (origin == Type.ELEMENT
097: || origin == Type.ATTRIBUTE ? "an "
098: : "a ")
099: + NodeKindTest.toString(origin)
100: + " node will never select anything", this );
101: return EmptySequence.getInstance();
102: }
103: }
104:
105: if (test != null) {
106: int kind = test.getPrimitiveType();
107: if (kind != Type.NODE) {
108: if (!Axis.containsNodeKind(axis, kind)) {
109: env.issueWarning("The " + Axis.axisName[axis]
110: + " axis will never select any "
111: + NodeKindTest.toString(kind)
112: + " nodes", this );
113: return EmptySequence.getInstance();
114: }
115: }
116: if (axis == Axis.SELF && kind != Type.NODE
117: && origin != Type.NODE && kind != origin) {
118: env
119: .issueWarning(
120: "The self axis will never select any "
121: + NodeKindTest
122: .toString(origin)
123: + " nodes when starting at "
124: + (origin == Type.ELEMENT
125: || origin == Type.ATTRIBUTE ? "an "
126: : "a ")
127: + NodeKindTest
128: .toString(kind)
129: + " node", this );
130: return EmptySequence.getInstance();
131: }
132: if (axis == Axis.SELF) {
133: itemType = contextItemType;
134: }
135:
136: // If the content type of the context item is known, see whether the node test can select anything
137:
138: SchemaType contentType = ((NodeTest) contextItemType)
139: .getContentType();
140: if (contentType == AnyType.getInstance()) {
141: // fast exit in non-schema-aware case
142: return this ;
143: }
144:
145: int targetfp = test.getFingerprint();
146:
147: if (contentType.isSimpleType()) {
148: if ((axis == Axis.CHILD || axis == Axis.ATTRIBUTE
149: || axis == Axis.DESCENDANT || axis == Axis.DESCENDANT_OR_SELF)
150: && (kind == Type.ELEMENT
151: || kind == Type.ATTRIBUTE || kind == Type.DOCUMENT)) {
152: env
153: .issueWarning(
154: "The "
155: + Axis.axisName[axis]
156: + " axis will never select any "
157: + NodeKindTest
158: .toString(kind)
159: + " nodes when starting at a node with simple type "
160: + contentType
161: .getDescription(),
162: this );
163: }
164: } else if (((ComplexType) contentType)
165: .isSimpleContent()
166: && (axis == Axis.CHILD
167: || axis == Axis.DESCENDANT || axis == Axis.DESCENDANT_OR_SELF)
168: && (kind == Type.ELEMENT || kind == Type.DOCUMENT)) {
169: env
170: .issueWarning(
171: "The "
172: + Axis.axisName[axis]
173: + " axis will never select any "
174: + NodeKindTest
175: .toString(kind)
176: + " nodes when starting at a node with type "
177: + contentType
178: .getDescription()
179: + ", as this type requires simple content",
180: this );
181: } else if (((ComplexType) contentType).isEmptyContent()
182: && (axis == Axis.CHILD
183: || axis == Axis.DESCENDANT || axis == Axis.DESCENDANT_OR_SELF)) {
184: env
185: .issueWarning(
186: "The "
187: + Axis.axisName[axis]
188: + " axis will never select any "
189: + " nodes when starting at a node with type "
190: + contentType
191: .getDescription()
192: + ", as this type requires empty content",
193: this );
194: } else if (axis == Axis.ATTRIBUTE && targetfp != -1) {
195: try {
196: SchemaType schemaType = ((ComplexType) contentType)
197: .getAttributeUseType(targetfp);
198: if (schemaType == null) {
199: String n = env.getNamePool()
200: .getDisplayName(targetfp);
201:
202: env
203: .issueWarning(
204: "The complex type "
205: + contentType
206: .getDescription()
207: + " does not allow an attribute named "
208: + n, this );
209: } else {
210: itemType = new CombinedNodeTest(
211: test,
212: Token.INTERSECT,
213: new ContentTypeTest(Type.ATTRIBUTE,
214: schemaType, env
215: .getConfiguration()));
216: }
217: } catch (SchemaException e) {
218: // ignore the exception
219: }
220: } else if (axis == Axis.CHILD && kind == Type.ELEMENT
221: && targetfp != -1) {
222: try {
223: SchemaType schemaType = ((ComplexType) contentType)
224: .getElementParticleType(targetfp);
225: if (schemaType == null) {
226: String n = env.getNamePool()
227: .getDisplayName(targetfp);
228: env
229: .issueWarning(
230: "The complex type "
231: + contentType
232: .getDescription()
233: + " does not allow a child element named "
234: + n, this );
235: } else {
236: itemType = new CombinedNodeTest(
237: test,
238: Token.INTERSECT,
239: new ContentTypeTest(Type.ELEMENT,
240: schemaType, env
241: .getConfiguration()));
242: computedCardinality = ((ComplexType) contentType)
243: .getElementParticleCardinality(targetfp);
244: resetStaticProperties();
245: }
246: } catch (SchemaException e) {
247: // ignore the exception
248: }
249: }
250: }
251: }
252:
253: return this ;
254: }
255:
256: /**
257: * Perform optimisation of an expression and its subexpressions.
258: * <p/>
259: * <p>This method is called after all references to functions and variables have been resolved
260: * to the declaration of the function or variable, and after all type checking has been done.</p>
261: *
262: * @param opt the optimizer in use. This provides access to supporting functions; it also allows
263: * different optimization strategies to be used in different circumstances.
264: * @param env the static context of the expression
265: * @param contextItemType the static type of "." at the point where this expression is invoked.
266: * The parameter is set to null if it is known statically that the context item will be undefined.
267: * If the type of the context item is not known statically, the argument is set to
268: * {@link net.sf.saxon.type.Type#ITEM_TYPE}
269: * @return the original expression, rewritten if appropriate to optimize execution
270: */
271:
272: public Expression optimize(Optimizer opt, StaticContext env,
273: ItemType contextItemType) {
274: return this ;
275: }
276:
277: /**
278: * Is this expression the same as another expression?
279: */
280:
281: public boolean equals(Object other) {
282: if (!(other instanceof AxisExpression)) {
283: return false;
284: }
285: if (axis != ((AxisExpression) other).axis) {
286: return false;
287: }
288: if (test == null) {
289: return ((AxisExpression) other).test == null;
290: }
291: return test.toString().equals(
292: ((AxisExpression) other).test.toString());
293: }
294:
295: /**
296: * get HashCode for comparing two expressions
297: */
298:
299: public int hashCode() {
300: // generate an arbitrary hash code that depends on the axis and the node test
301: int h = 9375162 + axis << 20;
302: if (test != null) {
303: h ^= test.getPrimitiveType() << 16;
304: h ^= test.getFingerprint();
305: }
306: return h;
307: }
308:
309: /**
310: * Determine which aspects of the context the expression depends on. The result is
311: * a bitwise-or'ed value composed from constants such as XPathContext.VARIABLES and
312: * XPathContext.CURRENT_NODE
313: */
314:
315: public int getIntrinsicDependencies() {
316: return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
317: }
318:
319: /**
320: * Get the static properties of this expression (other than its type). The result is
321: * bit-signficant. These properties are used for optimizations. In general, if
322: * property bit is set, it is true, but if it is unset, the value is unknown.
323: */
324:
325: public int computeSpecialProperties() {
326: return StaticProperty.CONTEXT_DOCUMENT_NODESET
327: | StaticProperty.SINGLE_DOCUMENT_NODESET
328: | StaticProperty.NON_CREATIVE
329: | (Axis.isForwards[axis] ? StaticProperty.ORDERED_NODESET
330: : StaticProperty.REVERSE_DOCUMENT_ORDER)
331: | (Axis.isPeerAxis[axis] ? StaticProperty.PEER_NODESET
332: : 0)
333: | (Axis.isSubtreeAxis[axis] ? StaticProperty.SUBTREE_NODESET
334: : 0)
335: | ((axis == Axis.ATTRIBUTE || axis == Axis.NAMESPACE) ? StaticProperty.ATTRIBUTE_NS_NODESET
336: : 0);
337: }
338:
339: /**
340: * Determine the data type of the items returned by this expression
341: * @return Type.NODE or a subtype, based on the NodeTest in the axis step, plus
342: * information about the content type if this is known from schema analysis
343: * @param th
344: */
345:
346: public final ItemType getItemType(TypeHierarchy th) {
347: if (itemType != null) {
348: return itemType;
349: }
350: int p = Axis.principalNodeType[axis];
351: switch (p) {
352: case Type.ATTRIBUTE:
353: case Type.NAMESPACE:
354: return NodeKindTest.makeNodeKindTest(p);
355: default:
356: if (test == null) {
357: return AnyNodeTest.getInstance();
358: } else {
359: return test;
360: //return NodeKindTest.makeNodeKindTest(test.getPrimitiveType());
361: }
362: }
363: }
364:
365: /**
366: * Specify that the expression returns a singleton
367: */
368:
369: public final int computeCardinality() {
370: if (computedCardinality != -1) {
371: return computedCardinality;
372: }
373: if (axis == Axis.ATTRIBUTE && test instanceof NameTest) {
374: return StaticProperty.ALLOWS_ZERO_OR_ONE;
375: } else if (axis == Axis.SELF) {
376: return StaticProperty.ALLOWS_ZERO_OR_ONE;
377: } else {
378: return StaticProperty.ALLOWS_ZERO_OR_MORE;
379: }
380: // the parent axis isn't handled by this class
381: }
382:
383: /**
384: * Get the axis
385: */
386:
387: public byte getAxis() {
388: return axis;
389: }
390:
391: /**
392: * Get the NodeTest. Returns null if the AxisExpression can return any node.
393: */
394:
395: public NodeTest getNodeTest() {
396: return test;
397: }
398:
399: /**
400: * Evaluate the path-expression in a given context to return a NodeSet
401: * @param context the evaluation context
402: */
403:
404: public SequenceIterator iterate(XPathContext context)
405: throws XPathException {
406: Item item = context.getContextItem();
407: try {
408: if (test == null) {
409: return ((NodeInfo) item).iterateAxis(axis);
410: } else {
411: return ((NodeInfo) item).iterateAxis(axis, test);
412: }
413: } catch (NullPointerException npe) {
414: DynamicError err = new DynamicError(
415: "The context item for axis step " + toString()
416: + " is undefined");
417: err.setErrorCode("XPDY0002");
418: err.setXPathContext(context);
419: err.setLocator(this );
420: err.setIsTypeError(true);
421: throw err;
422: } catch (ClassCastException cce) {
423: DynamicError err = new DynamicError(
424: "The context item for axis step " + toString()
425: + " is not a node");
426: err.setErrorCode("XPTY0020");
427: err.setXPathContext(context);
428: err.setLocator(this );
429: err.setIsTypeError(true);
430: throw err;
431: } catch (UnsupportedOperationException err) {
432: // the namespace axis is not supported for all tree implementations
433: dynamicError(err.getMessage(), context);
434: return null;
435: }
436: }
437:
438: /**
439: * Diagnostic print of expression structure
440: */
441:
442: public void display(int level, NamePool pool, PrintStream out) {
443: out.println(ExpressionTool.indent(level) + toString());
444: }
445:
446: /**
447: * Represent the expression as a string for diagnostics
448: */
449:
450: public String toString() {
451: return Axis.axisName[axis] + "::"
452: + (test == null ? "node()" : test.toString());
453: }
454: }
455:
456: //
457: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
458: // you may not use this file except in compliance with the License. You may obtain a copy of the
459: // License at http://www.mozilla.org/MPL/
460: //
461: // Software distributed under the License is distributed on an "AS IS" basis,
462: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
463: // See the License for the specific language governing rights and limitations under the License.
464: //
465: // The Original Code is: all this file.
466: //
467: // The Initial Developer of the Original Code is Michael H. Kay.
468: //
469: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
470: //
471: // Contributor(s): none.
472: //
|