001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.el;
031:
032: import com.caucho.util.CharBuffer;
033: import com.caucho.util.L10N;
034:
035: import javax.el.ELContext;
036: import javax.el.FunctionMapper;
037: import javax.el.ValueExpression;
038: import javax.el.VariableMapper;
039: import java.lang.reflect.Method;
040: import java.util.ArrayList;
041: import java.util.logging.Logger;
042: import java.util.logging.Level;
043:
044: /**
045: * Parses the expression.
046: */
047: public class ELParser {
048: private static final Logger log = Logger.getLogger(ELParser.class
049: .getName());
050: private static final L10N L = new L10N(ELParser.class);
051:
052: // The expression string
053: private String _string;
054: // Current parse index into the string
055: private int _index;
056: // The peek token
057: private int _peek = -1;
058: // The current lexeme
059: private String _lexeme;
060: // Temporary buffer
061: private CharBuffer _cb = new CharBuffer();
062:
063: protected final ELContext _elContext;
064:
065: private boolean _checkEscape = true;
066:
067: public ELParser(ELContext elContext, String string) {
068: if (elContext == null)
069: throw new NullPointerException();
070:
071: _elContext = elContext;
072: _string = string;
073: }
074:
075: protected ELParser create(String string) {
076: ELParser parser = new ELParser(_elContext, string);
077:
078: copyTo(parser);
079:
080: return parser;
081: }
082:
083: /**
084: * Copy to the dest parser.
085: */
086: protected void copyTo(ELParser parser) {
087: }
088:
089: /**
090: * Set true if escapes are checked.
091: */
092: public void setCheckEscape(boolean checkEscape) {
093: _checkEscape = checkEscape;
094: }
095:
096: /**
097: * Parses the expression string.
098: */
099: public Expr parse() throws ELParseException {
100: return parseInterpolate();
101: }
102:
103: /**
104: * Parses interpolated code.
105: */
106: public Expr parseInterpolate() throws ELParseException {
107: CharBuffer text = CharBuffer.allocate();
108: CharBuffer exprString = CharBuffer.allocate();
109: Expr expr = null;
110: int ch;
111:
112: while ((ch = read()) >= 0) {
113: if (_checkEscape && ch == '\\') {
114: ch = read();
115:
116: if (ch == '$' || ch == '#' || ch == '\\')
117: text.append((char) ch);
118: else {
119: text.append('\\');
120: unread();
121: }
122: } else if (ch == '$' || ch == '#') {
123: int origChar = ch;
124:
125: ch = read();
126:
127: if (ch == '{') {
128: if (text.length() > 0) {
129: StringLiteral right = new StringLiteral(text
130: .toString());
131:
132: if (expr == null)
133: expr = right;
134: else
135: expr = new InterpolateExpr(expr, right);
136:
137: text.clear();
138: }
139:
140: exprString.clear();
141:
142: for (ch = read(); ch > 0 && ch != '}'; ch = read()) {
143: exprString.append((char) ch);
144:
145: if (ch == '\'' || ch == '"') {
146: int end = ch;
147:
148: for (ch = read(); ch > 0 && ch != end; ch = read()) {
149: exprString.append((char) ch);
150:
151: if (ch == '\\') {
152: ch = read();
153: if (ch > 0)
154: exprString.append((char) ch);
155: }
156: }
157:
158: if (ch > 0)
159: exprString.append((char) ch);
160: }
161: }
162:
163: if (ch != '}')
164: throw error(L.l(
165: "expected '}' at end of EL expression",
166: exprString));
167:
168: Expr right = create(exprString.toString())
169: .parseExpr();
170:
171: if (expr == null)
172: expr = right;
173: else
174: expr = new InterpolateExpr(expr, right);
175: } else {
176: text.append((char) origChar);
177: unread();
178: }
179: } else
180: text.append((char) ch);
181: }
182:
183: if (text.length() > 0) {
184: StringLiteral right = new StringLiteral(text.toString());
185:
186: if (expr == null)
187: expr = right;
188: else
189: expr = new InterpolateExpr(expr, right);
190: }
191:
192: if (expr == null)
193: expr = new StringLiteral("");
194:
195: return expr;
196: }
197:
198: /**
199: * expr ::= term
200: */
201: private Expr parseExpr() throws ELParseException {
202: Expr left = parseTerm();
203:
204: while (true) {
205: int token = scanToken();
206:
207: switch (token) {
208: case '?': {
209: Expr trueExpr = parseExpr();
210: token = scanToken();
211: if (token != ':')
212: throw error(L
213: .l(
214: "Expected ':' at {0}. Conditional syntax is 'expr ? expr : expr'.",
215: badChar(token)));
216: Expr falseExpr = parseExpr();
217:
218: left = new ConditionalExpr(left, trueExpr, falseExpr);
219: }
220: break;
221:
222: case Expr.OR:
223: left = parseOrExpr(token, left, parseTerm());
224: break;
225:
226: case Expr.AND:
227: left = parseAndExpr(token, left, parseTerm());
228: break;
229:
230: case Expr.EQ:
231: case Expr.NE:
232: case Expr.LT:
233: case Expr.LE:
234: case Expr.GT:
235: case Expr.GE:
236: left = parseCmpExpr(token, left, parseTerm());
237: break;
238:
239: case Expr.ADD:
240: case Expr.SUB:
241: left = parseAddExpr(token, left, parseTerm());
242: break;
243:
244: case Expr.MUL:
245: case Expr.DIV:
246: 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: while (true) {
264: int token = scanToken();
265: switch (token) {
266: case Expr.OR:
267: left = new BooleanExpr(code, left, right);
268: code = token;
269: right = parseTerm();
270: break;
271:
272: case Expr.AND:
273: right = parseAndExpr(token, right, parseTerm());
274: break;
275:
276: case Expr.EQ:
277: case Expr.NE:
278: case Expr.LT:
279: case Expr.GT:
280: case Expr.LE:
281: case Expr.GE:
282: right = parseCmpExpr(token, right, parseTerm());
283: break;
284:
285: case Expr.ADD:
286: case Expr.SUB:
287: right = parseAddExpr(token, right, parseTerm());
288: break;
289:
290: case Expr.MUL:
291: case Expr.DIV:
292: case Expr.MOD:
293: right = parseMulExpr(token, right, parseTerm());
294: break;
295:
296: default:
297: _peek = token;
298: return new BooleanExpr(code, left, right);
299: }
300: }
301: }
302:
303: /**
304: * and-expr ::= and-expr 'and' expr
305: * ::= cmp-expr
306: */
307: private Expr parseAndExpr(int code, Expr left, Expr right)
308: throws ELParseException {
309: while (true) {
310: int token = scanToken();
311: switch (token) {
312: case Expr.AND:
313: left = new BooleanExpr(code, left, right);
314: code = token;
315: right = parseTerm();
316: break;
317:
318: case Expr.EQ:
319: case Expr.NE:
320: case Expr.LT:
321: case Expr.GT:
322: case Expr.LE:
323: case Expr.GE:
324: right = parseCmpExpr(token, right, parseTerm());
325: break;
326:
327: case Expr.ADD:
328: case Expr.SUB:
329: right = parseAddExpr(token, right, parseTerm());
330: break;
331:
332: case Expr.MUL:
333: case Expr.DIV:
334: case Expr.MOD:
335: right = parseMulExpr(token, right, parseTerm());
336: break;
337:
338: default:
339: _peek = token;
340: return new BooleanExpr(code, left, right);
341: }
342: }
343: }
344:
345: /**
346: * cmp-expr ::= cmp-expr '=' expr
347: * ::= add-expr
348: */
349: private Expr parseCmpExpr(int code, Expr left, Expr right)
350: throws ELParseException {
351: while (true) {
352: int token = scanToken();
353: switch (token) {
354: case Expr.EQ:
355: case Expr.NE:
356: case Expr.GT:
357: case Expr.LT:
358: case Expr.LE:
359: case Expr.GE:
360: left = CmpExpr.create(code, left, right);
361:
362: code = token;
363: right = parseTerm();
364: break;
365:
366: case Expr.ADD:
367: case Expr.SUB:
368: right = parseAddExpr(token, right, parseTerm());
369: break;
370:
371: case Expr.MUL:
372: case Expr.DIV:
373: case Expr.MOD:
374: right = parseMulExpr(token, right, parseTerm());
375: break;
376:
377: default:
378: _peek = token;
379: return CmpExpr.create(code, left, right);
380: }
381: }
382: }
383:
384: /**
385: * add-expr ::= add-expr '+' expr
386: * ::= mul-expr
387: */
388: private Expr parseAddExpr(int code, Expr left, Expr right)
389: throws ELParseException {
390: while (true) {
391: int token = scanToken();
392: switch (token) {
393: case Expr.ADD:
394: case Expr.SUB:
395: left = BinaryExpr.create(code, left, right);
396: code = token;
397: right = parseTerm();
398: break;
399:
400: case Expr.MUL:
401: case Expr.DIV:
402: case Expr.MOD:
403: right = parseMulExpr(token, right, parseTerm());
404: break;
405:
406: default:
407: _peek = token;
408: return BinaryExpr.create(code, left, right);
409: }
410: }
411: }
412:
413: /**
414: * mul-expr ::= mul-expr '*' expr
415: * ::= expr
416: */
417: private Expr parseMulExpr(int code, Expr left, Expr right)
418: throws ELParseException {
419: while (true) {
420: int token = scanToken();
421: switch (token) {
422: case Expr.MUL:
423: case Expr.DIV:
424: case Expr.MOD:
425: left = BinaryExpr.create(code, left, right);
426: right = parseTerm();
427: code = token;
428: break;
429:
430: default:
431: _peek = token;
432: return BinaryExpr.create(code, left, right);
433: }
434: }
435: }
436:
437: /**
438: * term ::= simple-term
439: * ::= term '[' expr ']'
440: * ::= term . identifier
441: */
442: private Expr parseTerm() throws ELParseException {
443: Expr term = parseSimpleTerm();
444:
445: while (true) {
446: int token = scanToken();
447:
448: switch (token) {
449: case '[': {
450: Expr expr = parseExpr();
451: token = scanToken();
452: if (token != ']')
453: throw error(L
454: .l(
455: "Expected `]' at {0}. All open array braces must have matching closing brace.",
456: badChar(token)));
457:
458: term = term.createField(expr);
459: break;
460: }
461:
462: case '(': {
463: ArrayList<Expr> argList = new ArrayList<Expr>();
464:
465: int ch = skipWhitespace(read());
466:
467: while (ch > 0 && ch != ')') {
468: unread();
469: argList.add(parseExpr());
470:
471: token = scanToken();
472:
473: if (token != ',') {
474: ch = token;
475: break;
476: }
477:
478: ch = skipWhitespace(read());
479: }
480:
481: if (ch != ')')
482: throw error(L
483: .l(
484: "Expected `)' at {0}. All functions must have matching closing parenthesis.",
485: badChar(ch)));
486:
487: // token = scanToken();
488:
489: Expr[] args = (Expr[]) argList.toArray(new Expr[argList
490: .size()]);
491:
492: Expr expr = term.createMethod(args);
493: if (expr == null)
494: throw error(L
495: .l(
496: "Method call not supported in this context `{0}'.",
497: term));
498: term = expr;
499: break;
500: }
501:
502: case '.': {
503: int ch = skipWhitespace(read());
504:
505: if (!Character.isJavaIdentifierStart((char) ch))
506: throw error(L
507: .l(
508: "Expected `]' at {0}. Field references must be identifiers.",
509: badChar(ch)));
510:
511: String field = readName(ch);
512:
513: term = term.createField(field);
514: break;
515: }
516:
517: default:
518: _peek = token;
519: return term;
520: }
521: }
522: }
523:
524: /**
525: * simple-term ::= number
526: * ::= '(' expr ')'
527: * ::= variable
528: * ::= '"' string '"'
529: * ::= true | false | null
530: */
531: private Expr parseSimpleTerm() throws ELParseException {
532: int ch = read();
533:
534: ch = skipWhitespace(ch);
535:
536: switch (ch) {
537: case '.':
538: case '0':
539: case '1':
540: case '2':
541: case '3':
542: case '4':
543: case '5':
544: case '6':
545: case '7':
546: case '8':
547: case '9': {
548: long value = 0;
549: double exp = 1;
550: int digits = 0;
551:
552: for (; ch >= '0' && ch <= '9'; ch = read())
553: value = 10 * value + ch - '0';
554:
555: if (ch != '.' && ch != 'e' && ch != 'E') {
556: unread();
557: return new LongLiteral(value);
558: }
559:
560: if (ch == '.') {
561: for (ch = read(); ch >= '0' && ch <= '9'; ch = read()) {
562: value = 10 * value + ch - '0';
563: exp *= 10;
564: digits--;
565: }
566: }
567:
568: if (ch == 'e' || ch == 'E') {
569: int sign = 1;
570: int expValue = 0;
571:
572: ch = read();
573: if (ch == '-') {
574: sign = -1;
575: ch = read();
576: } else if (ch == '+')
577: ch = read();
578:
579: for (; ch >= '0' && ch <= '9'; ch = read())
580: expValue = 10 * expValue + ch - '0';
581:
582: exp = Math.pow(10, digits + sign * expValue);
583:
584: unread();
585:
586: return new DoubleLiteral((double) value * (double) exp);
587: }
588:
589: unread();
590: return new DoubleLiteral((double) value / (double) exp);
591: }
592:
593: case '-':
594: return new MinusExpr(parseTerm());
595:
596: case '!':
597: return UnaryExpr.create(Expr.NOT, parseTerm());
598:
599: case '+':
600: return parseTerm();
601:
602: case '(': {
603: Expr expr = parseExpr();
604: if ((ch = scanToken()) != ')')
605: throw error(L
606: .l(
607: "Expected `)' at {0}. All open parentheses must have matching closing parentheses.",
608: badChar(ch)));
609:
610: return expr;
611: }
612:
613: case '\'':
614: case '"': {
615: int end = ch;
616: CharBuffer cb = new CharBuffer();
617: for (ch = read(); ch >= 0; ch = read()) {
618: if (ch == '\\')
619: cb.append((char) read());
620: else if (ch != end)
621: cb.append((char) ch);
622: else if ((ch = read()) == end)
623: cb.append((char) ch);
624: else {
625: unread();
626: break;
627: }
628: }
629:
630: return new StringLiteral(cb.toString());
631: }
632:
633: default:
634: if (!Character.isJavaIdentifierStart((char) ch)
635: && ch != ':')
636: throw error(L.l("Unexpected character at {0}.",
637: badChar(ch)));
638:
639: CharBuffer cb = CharBuffer.allocate();
640: for (; Character.isJavaIdentifierPart((char) ch)
641: || ch == ':'; ch = read())
642: cb.append((char) ch);
643:
644: unread();
645:
646: String name = cb.close();
647:
648: if (name.equals("null"))
649: return new NullLiteral();
650: else if (name.equals("true"))
651: return new BooleanLiteral(true);
652: else if (name.equals("false"))
653: return new BooleanLiteral(false);
654: else if (name.equals("not"))
655: return UnaryExpr.create(Expr.NOT, parseTerm());
656: else if (name.equals("empty"))
657: return UnaryExpr.create(Expr.EMPTY, parseTerm());
658: else {
659: VariableMapper varMapper = _elContext
660: .getVariableMapper();
661:
662: ValueExpression valueExpr = null;
663:
664: if (varMapper != null)
665: valueExpr = varMapper.resolveVariable(name);
666:
667: if (valueExpr != null)
668: return new ValueExpr(name, valueExpr);
669:
670: Expr expr = createImplicitObjectExpr(name);
671:
672: if (expr != null)
673: return expr;
674:
675: try {
676: Method method = getStaticMethod(name);
677:
678: if (method != null)
679: return new StaticMethodExpr(method);
680: else
681: return new IdExpr(name);
682: } catch (Exception e) {
683: log.log(Level.FINEST, e.toString(), e);
684:
685: return new IdExpr(name);
686: }
687: }
688: }
689: }
690:
691: /**
692: * Creates the implicit object for the name.
693: */
694: protected Expr createImplicitObjectExpr(String name) {
695: return null;
696: }
697:
698: /**
699: * Creates the implicit object for the name.
700: */
701: protected Method getStaticMethod(String name)
702: throws ELParseException {
703: Method method = null;
704:
705: FunctionMapper funMapper = _elContext.getFunctionMapper();
706:
707: if (funMapper != null) {
708: String prefix = "";
709: String localName = name;
710:
711: int p = name.indexOf(':');
712: if (p > 0) {
713: prefix = name.substring(0, p);
714: localName = name.substring(p + 1);
715: }
716:
717: method = funMapper.resolveFunction(prefix, localName);
718: }
719:
720: return method;
721: }
722:
723: /**
724: * Scans the next token.
725: *
726: * @return token code, expressed as an Expr enumeration.
727: */
728: private int scanToken() throws ELParseException {
729: if (_peek >= 0) {
730: int value = _peek;
731: _peek = -1;
732: return value;
733: }
734:
735: int ch = skipWhitespace(read());
736:
737: switch (ch) {
738: case '+':
739: return Expr.ADD;
740: case '-':
741: return Expr.SUB;
742: case '*':
743: return Expr.MUL;
744: case '/':
745: return Expr.DIV;
746: case '%':
747: return Expr.MOD;
748:
749: case '!':
750: ch = read();
751: if (ch == '=')
752: return Expr.NE;
753: else
754: return Expr.NOT;
755:
756: case '=':
757: ch = read();
758: if (ch == '=')
759: return Expr.EQ;
760: else
761: throw error(L.l("expected '==' at '={0}'", badChar(ch)));
762:
763: case '&':
764: ch = read();
765: if (ch == '&')
766: return Expr.AND;
767: else
768: throw error(L.l("expected '&&' at '&{0}'", badChar(ch)));
769:
770: case '|':
771: ch = read();
772: if (ch == '|')
773: return Expr.OR;
774: else
775: throw error(L.l("expected '||' at '|{0}'", badChar(ch)));
776:
777: case '<':
778: ch = read();
779: if (ch == '=')
780: return Expr.LE;
781: else {
782: unread();
783: return Expr.LT;
784: }
785:
786: case '>':
787: ch = read();
788: if (ch == '=')
789: return Expr.GE;
790: else {
791: unread();
792: return Expr.GT;
793: }
794:
795: case '[':
796: return '[';
797:
798: case ']':
799: return ']';
800:
801: case ')':
802: return ')';
803:
804: case '(':
805: return '(';
806:
807: case '.':
808: return '.';
809:
810: case ',':
811: return ',';
812:
813: case '?':
814: case ':':
815: return ch;
816:
817: default:
818: if (Character.isJavaIdentifierStart((char) ch)) {
819: String name = readName(ch);
820:
821: if (name.equals("div"))
822: return Expr.DIV;
823: else if (name.equals("mod"))
824: return Expr.MOD;
825: else if (name.equals("eq"))
826: return Expr.EQ;
827: else if (name.equals("ne"))
828: return Expr.NE;
829: else if (name.equals("lt"))
830: return Expr.LT;
831: else if (name.equals("le"))
832: return Expr.LE;
833: else if (name.equals("gt"))
834: return Expr.GT;
835: else if (name.equals("ge"))
836: return Expr.GE;
837: else if (name.equals("and"))
838: return Expr.AND;
839: else if (name.equals("or"))
840: return Expr.OR;
841: else
842: throw error(L.l(
843: "expected binary operation at `{0}'", name));
844: }
845:
846: unread();
847: return -1;
848: }
849: }
850:
851: private String readName(int ch) {
852: CharBuffer cb = CharBuffer.allocate();
853:
854: for (; Character.isJavaIdentifierPart((char) ch); ch = read())
855: cb.append((char) ch);
856:
857: unread();
858:
859: return cb.toString();
860: }
861:
862: /**
863: * Skips whitespace, returning the next meaningful character.
864: */
865: private int skipWhitespace(int ch) throws ELParseException {
866: for (; ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; ch = read()) {
867: }
868:
869: return ch;
870: }
871:
872: /**
873: * Reads the next character, returning -1 on end of file.
874: */
875: private int read() {
876: if (_index < _string.length())
877: return _string.charAt(_index++);
878: else {
879: _index++;
880: return -1;
881: }
882: }
883:
884: /**
885: * Unread the last character.
886: */
887: private void unread() {
888: _index--;
889: }
890:
891: /**
892: * Returns a readable version of the character.
893: */
894: private String badChar(int ch) {
895: if (ch < 0)
896: return L.l("end of file");
897: else if (ch == '\n')
898: return L.l("end of line");
899: else
900: return "`" + (char) ch + "'";
901: }
902:
903: /**
904: * Returns an new exception.
905: */
906: private ELParseException error(String message) {
907: return new ELParseException(message + " in " + _string);
908: }
909: }
|