KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > pattern > LocationPathPattern


1 package net.sf.saxon.pattern;
2 import net.sf.saxon.expr.*;
3 import net.sf.saxon.om.*;
4 import net.sf.saxon.style.ExpressionContext;
5 import net.sf.saxon.trace.Location;
6 import net.sf.saxon.trans.DynamicError;
7 import net.sf.saxon.trans.StaticError;
8 import net.sf.saxon.trans.XPathException;
9 import net.sf.saxon.type.ItemType;
10 import net.sf.saxon.type.Type;
11 import net.sf.saxon.value.BooleanValue;
12 import net.sf.saxon.value.IntegerValue;
13
14 import java.util.Arrays JavaDoc;
15 import java.util.Collections JavaDoc;
16 import java.util.Iterator JavaDoc;
17
18 /**
19 * A LocationPathPattern represents a path, for example of the form A/B/C... The components are represented
20 * as a linked list, each component pointing to its predecessor
21 */

22
23 public final class LocationPathPattern extends Pattern {
24
25     // the following public variables are exposed to the ExpressionParser
26

27     public Pattern parentPattern = null;
28     public Pattern ancestorPattern = null;
29     public NodeTest nodeTest = AnyNodeTest.getInstance();
30     protected Expression[] filters = null;
31     protected int numberOfFilters = 0;
32     protected Expression equivalentExpr = null;
33     protected boolean firstElementPattern = false;
34     protected boolean lastElementPattern = false;
35     protected boolean specialFilter = false;
36     private Expression variableBinding = null;
37     private NodeTest refinedNodeTest = null;
38
39     /**
40     * Add a filter to the pattern (while under construction)
41     * @param filter The predicate (a boolean expression or numeric expression) to be added
42     */

43
44     public void addFilter(Expression filter) {
45         if (filters==null) {
46             filters = new Expression[3];
47         } else if (numberOfFilters == filters.length) {
48             Expression[] f2 = new Expression[numberOfFilters * 2];
49             System.arraycopy(filters, 0, f2, 0, numberOfFilters);
50             filters = f2;
51         }
52         filters[numberOfFilters++] = filter;
53         ExpressionTool.makeParentReferences(filter);
54         if (filter instanceof ComputedExpression) {
55             ((ComputedExpression)filter).setParentExpression(this);
56         }
57     }
58
59     /**
60     * Simplify the pattern: perform any context-independent optimisations
61     */

62
63     public Pattern simplify(StaticContext env) throws XPathException {
64
65         // detect the simple cases: no parent or ancestor pattern, no predicates
66

67         if ( parentPattern == null &&
68                 ancestorPattern == null &&
69                 filters == null &&
70                 !firstElementPattern &&
71                 !lastElementPattern) {
72             NodeTestPattern ntp = new NodeTestPattern(nodeTest);
73             ntp.setSystemId(getSystemId());
74             ntp.setLineNumber(getLineNumber());
75             return ntp;
76         }
77
78         // simplify each component of the pattern
79

80         if (parentPattern != null) {
81             parentPattern = parentPattern.simplify(env);
82         } else if (ancestorPattern != null) {
83             ancestorPattern = ancestorPattern.simplify(env);
84         }
85
86         if (filters != null) {
87             for (int i=numberOfFilters-1; i>=0; i--) {
88                 Expression filter = filters[i];
89                 filter = filter.simplify(env);
90                 filters[i] = filter;
91                 int dep = filter.getDependencies();
92                 if ((dep & StaticProperty.DEPENDS_ON_CURRENT_GROUP) != 0) {
93                     String JavaDoc errorCode = "XTSE1060";
94                     String JavaDoc function = "current-group()";
95                     if (toString().indexOf("current-grouping-key") >= 0) {
96                         errorCode = "XTSE1070";
97                         function = "current-grouping-key()";
98                     }
99                     StaticError err = new StaticError("The " + function + " function cannot be used in a pattern");
100                     err.setErrorCode(errorCode);
101                     throw err;
102                 }
103             }
104         }
105
106         return this;
107     }
108
109     /**
110     * Type-check the pattern, performing any type-dependent optimizations.
111     * @return the optimised Pattern
112     */

113
114     public Pattern analyze(StaticContext env, ItemType contextItemType) throws XPathException {
115
116         // analyze each component of the pattern
117

118         if (parentPattern != null) {
119             parentPattern = parentPattern.analyze(env, contextItemType);
120             // Check that this step in the pattern makes sense in the context of the parent step
121
AxisExpression step;
122             if (nodeTest.getPrimitiveType() == Type.ATTRIBUTE) {
123                 step = new AxisExpression(Axis.ATTRIBUTE, nodeTest);
124             } else {
125                 step = new AxisExpression(Axis.CHILD, nodeTest);
126             }
127             step.setLocationId(env.getLocationMap().allocateLocationId(env.getSystemId(), getLineNumber()));
128             step.setParentExpression(this);
129             Expression exp = step.typeCheck(env, parentPattern.getNodeTest());
130             refinedNodeTest = (NodeTest)exp.getItemType();
131         } else if (ancestorPattern != null) {
132             ancestorPattern = ancestorPattern.analyze(env, contextItemType);
133         }
134
135         if (filters != null) {
136             for (int i=numberOfFilters-1; i>=0; i--) {
137                 Expression filter = filters[i].typeCheck(env, getNodeTest());
138                 // System.err.println("Filter after analyze:");filter.display(10);
139
filters[i] = filter;
140                 // if the last filter is constant true, remove it
141
if ((filter instanceof BooleanValue) && ((BooleanValue)filter).getBooleanValue()) {
142                     if (i==numberOfFilters-1) {
143                         numberOfFilters--;
144                     } // otherwise don't bother doing anything with it.
145
}
146
147                 // patterns are used outside XSLT, for internally-generated key definitions;
148
// but in this case they never contain variable references or calls on current(). So we only need to
149
// allocate slots in the XSLT case
150
if (env instanceof ExpressionContext) {
151                     ((ExpressionContext)env).getStyleElement().allocateSlots(filter);
152                     filters[i] = filter;
153                 }
154             }
155         }
156
157         // see if it's an element pattern with a single positional predicate of [1]
158

159         if (nodeTest.getPrimitiveType() == Type.ELEMENT && numberOfFilters==1) {
160             if (((filters[0] instanceof IntegerValue) &&
161                          ((IntegerValue)filters[0]).longValue()==1) ||
162                 ((filters[0] instanceof PositionRange) &&
163                          ((PositionRange)filters[0]).isFirstPositionOnly()) ) {
164                 firstElementPattern = true;
165                 specialFilter = true;
166                 numberOfFilters = 0;
167                 filters = null;
168             }
169         }
170
171         // see if it's an element pattern with a single positional predicate
172
// of [position()=last()]
173

174         if (nodeTest.getPrimitiveType() == Type.ELEMENT &&
175                 numberOfFilters==1 &&
176                 filters[0] instanceof IsLastExpression &&
177                 ((IsLastExpression)filters[0]).getCondition()) {
178             lastElementPattern = true;
179             specialFilter = true;
180             numberOfFilters = 0;
181             filters = null;
182         }
183
184         if (isPositional()) {
185             equivalentExpr = makeEquivalentExpression(env);
186             equivalentExpr = equivalentExpr.typeCheck(env, contextItemType);
187             // the rewritten expression may use additional variables; must ensure there are enough slots for these
188
// (see test match55)
189
((ExpressionContext)env).getStyleElement().allocateSlots(equivalentExpr);
190             specialFilter = true;
191         }
192
193         return this;
194
195         // TODO:PERF: identify subexpressions within a pattern predicate that could be promoted
196
// In the case of match patterns in template rules, these would have to become global variables.
197
}
198
199     /**
200      * Get the dependencies of the pattern. The only possible dependency for a pattern is
201      * on local variables. This is analyzed in those patterns where local variables may appear.
202      */

203
204     public int getDependencies() {
205         int dependencies = 0;
206         if (parentPattern != null) {
207             dependencies |= parentPattern.getDependencies();
208         }
209         if (ancestorPattern != null) {
210             dependencies |= ancestorPattern.getDependencies();
211         }
212         for (int i=0; i<numberOfFilters; i++) {
213             dependencies |= filters[i].getDependencies();
214         }
215         // the only dependency that's interesting is a dependency on local variables
216
dependencies &= StaticProperty.DEPENDS_ON_LOCAL_VARIABLES;
217         return dependencies;
218     }
219
220     /**
221      * Iterate over the subexpressions within this pattern
222      */

223
224     public Iterator JavaDoc iterateSubExpressions() {
225         Iterator JavaDoc iter;
226         if (numberOfFilters == 0) {
227             iter = Collections.EMPTY_LIST.iterator();
228         } else {
229             iter = Arrays.asList(filters).subList(0, numberOfFilters).iterator();
230         }
231         if (variableBinding != null) {
232             Iterator JavaDoc[] pair = {iter, new MonoIterator(variableBinding)};
233             iter = new MultiIterator(pair);
234         }
235         if (parentPattern != null) {
236             Iterator JavaDoc[] pair = {iter, parentPattern.iterateSubExpressions()};
237             iter = new MultiIterator(pair);
238         }
239         if (ancestorPattern != null) {
240             Iterator JavaDoc[] pair = {iter, ancestorPattern.iterateSubExpressions()};
241             iter = new MultiIterator(pair);
242         }
243         return iter;
244     }
245
246     /**
247      * Offer promotion for subexpressions within this pattern. The offer will be accepted if the subexpression
248      * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
249      * By default the offer is not accepted - this is appropriate in the case of simple expressions
250      * such as constant values and variable references where promotion would give no performance
251      * advantage. This method is always called at compile time.
252      * <p/>
253      * <p>Unlike the corresponding method on {@link net.sf.saxon.expr.Expression}, this method does not return anything:
254      * it can make internal changes to the pattern, but cannot return a different pattern. Only certain
255      * kinds of promotion are applicable within a pattern: specifically, promotions affecting local
256      * variable references within the pattern.
257      *
258      * @param offer details of the offer, for example the offer to move
259      * expressions that don't depend on the context to an outer level in
260      * the containing expression
261      * @throws net.sf.saxon.trans.XPathException
262      * if any error is detected
263      */

264
265     public void promote(PromotionOffer offer) throws XPathException {
266
267         if (parentPattern != null) {
268             parentPattern.promote(offer);
269         }
270         if (ancestorPattern != null) {
271             ancestorPattern.promote(offer);
272         }
273         for (int i=0; i<numberOfFilters; i++) {
274             filters[i] = filters[i].promote(offer);
275         }
276     }
277
278     /**
279     * For a positional pattern, make an equivalent nodeset expression to evaluate the filters
280     */

281
282     private ComputedExpression makeEquivalentExpression(StaticContext env) throws XPathException {
283         byte axis = (nodeTest.getPrimitiveType()==Type.ATTRIBUTE ?
284                         Axis.ATTRIBUTE :
285                         Axis.CHILD );
286         ComputedExpression step = new AxisExpression(axis, nodeTest);
287         for (int n=0; n<numberOfFilters; n++) {
288             step = new FilterExpression(step, filters[n], env);
289         }
290         PathExpression path = new PathExpression(new ParentNodeExpression(), step);
291         path.setParentExpression(this);
292         return path;
293         // Note, the resulting expression is not required to deliver results in document order
294
}
295
296     /**
297     * Determine whether the pattern matches a given node.
298     * @param node the node to be tested
299     * @return true if the pattern matches, else false
300     */

301
302     public boolean matches(NodeInfo node, XPathContext context) throws XPathException {
303         if (variableBinding != null) {
304             variableBinding.evaluateItem(context);
305         }
306         return internalMatches(node, context);
307         // matches() and internalMatches() differ in the way they handled the current() function.
308
// The variable holding the value of current() is initialized on entry to the top-level
309
// LocationPathPattern, but not on entry to its subordinate patterns.
310
}
311
312     /**
313     * Test whether the pattern matches, but without changing the current() node
314     */

315
316     protected boolean internalMatches(NodeInfo node, XPathContext context) throws XPathException {
317         // System.err.println("Matching node type and fingerprint");
318
if (!nodeTest.matches(node)) {
319             return false;
320         }
321         if (parentPattern!=null) {
322             //System.err.println("Testing parent pattern " + parentPattern + "(" + parentPattern.getClass() + ")");
323
NodeInfo par = node.getParent();
324             if (par==null) return false;
325             if (!parentPattern.internalMatches(par, context)) {
326                 return false;
327             }
328         }
329
330         if (ancestorPattern!=null) {
331             NodeInfo anc = node.getParent();
332             while (true) {
333                 if (anc==null) return false;
334                 if (ancestorPattern.internalMatches(anc, context)) break;
335                 anc = anc.getParent();
336             }
337         }
338
339         if (specialFilter) {
340             if (firstElementPattern) {
341                 SequenceIterator iter = node.iterateAxis(Axis.PRECEDING_SIBLING, nodeTest);
342                 return iter.next() == null;
343             }
344
345             if (lastElementPattern) {
346                 SequenceIterator iter = node.iterateAxis(Axis.FOLLOWING_SIBLING, nodeTest);
347                 return iter.next() == null;
348             }
349
350             if (equivalentExpr!=null) {
351
352                 // for a positional pattern, we do it the hard way: test whether the
353
// node is a member of the nodeset obtained by evaluating the
354
// equivalent expression
355

356                 // System.err.println("Testing positional pattern against node " + node.generateId());
357
XPathContext c2 = context.newMinorContext();
358                 c2.setOriginatingConstructType(Location.PATTERN);
359                 AxisIterator single = SingletonIterator.makeIterator(node);
360                 single.next();
361                 c2.setCurrentIterator(single);
362                 try {
363                     SequenceIterator nsv = equivalentExpr.iterate(c2);
364                     while (true) {
365                         NodeInfo n = (NodeInfo)nsv.next();
366                         if (n == null) {
367                             return false;
368                         }
369                         if (n.isSameNodeInfo(node)) {
370                             return true;
371                         }
372                     }
373                 } catch (DynamicError e) {
374                     DynamicError err = new DynamicError("An error occurred matching pattern {" + toString() + "}: ", e);
375                     err.setXPathContext(c2);
376                     err.setErrorCode(e.getErrorCodeLocalPart());
377                     err.setLocator(this);
378                     c2.getController().recoverableError(err);
379                     return false;
380                 }
381             }
382         }
383
384         if (filters!=null) {
385             XPathContext c2 = context.newMinorContext();
386             c2.setOriginatingConstructType(Location.PATTERN);
387             AxisIterator iter = SingletonIterator.makeIterator(node);
388             iter.next();
389             c2.setCurrentIterator(iter);
390                 // it's a non-positional filter, so we can handle each node separately
391

392             for (int i=0; i<numberOfFilters; i++) {
393                 try {
394                     if (!filters[i].effectiveBooleanValue(c2)) {
395                         return false;
396                     }
397                 } catch (DynamicError e) {
398                     DynamicError err = new DynamicError("An error occurred matching pattern {" + toString() + "}: ", e);
399                     err.setXPathContext(c2);
400                     err.setErrorCode(e.getErrorCodeLocalPart());
401                     err.setLocator(this);
402                     c2.getController().recoverableError(err);
403                     return false;
404                 }
405             }
406         }
407
408         return true;
409     }
410
411     /**
412     * Determine the types of nodes to which this pattern applies. Used for optimisation.
413     * For patterns that match nodes of several types, return Node.NODE
414     * @return the type of node matched by this pattern. e.g. Node.ELEMENT or Node.TEXT
415     */

416
417     public int getNodeKind() {
418         return nodeTest.getPrimitiveType();
419     }
420
421     /**
422     * Determine the fingerprint of nodes to which this pattern applies.
423     * Used for optimisation.
424     * @return the fingerprint of nodes matched by this pattern.
425     */

426
427     public int getFingerprint() {
428         return nodeTest.getFingerprint();
429     }
430
431     /**
432     * Get a NodeTest that all the nodes matching this pattern must satisfy
433     */

434
435     public NodeTest getNodeTest() {
436         if (refinedNodeTest != null) {
437             return refinedNodeTest;
438         } else {
439             return nodeTest;
440         }
441     }
442
443     /**
444     * Determine if the pattern uses positional filters
445     * @return true if there is a numeric filter in the pattern, or one that uses the position()
446     * or last() functions
447     */

448
449     private boolean isPositional() {
450         if (filters==null) return false;
451         for (int i=0; i<numberOfFilters; i++) {
452             int type = filters[i].getItemType().getPrimitiveType();
453             if (type==Type.DOUBLE || type==Type.DECIMAL ||
454                 type==Type.INTEGER || type==Type.FLOAT || type==Type.ATOMIC) return true;
455             if ((filters[i].getDependencies() &
456                      (StaticProperty.DEPENDS_ON_POSITION | StaticProperty.DEPENDS_ON_LAST)) != 0) return true;
457         }
458         return false;
459     }
460
461     /**
462      * If the pattern contains any calls on current(), this method is called to modify such calls
463      * to become variable references to a variable declared in a specially-allocated local variable
464      * @param let the expression that assigns the local variable. This returns a dummy result, and is executed
465      * just before evaluating the pattern, to get the value of the context item into the variable.
466      * @param offer A PromotionOffer used to process the expressions and change the call on current() into
467      * a variable reference
468      * @throws XPathException
469      */

470
471     public void resolveCurrent(LetExpression let, PromotionOffer offer) throws XPathException {
472         for (int i=0; i<numberOfFilters; i++) {
473             filters[i] = filters[i].promote(offer);
474         }
475         if (parentPattern instanceof LocationPathPattern) {
476             ((LocationPathPattern)parentPattern).resolveCurrent(let, offer);
477         }
478         if (ancestorPattern instanceof LocationPathPattern) {
479             ((LocationPathPattern)ancestorPattern).resolveCurrent(let, offer);
480         }
481         variableBinding = let;
482     }
483 }
484
485 //
486
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
487
// you may not use this file except in compliance with the License. You may obtain a copy of the
488
// License at http://www.mozilla.org/MPL/
489
//
490
// Software distributed under the License is distributed on an "AS IS" basis,
491
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
492
// See the License for the specific language governing rights and limitations under the License.
493
//
494
// The Original Code is: all this file.
495
//
496
// The Initial Developer of the Original Code is Michael H. Kay.
497
//
498
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
499
//
500
// Contributor(s): none.
501
//
502
Popular Tags