KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > xpath > XPathParser


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  * Free SoftwareFoundation, Inc.
23  * 59 Temple Place, Suite 330
24  * Boston, MA 02111-1307 USA
25  *
26  * @author Scott Ferguson
27  */

28
29 package com.caucho.xpath;
30
31 import com.caucho.log.Log;
32 import com.caucho.server.util.CauchoSystem;
33 import com.caucho.util.CharBuffer;
34 import com.caucho.util.IntMap;
35 import com.caucho.util.L10N;
36 import com.caucho.xml.XmlChar;
37 import com.caucho.xpath.expr.*;
38 import com.caucho.xpath.functions.BaseURI;
39 import com.caucho.xpath.functions.ResolveURI;
40 import com.caucho.xpath.functions.Trace;
41 import com.caucho.xpath.pattern.*;
42
43 import org.w3c.dom.Node JavaDoc;
44
45 import java.lang.reflect.Constructor JavaDoc;
46 import java.lang.reflect.Method JavaDoc;
47 import java.lang.reflect.Modifier JavaDoc;
48 import java.util.ArrayList JavaDoc;
49 import java.util.HashMap JavaDoc;
50 import java.util.logging.Logger JavaDoc;
51
52
53 /**
54  * Parses an XPath expression.
55  */

56 class XPathParser {
57   private static final Logger JavaDoc log = Log.open(XPathParser.class);
58   private static final L10N L = new L10N(XPathParser.class);
59
60   private final static int ANCESTOR_AXIS = 0;
61   private final static int ANCESTOR_OR_SELF_AXIS = ANCESTOR_AXIS + 1;
62   private final static int ATTRIBUTE_AXIS = ANCESTOR_OR_SELF_AXIS + 1;
63   private final static int CHILD_AXIS = ATTRIBUTE_AXIS + 1;
64   private final static int DESCENDANT_AXIS = CHILD_AXIS + 1;
65   private final static int DESCENDANT_OR_SELF_AXIS = DESCENDANT_AXIS + 1;
66   private final static int FOLLOWING_AXIS = DESCENDANT_OR_SELF_AXIS + 1;
67   private final static int FOLLOWING_SIBLING_AXIS = FOLLOWING_AXIS + 1;
68   private final static int NAMESPACE_AXIS = FOLLOWING_SIBLING_AXIS + 1;
69   private final static int PARENT_AXIS = NAMESPACE_AXIS + 1;
70   private final static int PRECEDING_AXIS = PARENT_AXIS + 1;
71   private final static int PRECEDING_SIBLING_AXIS = PRECEDING_AXIS + 1;
72   private final static int SELF_AXIS = PRECEDING_SIBLING_AXIS + 1;
73
74   private final static int TEXT = Expr.LAST_FUN + 1;
75   private final static int COMMENT = TEXT + 1;
76   private final static int ER = COMMENT + 1;
77   private final static int PI = ER + 1;
78   private final static int NODE = PI + 1;
79   private final static int CURRENT = NODE + 1;
80   private final static int NODE_TEXT = CURRENT + 1;
81   private final static int CONTEXT = NODE_TEXT + 1;
82
83   private static IntMap exprFunctions;
84   private static IntMap axisMap;
85
86   private static HashMap JavaDoc<String JavaDoc,Constructor JavaDoc> _exprFunctions;
87   
88   private CharBuffer tag = new CharBuffer();
89
90   private String JavaDoc _string;
91   private int index;
92   private int peek;
93
94   private NamespaceContext _namespace;
95
96   XPathParser(String JavaDoc string, NamespaceContext namespace)
97   {
98     _string = string;
99     _namespace = namespace;
100   }
101
102   /**
103    * Parse a select pattern, i.e. a path rooted in a context.
104    */

105   AbstractPattern parseSelect()
106     throws XPathParseException
107   {
108     AbstractPattern top = new FromContext();
109
110     AbstractPattern pattern = parseUnion(parseTop(top), top);
111
112     if (index < _string.length())
113       throw error(L.l("unexpected character at `{0}'", badChar(read())));
114
115     return pattern;
116   }
117
118   /**
119    * Parse a match pattern, i.e. a path with no root.
120    */

121   AbstractPattern parseMatch()
122     throws XPathParseException
123   {
124     AbstractPattern root = new FromAny();
125     AbstractPattern pattern = parseUnion(parseTop(root), root);
126
127     if (index < _string.length())
128       throw error(L.l("unexpected character at `{0}'", badChar(read())));
129
130     return pattern;
131   }
132
133
134   /**
135    * Parse an expression.
136    */

137   Expr parseExpr()
138     throws XPathParseException
139   {
140     Expr expr = parseExpr(null, null);
141
142     if (index < _string.length())
143       throw error(L.l("unexpected character at `{0}'", badChar(read())));
144
145     return expr;
146   }
147
148   private AbstractPattern parseStep(AbstractPattern root)
149     throws XPathParseException
150   {
151     return parseUnion(parseTop(root), root);
152   }
153
154   private AbstractPattern parseUnion(AbstractPattern left, AbstractPattern root)
155     throws XPathParseException
156   {
157     int ch = skipWhitespace(read());
158
159     while (ch >= 0) {
160       if (ch == '|') {
161     AbstractPattern tail = parseUnion(parseTop(root), root);
162     left = new UnionPattern(left, tail);
163       } else
164     break;
165
166       for (ch = read(); XmlChar.isWhitespace(ch); ch = read()) {
167       }
168     }
169
170     unread();
171
172     return left;
173   }
174
175   /**
176    * Parses the top expression.
177    *
178    * <pre>
179    * top ::= (expr)
180    * ::= term
181    * </pre>
182    */

183   private AbstractPattern parseTop(AbstractPattern pattern)
184     throws XPathParseException
185   {
186     int ch = skipWhitespace(read());
187     unread();
188
189     if (ch == '(') {
190       Expr expr = parseTerm(pattern, pattern);
191
192       // If the expression is really a pattern and the iterator is
193
// ascending, then just unwrap it.
194
if (expr instanceof NodeSetExpr) {
195         AbstractPattern nodeSet = ((NodeSetExpr) expr).getPattern();
196
197         if (nodeSet.isAscending())
198           return nodeSet;
199       }
200
201       return new FromExpr(null, expr);
202     }
203     else
204       return parseTerm(pattern, pattern).toNodeList();
205   }
206
207   private AbstractPattern parseBasisTop(AbstractPattern pattern)
208     throws XPathParseException
209   {
210     int ch;
211
212     ch = skipWhitespace(read());
213     if (ch == '/') {
214       ch = read();
215       if (ch == '/') {
216         pattern = new FromRoot();
217         pattern = new FromDescendants(pattern, false);
218     pattern = parseBasis(pattern);
219     pattern = parseFilter(pattern);
220         pattern = parsePath(pattern);
221
222         return pattern;
223       }
224       pattern = new FromRoot();
225       ch = skipWhitespace(ch);
226
227       if (ch == -1)
228     return pattern;
229     }
230
231     unread();
232
233     if (pattern == null)
234       pattern = new FromContext();
235
236     pattern = parseBasis(pattern);
237     pattern = parseFilter(pattern);
238
239     return parsePath(pattern);
240   }
241
242   /**
243    * path ::= top ('/' filter)*
244    */

245   private AbstractPattern parsePath(AbstractPattern pattern)
246     throws XPathParseException
247   {
248     int ch = skipWhitespace(read());
249
250     while (ch == '/') {
251       ch = read();
252
253       if (ch == '/') {
254     pattern = new FromDescendants(pattern, false);
255     pattern = parseBasis(pattern);
256     pattern = parseFilter(pattern);
257       }
258       else {
259     unread();
260     pattern = parseBasis(pattern);
261     pattern = parseFilter(pattern);
262       }
263
264       ch = skipWhitespace(read());
265     }
266
267     unread();
268     
269     return pattern;
270   }
271   
272   /**
273    * filter ::= atom('[' expr ']')*
274    */

275   private AbstractPattern parseFilter(AbstractPattern pattern)
276     throws XPathParseException
277   {
278     int ch = skipWhitespace(read());
279     while (ch == '[') {
280       AbstractPattern context = new FromContext();
281       Expr expr = parseExpr(context, pattern);
282       pattern = new FilterPattern(pattern, expr);
283
284       ch = skipWhitespace(read());
285       if (ch != ']')
286     throw error(L.l("expected `{0}' at {1}", "]", badChar(ch)));
287
288       ch = skipWhitespace(read());
289     }
290     unread();
291
292     return pattern;
293   }
294
295   /**
296    * basis ::= name::node-test
297    * | node-test
298    * | @node-test
299    * | .
300    * | ..
301    * ;
302    */

303   private AbstractPattern parseBasis(AbstractPattern pattern)
304     throws XPathParseException
305   {
306     boolean fromChildren = true;
307     int ch = skipWhitespace(read());
308
309     int nodeType = Node.ELEMENT_NODE;
310     String JavaDoc namespace = null;
311     tag.clear();
312
313     if (ch == '@') {
314       if (pattern instanceof FromDescendants)
315     pattern = NodeTypePattern.create(pattern, NodeTypePattern.NODE);
316       
317       pattern = new FromAttributes(pattern);
318       nodeType = Node.ATTRIBUTE_NODE;
319       fromChildren = false;
320       ch = read();
321     }
322     else if (ch == '.') {
323       ch = read();
324       if (ch == '.')
325     return NodeTypePattern.create(new FromParent(pattern),
326                                       NodeTypePattern.NODE);
327       else {
328     unread();
329         if (pattern != null)
330           return pattern;
331         else
332           return NodeTypePattern.create(new FromSelf(pattern), NodeTypePattern.ANY);
333       }
334     }
335     else if (ch == '(') {
336       // XXX: not strictly correct for counting
337
Expr expr = parseExpr(null, null);
338
339       if ((ch = read()) != ')')
340     throw error(L.l("expected `{0}' at {1}", ")", badChar(ch)));
341
342       return new FromExpr(pattern, expr);
343     }
344
345     if (ch == '*') {
346       tag.append('*');
347     }
348
349     else if (XmlChar.isNameStart(ch)) {
350       for (; XmlChar.isNameChar(ch); ch = read())
351     tag.append((char) ch);
352
353       if (ch == '*' && tag.endsWith(":"))
354     tag.append('*');
355       else
356     unread();
357     }
358     else
359       unread();
360
361     String JavaDoc name = tag.toString();
362
363     if (name.equals(""))
364       throw error(L.l("expected name at {0}", badChar(ch)));
365
366     return parseAxis(pattern, name, fromChildren, nodeType);
367   }
368
369   private AbstractPattern parseAxis(AbstractPattern pattern, String JavaDoc name,
370                                     boolean fromChildren, int nodeType)
371     throws XPathParseException
372   {
373     String JavaDoc axis = "";
374     int axisIndex = name.indexOf("::");
375
376     if (axisIndex >= 0 && nodeType != Node.ATTRIBUTE_NODE) {
377       axis = name.substring(0, axisIndex);
378       name = name.substring(axisIndex + 2);
379     }
380
381     if (pattern instanceof FromDescendants)
382       return parseNodeTest(pattern, name, false, Node.ELEMENT_NODE);
383
384     switch (axisMap.get(axis)) {
385     case ANCESTOR_AXIS:
386       return parseNodeTest(new FromAncestors(pattern, false),
387                name, false, Node.ELEMENT_NODE);
388
389     case ANCESTOR_OR_SELF_AXIS:
390       return parseNodeTest(new FromAncestors(pattern, true),
391                name, false, Node.ELEMENT_NODE);
392
393     case ATTRIBUTE_AXIS:
394       return parseNodeTest(new FromAttributes(pattern),
395                name, false, Node.ATTRIBUTE_NODE);
396
397     case CHILD_AXIS:
398       return parseNodeTest(new FromChildren(pattern),
399                name, false, Node.ELEMENT_NODE);
400
401     case DESCENDANT_AXIS:
402       return parseNodeTest(new FromDescendants(pattern, false),
403                name, false, Node.ELEMENT_NODE);
404
405     case DESCENDANT_OR_SELF_AXIS:
406       return parseNodeTest(new FromDescendants(pattern, true),
407                name, false, Node.ELEMENT_NODE);
408
409     case FOLLOWING_AXIS:
410       return parseNodeTest(new FromNext(pattern),
411                name, false, Node.ELEMENT_NODE);
412
413     case FOLLOWING_SIBLING_AXIS:
414       return parseNodeTest(new FromNextSibling(pattern),
415                name, false, Node.ELEMENT_NODE);
416
417     case NAMESPACE_AXIS:
418       return parseNodeTest(new FromNamespace(pattern),
419                name, false, Node.ATTRIBUTE_NODE);
420
421     case PARENT_AXIS:
422       return parseNodeTest(new FromParent(pattern),
423                name, false, Node.ELEMENT_NODE);
424
425     case PRECEDING_AXIS:
426       return parseNodeTest(new FromPrevious(pattern),
427                name, false, Node.ELEMENT_NODE);
428
429     case PRECEDING_SIBLING_AXIS:
430       return parseNodeTest(new FromPreviousSibling(pattern),
431                name, false, Node.ELEMENT_NODE);
432
433     case SELF_AXIS:
434       return parseNodeTest(new FromSelf(pattern),
435                name, false, Node.ELEMENT_NODE);
436
437     default:
438       return parseNodeTest(pattern, name, fromChildren, nodeType);
439     }
440   }
441
442   /**
443    * node-test ::= qname
444    * | *:name
445    * | *
446    * | name '(' args ')'
447    * ;
448    */

449   private AbstractPattern parseNodeTest(AbstractPattern pattern, String JavaDoc name,
450                                         boolean fromChildren, int nodeType)
451     throws XPathParseException
452   {
453     int ch = skipWhitespace(read());
454
455     AbstractPattern tagPattern;
456
457     if (ch == '(') {
458       Expr expr = parseFunction(pattern, pattern, name, fromChildren);
459       return expr.toNodeList();
460     }
461     else if (ch == '{') {
462       tag.clear();
463       while ((ch = read()) >= 0 && ch != '}')
464         tag.append((char) ch);
465       String JavaDoc url = tag.toString();
466       tag.clear();
467       for (ch = read(); XmlChar.isNameChar(ch); ch = read())
468         tag.append((char) ch);
469
470       pattern = new NSNamePattern(pattern, url, tag.toString(), nodeType);
471     }
472     else {
473       if (fromChildren)
474     pattern = new FromChildren(pattern);
475
476       if (name.equals("*"))
477     pattern = NodeTypePattern.create(pattern, nodeType);
478       else if (name.endsWith(":*")) {
479     pattern = new NamespacePattern(pattern,
480                        name.substring(0, name.length() - 2),
481                        nodeType);
482       }
483       else {
484     int p = name.indexOf(':');
485     String JavaDoc ns = null;
486     String JavaDoc local = name;
487
488     if (p > 0) {
489       String JavaDoc prefix = name.substring(0, p);
490       ns = NamespaceContext.find(_namespace, prefix);
491       local = name.substring(p + 1);
492     }
493     else if (nodeType != Node.ATTRIBUTE_NODE)
494       ns = _namespace.find(_namespace, "");
495     else
496       ns = null; // _namespace.find(_namespace, "");
497

498     if (ns == null)
499       pattern = new NodePattern(pattern, name, nodeType);
500     else
501       pattern = new NSNamePattern(pattern, ns, local, nodeType);
502       }
503     }
504     unread();
505
506     return pattern;
507   }
508
509   /**
510    * expr ::= or-expr
511    */

512   Expr parseExpr(AbstractPattern parent, AbstractPattern listParent)
513     throws XPathParseException
514   {
515     peek = -1;
516     Expr left = parseTerm(parent, listParent);
517
518     while (true) {
519       int token = scanToken();
520       switch (token) {
521       case Expr.OR:
522     left = parseOrExpr(token, left, parseTerm(parent, listParent),
523                parent, listParent);
524     break;
525
526       case Expr.AND:
527     left = parseAndExpr(token, left, parseTerm(parent, listParent),
528                 parent, listParent);
529     break;
530
531       case Expr.EQ: case Expr.NEQ: case Expr.LT:
532       case Expr.LE: case Expr.GT: case Expr.GE:
533     left = parseCmpExpr(token, left, parseTerm(parent, listParent),
534                 parent, listParent);
535     break;
536
537       case Expr.ADD: case Expr.SUB:
538     left = parseAddExpr(token, left, parseTerm(parent, listParent),
539                 parent, listParent);
540     break;
541
542       case Expr.MUL: case Expr.DIV: case Expr.QUO: case Expr.MOD:
543     left = parseMulExpr(token, left, parseTerm(parent, listParent),
544                 parent, listParent);
545     break;
546
547       default:
548     return left;
549       }
550     }
551   }
552
553   /**
554    * or-expr ::= or-expr 'OR' expr
555    * | and-expr
556    */

557   private Expr parseOrExpr(int code, Expr left, Expr right,
558                AbstractPattern parent, AbstractPattern listParent)
559     throws XPathParseException
560   {
561     while (true) {
562       int token = scanToken();
563       switch (token) {
564       case Expr.OR:
565     left = new BooleanExpr(code, left, right);
566     code = token;
567     right = parseTerm(parent, listParent);
568     break;
569
570       case Expr.AND:
571     right = parseAndExpr(token, right, parseTerm(parent, listParent),
572                  parent, listParent);
573     break;
574
575       case Expr.EQ: case Expr.NEQ: case Expr.LT:
576       case Expr.LE: case Expr.GT: case Expr.GE:
577     right = parseCmpExpr(token, right, parseTerm(parent, listParent),
578                  parent, listParent);
579     break;
580
581       case Expr.ADD: case Expr.SUB:
582     right = parseAddExpr(token, right, parseTerm(parent, listParent),
583                  parent, listParent);
584     break;
585
586       case Expr.MUL: case Expr.DIV: case Expr.QUO: case Expr.MOD:
587     right = parseMulExpr(token, right, parseTerm(parent, listParent),
588                  parent, listParent);
589     break;
590
591       default:
592     undoToken(token);
593     return new BooleanExpr(code, left, right);
594       }
595     }
596   }
597
598   /**
599    * and-expr ::= and-expr 'AND' expr
600    * | cmp-expr
601    */

602   private Expr parseAndExpr(int code, Expr left, Expr right,
603                 AbstractPattern parent, AbstractPattern listParent)
604     throws XPathParseException
605   {
606     while (true) {
607       int token = scanToken();
608       switch (token) {
609       case Expr.AND:
610     left = new BooleanExpr(code, left, right);
611     code = token;
612     right = parseTerm(parent, listParent);
613     break;
614
615       case Expr.EQ: case Expr.NEQ: case Expr.LT:
616       case Expr.LE: case Expr.GT: case Expr.GE:
617     right = parseCmpExpr(token, right, parseTerm(parent, listParent),
618                  parent, listParent);
619     break;
620
621       case Expr.ADD: case Expr.SUB:
622     right = parseAddExpr(token, right, parseTerm(parent, listParent),
623                  parent, listParent);
624     break;
625
626       case Expr.MUL: case Expr.DIV: case Expr.QUO: case Expr.MOD:
627     right = parseMulExpr(token, right, parseTerm(parent, listParent),
628                  parent, listParent);
629     break;
630
631       default:
632     undoToken(token);
633     return new BooleanExpr(code, left, right);
634       }
635     }
636   }
637
638   /**
639    * cmp-expr ::= cmp-expr '<' expr
640    * | add-expr
641    */

642   private Expr parseCmpExpr(int code, Expr left, Expr right,
643                 AbstractPattern parent, AbstractPattern listParent)
644     throws XPathParseException
645   {
646     while (true) {
647       int token = scanToken();
648       switch (token) {
649       case Expr.EQ: case Expr.NEQ: case Expr.LT:
650       case Expr.LE: case Expr.GT: case Expr.GE:
651     left = new BooleanExpr(code, left, right);
652     code = token;
653     right = parseTerm(parent, listParent);
654     break;
655
656       case Expr.ADD: case Expr.SUB:
657     right = parseAddExpr(token, right, parseTerm(parent, listParent),
658                  parent, listParent);
659     break;
660
661       case Expr.MUL: case Expr.DIV: case Expr.QUO: case Expr.MOD:
662     right = parseMulExpr(token, right, parseTerm(parent, listParent),
663                  parent, listParent);
664     break;
665
666       default:
667     undoToken(token);
668     return new BooleanExpr(code, left, right);
669       }
670     }
671   }
672
673   /**
674    * add-expr ::= add-expr '+' expr
675    * | mul-expr
676    */

677   private Expr parseAddExpr(int code, Expr left, Expr right,
678                 AbstractPattern parent, AbstractPattern listParent)
679     throws XPathParseException
680   {
681     while (true) {
682       int token = scanToken();
683       switch (token) {
684       case Expr.ADD: case Expr.SUB:
685     left = new NumericExpr(code, left, right);
686     code = token;
687     right = parseTerm(parent, listParent);
688     break;
689
690       case Expr.MUL: case Expr.DIV: case Expr.QUO: case Expr.MOD:
691     right = parseMulExpr(token, right, parseTerm(parent, listParent),
692                  parent, listParent);
693     break;
694
695       default:
696     undoToken(token);
697     return new NumericExpr(code, left, right);
698       }
699     }
700   }
701
702   /**
703    * mul-expr ::= mul-expr '*' expr
704    * | term
705    */

706   private Expr parseMulExpr(int code, Expr left, Expr right,
707                 AbstractPattern parent, AbstractPattern listParent)
708     throws XPathParseException
709   {
710     while (true) {
711       int token = scanToken();
712       switch (token) {
713       case Expr.MUL: case Expr.DIV: case Expr.QUO: case Expr.MOD:
714     left = new NumericExpr(code, left, right);
715     code = token;
716     right = parseTerm(parent, listParent);
717     break;
718
719       default:
720     undoToken(token);
721     return new NumericExpr(code, left, right);
722       }
723     }
724   }
725
726   /**
727    * term ::= simple-term path?
728    */

729   private Expr parseTerm(AbstractPattern parent, AbstractPattern listParent)
730     throws XPathParseException
731   {
732     int ch = skipWhitespace(read());
733     unread();
734     Expr expr = parseSimpleTerm(parent, listParent);
735
736     int nextCh = skipWhitespace(read());
737     unread();
738     if (nextCh == '/' || nextCh == '[') {
739       AbstractPattern pattern = expr.toNodeList();
740
741       if (ch == '(' && ! pattern.isStrictlyAscending())
742         pattern = new FromExpr(null, expr);
743     
744       return NodeSetExpr.create(parseUnion(parsePath(parseFilter(pattern)),
745                                            pattern));
746     }
747     else if (nextCh == '|') {
748       AbstractPattern pattern = expr.toNodeList();
749
750       return NodeSetExpr.create(parseUnion(parsePath(parseFilter(pattern)),
751                                            listParent));
752     }
753     else
754       return expr;
755   }
756
757   /**
758    * simple-term ::= number
759    * ::= '(' expr ')'
760    * ::= '$' variable
761    * ::= '"' string '"'
762    * ::= node-set
763    */

764   private Expr parseSimpleTerm(AbstractPattern parent, AbstractPattern listParent)
765     throws XPathParseException
766   {
767     int ch = read();
768     
769     ch = skipWhitespace(ch);
770
771     switch (ch) {
772     case '.':
773       ch = read();
774       unread();
775       unread();
776       if (! ('0' <= ch && ch <= '9')) {
777     return NodeSetExpr.create(parseUnion(parseBasisTop(parent), parent));
778       }
779       ch = read();
780       // fall through to parse the number
781

782     case '0': case '1': case '2': case '3': case '4':
783     case '5': case '6': case '7': case '8': case '9':
784       {
785     long value = 0;
786     double exp = 1;
787         int digits = 0;
788     for (; ch >= '0' && ch <= '9'; ch = read())
789       value = 10 * value + ch - '0';
790     if (ch == '.') {
791       for (ch = read(); ch >= '0' && ch <= '9'; ch = read()) {
792         value = 10 * value + ch - '0';
793         exp *= 10;
794             digits--;
795       }
796     }
797
798         if (ch == 'e' || ch == 'E') {
799           int sign = 1;
800           int expValue = 0;
801           
802           ch = read();
803           if (ch == '-') {
804             sign = -1;
805             ch = read();
806           }
807           else if (ch == '+')
808             ch = read();
809           
810           for (; ch >= '0' && ch <= '9'; ch = read())
811             expValue = 10 * expValue + ch - '0';
812
813           exp = Math.pow(10, digits + sign * expValue);
814
815           unread();
816           
817           return new NumericExpr((double) value * (double) exp);
818         }
819         
820     unread();
821     return new NumericExpr((double) value / (double) exp);
822       }
823
824     case '-':
825       return new NumericExpr(Expr.NEG, parseTerm(parent, listParent));
826
827     case '+':
828       return parseTerm(parent, listParent);
829
830     case '(':
831       {
832     Expr expr = parseExpr(parent, listParent);
833     if ((ch = skipWhitespace(read())) != ')')
834       throw error(L.l("expected `{0}' at {1}", ")", badChar(ch)));
835     
836     return expr;
837       }
838
839     case '/': case '@': case '*':
840       unread();
841       return NodeSetExpr.create(parseUnion(parseBasisTop(parent), parent));
842
843     case '\'': case '"':
844       {
845     int end = ch;
846     CharBuffer cb = new CharBuffer();
847         for (ch = read(); ch >= 0; ch = read()) {
848           if (ch != end)
849             cb.append((char) ch);
850           else if ((ch = read()) == end)
851             cb.append((char) ch);
852           else {
853             unread();
854             break;
855           }
856         }
857
858     return new StringExpr(cb.toString());
859       }
860
861     case '$':
862       {
863     String JavaDoc name = readName(read());
864     return new VarExpr(name);
865       }
866
867     default:
868       if (! XmlChar.isNameStart(ch))
869     throw error(L.l("unknown character at {0}", badChar(ch)));
870
871       String JavaDoc name = readName(ch);
872
873       ch = skipWhitespace(read());
874       int axisIndex = name.indexOf("::");
875
876       // make sure axis are treated as node sets
877
if (ch == '(' && axisIndex < 0) {
878     return parseFunction(parent, listParent, name, true);
879       }
880       else if (ch == '(') {
881     String JavaDoc axis = name.substring(0, axisIndex);
882     if (axisMap.get(axis) <= 0)
883       return parseFunction(parent, listParent, name, true);
884       }
885       
886       unread();
887
888       if (parent == null)
889     parent = new FromContext();
890       return parseNodeSetExpr(parent, name, Node.ELEMENT_NODE);
891     }
892   }
893
894   /**
895    * function ::= name '(' args ')'
896    *
897    * The XPath library functions are hard-coded for a little extra
898    * execution efficiency.
899    */

900   Expr parseFunction(AbstractPattern parent,
901                      AbstractPattern listParent,
902                      String JavaDoc name,
903              boolean fromChildren)
904     throws XPathParseException
905   {
906     int ch = skipWhitespace(read());
907
908     ArrayList JavaDoc<Expr> args = new ArrayList JavaDoc<Expr>();
909
910     for (; ch >= 0 && ch != ')'; ch = skipWhitespace(read())) {
911       if (ch != ',')
912     unread();
913       Expr expr = parseExpr(parent, listParent);
914
915       if (expr == null)
916         throw error(L.l("null expression"));
917       
918       args.add(expr);
919     }
920
921     int code = exprFunctions.get(name);
922     switch (code) {
923     case Expr.TRUE: case Expr.FALSE: case Expr.NOT: case Expr.BOOLEAN:
924     case Expr.STARTS_WITH: case Expr.CONTAINS: case Expr.LANG:
925     case Expr.FUNCTION_AVAILABLE:
926       return new BooleanExpr(code, args);
927
928     case Expr.NUMBER: case Expr.FLOOR: case Expr.CEILING: case Expr.ROUND:
929     case Expr.STRING_LENGTH:
930       return new NumericExpr(code, args);
931
932     case Expr.POSITION:
933     case Expr.LAST:
934       return new NumericExpr(code, listParent);
935
936     case Expr.COUNT: case Expr.SUM:
937       if (args.size() == 0)
938     args.add(NodeSetExpr.create(new FromContext()));
939       return new NumericExpr(code, ((Expr) args.get(0)).toNodeList());
940
941
942     case Expr.CONCAT:
943     case Expr.SUBSTRING_BEFORE:
944     case Expr.SUBSTRING: case Expr.SUBSTRING_AFTER:
945     case Expr.TRANSLATE:
946     case Expr.SYSTEM_PROPERTY:
947       return new StringExpr(code, args);
948
949     case Expr.STRING: case Expr.NORMALIZE:
950       return new StringExpr(code, args);
951
952     case Expr.LOCAL_PART: case Expr.NAMESPACE:
953     case Expr.QNAME: case Expr.GENERATE_ID:
954       if (args.size() == 0)
955     args.add(NodeSetExpr.create(new FromContext()));
956       return new StringExpr(code, args);
957
958     case Expr.ID:
959       if (args.size() == 0) {
960     args.add(NodeSetExpr.create(parent));
961     return new IdExpr(args);
962       }
963       else
964     return new IdExpr(args);
965
966     case Expr.IF:
967       if (args.size() != 3)
968         throw error(L.l("`if' needs three args."));
969
970       return new ObjectExpr(code, args);
971
972     case Expr.BASE_URI:
973       if (args.size() != 1)
974         throw error(L.l("`base-uri' needs one args."));
975
976       return new StringExpr(code, args.get(0));
977
978     case TEXT:
979       if (fromChildren)
980     parent = new FromChildren(parent);
981       AbstractPattern pattern = NodeTypePattern.create(parent, Node.TEXT_NODE);
982       return NodeSetExpr.create(pattern);
983
984     case COMMENT:
985       if (fromChildren)
986     parent = new FromChildren(parent);
987       pattern = NodeTypePattern.create(parent, Node.COMMENT_NODE);
988       return NodeSetExpr.create(pattern);
989
990     case ER:
991       if (fromChildren)
992     parent = new FromChildren(parent);
993       pattern = NodeTypePattern.create(parent, Node.ENTITY_REFERENCE_NODE);
994       return NodeSetExpr.create(pattern);
995
996     case PI:
997       if (fromChildren)
998     parent = new FromChildren(parent);
999       if (args.size() == 1) {
1000    Expr expr = (Expr) args.get(0);
1001    String JavaDoc value = null;
1002    if (expr instanceof StringExpr)
1003      value = ((StringExpr) expr).getValue();
1004    if (value == null)
1005      throw error(L.l("processing-instruction expects string literal"));
1006    pattern = new NodePattern(parent, value,
1007                  Node.PROCESSING_INSTRUCTION_NODE);
1008      }
1009      else
1010    pattern = NodeTypePattern.create(parent, Node.PROCESSING_INSTRUCTION_NODE);
1011      return NodeSetExpr.create(pattern);
1012
1013    case NODE:
1014      if (fromChildren)
1015    parent = new FromChildren(parent);
1016      pattern = NodeTypePattern.create(parent, NodeTypePattern.NODE);
1017      return NodeSetExpr.create(pattern);
1018
1019    case CURRENT:
1020      return NodeSetExpr.create(new CurrentPattern());
1021
1022    case CONTEXT:
1023      return NodeSetExpr.create(new FromContext());
1024
1025    default:
1026      Expr function = constructorFunction(name, args);
1027
1028      if (function != null)
1029    return function;
1030      
1031      int p = name.lastIndexOf(':');
1032      String JavaDoc prefix;
1033      
1034      if (p > 0)
1035        prefix = name.substring(0, p);
1036      else
1037    prefix = "";
1038      
1039      String JavaDoc context = NamespaceContext.find(_namespace, prefix);
1040
1041      if (context == null) {
1042      }
1043      else if (context.startsWith("java:"))
1044    name = context + "." + name.substring(p + 1);
1045      else if (context.indexOf(':') < 0)
1046    name = "java:" + context + "." + name.substring(p + 1);
1047      
1048      if (name.startsWith("java:")) {
1049        p = name.lastIndexOf('.');
1050        if (p < 0)
1051          throw error(L.l("`{0}' is an illegal extension function. Java extension functions must look like java:mypkg.MyClass.mymethod.", name));
1052
1053        String JavaDoc className = name.substring(5, p);
1054        String JavaDoc methodName = name.substring(p + 1);
1055
1056        Class JavaDoc cl;
1057        try {
1058          cl = CauchoSystem.loadClass(className);
1059        } catch (ClassNotFoundException JavaDoc e) {
1060          throw error(L.l("`{0}' is an unknown Java class. Java extension functions must use public classes.", className));
1061        }
1062
1063        if (methodName.equals("new")) {
1064          Constructor JavaDoc []constructors = cl.getConstructors();
1065          
1066          for (int i = 0; i < constructors.length; i++) {
1067            if (constructors[i].getParameterTypes().length == args.size())
1068              return new NewJavaExpr(constructors[i], args);
1069          }
1070            
1071          throw error(L.l("No matching public constructor in `{0}'",
1072                          className));
1073        }
1074
1075        Method JavaDoc method = null;
1076        Method JavaDoc []methods = cl.getMethods();
1077
1078        if (args.size() > 0) {
1079          for (int i = 0; i < methods.length; i++) {
1080            if (methods[i].getName().equals(methodName) &&
1081                methods[i].getParameterTypes().length == args.size() - 1 &&
1082                ! Modifier.isStatic(methods[i].getModifiers())) {
1083              Expr objArg = (Expr) args.remove(0);
1084
1085              return new ObjectJavaExpr(methods[i], objArg, args);
1086            }
1087          }
1088        }
1089
1090        for (int i = 0; i < methods.length; i++) {
1091          if (methods[i].getName().equals(methodName) &&
1092              methods[i].getParameterTypes().length == args.size()) {
1093            method = methods[i];
1094            break;
1095          }
1096        }
1097
1098        if (method == null)
1099          throw error(L.l("`{0}' does not match a public method in `{1}'",
1100                          methodName, className));
1101
1102        if (! Modifier.isStatic(method.getModifiers()))
1103          throw error(L.l("`{0}' is not a static method in `{1}'",
1104                          methodName, className));
1105
1106        return new StaticJavaExpr(method, args);
1107      }
1108      else if (name.equals(""))
1109    throw error(L.l("expected node-test at `{0}'", "("));
1110
1111      return new FunExpr(name, parent, args);
1112    }
1113  }
1114
1115  private Expr constructorFunction(String JavaDoc name, ArrayList JavaDoc<Expr> args)
1116    throws XPathParseException
1117  {
1118    Constructor JavaDoc constructor = _exprFunctions.get(name);
1119
1120    if (constructor == null)
1121      return null;
1122    
1123    Class JavaDoc []params = constructor.getParameterTypes();
1124
1125    if (params.length < args.size())
1126      throw error(L.l("`{0}' needs {1} arguments",
1127              name, "" + params.length));
1128
1129    Object JavaDoc []values = new Object JavaDoc[params.length];
1130    
1131    for (int i = 0; i < args.size(); i++)
1132      values[i] = args.get(i);
1133
1134    try {
1135      return (Expr) constructor.newInstance(values);
1136    } catch (Throwable JavaDoc e) {
1137      throw new XPathParseException(e);
1138    }
1139  }
1140
1141  private Expr parseNodeSetExpr(AbstractPattern parent, String JavaDoc name, int nodeType)
1142    throws XPathParseException
1143  {
1144    AbstractPattern top = parseAxis(parent, name, true, nodeType);
1145    top = parseFilter(top);
1146
1147    return NodeSetExpr.create(parseUnion(parsePath(top), parent));
1148  }
1149
1150  /**
1151   * Scans the next token.
1152   *
1153   * @return token code, expressed as an Expr enumeration.
1154   */

1155  private int scanToken()
1156    throws XPathParseException
1157  {
1158    if (peek >= 0) {
1159      int value = peek;
1160      peek = -1;
1161      return value;
1162    }
1163
1164    int ch = skipWhitespace(read());
1165
1166    switch (ch) {
1167    case '+': return Expr.ADD;
1168    case '-': return Expr.SUB;
1169    case '*': return Expr.MUL;
1170    case '=': return Expr.EQ;
1171
1172    case '!':
1173      ch = read();
1174      if (ch == '=')
1175    return Expr.NEQ;
1176      else
1177    throw error(L.l("expected `{0}' at {1}", "=", badChar(ch)));
1178
1179    case '<':
1180      ch = read();
1181      if (ch == '=')
1182    return Expr.LE;
1183      else {
1184    unread();
1185    return Expr.LT;
1186      }
1187
1188    case '>':
1189      ch = read();
1190      if (ch == '=')
1191    return Expr.GE;
1192      else {
1193    unread();
1194    return Expr.GT;
1195      }
1196
1197    default:
1198      if (XmlChar.isNameStart(ch)) {
1199    String JavaDoc name = readName(ch);
1200
1201    if (name.equals("div"))
1202      return Expr.DIV;
1203    else if (name.equals("quo"))
1204      return Expr.QUO;
1205    else if (name.equals("mod"))
1206      return Expr.MOD;
1207    else if (name.equals("and"))
1208      return Expr.AND;
1209    else if (name.equals("or"))
1210      return Expr.OR;
1211    else
1212      throw error(L.l("expected binary operation at `{0}'", name));
1213      }
1214
1215      unread();
1216      return -1;
1217    }
1218  }
1219
1220  private String JavaDoc readName(int ch)
1221  {
1222    tag.clear();
1223    for (; XmlChar.isNameChar(ch); ch = read())
1224      tag.append((char) ch);
1225    
1226    if (ch == '*' && tag.endsWith(":"))
1227      tag.append((char) ch);
1228    else
1229      unread();
1230
1231    return tag.toString();
1232  }
1233
1234  private void undoToken(int token)
1235  {
1236    peek = token;
1237  }
1238
1239  private int read()
1240  {
1241    if (index < _string.length()) {
1242      return _string.charAt(index++);
1243    }
1244    else {
1245      index++;
1246      return -1;
1247    }
1248  }
1249
1250  private void unread()
1251  {
1252    index--;
1253  }
1254  
1255  private XPathParseException error(String JavaDoc message)
1256  {
1257    return new XPathParseException(message + " in " + _string);
1258  }
1259  
1260  private String JavaDoc badChar(int ch)
1261  {
1262    if (ch < 0)
1263      return L.l("end of file");
1264    else if (ch == '\n')
1265      return L.l("end of line");
1266    else
1267      return "`" + (char) ch + "'";
1268  }
1269
1270  private int skipWhitespace(int ch)
1271    throws XPathParseException
1272  {
1273    for (; ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; ch = read()) {
1274    }
1275
1276    return ch;
1277  }
1278
1279  private static void addFunction(String JavaDoc name, Class JavaDoc cl)
1280  {
1281    Constructor JavaDoc []constructors = cl.getConstructors();
1282    
1283    _exprFunctions.put(name, constructors[0]);
1284  }
1285
1286  static {
1287    exprFunctions = new IntMap();
1288    exprFunctions.put("id", Expr.ID);
1289
1290    exprFunctions.put("true", Expr.TRUE);
1291    exprFunctions.put("false", Expr.FALSE);
1292    exprFunctions.put("not", Expr.NOT);
1293    exprFunctions.put("boolean", Expr.BOOLEAN);
1294    exprFunctions.put("starts-with", Expr.STARTS_WITH);
1295    exprFunctions.put("contains", Expr.CONTAINS);
1296    exprFunctions.put("lang", Expr.LANG);
1297
1298    exprFunctions.put("number", Expr.NUMBER);
1299    exprFunctions.put("sum", Expr.SUM);
1300    exprFunctions.put("floor", Expr.FLOOR);
1301    exprFunctions.put("ceiling", Expr.CEILING);
1302    exprFunctions.put("round", Expr.ROUND);
1303    exprFunctions.put("position", Expr.POSITION);
1304    exprFunctions.put("count", Expr.COUNT);
1305    exprFunctions.put("last", Expr.LAST);
1306    exprFunctions.put("string-length", Expr.STRING_LENGTH);
1307
1308    exprFunctions.put("string", Expr.STRING);
1309    exprFunctions.put("concat", Expr.CONCAT);
1310    exprFunctions.put("substring", Expr.SUBSTRING);
1311    exprFunctions.put("substring-before", Expr.SUBSTRING_BEFORE);
1312    exprFunctions.put("substring-after", Expr.SUBSTRING_AFTER);
1313    exprFunctions.put("normalize-space", Expr.NORMALIZE);
1314    exprFunctions.put("translate", Expr.TRANSLATE);
1315
1316    exprFunctions.put("local-name", Expr.LOCAL_PART);
1317    exprFunctions.put("local-part", Expr.LOCAL_PART);
1318    exprFunctions.put("namespace-uri", Expr.NAMESPACE);
1319    exprFunctions.put("name", Expr.QNAME);
1320    exprFunctions.put("generate-id", Expr.GENERATE_ID);
1321
1322    exprFunctions.put("if", Expr.IF);
1323    
1324    exprFunctions.put("text", TEXT);
1325    exprFunctions.put("comment", COMMENT);
1326    exprFunctions.put("er", ER);
1327    exprFunctions.put("entity-reference", ER);
1328    exprFunctions.put("pi", PI);
1329    exprFunctions.put("processing-instruction", PI);
1330    exprFunctions.put("node", NODE);
1331    exprFunctions.put("current", CURRENT);
1332    exprFunctions.put("context", CONTEXT);
1333
1334    axisMap = new IntMap();
1335    axisMap.put("ancestor", ANCESTOR_AXIS);
1336    axisMap.put("ancestor-or-self", ANCESTOR_OR_SELF_AXIS);
1337    axisMap.put("attribute", ATTRIBUTE_AXIS);
1338    axisMap.put("child", CHILD_AXIS);
1339    axisMap.put("descendant", DESCENDANT_AXIS);
1340    axisMap.put("descendant-or-self", DESCENDANT_OR_SELF_AXIS);
1341    axisMap.put("following", FOLLOWING_AXIS);
1342    axisMap.put("following-sibling", FOLLOWING_SIBLING_AXIS);
1343    axisMap.put("namespace", NAMESPACE_AXIS);
1344    axisMap.put("parent", PARENT_AXIS);
1345    axisMap.put("preceding", PRECEDING_AXIS);
1346    axisMap.put("preceding-sibling", PRECEDING_SIBLING_AXIS);
1347    axisMap.put("self", SELF_AXIS);
1348
1349    _exprFunctions = new HashMap JavaDoc<String JavaDoc,Constructor JavaDoc>();
1350    addFunction("fn:base-uri", BaseURI.class);
1351    addFunction("fn:resolve-uri", ResolveURI.class);
1352    addFunction("fn:trace", Trace.class);
1353  }
1354}
1355
Popular Tags