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: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.jms.selector;
030:
031: import com.caucho.log.Log;
032: import com.caucho.util.CharBuffer;
033: import com.caucho.util.IntMap;
034: import com.caucho.util.L10N;
035:
036: import javax.jms.InvalidSelectorException;
037: import javax.jms.JMSException;
038: import java.util.logging.Logger;
039:
040: /**
041: * Parsing the selector.
042: */
043: public class SelectorParser {
044: static final Logger log = Log.open(SelectorParser.class);
045: static final L10N L = new L10N(SelectorParser.class);
046:
047: static final int TRUE = 1;
048: static final int FALSE = TRUE + 1;
049: static final int NULL = FALSE + 1;
050:
051: static final int INTEGER = NULL + 1;
052: static final int DOUBLE = INTEGER + 1;
053: static final int LONG = DOUBLE + 1;
054: static final int STRING = LONG + 1;
055: static final int IDENTIFIER = STRING + 1;
056:
057: static final int EQ = IDENTIFIER + 1;
058: static final int NE = EQ + 1;
059: static final int LT = NE + 1;
060: static final int LE = LT + 1;
061: static final int GT = LE + 1;
062: static final int GE = GT + 1;
063:
064: static final int NOT = GE + 1;
065: static final int AND = NOT + 1;
066: static final int OR = AND + 1;
067: static final int BETWEEN = OR + 1;
068: static final int LIKE = BETWEEN + 1;
069: static final int ESCAPE = LIKE + 1;
070: static final int IN = ESCAPE + 1;
071: static final int IS = IN + 1;
072:
073: private static IntMap _reserved;
074:
075: private String _query;
076: private int _parseIndex;
077: private int _token = -1;
078: private String _lexeme;
079: private CharBuffer _cb = new CharBuffer();
080:
081: public Selector parse(String query) throws JMSException {
082: _query = query;
083: _parseIndex = 0;
084:
085: if (peekToken() == -1)
086: return null;
087:
088: Selector selector = parseExpr();
089:
090: if (!selector.isUnknown() && !selector.isBoolean())
091: throw new InvalidSelectorException(L.l(
092: "selector '{0}' must be a boolean", selector));
093:
094: return selector;
095: }
096:
097: private Selector parseExpr() throws JMSException {
098: return parseOr();
099: }
100:
101: private Selector parseOr() throws JMSException {
102: Selector left = parseAnd();
103:
104: while (true) {
105: int token = peekToken();
106:
107: switch (token) {
108: case OR:
109: scanToken();
110: left = new OrSelector(left, parseAnd());
111: break;
112:
113: default:
114: return left;
115: }
116: }
117: }
118:
119: private Selector parseAnd() throws JMSException {
120: Selector left = parseCmp();
121:
122: while (true) {
123: int token = peekToken();
124:
125: switch (token) {
126: case AND:
127: scanToken();
128: left = new BooleanBinarySelector(token, left,
129: parseCmp());
130: break;
131:
132: default:
133: return left;
134: }
135: }
136: }
137:
138: /**
139: * Parses a comparison expression.
140: *
141: * <pre>
142: * cmp-expr ::= add-expr '=' add-expr
143: * ::= add-expr
144: * </pre>
145: *
146: * @return the parsed expression
147: */
148: private Selector parseCmp() throws JMSException {
149: int token = peekToken();
150: boolean isNot = false;
151:
152: if (token == NOT) {
153: scanToken();
154: isNot = true;
155: token = peekToken();
156: }
157:
158: Selector left = parseAdd();
159:
160: token = peekToken();
161:
162: if (token == NOT) {
163: isNot = !isNot;
164: scanToken();
165: token = peekToken();
166: }
167:
168: if (token >= EQ && token <= GE) {
169: scanToken();
170:
171: left = new BooleanBinarySelector(token, left, parseAdd());
172: }
173:
174: else if (token == BETWEEN) {
175: scanToken();
176:
177: Selector low = parseAdd();
178: token = scanToken();
179: if (token != AND)
180: throw error("BETWEEN needs AND");
181: Selector high = parseAdd();
182:
183: left = new BetweenSelector(left, low, high);
184: }
185:
186: else if (token == LIKE) {
187: scanToken();
188:
189: token = scanToken();
190: if (token != STRING)
191: throw error("LIKE needs string pattern");
192:
193: String pattern = _lexeme;
194: char escape = '\\';
195:
196: if (peekToken() == ESCAPE) {
197: scanToken();
198:
199: token = scanToken();
200: if (token != STRING)
201: throw error("ESCAPE needs string pattern");
202:
203: if (_lexeme.length() > 0)
204: escape = _lexeme.charAt(0);
205: }
206:
207: left = new LikeSelector(left, pattern, escape);
208: }
209:
210: else if (token == IN) {
211: scanToken();
212:
213: InSelector inSelector = new InSelector(left);
214:
215: if (scanToken() != '(')
216: throw error("IN needs `('");
217:
218: while ((token = scanToken()) == STRING) {
219: inSelector.addValue(_lexeme);
220:
221: if (peekToken() == ',')
222: scanToken();
223: }
224:
225: if (token != ')')
226: throw error("IN needs `)'");
227: scanToken();
228:
229: left = inSelector;
230: }
231:
232: else if (token == IS) {
233: scanToken();
234:
235: if (peekToken() == NOT) {
236: isNot = !isNot;
237: scanToken();
238: }
239:
240: if ((token = scanToken()) != NULL)
241: throw error("IS needs NULL");
242:
243: left = new UnarySelector(NULL, left);
244: }
245:
246: if (isNot)
247: return new UnarySelector(NOT, left);
248: else
249: return left;
250: }
251:
252: private Selector parseAdd() throws JMSException {
253: Selector left = parseMul();
254:
255: while (true) {
256: int token = peekToken();
257:
258: switch (token) {
259: case '+':
260: case '-':
261: scanToken();
262: left = new NumericBinarySelector(token, left,
263: parseMul());
264: break;
265:
266: default:
267: return left;
268: }
269: }
270: }
271:
272: private Selector parseMul() throws JMSException {
273: Selector left = parseUnary();
274:
275: while (true) {
276: int token = peekToken();
277:
278: switch (token) {
279: case '*':
280: case '/':
281: scanToken();
282: left = new NumericBinarySelector(token, left,
283: parseUnary());
284: break;
285:
286: default:
287: return left;
288: }
289: }
290: }
291:
292: private Selector parseUnary() throws JMSException {
293: int token = peekToken();
294:
295: switch (token) {
296: case '+':
297: scanToken();
298: return new UnarySelector(token, parseUnary());
299: case '-':
300: scanToken();
301: return new UnarySelector('-', parseUnary());
302:
303: default:
304: return parseTerm(false);
305: }
306: }
307:
308: private Selector parseTerm(boolean hasSign) throws JMSException {
309: Selector value = null;
310: String prefix;
311: int token = scanToken();
312:
313: switch (token) {
314: case TRUE:
315: value = new BooleanLiteralSelector(true);
316: break;
317: case FALSE:
318: value = new BooleanLiteralSelector(false);
319: break;
320: case IDENTIFIER:
321: value = IdentifierSelector.create(_lexeme);
322: break;
323: case STRING:
324: value = new LiteralSelector(_lexeme);
325: break;
326: case INTEGER:
327: if (hasSign)
328: return new LiteralSelector(Long.decode("-" + _lexeme));
329: else
330: return new LiteralSelector(Long.decode(_lexeme));
331: case LONG:
332: if (hasSign)
333: return new LiteralSelector(Long.decode("-" + _lexeme));
334: else
335: return new LiteralSelector(Long.decode(_lexeme));
336: case DOUBLE:
337: if (hasSign)
338: return new LiteralSelector(new Double("-" + _lexeme));
339: else
340: return new LiteralSelector(new Double(_lexeme));
341:
342: case '(':
343: value = parseExpr();
344: if (scanToken() != ')')
345: throw error("expected ')'");
346: break;
347:
348: default:
349: throw error("unknown token: " + token);
350: }
351:
352: if (hasSign)
353: return new UnarySelector('-', value);
354: else
355: return value;
356: }
357:
358: /**
359: * Peeks the next token
360: *
361: * @return integer code for the token
362: */
363: private int peekToken() throws JMSException {
364: if (_token > 0)
365: return _token;
366:
367: _token = scanToken();
368:
369: return _token;
370: }
371:
372: /**
373: * Scan the next token. If the lexeme is a string, its string
374: * representation is in "lexeme".
375: *
376: * @return integer code for the token
377: */
378: private int scanToken() throws JMSException {
379: if (_token > 0) {
380: int value = _token;
381: _token = -1;
382: return value;
383: }
384:
385: int sign = 1;
386: int ch;
387:
388: for (ch = read(); Character.isWhitespace((char) ch); ch = read()) {
389: }
390:
391: switch (ch) {
392: case -1:
393: case '(':
394: case ')':
395: case '*':
396: case '/':
397: case '+':
398: case '-':
399: case ',':
400: return ch;
401:
402: case '=':
403: return EQ;
404:
405: case '<':
406: if ((ch = read()) == '=')
407: return LE;
408: else if (ch == '>')
409: return NE;
410: else {
411: unread(ch);
412: return LT;
413: }
414:
415: case '>':
416: if ((ch = read()) == '=')
417: return GE;
418: else {
419: unread(ch);
420: return GT;
421: }
422: }
423:
424: if (Character.isJavaIdentifierStart((char) ch)) {
425: _cb.clear();
426:
427: for (; ch > 0 && Character.isJavaIdentifierPart((char) ch); ch = read())
428: _cb.append((char) ch);
429:
430: unread(ch);
431:
432: _lexeme = _cb.toString();
433: String lower = _lexeme.toLowerCase();
434:
435: int token = _reserved.get(lower);
436:
437: if (token > 0)
438: return token;
439: else
440: return IDENTIFIER;
441: } else if (ch >= '0' && ch <= '9' || ch == '.') {
442: _cb.clear();
443:
444: int type = INTEGER;
445:
446: if (sign < 0)
447: _cb.append('-');
448:
449: for (; '0' <= ch && ch <= '9'; ch = read())
450: _cb.append((char) ch);
451:
452: if ((ch == 'x' || ch == 'X') && _cb.length() == 1
453: && _cb.charAt(0) == '0') {
454:
455: _cb.append('x');
456: for (ch = read(); '0' <= ch && ch <= '9' || 'a' <= ch
457: && ch <= 'f' || 'A' <= ch && ch <= 'F'; ch = read()) {
458: _cb.append((char) ch);
459: }
460:
461: _lexeme = _cb.toString();
462:
463: if (ch == 'l' || ch == 'L')
464: return LONG;
465: else {
466: unread(ch);
467: return INTEGER;
468: }
469: }
470:
471: if (ch == '.') {
472: type = DOUBLE;
473:
474: _cb.append('.');
475: for (ch = read(); ch >= '0' && ch <= '9'; ch = read())
476: _cb.append((char) ch);
477: }
478:
479: if (ch == 'e' || ch == 'E') {
480: type = DOUBLE;
481:
482: _cb.append('e');
483: if ((ch = read()) == '+' || ch == '-') {
484: _cb.append((char) ch);
485: ch = read();
486: }
487:
488: if (!(ch >= '0' && ch <= '9'))
489: throw error(L.l("exponent needs digits at {0}",
490: charName(ch)));
491:
492: for (; ch >= '0' && ch <= '9'; ch = read())
493: _cb.append((char) ch);
494: }
495:
496: if (ch == 'F' || ch == 'D' || ch == 'f' || ch == 'd')
497: type = DOUBLE;
498: else if (ch == 'L' || ch == 'l') {
499: type = LONG;
500: } else
501: unread(ch);
502:
503: _lexeme = _cb.toString();
504:
505: return type;
506: }
507: // else if (ch == '\'' || ch == '\"') {
508: else if (ch == '\'') {
509: int end = ch;
510: _cb.clear();
511:
512: for (ch = read(); ch >= 0; ch = read()) {
513: if (ch == end) {
514: int ch1;
515: if ((ch1 = read()) == end)
516: _cb.append((char) end);
517: else {
518: unread(ch1);
519: break;
520: }
521: } else
522: _cb.append((char) ch);
523: }
524:
525: if (ch < 0)
526: throw error(L.l("unexpected end of selector"));
527:
528: _lexeme = _cb.toString();
529:
530: return STRING;
531: }
532:
533: throw error(L.l("unexpected char at {0}", "" + (char) ch));
534: }
535:
536: /**
537: * Returns the next character.
538: */
539: private int read() {
540: if (_parseIndex < _query.length())
541: return _query.charAt(_parseIndex++);
542: else
543: return -1;
544: }
545:
546: /**
547: * Unread the last character.
548: */
549: private void unread(int ch) {
550: if (ch >= 0)
551: _parseIndex--;
552: }
553:
554: /**
555: * Creates an error.
556: */
557: public JMSException error(String msg) {
558: msg += "\nin \"" + _query + "\"";
559:
560: return new InvalidSelectorException(msg);
561: }
562:
563: /**
564: * Returns the name for a character
565: */
566: private String charName(int ch) {
567: if (ch < 0)
568: return L.l("end of query");
569: else
570: return String.valueOf((char) ch);
571: }
572:
573: static {
574: _reserved = new IntMap();
575: _reserved.put("true", TRUE);
576: _reserved.put("false", FALSE);
577: _reserved.put("and", AND);
578: _reserved.put("or", OR);
579: _reserved.put("not", NOT);
580: _reserved.put("null", NULL);
581: _reserved.put("is", IS);
582: _reserved.put("in", IN);
583: _reserved.put("like", LIKE);
584: _reserved.put("escape", ESCAPE);
585: _reserved.put("between", BETWEEN);
586: }
587: }
|