KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > el > ELParser


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  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.el;
31
32 import com.caucho.util.CharBuffer;
33 import com.caucho.util.L10N;
34
35 import javax.el.ELContext;
36 import javax.el.FunctionMapper;
37 import javax.el.ValueExpression;
38 import javax.el.VariableMapper;
39 import java.lang.reflect.Method JavaDoc;
40 import java.util.ArrayList JavaDoc;
41 import java.util.logging.Logger JavaDoc;
42
43 /**
44  * Parses the expression.
45  */

46 public class ELParser
47 {
48   private static final Logger JavaDoc log = Logger.getLogger(ELParser.class.getName());
49   private static final L10N L = new L10N(ELParser.class);
50
51   // The expression string
52
private String JavaDoc _string;
53   // Current parse index into the string
54
private int _index;
55   // The peek token
56
private int _peek = -1;
57   // The current lexeme
58
private String JavaDoc _lexeme;
59   // Temporary buffer
60
private CharBuffer _cb = new CharBuffer();
61
62   protected final ELContext _elContext;
63
64   private boolean _checkEscape = true;
65
66   public ELParser(ELContext elContext, String JavaDoc string)
67   {
68     if (elContext == null)
69       throw new NullPointerException JavaDoc();
70     
71     _elContext = elContext;
72     _string = string;
73   }
74
75   protected ELParser create(String JavaDoc string)
76   {
77     ELParser parser = new ELParser(_elContext, string);
78
79     copyTo(parser);
80
81     return parser;
82   }
83
84   /**
85    * Copy to the dest parser.
86    */

87   protected void copyTo(ELParser parser)
88   {
89   }
90
91   /**
92    * Set true if escapes are checked.
93    */

94   public void setCheckEscape(boolean checkEscape)
95   {
96     _checkEscape = checkEscape;
97   }
98
99   /**
100    * Parses the expression string.
101    */

102   public Expr parse()
103     throws ELParseException
104   {
105     return parseInterpolate();
106   }
107
108   /**
109    * Parses interpolated code.
110    */

111   public Expr parseInterpolate()
112     throws ELParseException
113   {
114     CharBuffer text = CharBuffer.allocate();
115     CharBuffer exprString = CharBuffer.allocate();
116     Expr expr = null;
117     int ch;
118
119     while ((ch = read()) >= 0) {
120       if (_checkEscape && ch == '\\') {
121     ch = read();
122
123     if (ch == '$' || ch == '#' || ch == '\\')
124       text.append((char) ch);
125     else {
126       text.append('\\');
127       unread();
128     }
129       }
130       else if (ch == '$' || ch == '#') {
131     int origChar = ch;
132     
133         ch = read();
134         
135         if (ch == '{') {
136           if (text.length() > 0) {
137             StringLiteral right = new StringLiteral(text.toString());
138
139             if (expr == null)
140               expr = right;
141             else
142               expr = new InterpolateExpr(expr, right);
143
144             text.clear();
145           }
146
147           exprString.clear();
148
149           for (ch = read(); ch > 0 && ch != '}'; ch = read()) {
150             exprString.append((char) ch);
151         
152         if (ch == '\'' || ch == '"') {
153           int end = ch;
154
155           for (ch = read(); ch > 0 && ch != end; ch = read()) {
156         exprString.append((char) ch);
157
158         if (ch == '\\') {
159           ch = read();
160           if (ch > 0)
161             exprString.append((char) ch);
162         }
163           }
164
165           if (ch > 0)
166         exprString.append((char) ch);
167         }
168       }
169
170       if (ch != '}')
171         throw error(L.l("expected '}' at end of EL expression",
172                 exprString));
173
174           Expr right = create(exprString.toString()).parseExpr();
175
176           if (expr == null)
177             expr = right;
178           else
179             expr = new InterpolateExpr(expr, right);
180         }
181         else {
182           text.append((char) origChar);
183           unread();
184         }
185       }
186       else
187         text.append((char) ch);
188     }
189
190     if (text.length() > 0) {
191       StringLiteral right = new StringLiteral(text.toString());
192
193       if (expr == null)
194         expr = right;
195       else
196         expr = new InterpolateExpr(expr, right);
197     }
198
199     if (expr == null)
200       expr = new StringLiteral("");
201
202     return expr;
203   }
204
205   /**
206    * expr ::= term
207    */

208   private Expr parseExpr()
209     throws ELParseException
210   {
211     Expr left = parseTerm();
212
213     while (true) {
214       int token = scanToken();
215       
216       switch (token) {
217       case '?':
218     {
219       Expr trueExpr = parseExpr();
220       token = scanToken();
221       if (token != ':')
222         throw error(L.l("Expected `:' at {0}. Conditional syntax is 'expr ? expr : expr'.", badChar(token)));
223       Expr falseExpr = parseExpr();
224
225       left = new ConditionalExpr(left, trueExpr, falseExpr);
226     }
227     break;
228     
229       case Expr.OR:
230     left = parseOrExpr(token, left, parseTerm());
231     break;
232
233       case Expr.AND:
234     left = parseAndExpr(token, left, parseTerm());
235     break;
236
237       case Expr.EQ: case Expr.NE: case Expr.LT:
238       case Expr.LE: case Expr.GT: case Expr.GE:
239     left = parseCmpExpr(token, left, parseTerm());
240     break;
241         
242       case Expr.ADD: case Expr.SUB:
243     left = parseAddExpr(token, left, parseTerm());
244     break;
245
246       case Expr.MUL: case Expr.DIV: case Expr.MOD:
247     left = parseMulExpr(token, left, parseTerm());
248     break;
249
250       default:
251         _peek = token;
252     return left;
253       }
254     }
255   }
256
257   /**
258    * or-expr ::= or-expr 'or' expr
259    * ::= and-expr
260    */

261   private Expr parseOrExpr(int code, Expr left, Expr right)
262     throws ELParseException
263   {
264     while (true) {
265       int token = scanToken();
266       switch (token) {
267       case Expr.OR:
268         left = new BooleanExpr(code, left, right);
269     code = token;
270     right = parseTerm();
271     break;
272         
273       case Expr.AND:
274     right = parseAndExpr(token, right, parseTerm());
275     break;
276         
277       case Expr.EQ: case Expr.NE:
278       case Expr.LT: case Expr.GT:
279       case Expr.LE: case Expr.GE:
280     right = parseCmpExpr(token, right, parseTerm());
281     break;
282         
283       case Expr.ADD: case Expr.SUB:
284     right = parseAddExpr(token, right, parseTerm());
285     break;
286
287       case Expr.MUL: case Expr.DIV: case Expr.MOD:
288     right = parseMulExpr(token, right, parseTerm());
289     break;
290
291       default:
292     _peek = token;
293         return new BooleanExpr(code, left, right);
294       }
295     }
296   }
297
298   /**
299    * and-expr ::= and-expr 'and' expr
300    * ::= cmp-expr
301    */

302   private Expr parseAndExpr(int code, Expr left, Expr right)
303     throws ELParseException
304   {
305     while (true) {
306       int token = scanToken();
307       switch (token) {
308       case Expr.AND:
309         left = new BooleanExpr(code, left, right);
310     code = token;
311     right = parseTerm();
312     break;
313         
314       case Expr.EQ: case Expr.NE:
315       case Expr.LT: case Expr.GT:
316       case Expr.LE: case Expr.GE:
317     right = parseCmpExpr(token, right, parseTerm());
318     break;
319         
320       case Expr.ADD: case Expr.SUB:
321     right = parseAddExpr(token, right, parseTerm());
322     break;
323
324       case Expr.MUL: case Expr.DIV: case Expr.MOD:
325     right = parseMulExpr(token, right, parseTerm());
326     break;
327
328       default:
329     _peek = token;
330         return new BooleanExpr(code, left, right);
331       }
332     }
333   }
334
335   /**
336    * cmp-expr ::= cmp-expr '=' expr
337    * ::= add-expr
338    */

339   private Expr parseCmpExpr(int code, Expr left, Expr right)
340     throws ELParseException
341   {
342     while (true) {
343       int token = scanToken();
344       switch (token) {
345       case Expr.EQ: case Expr.NE:
346       case Expr.GT: case Expr.LT:
347       case Expr.LE: case Expr.GE:
348     left = CmpExpr.create(code, left, right);
349           
350     code = token;
351     right = parseTerm();
352     break;
353         
354       case Expr.ADD: case Expr.SUB:
355     right = parseAddExpr(token, right, parseTerm());
356     break;
357
358       case Expr.MUL: case Expr.DIV: case Expr.MOD:
359     right = parseMulExpr(token, right, parseTerm());
360     break;
361
362       default:
363     _peek = token;
364     return CmpExpr.create(code, left, right);
365       }
366     }
367   }
368
369   /**
370    * add-expr ::= add-expr '+' expr
371    * ::= mul-expr
372    */

373   private Expr parseAddExpr(int code, Expr left, Expr right)
374     throws ELParseException
375   {
376     while (true) {
377       int token = scanToken();
378       switch (token) {
379       case Expr.ADD: case Expr.SUB:
380     left = BinaryExpr.create(code, left, right);
381     code = token;
382     right = parseTerm();
383     break;
384
385       case Expr.MUL: case Expr.DIV: case Expr.MOD:
386     right = parseMulExpr(token, right, parseTerm());
387     break;
388
389       default:
390     _peek = token;
391     return BinaryExpr.create(code, left, right);
392       }
393     }
394   }
395
396   /**
397    * mul-expr ::= mul-expr '*' expr
398    * ::= expr
399    */

400   private Expr parseMulExpr(int code, Expr left, Expr right)
401     throws ELParseException
402   {
403     while (true) {
404       int token = scanToken();
405       switch (token) {
406       case Expr.MUL: case Expr.DIV: case Expr.MOD:
407     left = BinaryExpr.create(code, left, right);
408     right = parseTerm();
409     code = token;
410     break;
411
412       default:
413     _peek = token;
414     return BinaryExpr.create(code, left, right);
415       }
416     }
417   }
418
419   /**
420    * term ::= simple-term
421    * ::= term '[' expr ']'
422    * ::= term . identifier
423    */

424   private Expr parseTerm()
425     throws ELParseException
426   {
427     Expr term = parseSimpleTerm();
428     
429     while (true) {
430       int token = scanToken();
431
432       switch (token) {
433       case '[':
434       {
435     Expr expr = parseExpr();
436         token = scanToken();
437     if (token != ']')
438       throw error(L.l("Expected `]' at {0}. All open array braces must have matching closing brace.", badChar(token)));
439
440     term = term.createField(expr);
441         break;
442       }
443       
444       case '(':
445       {
446         ArrayList JavaDoc<Expr> argList = new ArrayList JavaDoc<Expr>();
447
448         int ch = skipWhitespace(read());
449
450         while (ch > 0 && ch != ')') {
451           unread();
452           argList.add(parseExpr());
453           
454           token = scanToken();
455           
456           if (token != ',') {
457             ch = token;
458             break;
459           }
460           
461           ch = skipWhitespace(read());
462         }
463         
464     if (ch != ')')
465       throw error(L.l("Expected `)' at {0}. All functions must have matching closing parenthesis.", badChar(ch)));
466
467         // token = scanToken();
468

469         Expr []args = (Expr []) argList.toArray(new Expr[argList.size()]);
470
471     Expr expr = term.createMethod(args);
472         if (expr == null)
473       throw error(L.l("Method call not supported in this context `{0}'.",
474                           term));
475         term = expr;
476         break;
477       }
478
479       case '.':
480       {
481         int ch = skipWhitespace(read());
482         
483         if (! Character.isJavaIdentifierStart((char) ch))
484       throw error(L.l("Expected `]' at {0}. Field references must be identifiers.", badChar(ch)));
485
486         String JavaDoc field = readName(ch);
487
488         term = term.createField(field);
489         break;
490       }
491         
492       default:
493         _peek = token;
494         return term;
495       }
496     }
497   }
498
499   /**
500    * simple-term ::= number
501    * ::= '(' expr ')'
502    * ::= variable
503    * ::= '"' string '"'
504    * ::= true | false | null
505    */

506   private Expr parseSimpleTerm()
507     throws ELParseException
508   {
509     int ch = read();
510     
511     ch = skipWhitespace(ch);
512
513     switch (ch) {
514     case '.':
515     case '0': case '1': case '2': case '3': case '4':
516     case '5': case '6': case '7': case '8': case '9':
517       {
518     long value = 0;
519     double exp = 1;
520         int digits = 0;
521
522     for (; ch >= '0' && ch <= '9'; ch = read())
523       value = 10 * value + ch - '0';
524
525         if (ch != '.' && ch != 'e' && ch != 'E') {
526           unread();
527           return new LongLiteral(value);
528         }
529         
530     if (ch == '.') {
531       for (ch = read(); ch >= '0' && ch <= '9'; ch = read()) {
532         value = 10 * value + ch - '0';
533         exp *= 10;
534             digits--;
535       }
536     }
537
538         if (ch == 'e' || ch == 'E') {
539           int sign = 1;
540           int expValue = 0;
541           
542           ch = read();
543           if (ch == '-') {
544             sign = -1;
545             ch = read();
546           }
547           else if (ch == '+')
548             ch = read();
549           
550           for (; ch >= '0' && ch <= '9'; ch = read())
551             expValue = 10 * expValue + ch - '0';
552
553           exp = Math.pow(10, digits + sign * expValue);
554
555           unread();
556           
557           return new DoubleLiteral((double) value * (double) exp);
558         }
559         
560     unread();
561     return new DoubleLiteral((double) value / (double) exp);
562       }
563
564     case '-':
565       return new MinusExpr(parseTerm());
566
567     case '!':
568       return UnaryExpr.create(Expr.NOT, parseTerm());
569
570     case '+':
571       return parseTerm();
572
573     case '(':
574       {
575     Expr expr = parseExpr();
576     if ((ch = scanToken()) != ')')
577       throw error(L.l("Expected `)' at {0}. All open parentheses must have matching closing parentheses.", badChar(ch)));
578     
579     return expr;
580       }
581
582     case '\'': case '"':
583       {
584     int end = ch;
585     CharBuffer cb = new CharBuffer();
586         for (ch = read(); ch >= 0; ch = read()) {
587           if (ch == '\\')
588             cb.append((char) read());
589           else if (ch != end)
590             cb.append((char) ch);
591           else if ((ch = read()) == end)
592             cb.append((char) ch);
593           else {
594             unread();
595             break;
596           }
597         }
598
599     return new StringLiteral(cb.toString());
600       }
601
602     default:
603       if (! Character.isJavaIdentifierStart((char) ch) && ch != ':')
604     throw error(L.l("Unexpected character at {0}.", badChar(ch)));
605       
606       CharBuffer cb = CharBuffer.allocate();
607       for (;
608            Character.isJavaIdentifierPart((char) ch) || ch == ':';
609            ch = read())
610         cb.append((char) ch);
611
612       unread();
613
614       String JavaDoc name = cb.close();
615
616       if (name.equals("null"))
617         return new NullLiteral();
618       else if (name.equals("true"))
619         return new BooleanLiteral(true);
620       else if (name.equals("false"))
621         return new BooleanLiteral(false);
622       else if (name.equals("not"))
623         return UnaryExpr.create(Expr.NOT, parseTerm());
624       else if (name.equals("empty"))
625         return UnaryExpr.create(Expr.EMPTY, parseTerm());
626       else {
627     VariableMapper varMapper = _elContext.getVariableMapper();
628     
629     ValueExpression valueExpr = null;
630
631     if (varMapper != null)
632       valueExpr = varMapper.resolveVariable(name);
633
634     if (valueExpr != null)
635       return new ValueExpr(name, valueExpr);
636     
637         Expr expr = createImplicitObjectExpr(name);
638
639         if (expr != null)
640           return expr;
641
642         Method JavaDoc method = getStaticMethod(name);
643
644         if (method != null)
645           return new StaticMethodExpr(method);
646         else
647           return new IdExpr(name);
648       }
649     }
650   }
651
652   /**
653    * Creates the implicit object for the name.
654    */

655   protected Expr createImplicitObjectExpr(String JavaDoc name)
656   {
657     return null;
658   }
659
660   /**
661    * Creates the implicit object for the name.
662    */

663   protected Method JavaDoc getStaticMethod(String JavaDoc name)
664     throws ELParseException
665   {
666     Method JavaDoc method = null;
667
668     FunctionMapper funMapper = _elContext.getFunctionMapper();
669
670     if (funMapper != null) {
671       String JavaDoc prefix = "";
672       String JavaDoc localName = name;
673
674       int p = name.indexOf(':');
675       if (p > 0) {
676     prefix = name.substring(0, p);
677     localName = name.substring(p + 1);
678       }
679       
680       method = funMapper.resolveFunction(prefix, localName);
681     }
682     
683     return method;
684   }
685
686   /**
687    * Scans the next token.
688    *
689    * @return token code, expressed as an Expr enumeration.
690    */

691   private int scanToken()
692     throws ELParseException
693   {
694     if (_peek >= 0) {
695       int value = _peek;
696       _peek = -1;
697       return value;
698     }
699
700     int ch = skipWhitespace(read());
701
702     switch (ch) {
703     case '+': return Expr.ADD;
704     case '-': return Expr.SUB;
705     case '*': return Expr.MUL;
706     case '/': return Expr.DIV;
707     case '%': return Expr.MOD;
708
709     case '!':
710       ch = read();
711       if (ch == '=')
712     return Expr.NE;
713       else
714         return Expr.NOT;
715       
716     case '=':
717       ch = read();
718       if (ch == '=')
719     return Expr.EQ;
720       else
721     throw error(L.l("expected '==' at '={0}'", badChar(ch)));
722       
723     case '&':
724       ch = read();
725       if (ch == '&')
726     return Expr.AND;
727       else
728     throw error(L.l("expected '&&' at '&{0}'", badChar(ch)));
729       
730     case '|':
731       ch = read();
732       if (ch == '|')
733     return Expr.OR;
734       else
735     throw error(L.l("expected '||' at '|{0}'", badChar(ch)));
736
737     case '<':
738       ch = read();
739       if (ch == '=')
740     return Expr.LE;
741       else {
742     unread();
743     return Expr.LT;
744       }
745
746     case '>':
747       ch = read();
748       if (ch == '=')
749     return Expr.GE;
750       else {
751     unread();
752     return Expr.GT;
753       }
754
755     case '[':
756       return '[';
757       
758     case ']':
759       return ']';
760       
761     case ')':
762       return ')';
763       
764     case '(':
765       return '(';
766       
767     case '.':
768       return '.';
769       
770     case ',':
771       return ',';
772       
773     case '?':
774     case ':':
775       return ch;
776
777     default:
778       if (Character.isJavaIdentifierStart((char) ch)) {
779     String JavaDoc name = readName(ch);
780
781     if (name.equals("div"))
782       return Expr.DIV;
783     else if (name.equals("mod"))
784       return Expr.MOD;
785     else if (name.equals("eq"))
786       return Expr.EQ;
787     else if (name.equals("ne"))
788       return Expr.NE;
789     else if (name.equals("lt"))
790       return Expr.LT;
791     else if (name.equals("le"))
792       return Expr.LE;
793     else if (name.equals("gt"))
794       return Expr.GT;
795     else if (name.equals("ge"))
796       return Expr.GE;
797     else if (name.equals("and"))
798       return Expr.AND;
799     else if (name.equals("or"))
800       return Expr.OR;
801     else
802       throw error(L.l("expected binary operation at `{0}'", name));
803       }
804
805       unread();
806       return -1;
807     }
808   }
809
810   private String JavaDoc readName(int ch)
811   {
812     CharBuffer cb = CharBuffer.allocate();
813
814     for (; Character.isJavaIdentifierPart((char) ch); ch = read())
815       cb.append((char) ch);
816
817     unread();
818
819     return cb.toString();
820   }
821
822   /**
823    * Skips whitespace, returning the next meaningful character.
824    */

825   private int skipWhitespace(int ch)
826     throws ELParseException
827   {
828     for (; ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; ch = read()) {
829     }
830
831     return ch;
832   }
833
834   /**
835    * Reads the next character, returning -1 on end of file.
836    */

837   private int read()
838   {
839     if (_index < _string.length())
840       return _string.charAt(_index++);
841     else {
842       _index++;
843       return -1;
844     }
845   }
846
847   /**
848    * Unread the last character.
849    */

850   private void unread()
851   {
852     _index--;
853   }
854
855   /**
856    * Returns a readable version of the character.
857    */

858   private String JavaDoc badChar(int ch)
859   {
860     if (ch < 0)
861       return L.l("end of file");
862     else if (ch == '\n')
863       return L.l("end of line");
864     else
865       return "`" + (char) ch + "'";
866   }
867
868   /**
869    * Returns an new exception.
870    */

871   private ELParseException error(String JavaDoc message)
872   {
873     return new ELParseException(message + " in " + _string);
874   }
875 }
876
Popular Tags