KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > icl > saxon > expr > PathExpression


1 package com.icl.saxon.expr;
2 import com.icl.saxon.Context;
3 import com.icl.saxon.om.Axis;
4 import com.icl.saxon.om.NodeInfo;
5 import com.icl.saxon.om.NodeEnumeration;
6 import com.icl.saxon.pattern.AnyNodeTest;
7 import com.icl.saxon.pattern.NameTest;
8 import com.icl.saxon.sort.NodeOrderComparer;
9 import com.icl.saxon.sort.LocalOrderComparer;
10
11 /**
12 * An expression that establishes a set of nodes by following relationships between nodes
13 * in the document. Specifically, it consists of a start expression which defines a set of
14 * nodes, and a Step which defines a relationship to be followed from those nodes to create
15 * a new set of nodes.
16 */

17
18 public class PathExpression extends NodeSetExpression {
19
20     private Expression start;
21     private Step step;
22     int dependencies = -1;
23
24     /**
25     * Constructor
26     * @param start A node-set expression denoting the absolute or relative set of nodes from which the
27     * navigation path should start.
28     * @param step The step to be followed from each node in the start expression to yield a new
29     * node-set
30     */

31
32     public PathExpression(Expression start, Step step) {
33         this.start = start;
34         this.step = step;
35     }
36
37     /**
38     * Simplify an expression
39     * @return the simplified expression
40     */

41
42     public Expression simplify() throws XPathException {
43
44         start = start.simplify();
45         step = step.simplify();
46
47         // if the start expression is an empty node-set, then the whole PathExpression is empty
48
if (start instanceof EmptyNodeSet) {
49             return start;
50         }
51
52         // if the simplified Step is null, then by convention the whole PathExpression is empty
53
if (step==null) {
54             return new EmptyNodeSet();
55         }
56         
57         byte axis = step.getAxis();
58
59         // the expression /.. is sometimes used to represent the empty node-set
60

61         if (start instanceof RootExpression && axis == Axis.PARENT) {
62             return new EmptyNodeSet();
63         }
64         
65         // simplify a straightforward attribute reference such as "@name"
66

67         if ( start instanceof ContextNodeExpression &&
68                 axis == Axis.ATTRIBUTE &&
69                 step.getNodeTest() instanceof NameTest &&
70                 step.getNumberOfFilters() == 0) {
71                     
72             return new AttributeReference(step.getNodeTest().getFingerprint());
73         }
74
75         // Simplify a path expression that starts at the context node and uses no
76
// filters.
77

78         if ( start instanceof ContextNodeExpression &&
79                 step.getNumberOfFilters() == 0) {
80             return new AxisExpression(axis, step.getNodeTest());
81         }
82
83         // Simplify an expression of the form a//b, where b has no filters.
84
// This comes out of the parser as a/descendent-or-self::node()/child::b,
85
// but it is equivalent to a/descendant::b; and the latter is better as it
86
// doesn't require sorting
87

88         if ( axis == Axis.CHILD &&
89                 step.getNumberOfFilters() == 0 &&
90                 start instanceof PathExpression &&
91                 ((PathExpression)start).step.getAxis() == Axis.DESCENDANT_OR_SELF &&
92                 ((PathExpression)start).step.getNumberOfFilters() == 0 &&
93                 ((PathExpression)start).step.getNodeTest() instanceof AnyNodeTest )
94         {
95             // detect a simple "//name" expression
96

97             // this optimisation is now done at run time by NodeImpl.getEnumeration()
98
// and TinyNodeImpl.getEnumeration()
99

100             //Expression newstart = ((PathExpression)start).start;
101
//if ((newstart instanceof RootExpression) &&
102
// (step.getNodeTest().getNodeType() == NodeInfo.ELEMENT) &&
103
// (step.getNodeTest() instanceof NameTest)) {
104
// return new AllElementsExpression(((NameTest)step.getNodeTest()).getFingerprint());
105
//
106
//} else {
107

108                 return new PathExpression(
109                     ((PathExpression)start).start,
110                     new Step(Axis.DESCENDANT, step.getNodeTest()));
111             //}
112
}
113
114         return this;
115     }
116
117     /**
118     * Determine which aspects of the context the expression depends on. The result is
119     * a bitwise-or'ed value composed from constants such as Context.VARIABLES and
120     * Context.CURRENT_NODE
121     */

122
123     public int getDependencies() {
124         if (dependencies==-1) {
125             dependencies = start.getDependencies();
126             Expression[] filters = step.getFilters();
127             for (int f=0; f<step.getNumberOfFilters(); f++) {
128                 Expression exp = filters[f];
129                 // Not all dependencies in the filter matter, because the context node, etc,
130
// are not dependent on the outer context of the PathExpression
131
dependencies |= (exp.getDependencies() & Context.XSLT_CONTEXT);
132                                  //(Context.XSLT_CONTEXT | Context.CONTEXT_DOCUMENT));
133
}
134         }
135         return dependencies;
136     }
137
138     /**
139     * Determine, in the case of an expression whose data type is Value.NODESET,
140     * whether all the nodes in the node-set are guaranteed to come from the same
141     * document as the context node. Used for optimization.
142     */

143     
144     public boolean isContextDocumentNodeSet() {
145         return start.isContextDocumentNodeSet();
146     }
147
148     /**
149     * Perform a partial evaluation of the expression, by eliminating specified dependencies
150     * on the context.
151     * @param dep The dependencies to be removed
152     * @param context The context to be used for the partial evaluation
153     * @return a new expression that does not have any of the specified
154     * dependencies
155     */

156
157     public Expression reduce(int dep, Context context) throws XPathException {
158         Expression path = this;
159         if ((dep & getDependencies()) != 0) {
160             Expression newstart = start.reduce(dep, context);
161             Step newstep = new Step(step.getAxis(), step.getNodeTest());
162             Expression[] filters = step.getFilters();
163             
164             int removedep = dep & Context.XSLT_CONTEXT;
165             if (start.isContextDocumentNodeSet() &&
166                  ((dep & Context.CONTEXT_DOCUMENT)!=0)) {
167                 removedep |= Context.CONTEXT_DOCUMENT;
168             }
169             
170             for (int f=0; f<step.getNumberOfFilters(); f++) {
171                 Expression exp = filters[f];
172                 // Not all dependencies in the filter matter, because the context node, etc,
173
// are not dependent on the outer context of the PathExpression
174
Expression newfilter = exp.reduce(removedep, context);
175                 newstep.addFilter(newfilter);
176             }
177             path = new PathExpression(newstart, newstep);
178             path.setStaticContext(getStaticContext());
179             path = path.simplify();
180         }
181          
182         // Pre-evaluate an expression if the start is now a constant node-set
183
// (this will evaluate to a NodeSetIntent, which will be replaced by
184
// the corresponding node-set extent if it is used more than thrice).
185

186         if ((path instanceof PathExpression) &&
187              ((PathExpression)path).start instanceof NodeSetValue) {
188             return new NodeSetIntent((PathExpression)path, context.getController());
189         }
190         
191         return path;
192     }
193
194
195     /**
196     * Evaluate the path-expression in a given context to return a NodeSet
197     * @param context the evaluation context
198     * @param sort true if the returned nodes must be in document order
199     */

200
201     public NodeEnumeration enumerate(Context context, boolean sort) throws XPathException {
202         // if the expression references variables, or depends on other aspects of
203
// the XSLT context, then resolve these dependencies now. Also, if the nodes
204
// are all known to be in the context document, then any dependency on the
205
// context document (e.g. an absolute path expression in a predicate) can also
206
// be removed now.
207

208         int actualdep = getDependencies();
209         int removedep = 0;
210         
211         if ((actualdep & Context.XSLT_CONTEXT) != 0) {
212             removedep |= Context.XSLT_CONTEXT;
213         }
214                 
215         if (start.isContextDocumentNodeSet() &&
216               ((actualdep & Context.CONTEXT_DOCUMENT) != 0)) {
217             removedep |= Context.CONTEXT_DOCUMENT;
218         }
219         
220         if (( removedep & (Context.XSLT_CONTEXT | Context.CONTEXT_DOCUMENT)) != 0) {
221             Expression temp = reduce(removedep, context);
222             return temp.enumerate(context, sort);
223         }
224             
225         NodeEnumeration enum = new PathEnumeration(start, context);
226         if (sort && !enum.isSorted()) {
227             NodeOrderComparer comparer;
228             if (start instanceof SingletonNodeSet || start.isContextDocumentNodeSet()) {
229                 // nodes are all in the same document
230
comparer = LocalOrderComparer.getInstance();
231             } else {
232                 comparer = context.getController();
233             }
234             NodeSetExtent ns = new NodeSetExtent(enum, comparer);
235             ns.sort();
236             return ns.enumerate();
237         }
238         return enum;
239     }
240
241     /**
242     * Diagnostic print of expression structure
243     */

244     
245     public void display(int level) {
246         System.err.println(indent(level) + "path");
247         start.display(level+1);
248         step.display(level+1);
249     }
250
251
252     /**
253     * Inner class PathEnumeration
254     */

255
256     private class PathEnumeration implements NodeEnumeration {
257
258         private Expression thisStart;
259         private NodeEnumeration base=null;
260         private NodeEnumeration thisStep=null;
261         private NodeInfo next=null;
262         private Context context;
263
264         public PathEnumeration(Expression start, Context context) throws XPathException {
265             if (start instanceof SingletonNodeSet) {
266                 if (!((SingletonNodeSet)start).isGeneralUseAllowed()) {
267                     throw new XPathException("To use a result tree fragment in a path expression, either use exsl:node-set() or specify version='1.1'");
268                 }
269             }
270             thisStart = start;
271             this.context = context.newContext();
272             base = start.enumerate(this.context, false);
273             next = getNextNode();
274         }
275
276         public boolean hasMoreElements() {
277             return next!=null;
278         }
279
280         public NodeInfo nextElement() throws XPathException {
281             NodeInfo curr = next;
282             next = getNextNode();
283             return curr;
284         }
285
286         private NodeInfo getNextNode() throws XPathException {
287             
288             // if we are currently processing a step, we continue with it. Otherwise,
289
// we get the next base element, and apply the step to that.
290

291             if (thisStep!=null && thisStep.hasMoreElements()) {
292                 return thisStep.nextElement();
293                                 //NodeInfo n = thisStep.nextElement();
294
//System.err.println("Continuing Step.nextElement() = " + n);
295
//return n;
296
}
297             
298             while (base.hasMoreElements()) {
299                 NodeInfo node = base.nextElement();
300                                 //System.err.println("Base.nextElement = " + node);
301
thisStep = step.enumerate(node, context);
302                 if (thisStep.hasMoreElements()) {
303                     return thisStep.nextElement();
304                                 //NodeInfo n2 = thisStep.nextElement();
305
//System.err.println("Starting Step.nextElement() = " + n2);
306
//return n2;
307
}
308             }
309             
310             return null;
311
312         }
313
314         /**
315         * Determine if we can guarantee that the nodes are in document order. This is true if the
316         * start nodes are sorted peer nodes and the step is within the subtree rooted at each node.
317         * It is also true if the start is a singleton node and the axis is sorted.
318         */

319
320         public boolean isSorted() {
321             byte axis = step.getAxis();
322             return Axis.isForwards[axis] && (
323                 ( (thisStart instanceof SingletonExpression) ||
324                  (base.isSorted() && base.isPeer() && Axis.isSubtreeAxis[axis]) ||
325                  (base.isSorted() && (axis==Axis.ATTRIBUTE || axis==Axis.NAMESPACE))
326                 ));
327         }
328
329         /**
330         * Determine if the nodes are guaranteed to be in reverse document order. This is true if the
331         * base is singular (e.g. the root node or the current node) and the axis is a reverse axis
332         */

333
334         public boolean isReverseSorted() {
335             return thisStart instanceof SingletonExpression && Axis.isReverse[step.getAxis()];
336         }
337
338         /**
339         * Determine if the resulting nodes are peer nodes, that is, if no node is a descendant of any
340         * other. This is the case if the start nodes are peer nodes and the axis is a peer axis.
341         */

342
343         public boolean isPeer() {
344             return (base.isPeer() && Axis.isPeerAxis[step.getAxis()]);
345         }
346
347     } // end of inner class PathEnumeration
348

349
350 }
351
352
353
354 //
355
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
356
// you may not use this file except in compliance with the License. You may obtain a copy of the
357
// License at http://www.mozilla.org/MPL/
358
//
359
// Software distributed under the License is distributed on an "AS IS" basis,
360
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
361
// See the License for the specific language governing rights and limitations under the License.
362
//
363
// The Original Code is: all this file.
364
//
365
// The Initial Developer of the Original Code is
366
// Michael Kay of International Computers Limited (mhkay@iclway.co.uk).
367
//
368
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
369
//
370
// Contributor(s): none.
371
//
372
Popular Tags