KickJava   Java API By Example, From Geeks To Geeks.

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


1 package com.icl.saxon.expr;
2 import com.icl.saxon.om.*;
3 import com.icl.saxon.*;
4 import com.icl.saxon.pattern.*;
5 import com.icl.saxon.functions.*;
6 import javax.xml.transform.TransformerException JavaDoc;
7
8
9 import java.util.*;
10
11 /**
12 * Parser for XSL expressions and patterns.
13 *
14 * This code was originally inspired by James Clark's xt but has been totally rewritten (twice!)
15 * @author Michael Kay
16 *
17 */

18
19
20 public final class ExpressionParser {
21
22     private Tokenizer t;
23     private StaticContext env;
24     
25     private final static int CHILD_AXIS = 0;
26     private final static int ATTRIBUTE_AXIS = 1;
27
28     /**
29     * Expect a given token, fail if the current token is different
30     */

31
32     private void expect(int token) throws XPathException {
33         if (t.currentToken != token)
34             grumble("expected \"" + Tokenizer.tokens[token] + "\"" +
35                              ", found \"" + Tokenizer.tokens[t.currentToken] + "\"");
36     }
37
38     /**
39     * Report a parsing error
40     */

41
42     private void grumble(String JavaDoc message) throws XPathException {
43         throw new XPathException("Error in expression " + t.pattern + ": " + message);
44     }
45
46     /**
47     * Parse a string representing an expression
48     * @expression the expression expressed as a String
49     * @return an Expression object representing the result of parsing
50     */

51
52     public Expression parse(String JavaDoc expression, StaticContext env) throws XPathException {
53         //System.err.println("Parse expression: " + expression);
54
this.env = env;
55         t = new Tokenizer();
56         t.tokenize(expression);
57         Expression exp = parseExpression();
58         if (t.currentToken != Tokenizer.EOF)
59             grumble("Unexpected token " + Tokenizer.tokens[t.currentToken] + " beyond end of expression");
60         exp.setStaticContext(env);
61         return exp;
62     }
63
64     /**
65     * Parse a string representing a pattern
66     * @pattern the pattern expressed as a String
67     * @return a Pattern object representing the result of parsing
68     */

69
70     public Pattern parsePattern(String JavaDoc pattern, StaticContext env) throws XPathException {
71         //System.err.println("Parse pattern: " + pattern);
72
this.env = env;
73         t = new Tokenizer();
74         t.tokenize(pattern);
75         Pattern pat = parseUnionPattern();
76         if (t.currentToken != Tokenizer.EOF)
77             grumble("Unexpected token " + Tokenizer.tokens[t.currentToken] + " beyond end of pattern");
78         pat.setStaticContext(env);
79         return pat;
80     }
81
82
83     //////////////////////////////////////////////////////////////////////////////////
84
// EXPRESSIONS //
85
//////////////////////////////////////////////////////////////////////////////////
86

87
88     /**
89     * Parse an Expression ( ::= OrExpression ):
90     * AndExpr ( 'or' AndExpr )*
91     */

92
93     private Expression parseExpression() throws XPathException {
94         Expression exp = parseAndExpression();
95         while (t.currentToken == Tokenizer.OR) {
96             t.next();
97             exp = new BooleanExpression(exp, Tokenizer.OR, parseAndExpression());
98             exp.setStaticContext(env);
99         }
100         return exp;
101     }
102
103     /**
104     * Parse an AndExpr:
105     * EqualityExpr ( 'and' EqualityExpr )*
106     */

107
108     private Expression parseAndExpression() throws XPathException {
109         Expression exp = parseEqualityExpression();
110         while (t.currentToken == Tokenizer.AND) {
111             t.next();
112             exp = new BooleanExpression(exp, Tokenizer.AND, parseEqualityExpression());
113             exp.setStaticContext(env);
114         }
115         return exp;
116     }
117
118     /**
119     * Parse an EqualityExpr:<br>
120     * RelationalExpr ( '=' | '!=' RelationalExpr )*
121     */

122
123     private Expression parseEqualityExpression() throws XPathException {
124         Expression exp = parseRelationalExpression();
125         while (t.currentToken == Tokenizer.EQUALS ||
126                 t.currentToken == Tokenizer.NE) {
127             int op = t.currentToken;
128             t.next();
129             exp = new RelationalExpression(exp, op, parseRelationalExpression());
130             exp.setStaticContext(env);
131         }
132         return exp;
133     }
134
135     /**
136     * Parse a RelationalExpr:
137     * AdditiveExpr ( ('&lt;'|'&gt;'|'&gt;='|'&lt;=') AdditiveExpr )*
138     */

139
140     private Expression parseRelationalExpression() throws XPathException {
141         Expression exp = parseAdditiveExpression();
142         while (t.currentToken == Tokenizer.LT ||
143                 t.currentToken == Tokenizer.GT ||
144                 t.currentToken == Tokenizer.LE ||
145                 t.currentToken == Tokenizer.GE ) {
146             int op = t.currentToken;
147             t.next();
148             exp = new RelationalExpression(exp, op, parseAdditiveExpression());
149             exp.setStaticContext(env);
150         }
151         return exp;
152     }
153
154     /**
155     * Parse an AdditiveExpr:
156     * MultiplicativeExpr ( (+|-) MultiplicativeExpr )*
157     */

158
159     private Expression parseAdditiveExpression() throws XPathException {
160         Expression exp = parseMultiplicativeExpression();
161         while (t.currentToken == Tokenizer.PLUS ||
162                 t.currentToken == Tokenizer.MINUS ) {
163             int op = t.currentToken;
164             t.next();
165             exp = new ArithmeticExpression(exp, op, parseMultiplicativeExpression());
166             exp.setStaticContext(env);
167         }
168         return exp;
169     }
170
171     /**
172     * Parse a MultiplicativeExpr:<br>
173     * UnaryExpr ( (*|div|mod) UnaryExpr )*
174     */

175
176     private Expression parseMultiplicativeExpression() throws XPathException {
177         Expression exp = parseUnaryExpression();
178         while (t.currentToken == Tokenizer.MULT ||
179                 t.currentToken == Tokenizer.DIV ||
180                 t.currentToken == Tokenizer.MOD ) {
181             int op = t.currentToken;
182             t.next();
183             exp = new ArithmeticExpression(exp, op, parseUnaryExpression());
184             exp.setStaticContext(env);
185         }
186         return exp;
187     }
188
189     /**
190     * Parse a UnaryExpr:<br>
191     * UnionExpr | '-' UnaryExpr
192     */

193
194     private Expression parseUnaryExpression() throws XPathException {
195         Expression exp;
196         if (t.currentToken == Tokenizer.MINUS) {
197             t.next();
198             exp = new ArithmeticExpression(new NumericValue(0),
199                                           Tokenizer.NEGATE,
200                                           parseUnaryExpression());
201             exp.setStaticContext(env);
202         }
203         else {
204             exp = parseUnionExpression();
205         }
206         return exp;
207     }
208
209
210     /**
211     * Parse a UnionExpr:<br>
212     * path ( | path )*
213     */

214
215     private Expression parseUnionExpression() throws XPathException {
216         Expression exp = parsePathExpression();
217         while (t.currentToken == Tokenizer.VBAR ) {
218             t.next();
219             exp = new UnionExpression(exp, parsePathExpression());
220             exp.setStaticContext(env);
221         }
222         return exp;
223     }
224
225     /**
226     * Parse a PathExpresssion. This includes "true" path expressions such as A/B/C, and also
227     * elements that may start a path expression such as a variable reference $name or a
228     * parenthesed expression (A|B). For some reason numeric and string literals also come
229     * under this heading.
230     */

231
232     private Expression parsePathExpression() throws XPathException {
233         Expression start;
234         switch (t.currentToken) {
235         case Tokenizer.SLASH:
236             t.next();
237             switch(t.currentToken) {
238                 // some of these, eg. "/@x" or "/." are technically permitted but meaningless
239
case Tokenizer.NAME:
240                 case Tokenizer.PREFIX:
241                 case Tokenizer.STAR:
242                 case Tokenizer.AT:
243                 case Tokenizer.NODETYPE:
244                 case Tokenizer.AXIS:
245                 case Tokenizer.DOT:
246                 case Tokenizer.DOTDOT:
247                     return parseRelativePath(new RootExpression());
248                 default:
249                     return new RootExpression();
250             }
251             
252         case Tokenizer.SLSL:
253             // add in the implicit from-descendants-or-self() step
254
return parsePathContinuation(new RootExpression());
255
256         case Tokenizer.DOT:
257             t.next();
258             return parsePathContinuation(new ContextNodeExpression());
259             
260         case Tokenizer.DOTDOT:
261             t.next();
262             return parsePathContinuation(new ParentNodeExpression());
263             
264         case Tokenizer.NAME:
265         case Tokenizer.PREFIX:
266         case Tokenizer.STAR:
267         case Tokenizer.NODETYPE:
268         case Tokenizer.AXIS:
269         case Tokenizer.AT:
270             return parseRelativePath(new ContextNodeExpression());
271
272         default:
273             start = parseFilterExpression();
274             Expression continuation = parsePathContinuation(start);
275             continuation.setStaticContext(env);
276             return continuation;
277         }
278     }
279
280     /**
281     * Parse filter expression
282     */

283
284     private Expression parseFilterExpression() throws XPathException {
285         Expression primary = parsePrimaryExpression();
286         while (t.currentToken == Tokenizer.LSQB) {
287             t.next();
288             Expression predicate = parseExpression();
289             expect(Tokenizer.RSQB);
290             primary = new FilterExpression(primary, predicate);
291             primary.setStaticContext(env);
292             t.next();
293         }
294         return primary;
295     }
296
297     /**
298     * Parse a primary expression. One of:
299     * variable-reference: $ name
300     * parentheses: ( expr )
301     * string literal: 'fred'
302     * numeric literal: 93.7
303     * function call: name(args,...)
304     */

305
306     private Expression parsePrimaryExpression() throws XPathException {
307         switch (t.currentToken) {
308         case Tokenizer.DOLLAR:
309             t.next();
310             expect(Tokenizer.NAME);
311             String JavaDoc var = t.currentTokenValue;
312             t.next();
313
314             int vtest = env.makeNameCode(var, false) & 0xfffff;
315             return new VariableReference(vtest, env);
316
317         case Tokenizer.LPAR:
318             t.next();
319             Expression exp = parseExpression();
320             expect(Tokenizer.RPAR);
321             t.next();
322             return exp;
323
324         case Tokenizer.LITERAL:
325             Expression literal = new StringValue(t.currentTokenValue);
326             t.next();
327             return literal;
328             
329         case Tokenizer.NUMBER:
330             Expression number = new NumericValue(t.currentTokenValue);
331             t.next();
332             return number;
333             
334         case Tokenizer.FUNCTION:
335             return parseFunctionCall();
336
337         default:
338             grumble("Unexpected token " + Tokenizer.tokens[t.currentToken] + " in expression");
339             return null;
340         }
341     }
342
343
344     /**
345     * Parse path continuation. Called when the current token is a separator (/ or //);
346     * if it is not a separator, this is effectively a null operation.
347     */

348
349     private Expression parsePathContinuation(Expression start) throws XPathException {
350         switch (t.currentToken) {
351         case Tokenizer.SLASH:
352             t.next();
353             return parseRelativePath(start);
354
355         case Tokenizer.SLSL:
356             // add in the implicit from-descendants-or-self() step
357
Expression exp = new PathExpression(
358                                     start,
359                                     new Step(Axis.DESCENDANT_OR_SELF, AnyNodeTest.getInstance()));
360             exp.setStaticContext(env);
361             t.next();
362             return parseRelativePath(exp);
363
364         default:
365             return start;
366         }
367     }
368
369     /**
370     * Parse a relative path (a sequence of steps). Called when the current token immediately
371     * follows a separator (/ or //), or an implicit separator (XYZ is equivalent to ./XYZ)
372     */

373
374     private Expression parseRelativePath(Expression start) throws XPathException {
375         Step step = parseStep();
376         Expression exp = new PathExpression(start, step);
377         exp.setStaticContext(env);
378         return parsePathContinuation(exp);
379     }
380
381     /**
382     * Parse a step (including an optional sequence of qualifiers)
383     */

384
385     private Step parseStep() throws XPathException {
386         Step step=null;
387         
388         switch(t.currentToken) {
389
390         case Tokenizer.DOT:
391             step = new Step(Axis.SELF, AnyNodeTest.getInstance());
392             t.next();
393             break;
394
395         case Tokenizer.DOTDOT:
396             step = new Step(Axis.PARENT, AnyNodeTest.getInstance());
397             t.next();
398             break;
399             
400         case Tokenizer.NAME:
401             step = new Step(Axis.CHILD,
402                             env.makeNameTest(NodeInfo.ELEMENT,
403                                              t.currentTokenValue, false));
404             t.next();
405             while (t.currentToken == Tokenizer.LSQB) {
406                 step = parseStepPredicate(step);
407             }
408             break;
409
410         case Tokenizer.PREFIX:
411             NamespaceTest nstest1 = env.makeNamespaceTest(
412                                             NodeInfo.ELEMENT,
413                                             t.currentTokenValue);
414             step = new Step(Axis.CHILD, nstest1);
415             t.next();
416             while (t.currentToken == Tokenizer.LSQB) {
417                 step = parseStepPredicate(step);
418             }
419             break;
420
421         case Tokenizer.STAR:
422             step = new Step(Axis.CHILD, new NodeTypeTest(NodeInfo.ELEMENT));
423             t.next();
424             while (t.currentToken == Tokenizer.LSQB) {
425                 step = parseStepPredicate(step);
426             }
427             break;
428             
429         case Tokenizer.AT:
430             t.next();
431             switch(t.currentToken) {
432
433             case Tokenizer.NAME:
434                 step = new Step(Axis.ATTRIBUTE,
435                                 env.makeNameTest(NodeInfo.ATTRIBUTE,
436                                                  t.currentTokenValue, false));
437                 t.next();
438                 break;
439
440             case Tokenizer.PREFIX:
441                 NamespaceTest nstest2 = env.makeNamespaceTest(
442                                                 NodeInfo.ATTRIBUTE,
443                                                 t.currentTokenValue);
444                 step = new Step(Axis.ATTRIBUTE, nstest2);
445                                 
446                 t.next();
447                 break;
448
449             case Tokenizer.STAR:
450                 step = new Step(Axis.ATTRIBUTE,
451                                 AnyNodeTest.getInstance());
452                 t.next();
453                 break;
454
455             case Tokenizer.NODETYPE:
456                 String JavaDoc nodetype = t.currentTokenValue; // already interned
457
t.next();
458
459                 // most of these nodes can't occur in the attribute axis but the syntax allows them
460
if (nodetype=="text") {
461                     step = new Step(Axis.ATTRIBUTE, NoNodeTest.getInstance());
462                 } else if (nodetype=="node") {
463                     step = new Step(Axis.ATTRIBUTE, AnyNodeTest.getInstance());
464                 } else if (nodetype=="comment") {
465                     step = new Step(Axis.ATTRIBUTE, NoNodeTest.getInstance());
466                 } else if (nodetype=="processing-instruction") {
467                     // optional PI name allowed (as a literal)
468
if (t.currentToken==Tokenizer.LITERAL) {
469                         t.next(); // ignore it
470
}
471                     step = new Step(Axis.ATTRIBUTE, NoNodeTest.getInstance());
472                 }
473                 expect(Tokenizer.RPAR);
474                 t.next();
475                 break;
476
477             default:
478                 grumble("@ must be followed by a NameTest or NodeTest");
479             }
480             while (t.currentToken == Tokenizer.LSQB) {
481                 step = parseStepPredicate(step);
482             }
483             break;
484             
485         case Tokenizer.NODETYPE:
486             String JavaDoc nodetype = t.currentTokenValue; // already interned
487
t.next();
488
489             if (nodetype=="text") {
490                 step = new Step(Axis.CHILD, new NodeTypeTest(NodeInfo.TEXT));
491             } else if (nodetype=="node") {
492                 step = new Step(Axis.CHILD, AnyNodeTest.getInstance());
493             } else if (nodetype=="comment") {
494                 step = new Step(Axis.CHILD, new NodeTypeTest(NodeInfo.COMMENT));
495             } else if (nodetype=="processing-instruction") {
496                 // optional PI name allowed (as a literal)
497
if (t.currentToken==Tokenizer.LITERAL) {
498                     if (Name.isNCName(t.currentTokenValue)) {
499                         step = new Step(Axis.CHILD,
500                                         env.makeNameTest(NodeInfo.PI,
501                                                     t.currentTokenValue, false));
502                     } else {
503                         // a non-name literal can't match anything
504
step = new Step(Axis.CHILD, NoNodeTest.getInstance());
505                     }
506                     t.next();
507                 } else {
508                     step = new Step(Axis.CHILD, new NodeTypeTest(NodeInfo.PI));
509                 }
510             }
511             expect(Tokenizer.RPAR);
512             t.next();
513
514             while (t.currentToken == Tokenizer.LSQB) {
515                 step = parseStepPredicate(step);
516             }
517             break;
518
519         case Tokenizer.AXIS:
520             byte axis = Axis.getAxisNumber(t.currentTokenValue);
521             short principalNodeType = Axis.principalNodeType[axis];
522             t.next();
523             switch (t.currentToken) {
524
525             case Tokenizer.NAME:
526                 step = new Step(axis,
527                                 env.makeNameTest(principalNodeType,
528                                                  t.currentTokenValue, false));
529                 t.next();
530                 break;
531
532             case Tokenizer.PREFIX:
533                 NamespaceTest nstest3 = env.makeNamespaceTest(
534                                                 principalNodeType,
535                                                 t.currentTokenValue);
536                 step = new Step(axis, nstest3);
537                 t.next();
538                 break;
539
540             case Tokenizer.STAR:
541                 step = new Step(axis, new NodeTypeTest(principalNodeType));
542                 t.next();
543                 break;
544                 
545             case Tokenizer.NODETYPE:
546                 String JavaDoc nt = t.currentTokenValue; // already interned
547
t.next();
548                 
549                 if (nt=="node") {
550                     step = new Step(axis, AnyNodeTest.getInstance());
551                 } else if (nt=="text") {
552                     step = new Step(axis, new NodeTypeTest(NodeInfo.TEXT));
553                 } else if (nt=="comment") {
554                     step = new Step(axis, new NodeTypeTest(NodeInfo.COMMENT));
555                 } else if (nt=="processing-instruction") {
556                     // optional PI name allowed, as a literal
557
if (t.currentToken==Tokenizer.LITERAL) {
558                         if (Name.isNCName(t.currentTokenValue)) {
559                             step = new Step(axis,
560                                             env.makeNameTest(NodeInfo.PI,
561                                                              t.currentTokenValue, false));
562                         } else {
563                             // not a name: allowed, but can't match anything
564
step = new Step(axis, NoNodeTest.getInstance());
565                         }
566                         t.next();
567                     } else {
568                         step = new Step(axis, new NodeTypeTest(NodeInfo.PI));
569                     }
570                 } else {
571                     grumble("Unsupported node type");
572                 }
573
574                 expect(Tokenizer.RPAR);
575                 t.next();
576                 break;
577             default:
578                 grumble("Unexpected token [" + Tokenizer.tokens[t.currentToken] + "] after axis name");
579             }
580             while (t.currentToken == Tokenizer.LSQB) {
581                 step = parseStepPredicate(step);
582             }
583             break;
584             
585         default:
586             grumble("Unexpected token [" + Tokenizer.tokens[t.currentToken] + "] in path expression");
587             //break;
588
}
589         return step;
590     }
591
592     /**
593     * Parse a predicate appearing as part of a Step
594     */

595
596     private Step parseStepPredicate(Step start) throws XPathException {
597         t.next();
598         Expression predicate = parseExpression();
599         expect(Tokenizer.RSQB);
600         t.next();
601         return start.addFilter(predicate);
602     }
603
604     /**
605     * Parse a function call
606     * function-name '(' ( Expression (',' Expression )* )? ')'
607     */

608
609     private Expression parseFunctionCall() throws XPathException {
610         
611         String JavaDoc fname = t.currentTokenValue;
612         Function f=null;
613
614         int colon = fname.indexOf(":");
615         if (colon<0) {
616             Expression e = makeSystemFunction(fname);
617             if (e==null) grumble("Unknown system function: " + fname);
618             e.setStaticContext(env);
619                         
620             if (e instanceof Function) {
621                 f = (Function)e;
622             } else { // shortcut for true() and false()
623
t.next();
624                 expect(Tokenizer.RPAR);
625                 t.next();
626                 return e;
627             }
628         } else { // try a saxon:function
629
f = env.getStyleSheetFunction(env.makeNameCode(fname, false) & 0xfffff);
630         }
631         if (f==null) { // try a Java function
632
f = new FunctionProxy();
633         }
634         f.setStaticContext(env);
635
636         // the "(" has already been read by the Tokenizer
637

638         t.next();
639         if (t.currentToken!=Tokenizer.RPAR) {
640             // parse arguments
641
Expression arg = parseExpression();
642             f.addArgument(arg);
643             while(t.currentToken==Tokenizer.COMMA) {
644                 t.next();
645                 arg = parseExpression();
646                 f.addArgument(arg);
647             }
648             expect(Tokenizer.RPAR);
649         }
650         t.next();
651         if (f instanceof FunctionProxy) {
652             String JavaDoc uri = env.getURIForPrefix(fname.substring(0, colon));
653             String JavaDoc lname = fname.substring(colon+1);
654             Class JavaDoc className = null;
655             try {
656                 className = env.getExternalJavaClass(uri);
657             } catch (TransformerException JavaDoc err) {
658                 XPathException xx = new XPathException(
659                     "Failed to load external Java class for uri " + uri);
660                 return new ErrorExpression(xx);
661                     //not an error unless it's executed
662
}
663             if (className==null) {
664                 XPathException xx = new XPathException(
665                     "The URI " + uri + " does not identify an external Java class");
666                 return new ErrorExpression(xx);
667                     //not an error unless it's executed
668
}
669             ((FunctionProxy)f).setFunctionName(className, lname);
670         }
671         return f;
672     }
673
674     /**
675     * Make a system function (one whose name has no prefix). Note this is static and public
676     * so it can also be used from extension-function-available()
677     */

678
679     public static Expression makeSystemFunction(String JavaDoc name) {
680         if (name=="last") return new Last();
681         if (name=="position") return new Position();
682
683         if (name=="count") return new Count();
684         if (name=="current") return new Current();
685         if (name=="id") return new Id();
686         if (name=="key") return new Key();
687         if (name=="document") return new Document();
688         if (name=="local-name") return new LocalName();
689         if (name=="namespace-uri") return new NamespaceURI();
690         if (name=="name") return new NameFn();
691         if (name=="generate-id") return new GenerateId();
692
693         if (name=="not") return new Not();
694         if (name=="true") return new BooleanValue(true);
695         if (name=="false") return new BooleanValue(false);
696         if (name=="boolean") return new BooleanFn();
697         if (name=="lang") return new Lang();
698
699         if (name=="number") return new NumberFn();
700         if (name=="floor") return new Floor();
701         if (name=="ceiling") return new Ceiling();
702         if (name=="round") return new Round();
703         if (name=="sum") return new Sum();
704
705         if (name=="string") return new StringFn();
706
707         if (name=="starts-with") return new StartsWith();
708         if (name=="string-length") return new StringLength();
709         if (name=="substring") return new Substring();
710         if (name=="contains") return new Contains();
711         if (name=="substring-before") return new SubstringBefore();
712         if (name=="substring-after") return new SubstringAfter();
713         if (name=="normalize-space") return new NormalizeSpace();
714         if (name=="translate") return new Translate();
715         if (name=="concat") return new Concat();
716         if (name=="format-number") return new FormatNumber();
717         
718         if (name=="system-property") return new SystemProperty();
719         if (name=="function-available") return new FunctionAvailable();
720         if (name=="element-available") return new ElementAvailable();
721         if (name=="unparsed-entity-uri") return new UnparsedEntityURI();
722         return null;
723     }
724
725     //////////////////////////////////////////////////////////////////////////////////
726
// PATTERNS //
727
//////////////////////////////////////////////////////////////////////////////////
728

729
730     /**
731     * Parse a Union Pattern:<br>
732     * pathPattern ( | pathPattern )*
733     */

734
735     private Pattern parseUnionPattern() throws XPathException {
736         Pattern exp1 = parsePathPattern();
737
738         while (t.currentToken == Tokenizer.VBAR ) {
739             t.next();
740             Pattern exp2 = parsePathPattern();
741             exp1 = new UnionPattern(exp1, exp2);
742             exp1.setStaticContext(env);
743             exp2.setStaticContext(env);
744         }
745
746         return exp1;
747     }
748
749     /**
750     * Parse a Location Path Pattern:
751     */

752
753     private Pattern parsePathPattern() throws XPathException {
754
755         LocationPathPattern lppat = new LocationPathPattern();
756         lppat.setStaticContext(env);
757         Pattern pat = lppat;
758         Pattern prev = null;
759         int connector = -1;
760         boolean rootonly = false;
761
762         // special handling of stuff before the first component
763

764         switch(t.currentToken) {
765             case Tokenizer.SLASH:
766                 connector = t.currentToken;
767                 t.next();
768                 prev = new NodeTypeTest(NodeInfo.ROOT);
769                 rootonly = true;
770                 break;
771             case Tokenizer.SLSL: // leading double slash can't be ignored
772
// because it changes the default priority
773
connector = t.currentToken;
774                 t.next();
775                 prev = new NodeTypeTest(NodeInfo.ROOT);
776                 rootonly = false;
777                 break;
778             default:
779                 break;
780         }
781         
782         boolean more = true;
783         while(more) {
784
785             switch(t.currentToken) {
786                 case Tokenizer.AXIS:
787                     if (t.currentTokenValue.equals("child")) {
788                         t.next();
789                         pat = patternStep(CHILD_AXIS, lppat, prev, connector);
790                     } else if (t.currentTokenValue.equals("attribute")) {
791                         t.next();
792                         pat = patternStep(ATTRIBUTE_AXIS, lppat, prev, connector);
793                     } else {
794                         grumble("Axis in pattern must be child or attribute");
795                     }
796                     break;
797
798                 case Tokenizer.STAR:
799                 case Tokenizer.NAME:
800                 case Tokenizer.PREFIX:
801                 case Tokenizer.NODETYPE:
802                     pat = patternStep(CHILD_AXIS, lppat, prev, connector);
803                     break;
804
805                 case Tokenizer.AT:
806                     t.next();
807                     pat = patternStep(ATTRIBUTE_AXIS, lppat, prev, connector);
808                     break;
809
810                 case Tokenizer.FUNCTION: // must be id(literal) or key(literal,literal)
811
if (prev!=null) {
812                         grumble("Function may appear only at the start of a pattern");
813                     }
814                     if (t.currentTokenValue.equals("id")) {
815                         t.next();
816                         expect(Tokenizer.LITERAL);
817                         pat = new IDPattern(t.currentTokenValue);
818                         pat.setStaticContext(env);
819                         t.next();
820                         expect(Tokenizer.RPAR);
821                         t.next();
822                     } else if (t.currentTokenValue.equals("key")) {
823                         t.next();
824                         expect(Tokenizer.LITERAL);
825                         String JavaDoc keyname = t.currentTokenValue;
826                         t.next();
827                         expect(Tokenizer.COMMA);
828                         t.next();
829                         expect(Tokenizer.LITERAL);
830                         if (!env.allowsKeyFunction()) {
831                             grumble("key() function cannot be used here");
832                         }
833                         pat = new KeyPattern(env.makeNameCode(keyname, false),
834                                                 t.currentTokenValue);
835                         pat.setStaticContext(env);
836                         t.next();
837                         expect(Tokenizer.RPAR);
838                         t.next();
839                     } else {
840                         grumble("The only functions allowed in a pattern are id() and key()");
841                     }
842                     break;
843
844                 default:
845                     if (rootonly) return prev;
846                     grumble("Unexpected token in pattern, found " + Tokenizer.tokens[t.currentToken]);
847             }
848             
849             connector = t.currentToken;
850             rootonly = false;
851             more = (connector == Tokenizer.SLASH || connector == Tokenizer.SLSL);
852             if (more) {
853                 prev = pat;
854                 lppat = new LocationPathPattern();
855                 lppat.setStaticContext(env);
856                 if (connector==Tokenizer.SLASH) {
857                      lppat.parentPattern = prev;
858                 } else { // connector == SLSL
859
lppat.ancestorPattern = prev;
860                 }
861                 t.next();
862             }
863         }
864         pat.setStaticContext(env);
865         return pat;
866
867     }
868
869     private Pattern patternStep(int axis, LocationPathPattern lppat, Pattern prev, int connector)
870         throws XPathException {
871
872         if (axis==CHILD_AXIS) {
873             if (t.currentToken==Tokenizer.STAR) {
874                 lppat.nodeTest = new NodeTypeTest(NodeInfo.ELEMENT);
875             } else if (t.currentToken==Tokenizer.NAME) {
876                 lppat.nodeTest = env.makeNameTest(NodeInfo.ELEMENT,
877                                          t.currentTokenValue, false);
878             } else if (t.currentToken==Tokenizer.PREFIX) {
879                 lppat.nodeTest = env.makeNamespaceTest(NodeInfo.ELEMENT,
880                                                         t.currentTokenValue);
881             } else if (t.currentToken==Tokenizer.NODETYPE) {
882                 String JavaDoc nodetype = t.currentTokenValue; // already interned
883
t.next();
884                 if (nodetype=="text") {
885                     lppat.nodeTest = new NodeTypeTest(NodeInfo.TEXT);
886                 } else if (nodetype=="node") {
887                     lppat.nodeTest = new AnyChildNodePattern();
888                 } else if (nodetype=="comment") {
889                     lppat.nodeTest = new NodeTypeTest(NodeInfo.COMMENT);
890                 } else if (nodetype=="processing-instruction") {
891                     // optional PI name allowed, as a literal
892
if (t.currentToken == Tokenizer.LITERAL) {
893                         if (Name.isNCName(t.currentTokenValue)) {
894                             lppat.nodeTest = env.makeNameTest(NodeInfo.PI,
895                                                           t.currentTokenValue, false);
896                         } else {
897                             // not a name, so will never match any PI
898
lppat.nodeTest = NoNodeTest.getInstance();
899                         }
900                         t.next();
901                     } else {
902                         lppat.nodeTest = new NodeTypeTest(NodeInfo.PI);
903                     }
904                 }
905                 expect(Tokenizer.RPAR);
906             } else {
907                 grumble("Unexpected token in pattern, found " + Tokenizer.tokens[t.currentToken]);
908             }
909             
910             if (prev!=null) {
911                 if (connector==Tokenizer.SLASH) {
912                     lppat.parentPattern = prev;
913                 } else { // connector == SLSL
914
lppat.ancestorPattern = prev;
915                 }
916             }
917             t.next();
918             parseFilters(lppat);
919             return lppat;
920
921         } else if (axis==ATTRIBUTE_AXIS) {
922
923             if (t.currentToken==Tokenizer.STAR) {
924                 lppat.nodeTest = new NodeTypeTest(NodeInfo.ATTRIBUTE);
925             } else if (t.currentToken==Tokenizer.NAME) {
926                 lppat.nodeTest = env.makeNameTest(NodeInfo.ATTRIBUTE,
927                                                   t.currentTokenValue, false);
928             } else if (t.currentToken==Tokenizer.PREFIX) {
929                 lppat.nodeTest = env.makeNamespaceTest(NodeInfo.ATTRIBUTE,
930                                                         t.currentTokenValue);
931             } else if (t.currentToken==Tokenizer.NODETYPE) {
932                 String JavaDoc nodetype = t.currentTokenValue; // already interned
933
t.next();
934                 // the syntax allows a test for any kind of node, but text, comment and PI nodes
935
// can't appear on the attribute axis, so we create a test that is never satisfied
936
if (nodetype=="text") {
937                     lppat.nodeTest = NoNodeTest.getInstance();
938                 } else if (nodetype=="node") {
939                     lppat.nodeTest = new NodeTypeTest(NodeInfo.ATTRIBUTE);
940                 } else if (nodetype=="comment") {
941                     lppat.nodeTest = NoNodeTest.getInstance();
942                 } else if (nodetype=="processing-instruction") {
943                     // optional PI name allowed, as a literal
944
lppat.nodeTest = NoNodeTest.getInstance();
945                     if (t.currentToken == Tokenizer.LITERAL) {
946                         //lppat.nameTest = env.makeName(t.currentTokenValue, false); // ignore the name
947
t.next();
948                     }
949                 }
950                 expect(Tokenizer.RPAR);
951
952             } else {
953                 grumble("@ in pattern not followed by NameTest or NodeTest");
954             }
955             t.next();
956             parseFilters(lppat);
957             return lppat;
958
959         } else {
960             grumble("Axis in pattern must be child or attribute");
961             return null;
962         }
963     }
964
965     /**
966     * Test to see if there are filters for a Pattern, if so, parse them
967     */

968
969     private void parseFilters(LocationPathPattern path) throws XPathException {
970         while (t.currentToken == Tokenizer.LSQB) {
971             t.next();
972             Expression qual = parseExpression();
973             expect(Tokenizer.RSQB);
974             t.next();
975             path.addFilter(qual);
976             if (qual.usesCurrent()) {
977                 grumble("The current() function may not be used in a pattern");
978             }
979         }
980     }
981
982 }
983
984 //
985
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
986
// you may not use this file except in compliance with the License. You may obtain a copy of the
987
// License at http://www.mozilla.org/MPL/
988
//
989
// Software distributed under the License is distributed on an "AS IS" basis,
990
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
991
// See the License for the specific language governing rights and limitations under the License.
992
//
993
// The Original Code is: all this file.
994
//
995
// The Initial Developer of the Original Code is
996
// Michael Kay of International Computers Limited (mhkay@iclway.co.uk).
997
//
998
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
999
//
1000
// Contributor(s): none.
1001
//
1002
Popular Tags