KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > amber > query > QueryParser


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.amber.query;
31
32 import com.caucho.amber.AmberException;
33 import com.caucho.amber.entity.AmberEntityHome;
34 import com.caucho.amber.expr.*;
35 import com.caucho.amber.expr.fun.*;
36 import com.caucho.amber.manager.AmberPersistenceUnit;
37 import com.caucho.amber.table.ForeignColumn;
38 import com.caucho.amber.table.LinkColumns;
39 import com.caucho.amber.table.Table;
40 import com.caucho.amber.type.EntityType;
41 import com.caucho.log.Log;
42 import com.caucho.util.CharBuffer;
43 import com.caucho.util.IntMap;
44 import com.caucho.util.L10N;
45
46 import javax.sql.DataSource JavaDoc;
47 import java.sql.Connection JavaDoc;
48 import java.sql.ResultSet JavaDoc;
49 import java.sql.SQLException JavaDoc;
50 import java.sql.Statement JavaDoc;
51 import java.util.ArrayList JavaDoc;
52 import java.util.HashMap JavaDoc;
53 import java.util.Map JavaDoc;
54 import java.util.logging.Level JavaDoc;
55 import java.util.logging.Logger JavaDoc;
56
57 /**
58  * Contains the parser for EJB 3.0 style queries and stores
59  * the parsed expressions.
60  */

61 public class QueryParser {
62   static final Logger JavaDoc log = Log.open(QueryParser.class);
63   static final L10N L = new L10N(QueryParser.class);
64
65   public final static int IDENTIFIER = 128;
66   public final static int INTEGER = IDENTIFIER + 1;
67   public final static int LONG = INTEGER + 1;
68   public final static int DOUBLE = LONG + 1;
69   public final static int STRING = DOUBLE + 1;
70   public final static int TRUE = STRING + 1;
71   public final static int FALSE = TRUE + 1;
72   public final static int UNKNOWN = FALSE + 1;
73   public final static int MEMBER = UNKNOWN + 1;
74   public final static int OF = MEMBER + 1;
75   public final static int EMPTY = OF + 1;
76   public final static int NULL = EMPTY + 1;
77
78   public final static int FROM = NULL + 1;
79   public final static int IN = FROM + 1;
80   public final static int SELECT = IN + 1;
81   public final static int UPDATE = SELECT + 1;
82   public final static int DELETE = UPDATE + 1;
83   public final static int DISTINCT = DELETE + 1;
84   public final static int WHERE = DISTINCT + 1;
85   public final static int AS = WHERE + 1;
86   public final static int SET = AS + 1;
87   public final static int ORDER = SET + 1;
88   public final static int GROUP = ORDER + 1;
89   public final static int BY = GROUP + 1;
90   public final static int ASC = BY + 1;
91   public final static int DESC = ASC + 1;
92   public final static int LIMIT = DESC + 1;
93   public final static int OFFSET = LIMIT + 1;
94
95   public final static int JOIN = OFFSET + 1;
96   public final static int INNER = JOIN + 1;
97   public final static int LEFT = INNER + 1;
98   public final static int OUTER = LEFT + 1;
99   public final static int FETCH = OUTER + 1;
100
101   public final static int BETWEEN = FETCH + 1;
102   public final static int LIKE = BETWEEN + 1;
103   public final static int ESCAPE = LIKE + 1;
104   public final static int IS = ESCAPE + 1;
105
106   public final static int CONCAT_OP = IS + 1;
107
108   public final static int EQ = CONCAT_OP + 1;
109   public final static int NE = EQ + 1;
110   public final static int LT = NE + 1;
111   public final static int LE = LT + 1;
112   public final static int GT = LE + 1;
113   public final static int GE = GT + 1;
114
115   public final static int AND = GE + 1;
116   public final static int OR = AND + 1;
117   public final static int NOT = OR + 1;
118
119   public final static int LENGTH = NOT + 1;
120   public final static int LOCATE = LENGTH + 1;
121
122   public final static int ABS = LOCATE + 1;
123   public final static int SQRT = ABS + 1;
124   public final static int MOD = SQRT + 1;
125   public final static int SIZE = MOD + 1;
126
127   public final static int MAX = SIZE + 1;
128   public final static int MIN = MAX + 1;
129   public final static int SUM = MIN + 1;
130
131   public final static int CONCAT = SUM + 1;
132   public final static int LOWER = CONCAT + 1;
133   public final static int UPPER = LOWER + 1;
134   public final static int SUBSTRING = UPPER + 1;
135   public final static int TRIM = SUBSTRING + 1;
136
137   public final static int BOTH = TRIM + 1;
138   public final static int LEADING = BOTH + 1;
139   public final static int TRAILING = LEADING + 1;
140
141   public final static int CURRENT_DATE = TRAILING + 1;
142   public final static int CURRENT_TIME = CURRENT_DATE + 1;
143   public final static int CURRENT_TIMESTAMP = CURRENT_TIME + 1;
144
145   public final static int EXTERNAL_DOT = CURRENT_TIMESTAMP + 1;
146
147   public final static int ARG = EXTERNAL_DOT + 1;
148   public final static int NAMED_ARG = ARG + 1;
149
150   public final static int NEW = NAMED_ARG + 1;
151
152   public final static int THIS = NEW + 1;
153
154   public final static int NOT_NULL = THIS + 1;
155
156   public final static int HAVING = NOT_NULL + 1;
157
158   private static IntMap _reserved;
159
160   private AmberPersistenceUnit _amberPersistenceUnit;
161
162   // The query
163
private String JavaDoc _sql;
164
165   /*
166   // list of the relation links
167   private ArrayList<LinkItem> _linkList;
168   // select expression
169   private Expr _selectExpr;
170   // is distinct (set)
171   private boolean _isDistinct;
172   */

173
174   // True if entities should be lazily loaded
175
private boolean _isLazyResult;
176
177   // The select query
178
private AbstractQuery _query;
179
180   // list of relations
181
private HashMap JavaDoc<PathExpr,PathExpr> _pathMap
182     = new HashMap JavaDoc<PathExpr,PathExpr>();
183
184   // parse index
185
private int _parseIndex;
186   // current token
187
private int _token;
188   // unique
189
private int _unique;
190   // parameter count
191
private int _parameterCount;
192   // temp for parsing
193
private String JavaDoc _lexeme;
194
195   private ArrayList JavaDoc<ArgExpr> _argList = new ArrayList JavaDoc<ArgExpr>();
196
197   private HashMap JavaDoc<AmberExpr, String JavaDoc> _joinFetchMap;
198
199   ArrayList JavaDoc<AmberExpr> _groupList = null;
200
201   private int _sqlArgCount;
202
203   private FromItem.JoinSemantics _joinSemantics
204     = FromItem.JoinSemantics.UNKNOWN;
205
206   private boolean _isJoinFetch = false;
207
208   // Parsing control variable, jpa/0tp4 (TRIM FROM)
209
// SELECT .._depth=0.. TRIM(.._depth=1.. 'a' FROM o.d1) .._depth=0 FROM ...
210
private int _depth = 0;
211
212   private boolean _parsingResult;
213   private boolean _parsingFrom;
214   private boolean _parsingHaving;
215
216   // jpa/119l: WHERE SIZE(xxx) > 0 => GROUP BY ... HAVING COUNT(xxx) > 0
217
private boolean _isSizeFunExpr;
218   private AmberExpr _havingExpr;
219   // jpa/1199
220
ArrayList JavaDoc<AmberExpr> _appendResultList = null;
221
222   private boolean _isDerbyDBMS;
223   private boolean _isPostgresDBMS;
224
225   /**
226    * Creates the query parser.
227    */

228   public QueryParser(String JavaDoc query)
229   {
230     _sql = query;
231   }
232
233   /**
234    * Returns true for Derby-like DBMS.
235    */

236   public boolean isDerbyDBMS()
237   {
238     return _isDerbyDBMS;
239   }
240
241   /**
242    * Returns true for Postgres-like DBMS.
243    */

244   public boolean isPostgresDBMS()
245   {
246     return _isPostgresDBMS;
247   }
248
249   /**
250    * Sets the entity home manager.
251    */

252   public void setAmberManager(AmberPersistenceUnit amberPersistenceUnit)
253   {
254     _amberPersistenceUnit = amberPersistenceUnit;
255
256     _isDerbyDBMS = false;
257     _isPostgresDBMS = false;
258
259     if (amberPersistenceUnit == null)
260       return;
261
262     try {
263       Connection JavaDoc conn = null;
264
265       try {
266         DataSource JavaDoc ds = amberPersistenceUnit.getDataSource();
267         conn = ds.getConnection();
268
269         Statement JavaDoc stmt = conn.createStatement();
270
271         try {
272           String JavaDoc sql = "select position('a' in 'abc')";
273
274           ResultSet JavaDoc rs = stmt.executeQuery(sql);
275           rs.close();
276
277         } catch (SQLException JavaDoc e) {
278           _isDerbyDBMS = true;
279           log.log(Level.FINER, e.toString(), e);
280         }
281
282         try {
283           String JavaDoc sql = "select false";
284
285           ResultSet JavaDoc rs = stmt.executeQuery(sql);
286           rs.close();
287
288           _isPostgresDBMS = true;
289
290         } catch (SQLException JavaDoc e) {
291           log.log(Level.FINER, e.toString(), e);
292         }
293       } catch (Exception JavaDoc e) {
294         log.log(Level.WARNING, e.toString(), e);
295       } finally {
296         if (conn != null)
297           conn.close();
298       }
299     } catch (Exception JavaDoc e) {
300       log.log(Level.WARNING, e.toString(), e);
301     }
302   }
303
304   /**
305    * Sets true for lazy loading.
306    */

307   public void setLazyResult(boolean isLazy)
308   {
309     _isLazyResult = isLazy;
310   }
311
312   /**
313    * Returns the query string
314    */

315   public String JavaDoc getQuery()
316   {
317     return _sql;
318   }
319
320   /**
321    * Returns the query string
322    */

323   public AbstractQuery getSelectQuery()
324   {
325     return _query;
326   }
327
328   /**
329    * Initialize the parse.
330    */

331   private void init()
332   {
333     _parseIndex = 0;
334     _unique = 0;
335     _token = -1;
336     _depth = 0;
337     _parsingResult = false;
338     _parsingFrom = false;
339     _parsingHaving = false;
340     _havingExpr = null;
341     _appendResultList = null;
342     _groupList = null;
343     _joinFetchMap = new HashMap JavaDoc<AmberExpr, String JavaDoc>();
344   }
345
346   /**
347    * Generates a new arg.
348    */

349   public int generateSQLArg()
350   {
351     return _sqlArgCount++;
352   }
353
354   /**
355    * Parses the query.
356    */

357   public AbstractQuery parse()
358     throws AmberException
359   {
360     /*
361       _query = query;
362       _fromList = new ArrayList<FromItem>();
363       _pathMap = new HashMap<Expr,Expr>();
364       _idMap = new HashMap<String,PathExpr>();
365       _argList = new ArrayList<Expr>();
366       _linkList = new ArrayList<LinkItem>();
367     */

368
369     init();
370
371     int token = scanToken();
372     if (token == UPDATE)
373       return parseUpdate();
374     else if (token == DELETE)
375       return parseDelete();
376
377     _token = token;
378     return parseSelect(false);
379   }
380
381   private SelectQuery parseSelect(boolean innerSelect)
382     throws QueryParseException
383   {
384     int oldParseIndex = _parseIndex;
385     int oldToken = _token;
386     FromItem.JoinSemantics oldJoinSemantics = _joinSemantics;
387     boolean oldIsJoinFetch = _isJoinFetch;
388     AbstractQuery oldQuery = _query;
389     int oldDepth = _depth;
390     AmberExpr oldHavingExpr = _havingExpr;
391     ArrayList JavaDoc oldAppendResultList = _appendResultList;
392
393     // Reset depth: subselect
394
_depth = 0;
395
396     _havingExpr = null;
397     _appendResultList = null;
398
399     SelectQuery query = new SelectQuery(_sql);
400     query.setParentQuery(_query);
401     _query = query;
402
403     int token;
404     while ((token = scanToken()) >= 0 &&
405            ((token != FROM) || (_depth > 0))) {
406     }
407
408     // "SELECT CURRENT_DATE" does NOT have a FROM clause.
409
boolean hasFrom = (token == FROM);
410
411     _token = token;
412
413     if (hasFrom) {
414
415       _parsingFrom = true;
416
417       do {
418
419         scanToken();
420
421         _isJoinFetch = false;
422
423         if (token == JOIN) {
424           if ((token = peekToken()) == FETCH) {
425             scanToken();
426             _isJoinFetch = true;
427           }
428         }
429
430         FromItem from = parseFrom();
431
432         token = peekToken();
433
434         _joinSemantics = FromItem.JoinSemantics.UNKNOWN;
435
436         if (token == INNER) {
437           scanToken();
438
439           token = peekToken();
440
441           if (token != JOIN) {
442             throw error(L.l("expected JOIN at {0}", tokenName(token)));
443           }
444
445           _joinSemantics = FromItem.JoinSemantics.INNER;
446         }
447         else if (token == LEFT) {
448           scanToken();
449
450           token = peekToken();
451
452           if (token == OUTER) {
453             scanToken();
454
455             token = peekToken();
456           }
457
458           if (token != JOIN)
459             throw error(L.l("expected JOIN at {0}", tokenName(token)));
460
461           _joinSemantics = FromItem.JoinSemantics.OUTER;
462         }
463         else if (token == JOIN) {
464           _joinSemantics = FromItem.JoinSemantics.INNER;
465         }
466
467       } while ((token == ',') ||
468                (token == JOIN));
469
470       _parsingFrom = false;
471     }
472
473     int fromParseIndex = _parseIndex;
474     int fromToken = _token;
475
476     _parseIndex = oldParseIndex;
477     _token = oldToken;
478
479     ArrayList JavaDoc<AmberExpr> resultList = new ArrayList JavaDoc<AmberExpr>();
480
481     if (peekToken() == SELECT) {
482       scanToken();
483
484       if (peekToken() == DISTINCT) {
485         scanToken();
486         query.setDistinct(true);
487       }
488
489       String JavaDoc constructorName = null;
490
491       if (peekToken() == NEW) {
492
493         scanToken();
494
495         // Scans the fully qualified constructor
496

497         String JavaDoc s = "";
498
499         boolean isDot = false;
500
501         while ((token = scanToken()) != '(') {
502
503           if (! isDot) {
504             s += _lexeme;
505             isDot = true;
506           }
507           else if (token == '.') {
508             s += '.';
509             isDot = false;
510           }
511           else
512             throw error(L.l("Constructor with SELECT NEW must be fully qualified. Expected '.' at {0}", tokenName(token)));
513         }
514
515         constructorName = s;
516       }
517
518       do {
519
520         AmberExpr expr = parseExpr();
521
522         if (! hasFrom) {
523           if (! (expr instanceof DateTimeFunExpr))
524             throw error(L.l("expected FROM clause when the select clause has not date/time functions only"));
525         }
526         else {
527
528           // jpa/1199
529
if (expr == null)
530             continue;
531
532           expr = expr.bindSelect(this);
533
534           if (_isLazyResult) {
535           }
536           else if (expr instanceof PathExpr) {
537             PathExpr pathExpr = (PathExpr) expr;
538
539             expr = LoadExpr.create(pathExpr);
540
541             expr = expr.bindSelect(this);
542           }
543         }
544
545         resultList.add(expr);
546       } while ((token = scanToken()) == ',');
547
548       query.setHasFrom(hasFrom);
549
550       if (hasFrom && (constructorName != null)) {
551
552         if (token != ')')
553           throw error(L.l("Expected ')' at {0} when calling constructor with SELECT NEW", tokenName(token)));
554
555         token = scanToken();
556
557         try {
558
559           ClassLoader JavaDoc loader = Thread.currentThread().getContextClassLoader();
560
561           Class JavaDoc cl = Class.forName(constructorName, false, loader);
562
563           query.setConstructorClass(cl);
564
565         } catch (ClassNotFoundException JavaDoc ex) {
566           throw error(L.l("Unable to find class {0}. Make sure the class is fully qualified.", constructorName));
567         }
568       }
569
570       _token = token;
571     }
572
573     if (hasFrom && (peekToken() != FROM))
574       throw error(L.l("expected FROM at {0}", tokenName(token)));
575
576     _parsingResult = true;
577
578     if (resultList.size() == 0) {
579
580       if (_joinFetchMap.size() > 0)
581         throw error(L.l("All associations referenced by JOIN FETCH must belong to an entity that is returned as a result of the query"));
582
583       ArrayList JavaDoc<FromItem> fromList = _query.getFromList();
584
585       if (fromList.size() > 0) {
586         FromItem fromItem = fromList.get(0);
587
588         AmberExpr expr = fromItem.getIdExpr();
589
590         if (_isLazyResult) {
591         }
592         else if (expr instanceof PathExpr) {
593           PathExpr pathExpr = (PathExpr) expr;
594
595           expr = LoadExpr.create(pathExpr);
596           expr = expr.bindSelect(this);
597         }
598
599         resultList.add(expr);
600       }
601     }
602     else if (hasFrom) {
603
604       int size = resultList.size();
605
606       int matches = 0;
607
608       for (int i = 0; i < size; i++) {
609
610         AmberExpr expr = resultList.get(i);
611
612         if (expr instanceof LoadEntityExpr) {
613
614           expr = ((LoadEntityExpr) expr).getExpr();
615
616           if (_joinFetchMap.get(expr) != null) {
617             matches++;
618           }
619         }
620       }
621
622       if (matches < _joinFetchMap.size())
623         throw error(L.l("All associations referenced by JOIN FETCH must belong to an entity that is returned as a result of the query"));
624
625     }
626
627     // jpa/1199
628
if (_appendResultList != null)
629       resultList.addAll(_appendResultList);
630
631     query.setResultList(resultList);
632
633     _parsingResult = false;
634
635     _parseIndex = fromParseIndex;
636     _token = fromToken;
637
638     token = peekToken();
639
640     if (token == WHERE) {
641       scanToken();
642
643       AmberExpr expr = parseExpr();
644
645       // jpa/119l: WHERE SIZE() is moved to HAVING COUNT()
646
if (expr != null) {
647         expr = expr.createBoolean();
648
649         query.setWhere(expr.bindSelect(this));
650       }
651     }
652
653     boolean hasGroupBy = false;
654
655     ArrayList JavaDoc<AmberExpr> groupList = _groupList;
656
657     token = peekToken();
658     if (token == GROUP) {
659       scanToken();
660
661       if (peekToken() == BY) {
662         scanToken();
663         hasGroupBy = true;
664       }
665
666       if (groupList == null)
667         groupList = new ArrayList JavaDoc<AmberExpr>();
668
669       while (true) {
670         // jpa/0w23
671
AmberExpr groupExpr = parseExpr();
672
673         groupExpr = groupExpr.bindSelect(this);
674
675         if (groupExpr instanceof PathExpr) {
676           // jpa/119n
677

678           PathExpr pathExpr = (PathExpr) groupExpr;
679
680           groupExpr = LoadExpr.create(pathExpr);
681
682           groupExpr = groupExpr.bindSelect(this);
683         }
684
685         groupList.add(groupExpr);
686
687         if (peekToken() == ',')
688           scanToken();
689         else
690           break;
691       }
692
693       query.setGroupList(groupList);
694
695       // Reset temp group list after parsing subselect.
696
_groupList = null;
697     }
698
699     token = peekToken();
700     if (token == HAVING) {
701
702       if (! hasGroupBy)
703         throw error(L.l("Use of HAVING without GROUP BY is not currently supported"));
704
705       _parsingHaving = true;
706
707       scanToken();
708
709       AmberExpr havingExpr = parseExpr();
710
711       // jpa/119l: SIZE()
712
if (_havingExpr != null)
713         havingExpr = AndExpr.create(havingExpr, _havingExpr);
714
715       query.setHaving(havingExpr.createBoolean().bindSelect(this));
716
717       _parsingHaving = false;
718     }
719     else if (_havingExpr != null) // jpa/119l
720
query.setHaving(_havingExpr.createBoolean().bindSelect(this));
721
722     token = peekToken();
723     if (token == ORDER) {
724       scanToken();
725
726       if (peekToken() == BY)
727         scanToken();
728
729       ArrayList JavaDoc<AmberExpr> orderList = new ArrayList JavaDoc<AmberExpr>();
730       ArrayList JavaDoc<Boolean JavaDoc> ascList = new ArrayList JavaDoc<Boolean JavaDoc>();
731
732       while (true) {
733         AmberExpr expr = parseExpr();
734         expr = expr.bindSelect(this);
735
736         orderList.add(expr);
737
738         if (peekToken() == DESC) {
739           scanToken();
740           ascList.add(Boolean.FALSE);
741         }
742         else if (peekToken() == ASC) {
743           scanToken();
744           ascList.add(Boolean.TRUE);
745         }
746         else
747           ascList.add(Boolean.TRUE);
748
749         if (peekToken() == ',')
750           scanToken();
751         else
752           break;
753       }
754
755       query.setOrderList(orderList, ascList);
756     }
757
758     token = peekToken();
759
760     if (! innerSelect) {
761
762       query.setJoinFetchMap(_joinFetchMap);
763
764       if (token > 0)
765         throw error(L.l("expected end of query at {0}", tokenName(token)));
766
767       if (! query.setArgList(_argList.toArray(new ArgExpr[_argList.size()])))
768         throw error(L.l("Unable to parse all query parameters. Make sure named parameters are not mixed with positional parameters"));
769
770     }
771
772     try {
773       query.init();
774     } catch (SQLException JavaDoc e) {
775       throw new QueryParseException(e);
776     }
777
778     _joinSemantics = oldJoinSemantics;
779     _isJoinFetch = oldIsJoinFetch;
780     _query = oldQuery;
781     _depth = oldDepth;
782     _havingExpr = oldHavingExpr;
783     _appendResultList = oldAppendResultList;
784
785     return query;
786   }
787
788   private AbstractQuery parseUpdate()
789     throws QueryParseException
790   {
791     UpdateQuery query = new UpdateQuery(_sql);
792     _query = query;
793
794     FromItem fromItem = parseFrom();
795
796     int token = scanToken();
797     if (token != SET)
798       throw error(L.l("expected 'SET' at {0}", tokenName(token)));
799
800     ArrayList JavaDoc<AmberExpr> fields = new ArrayList JavaDoc<AmberExpr>();
801     ArrayList JavaDoc<AmberExpr> values = new ArrayList JavaDoc<AmberExpr>();
802
803     parseSetValues(fromItem, fields, values);
804
805     query.setFieldList(fields);
806     query.setValueList(values);
807
808     token = scanToken();
809     if (token == WHERE) {
810       query.setWhere(parseExpr().createBoolean().bindSelect(this));
811
812       token = scanToken();
813     }
814
815     if (token >= 0)
816       throw error(L.l("'{0}' not expected at end of query.", tokenName(token)));
817
818     if (! query.setArgList(_argList.toArray(new ArgExpr[_argList.size()])))
819       throw error(L.l("Unable to parse all query parameters. Make sure named parameters are not mixed with positional parameters"));
820
821     query.init();
822
823     return query;
824   }
825
826   private AbstractQuery parseDelete()
827     throws QueryParseException
828   {
829     DeleteQuery query = new DeleteQuery(_sql);
830     _query = query;
831
832     int token = peekToken();
833     if (token == FROM)
834       scanToken();
835
836     FromItem fromItem = parseFrom();
837
838     token = scanToken();
839     if (token == WHERE) {
840       query.setWhere(parseExpr().createBoolean().bindSelect(this));
841
842       token = scanToken();
843     }
844
845     if (token >= 0)
846       throw error(L.l("'{0}' not expected at end of query.", tokenName(token)));
847
848     if (! query.setArgList(_argList.toArray(new ArgExpr[_argList.size()])))
849       throw error(L.l("Unable to parse all query parameters. Make sure named parameters are not mixed with positional parameters"));
850
851     query.init();
852
853     return query;
854   }
855
856   /**
857    * Parses the set values.
858    */

859   private void parseSetValues(FromItem fromItem,
860                               ArrayList JavaDoc<AmberExpr> fields,
861                               ArrayList JavaDoc<AmberExpr> values)
862     throws QueryParseException
863   {
864     EntityType entity = fromItem.getEntityType();
865
866     int token = -1;
867
868     do {
869
870       token = scanToken();
871
872       AmberExpr expr = null;
873
874       String JavaDoc name = _lexeme.toString();
875
876       IdExpr tableExpr = getIdentifier(name);
877
878       if (tableExpr != null) {
879         expr = parsePath(tableExpr);
880       }
881       else {
882
883         tableExpr = fromItem.getIdExpr();
884
885         AmberExpr next = tableExpr.createField(this, name);
886
887         if (next instanceof PathExpr)
888           expr = addPath((PathExpr) next);
889         else if (next != null)
890           expr = next;
891       }
892
893       expr = expr.bindSelect(this);
894
895       fields.add(expr);
896
897       if ((token = peekToken()) != EQ)
898         throw error(L.l("expected '=' at {0}", tokenName(token)));
899
900       scanToken();
901
902       // jpa/1222 expr = parseSimpleTerm();
903
expr = parseConcatExpr();
904
905       expr = expr.bindSelect(this);
906
907       values.add(expr);
908
909     } while ((token = scanToken()) == ',');
910
911     _token = token;
912   }
913
914   /**
915    * Parses the FROM block. parseFrom's effect is to populate the
916    * core identifiers.
917    *
918    * <pre>
919    * from-item ::= schema AS? IDENTIFIER
920    * </pre>
921    */

922   private FromItem parseFrom()
923     throws QueryParseException
924   {
925     SchemaExpr schema = parseSchema();
926
927     String JavaDoc id;
928
929     int token = peekToken();
930     if (token == AS) {
931       scanToken();
932       token = peekToken();
933       id = parseIdentifier();
934     }
935     else if (token == IDENTIFIER)
936       id = parseIdentifier();
937     else {
938       id = schema.getTailName();
939     }
940
941     /*
942       AmberEntityHome home = _amberPersistenceUnitenceUnitenceUnit.getHomeBySchema(schema);
943
944       if (home == null)
945       throw error(L.l("`{0}' is an unknown persistent class.",
946       schema));
947     */

948
949     FromItem item = schema.addFromItem(this, id);
950
951     if (schema instanceof EmbeddedSchemaExpr) {
952
953       // jpa/0w22
954

955       EmbeddedSchemaExpr embeddedSchema = (EmbeddedSchemaExpr) schema;
956
957       _query.addEmbeddedAlias(id, embeddedSchema.getExpr()); // pathString);
958
}
959
960     // jpa/114h
961
item.setJoinSemantics(_joinSemantics);
962
963     return item;
964   }
965
966   /**
967    * Adds a new FromItem.
968    */

969   public FromItem addFromItem(Table table)
970   {
971     return addFromItem(table, createTableName());
972   }
973
974   /**
975    * Returns a unique table name
976    */

977   public String JavaDoc createTableName()
978   {
979     return "caucho" + _unique++;
980   }
981
982   /**
983    * Adds a new FromItem.
984    */

985   public FromItem addFromItem(Table table, String JavaDoc id)
986   {
987     if (id == null)
988       id = createTableName();
989
990     FromItem item = _query.createFromItem(table, id);
991
992     item.setJoinSemantics(_joinSemantics);
993
994     return item;
995   }
996
997   /**
998    * Adds a new FromItem.
999    */

1000  public FromItem createDependentFromItem(FromItem item,
1001                                          LinkColumns link)
1002  {
1003    item = _query.createDependentFromItem(item, link, createTableName());
1004
1005    item.setJoinSemantics(_joinSemantics);
1006
1007    return item;
1008  }
1009
1010  /**
1011   * Adds a new link
1012   */

1013  void addLink(AmberExpr expr)
1014  {
1015    // _andExpr.add(expr);
1016
throw new IllegalStateException JavaDoc();
1017  }
1018
1019  /**
1020   * Adds an entity path
1021   */

1022  public PathExpr addPath(PathExpr path)
1023  {
1024    PathExpr oldPath = _pathMap.get(path);
1025
1026    if (oldPath != null)
1027      return oldPath;
1028
1029    _pathMap.put(path, path);
1030
1031    return path;
1032  }
1033
1034  /**
1035   * Adds a new argument
1036   */

1037  public void addArg(ArgExpr arg)
1038  {
1039    _argList.add(arg);
1040  }
1041
1042  /**
1043   * Parses a schema.
1044   */

1045  private SchemaExpr parseSchema()
1046    throws QueryParseException
1047  {
1048    int token = peekToken();
1049    boolean isIn = token == IN;
1050
1051    if (isIn) {
1052
1053      scanToken();
1054
1055      _joinSemantics = FromItem.JoinSemantics.INNER;
1056
1057      if ((token = scanToken()) != '(')
1058        throw error(L.l("expected '(' at '{0}'", tokenName(token)));
1059    }
1060
1061    String JavaDoc name = parseIdentifier();
1062
1063    SchemaExpr schema = null;
1064
1065    if (! isIn) {
1066      AmberEntityHome home = _amberPersistenceUnit.getHomeBySchema(name);
1067
1068      if (home != null) {
1069        EntityType type = home.getEntityType();
1070
1071        schema = new TableIdExpr(home.getEntityType(),
1072                                 type.getTable().getName());
1073      }
1074    }
1075
1076    IdExpr id = null;
1077
1078    if (schema == null) {
1079      id = getIdentifier(name);
1080
1081      if (id != null)
1082        schema = new FromIdSchemaExpr(id);
1083    }
1084
1085    if (! isIn && schema == null) {
1086      while (peekToken() == '.') {
1087        scanToken();
1088        String JavaDoc segment = parseIdentifier();
1089
1090        name = name + '.' + segment;
1091
1092        AmberEntityHome home = _amberPersistenceUnit.getHomeBySchema(name);
1093
1094        if (home != null) {
1095          schema = new TableIdExpr(home.getEntityType(), name);
1096          break;
1097        }
1098      }
1099    }
1100
1101    if (schema == null) {
1102      throw error(L.l("'{0}' is an unknown entity.",
1103                      name));
1104    }
1105
1106    name = "";
1107    boolean isFirst = true;
1108
1109    while (peekToken() == '.') {
1110      scanToken();
1111      String JavaDoc segment = parseIdentifier();
1112
1113      if (isFirst) {
1114        name += segment;
1115        isFirst = false;
1116      }
1117      else
1118        name += "." + segment;
1119
1120      schema = schema.createField(this, segment);
1121    }
1122
1123    if (_isJoinFetch && (! name.equals(""))) {
1124      _joinFetchMap.put(id, name);
1125    }
1126
1127    if (isIn) {
1128      if ((token = scanToken()) != ')')
1129        throw error(L.l("expected ')' at '{0}'", tokenName(token)));
1130    }
1131
1132    return schema;
1133  }
1134
1135  /**
1136   * Parses an expression.
1137   */

1138  private AmberExpr parseExpr()
1139    throws QueryParseException
1140  {
1141    if (peekToken() == SELECT) {
1142      SelectQuery select = parseSelect(true);
1143
1144      return new SubSelectExpr(select);
1145    }
1146
1147    AmberExpr expr = parseOrExpr();
1148
1149    return expr; // .bindSelect(this);
1150
}
1151
1152  /**
1153   * Parses an or expression.
1154   */

1155  private AmberExpr parseOrExpr()
1156    throws QueryParseException
1157  {
1158    AmberExpr expr = parseAndExpr();
1159    OrExpr orExpr = null;
1160
1161    while (peekToken() == OR) {
1162      scanToken();
1163
1164      if (orExpr == null) {
1165        orExpr = new OrExpr();
1166        orExpr.add(expr);
1167      }
1168
1169      AmberExpr andExpr = parseAndExpr();
1170
1171      if (andExpr == null)
1172        continue;
1173
1174      orExpr.add(andExpr);
1175    }
1176
1177    return orExpr == null ? expr : orExpr;
1178  }
1179
1180  /**
1181   * Parses an and expression.
1182   */

1183  private AmberExpr parseAndExpr()
1184    throws QueryParseException
1185  {
1186    AmberExpr expr = parseNotExpr();
1187    AndExpr andExpr = null;
1188
1189    while (peekToken() == AND) {
1190      scanToken();
1191
1192      if (andExpr == null) {
1193        andExpr = new AndExpr();
1194        andExpr.add(expr);
1195      }
1196
1197      AmberExpr notExpr = parseNotExpr();
1198
1199      if (notExpr == null)
1200        continue;
1201
1202      andExpr.add(notExpr);
1203    }
1204
1205    return andExpr == null ? expr : andExpr;
1206  }
1207
1208  /**
1209   * Parses a NOT expression.
1210   *
1211   */

1212  private AmberExpr parseNotExpr()
1213    throws QueryParseException
1214  {
1215    AmberExpr expr;
1216
1217    if (peekToken() == NOT) {
1218      scanToken();
1219
1220      expr = new UnaryExpr(NOT, parseCmpExpr());
1221    }
1222    else
1223      expr = parseCmpExpr();
1224
1225    // jpa/1199, jpa/119l
1226

1227    if (_parsingResult || _parsingHaving)
1228      return expr;
1229
1230    if (! _isSizeFunExpr)
1231      return expr;
1232
1233    _isSizeFunExpr = false;
1234
1235    if (_havingExpr == null)
1236      _havingExpr = expr;
1237    else
1238      _havingExpr = AndExpr.create(_havingExpr, expr);
1239
1240    return null;
1241  }
1242
1243  /**
1244   * Parses a comparison expression.
1245   *
1246   * <pre>
1247   * cmp-expr ::= add-expr '=' add-expr is-term?
1248   * ::= add-expr 'NOT'? 'BETWEEN' add-expr 'AND' add-expr is-term?
1249   * ::= add-expr 'NOT'? 'LIKE' string ('ESCAPE' string)? is-term?
1250   * ::= add-expr 'NOT'? 'IN' ('lit-1', ..., 'lit-n')
1251   * ::= add-expr
1252   * </pre>
1253   *
1254   * @return the parsed expression
1255   */

1256  private AmberExpr parseCmpExpr()
1257    throws QueryParseException
1258  {
1259    AmberExpr expr = parseConcatExpr();
1260
1261    int token = peekToken();
1262    boolean isNot = false;
1263
1264    if (token == NOT) {
1265      scanToken();
1266      isNot = true;
1267      token = peekToken();
1268
1269      if (token != BETWEEN &&
1270          token != LIKE &&
1271          token != MEMBER &&
1272          token != IN)
1273        throw error(L.l("'NOT' is not expected here."));
1274    }
1275
1276    if (token >= EQ && token <= GE) {
1277      scanToken();
1278
1279      return parseIs(new BinaryExpr(token, expr, parseConcatExpr()));
1280    }
1281    else if (token == BETWEEN) {
1282      scanToken();
1283
1284      AmberExpr min = parseConcatExpr();
1285
1286      if ((token = scanToken()) != AND)
1287        throw error(L.l("Expected 'AND' at {0}", tokenName(token)));
1288
1289      AmberExpr max = parseConcatExpr();
1290
1291      return new BetweenExpr(expr, min, max, isNot);
1292    }
1293    else if (token == LIKE) {
1294      scanToken();
1295
1296      AmberExpr pattern = parseConcatExpr();
1297
1298      String JavaDoc escape = null;
1299      if (peekToken() == ESCAPE) {
1300        scanToken();
1301
1302        if ((token = scanToken()) != STRING)
1303          throw error(L.l("Expected string at {0}", tokenName(token)));
1304
1305        escape = _lexeme.toString();
1306      }
1307
1308      return parseIs(new LikeExpr(expr, pattern, escape, isNot));
1309    }
1310    else if (token == IN) {
1311      scanToken();
1312      token = scanToken();
1313
1314      if (token != '(')
1315        throw error(L.l("Expected '(' after IN at {0}", tokenName(token)));
1316
1317      ArrayList JavaDoc<AmberExpr> args = new ArrayList JavaDoc<AmberExpr>();
1318      while ((token = peekToken()) > 0 && token != ')') {
1319        AmberExpr arg = parseExpr();
1320
1321        args.add(arg);
1322
1323        token = peekToken();
1324        if (token == ',') {
1325          scanToken();
1326          token = peekToken();
1327        }
1328      }
1329
1330      if (peekToken() != ')')
1331        throw error(L.l("Expected ')' after IN at {0}", tokenName(token)));
1332
1333      scanToken();
1334
1335      return new InExpr(expr, args, isNot);
1336    }
1337    else if (token == MEMBER) {
1338      scanToken();
1339
1340      token = peekToken();
1341      if (token == OF)
1342        token = scanToken();
1343
1344      AmberExpr collection = parseExpr();
1345
1346      // jpa/10c8
1347
if (expr instanceof ArgExpr) {
1348        addArg((ArgExpr) expr);
1349      }
1350      else if (! (expr instanceof PathExpr))
1351        throw error(L.l("MEMBER OF requires an entity-valued item."));
1352
1353      // ManyToMany is implemented as a
1354
// ManyToOne[embeddeding OneToMany]
1355
if ((collection instanceof ManyToOneExpr) &&
1356          (((ManyToOneExpr) collection).getParent() instanceof OneToManyExpr)) {
1357      }
1358      else if (collection instanceof OneToManyExpr) {
1359      }
1360      else if (collection instanceof CollectionIdExpr) {
1361      }
1362      else {
1363        throw error(L.l("MEMBER OF requires an entity-valued collection at '{0}'.",
1364                        collection.getClass().getName()));
1365      }
1366
1367      return parseIs(MemberExpr.create(this,
1368                                       expr,
1369                                       collection,
1370                                       isNot));
1371    }
1372    else
1373      return parseIs(expr);
1374  }
1375
1376  private AmberExpr parseIs(AmberExpr expr)
1377    throws QueryParseException
1378  {
1379    int token = peekToken();
1380
1381    if (token != IS)
1382      return expr;
1383
1384    scanToken();
1385
1386    boolean isNot = false;
1387    token = scanToken();
1388
1389    if (token == NOT) {
1390      isNot = true;
1391      token = scanToken();
1392    }
1393
1394    if (token == NULL) {
1395
1396      if (expr instanceof KeyColumnExpr)
1397        expr = ((KeyColumnExpr) expr).getParent();
1398
1399      if (isNot)
1400        return new UnaryExpr(NOT_NULL, expr);
1401      else
1402        return new UnaryExpr(NULL, expr);
1403    }
1404    else if (token == EMPTY) {
1405
1406      expr = new EmptyExpr(expr);
1407
1408      if (! isNot)
1409        expr = new UnaryExpr(NOT, expr);
1410
1411      return expr;
1412    }
1413    else
1414      throw error(L.l("expected NULL at '{0}'", tokenName(token)));
1415  }
1416
1417  /**
1418   * Parses a concat expression.
1419   */

1420  private AmberExpr parseConcatExpr()
1421    throws QueryParseException
1422  {
1423    AmberExpr expr = parseAddExpr();
1424
1425    while (true) {
1426      int token = peekToken();
1427
1428      switch (token) {
1429      case CONCAT_OP:
1430        scanToken();
1431
1432        ArrayList JavaDoc<AmberExpr> args = new ArrayList JavaDoc<AmberExpr>();
1433
1434        args.add(expr);
1435        args.add(parseAddExpr());
1436
1437        expr = ConcatFunExpr.create(this, args);
1438        break;
1439      default:
1440        return expr;
1441      }
1442    }
1443  }
1444
1445  /**
1446   * Parses an add expression.
1447   */

1448  private AmberExpr parseAddExpr()
1449    throws QueryParseException
1450  {
1451    AmberExpr expr = parseMulExpr();
1452
1453    while (true) {
1454      int token = peekToken();
1455
1456      switch (token) {
1457      case '+':
1458      case '-':
1459        scanToken();
1460        expr = new BinaryExpr(token, expr, parseMulExpr());
1461        break;
1462      default:
1463        return expr;
1464      }
1465    }
1466  }
1467
1468  /**
1469   * Parses a mul expression.
1470   */

1471  private AmberExpr parseMulExpr()
1472    throws QueryParseException
1473  {
1474    AmberExpr expr = parseTerm();
1475
1476    while (true) {
1477      int token = peekToken();
1478
1479      switch (token) {
1480      case '*':
1481      case '/':
1482        scanToken();
1483        expr = new BinaryExpr(token, expr, parseTerm());
1484        break;
1485      default:
1486        return expr;
1487      }
1488    }
1489  }
1490
1491  /**
1492   * Parses a term
1493   *
1494   * <pre>
1495   * term ::= - term
1496   * ::= + term
1497   * ::= NOT term
1498   * </pre>
1499   */

1500  private AmberExpr parseTerm()
1501    throws QueryParseException
1502  {
1503    int token = peekToken();
1504
1505    switch (token) {
1506    case '+':
1507    case '-':
1508    case NOT:
1509      scanToken();
1510
1511      return new UnaryExpr(token, parseTerm());
1512
1513    default:
1514      return parseSimpleTerm();
1515    }
1516  }
1517
1518  /**
1519   * Parses a simple term
1520   *
1521   * <pre>
1522   * term ::= INTEGER | LONG | DOUBLE | STRING
1523   * ::= THIS
1524   * ::= IDENTIFIER
1525   * ::= IDENTIFIER '(' args ')'
1526   * ::= '(' expr ')'
1527   * </pre>
1528   */

1529  private AmberExpr parseSimpleTerm()
1530    throws QueryParseException
1531  {
1532    int token = scanToken();
1533
1534    switch (token) {
1535    case IDENTIFIER:
1536    case LOCATE:
1537    case LENGTH:
1538    case MAX:
1539    case MIN:
1540    case SUM:
1541    case ABS:
1542    case SQRT:
1543    case MOD:
1544    case SIZE:
1545    case CONCAT:
1546    case LOWER:
1547    case UPPER:
1548    case SUBSTRING:
1549    case TRIM:
1550      {
1551        String JavaDoc name = _lexeme.toString();
1552
1553        if (peekToken() != '(') {
1554          // Either IdExpr or EmbeddedExpr
1555
AbstractPathExpr tableExpr = getIdentifier(name);
1556
1557          if (tableExpr == null) {
1558            // jpa/0w22
1559
tableExpr = getEmbeddedAlias(name);
1560          }
1561
1562          if (tableExpr == null) {
1563            // jpa/11z6
1564
AmberExpr amberExpr = parseEnum(name);
1565
1566            if (amberExpr != null)
1567              return amberExpr;
1568          }
1569
1570          if (tableExpr != null) {
1571            AmberExpr amberExpr = parsePath(tableExpr);
1572
1573            return amberExpr;
1574          }
1575
1576          if (_query.getFromList().size() == 0)
1577            throw error(L.l("Expected a FROM clause before '{0}'", name));
1578
1579          FromItem fromItem = _query.getFromList().get(0);
1580
1581          tableExpr = fromItem.getIdExpr();
1582
1583          AmberExpr next = tableExpr.createField(this, name);
1584
1585          if (next instanceof PathExpr)
1586            return addPath((PathExpr) next);
1587          else if (next != null)
1588            return next;
1589
1590          throw error(L.l("'{0}' is an unknown table or column", name));
1591        }
1592        else {
1593
1594          name = name.toLowerCase();
1595
1596          // EXISTS | ALL | ANY | SOME
1597
if (name.equals("exists") ||
1598              name.equals("all") ||
1599              name.equals("any") ||
1600              name.equals("some")) {
1601
1602            scanToken();
1603
1604            if (peekToken() != SELECT && peekToken() != FROM)
1605              throw error(L.l(name.toUpperCase() + " requires '(SELECT'"));
1606
1607            SelectQuery select = parseSelect(true);
1608
1609            if (peekToken() != ')')
1610              throw error(L.l(name.toUpperCase() + " requires ')'"));
1611
1612            scanToken();
1613
1614            ArrayList JavaDoc<FromItem> parentFromList;
1615            parentFromList = select.getParentQuery().getFromList();
1616
1617            // jpa/1178
1618
select.getFromList().addAll(0, parentFromList);
1619
1620            if (name.equals("exists"))
1621              return new ExistsExpr(select);
1622            else if (name.equals("all"))
1623              return new AllExpr(select);
1624            else // SOME is a synonymous with ANY
1625
return new AnyExpr(select);
1626          }
1627          else {
1628            return parseFunction(name, token);
1629          }
1630        }
1631      }
1632
1633    case CURRENT_DATE:
1634    case CURRENT_TIME:
1635    case CURRENT_TIMESTAMP:
1636      {
1637        String JavaDoc name = _lexeme.toString();
1638
1639        return parseFunction(name, token);
1640      }
1641
1642    case FALSE:
1643      return new LiteralExpr(this, _lexeme, boolean.class);
1644
1645    case TRUE:
1646      return new LiteralExpr(this, _lexeme, boolean.class);
1647
1648    case NULL:
1649      return new NullExpr();
1650
1651    case INTEGER:
1652      return new LiteralExpr(this, _lexeme, int.class);
1653
1654    case LONG:
1655      return new LiteralExpr(this, _lexeme, long.class);
1656
1657    case DOUBLE:
1658      return new LiteralExpr(this, _lexeme, double.class);
1659
1660    case STRING:
1661      return new LiteralExpr(this, _lexeme, String JavaDoc.class);
1662
1663    case ARG:
1664      {
1665        ArgExpr arg = new ArgExpr(this, Integer.parseInt(_lexeme));
1666        /*
1667          if (_addArgToQuery)
1668          addArg(arg);
1669        */

1670        return arg;
1671      }
1672
1673    case NAMED_ARG:
1674      {
1675        ArgExpr arg = new ArgExpr(this, _lexeme, _parameterCount);
1676        return arg;
1677      }
1678
1679      /*
1680        case THIS:
1681        {
1682        if (_thisExpr == null) {
1683        _thisExpr = new IdExpr(this, "caucho_this", _bean);
1684        addFromItem("caucho_this", _bean.getSQLTable());
1685        _argList.add(0, new ThisExpr(this, _bean));
1686        }
1687
1688        return _thisExpr;
1689        }
1690      */

1691
1692    case '(':
1693      AmberExpr expr = parseExpr();
1694      if ((token = scanToken()) != ')')
1695        throw error(L.l("expected `)' at {0}", tokenName(token)));
1696
1697      return expr;
1698
1699
1700    default:
1701      throw error(L.l("expected term at {0}", tokenName(token)));
1702    }
1703  }
1704
1705  /**
1706   * Parses a path
1707   *
1708   * <pre>
1709   * path ::= IDENTIFIER
1710   * ::= path . IDENTIFIER
1711   * </pre>
1712   */

1713  private AmberExpr parsePath(PathExpr path)
1714    throws QueryParseException
1715  {
1716    while (peekToken() == '.') {
1717      scanToken();
1718
1719      String JavaDoc field = parseIdentifier();
1720
1721      AmberExpr next = path.createField(this, field);
1722
1723      if (next == null)
1724        throw error(L.l("'{0}' does not have a field '{1}'",
1725                        path, field));
1726
1727      if (! (next instanceof PathExpr))
1728        return next;
1729
1730      PathExpr nextPath = addPath((PathExpr) next);
1731
1732      if (peekToken() == '[') {
1733        scanToken();
1734
1735        AmberExpr index = parseExpr();
1736
1737        next = nextPath.createArray(index);
1738
1739        if (next == null)
1740          throw error(L.l("'{0}' does not have a map field '{1}'",
1741                          path, field));
1742
1743        if (peekToken() != ']') {
1744          throw error(L.l("expected ']' at '{0}'", tokenName(peekToken())));
1745        }
1746
1747        scanToken();
1748      }
1749
1750      if (next instanceof PathExpr)
1751        path = addPath((PathExpr) next);
1752      else
1753        return next;
1754    }
1755
1756    return path;
1757  }
1758
1759  /**
1760   * Parses a enum value
1761   *
1762   * <pre>
1763   * enum ::= (IDENTIFIER '.')+ IDENTIFIER
1764   * </pre>
1765   */

1766  private EnumExpr parseEnum(String JavaDoc head)
1767    throws QueryParseException
1768  {
1769    CharBuffer cb = CharBuffer.allocate();
1770
1771    int token;
1772
1773    while ((token = scanToken()) == '.') {
1774
1775      if (cb.length() > 0)
1776        cb.append('.');
1777
1778      cb.append(head);
1779
1780      token = scanToken();
1781
1782      if (token != IDENTIFIER)
1783        throw error(L.l("expected identifier for enumerated type {0} at {1}",
1784                        cb.toString(),
1785                        tokenName(token)));
1786
1787      head = _lexeme.toString();
1788    }
1789
1790    int value = -1;
1791    Class JavaDoc cl = null;
1792
1793    try {
1794      ClassLoader JavaDoc loader = Thread.currentThread().getContextClassLoader();
1795
1796      cl = Class.forName(cb.toString(), false, loader);
1797
1798      Enum JavaDoc enumValue = Enum.valueOf(cl, head);
1799
1800      value = enumValue.ordinal();
1801    } catch (ClassNotFoundException JavaDoc e) {
1802      // Not an error; only this is not a enum.
1803
// Continue - see parseSimpleTerm().
1804
return null;
1805    }
1806
1807    return new EnumExpr(cl, head, value);
1808  }
1809
1810  /**
1811   * Parses a function
1812   *
1813   * <pre>
1814   * fun ::= IDENTIFIER ( expr* )
1815   * ::= IDENTIFIER ( DISTINCT expr* )
1816   * </pre>
1817   */

1818  private AmberExpr parseFunction(String JavaDoc id,
1819                                  int functionToken)
1820    throws QueryParseException
1821  {
1822    // Function with no arguments.
1823
switch (functionToken) {
1824
1825    case CURRENT_DATE:
1826      return CurrentDateFunExpr.create(this);
1827
1828    case CURRENT_TIME:
1829      return CurrentTimeFunExpr.create(this);
1830
1831    case CURRENT_TIMESTAMP:
1832      return CurrentTimestampFunExpr.create(this);
1833    }
1834
1835    // Function with arguments.
1836

1837    scanToken();
1838
1839    // Example: "'c'"
1840
AmberExpr trimChar = null;
1841    TrimFunExpr.TrimSemantics trimSemantics
1842      = TrimFunExpr.TrimSemantics.BOTH;
1843    boolean distinct = false;
1844
1845    ArrayList JavaDoc<AmberExpr> args = new ArrayList JavaDoc<AmberExpr>();
1846
1847    if (functionToken == TRIM) {
1848
1849      switch (peekToken()) {
1850
1851      case LEADING:
1852        trimSemantics = TrimFunExpr.TrimSemantics.LEADING;
1853        scanToken();
1854        break;
1855
1856      case TRAILING:
1857        trimSemantics = TrimFunExpr.TrimSemantics.TRAILING;
1858        scanToken();
1859        break;
1860
1861      case BOTH:
1862        scanToken();
1863        break;
1864
1865      // default: [BOTH], but no scanToken().
1866
}
1867
1868      AmberExpr arg = null;
1869
1870      if (peekToken() != FROM) {
1871
1872        arg = parseExpr();
1873
1874        if (arg instanceof LiteralExpr) {
1875
1876          String JavaDoc v = ((LiteralExpr) arg).getValue();
1877
1878          if (v.length() != 3) // "'c'"
1879
throw error(L.l("expected a single char expression for TRIM at {0}", v));
1880        }
1881      }
1882
1883      if (peekToken() == FROM) {
1884        scanToken();
1885
1886        trimChar = arg;
1887
1888        arg = parseExpr();
1889      }
1890
1891      args.add(arg);
1892    }
1893    else {
1894
1895      if (peekToken() == DISTINCT) {
1896        distinct = true;
1897        scanToken();
1898      }
1899
1900      while ((peekToken() >= 0) && (peekToken() != ')')) {
1901
1902        AmberExpr arg = parseExpr();
1903
1904        if (id.equalsIgnoreCase("object")) {
1905          if (arg instanceof PathExpr) {
1906            PathExpr pathExpr = (PathExpr) arg;
1907
1908            arg = LoadExpr.create(pathExpr);
1909
1910            arg = arg.bindSelect(this);
1911
1912            int token = scanToken();
1913
1914            if (token != ')')
1915              throw error(L.l("expected ')' at '{0}'", tokenName(token)));
1916
1917            return arg;
1918          }
1919        }
1920
1921        args.add(arg);
1922
1923        if (peekToken() != ',')
1924          break;
1925
1926        scanToken();
1927      }
1928    }
1929
1930    if (peekToken() != ')')
1931      throw error(L.l("expected ')' at '{0}'", tokenName(scanToken())));
1932
1933    scanToken();
1934
1935    FunExpr funExpr;
1936
1937    switch (functionToken) {
1938
1939    case LOCATE:
1940      funExpr = LocateFunExpr.create(this, args);
1941      break;
1942
1943    case LENGTH:
1944      funExpr = LengthFunExpr.create(this, args);
1945      break;
1946
1947    case MAX:
1948      funExpr = MaxFunExpr.create(this, id, args, distinct);
1949      break;
1950
1951    case MIN:
1952      funExpr = MinFunExpr.create(this, id, args, distinct);
1953      break;
1954
1955    case SUM:
1956      funExpr = SumFunExpr.create(this, id, args, distinct);
1957      break;
1958
1959    case ABS:
1960      funExpr = AbsFunExpr.create(this, args);
1961      break;
1962
1963    case SQRT:
1964      funExpr = SqrtFunExpr.create(this, args);
1965      break;
1966
1967    case MOD:
1968      funExpr = ModFunExpr.create(this, args);
1969      break;
1970
1971    case SIZE:
1972      if (! (_query instanceof SelectQuery))
1973        throw error(L.l("The SIZE() function is only supported for SELECT or subselect queries"));
1974
1975      // jpa/119l
1976

1977      AmberExpr arg = args.get(0);
1978      if (arg instanceof ManyToOneExpr) {
1979        // @ManyToMany
1980
arg = ((ManyToOneExpr) arg).getParent();
1981      }
1982
1983      if (! (arg instanceof OneToManyExpr))
1984        throw error(L.l("The SIZE() function is only supported for @ManyToMany or @OneToMany relationships"));
1985
1986      OneToManyExpr oneToMany = (OneToManyExpr) arg;
1987
1988      _groupList = new ArrayList JavaDoc<AmberExpr>();
1989
1990      LinkColumns linkColumns = oneToMany.getLinkColumns();
1991      ForeignColumn fkColumn = linkColumns.getColumns().get(0);
1992
1993      AmberExpr groupExpr = oneToMany.getParent();
1994
1995      if (groupExpr instanceof PathExpr) {
1996        // jpa/119n
1997

1998        PathExpr pathExpr = (PathExpr) groupExpr;
1999
2000        groupExpr = LoadExpr.create(pathExpr);
2001
2002        groupExpr = groupExpr.bindSelect(this);
2003      }
2004
2005      // groupExpr = new ColumnExpr(oneToMany.getParent(),
2006
// fkColumn.getTargetColumn());
2007

2008      _groupList.add(groupExpr);
2009
2010      ((SelectQuery) _query).setGroupList(_groupList);
2011
2012      funExpr = SizeFunExpr.create(this, args);
2013
2014      if (_appendResultList == null)
2015        _appendResultList = new ArrayList JavaDoc<AmberExpr>();
2016
2017      // jpa/1199
2018
_appendResultList.add(funExpr.bindSelect(this));
2019
2020      _isSizeFunExpr = true;
2021
2022      break;
2023
2024    case CONCAT:
2025      funExpr = ConcatFunExpr.create(this, args);
2026      break;
2027
2028    case LOWER:
2029      funExpr = LowerFunExpr.create(this, args);
2030      break;
2031
2032    case UPPER:
2033      funExpr = UpperFunExpr.create(this, args);
2034      break;
2035
2036    case SUBSTRING:
2037      funExpr = SubstringFunExpr.create(this, args);
2038      break;
2039
2040    case TRIM:
2041      {
2042        TrimFunExpr trimFunExpr = TrimFunExpr.create(this, args);
2043        trimFunExpr.setTrimChar(trimChar);
2044        trimFunExpr.setTrimSemantics(trimSemantics);
2045        funExpr = trimFunExpr;
2046        break;
2047      }
2048
2049    default:
2050      funExpr = FunExpr.create(this, id, args, distinct);
2051    }
2052
2053    return funExpr;
2054  }
2055
2056  /**
2057   * Returns the matching identifier.
2058   */

2059  private IdExpr getIdentifier(String JavaDoc name)
2060    throws QueryParseException
2061  {
2062    AbstractQuery query = _query;
2063
2064    for (; query != null; query = query.getParentQuery()) {
2065      ArrayList JavaDoc<FromItem> fromList = query.getFromList();
2066
2067      for (int i = 0; i < fromList.size(); i++) {
2068        FromItem from = fromList.get(i);
2069
2070        if (from.getName().equalsIgnoreCase(name))
2071          return from.getIdExpr();
2072      }
2073    }
2074
2075    return null;
2076
2077    // throw error(L.l("`{0}' is an unknown table", name));
2078
}
2079
2080  /**
2081   * Returns the matching embedded alias.
2082   */

2083  private EmbeddedExpr getEmbeddedAlias(String JavaDoc name)
2084    throws QueryParseException
2085  {
2086    // jpa/0w22
2087

2088    AbstractQuery query = _query;
2089
2090    for (; query != null; query = query.getParentQuery()) {
2091      HashMap JavaDoc<String JavaDoc, EmbeddedExpr> embeddedAliases =
2092        query.getEmbeddedAliases();
2093
2094      for (Map.Entry<String JavaDoc, EmbeddedExpr> entry :
2095             embeddedAliases.entrySet()) {
2096
2097        if (entry.getKey().equalsIgnoreCase(name))
2098          return entry.getValue();
2099      }
2100    }
2101
2102    return null;
2103  }
2104
2105  /**
2106   * Parses an identifier.
2107   */

2108  private String JavaDoc parseIdentifier()
2109    throws QueryParseException
2110  {
2111    int token = scanToken();
2112
2113    String JavaDoc identifier = _lexeme;
2114
2115    // Resolves ambiguous identifiers:
2116
// 1. 'order': "SELECT o FROM Order o"
2117
if (token == ORDER) {
2118      int parseIndex = _parseIndex;
2119
2120      scanToken();
2121
2122      if (peekToken() != BY) {
2123        token = IDENTIFIER;
2124
2125        // Restores parse index right after ORDER BY.
2126
_parseIndex = parseIndex;
2127        _lexeme = identifier;
2128        _token = -1;
2129      }
2130    } // 2. 'member': "SELECT m FROM Member m" (jpa/0x02)
2131
else if (_parsingFrom && token == MEMBER) {
2132      token = IDENTIFIER;
2133    }
2134
2135    if (token != IDENTIFIER) {
2136      throw error(L.l("expected identifier at `{0}'", tokenName(token)));
2137    }
2138
2139    return identifier;
2140  }
2141
2142  /**
2143   * Peeks the next token
2144   *
2145   * @return integer code for the token
2146   */

2147  private int peekToken()
2148    throws QueryParseException
2149  {
2150    if (_token > 0)
2151      return _token;
2152
2153    _token = scanToken();
2154
2155    return _token;
2156  }
2157
2158  /**
2159   * Scan the next token. If the lexeme is a string, its string
2160   * representation is in "lexeme".
2161   *
2162   * @return integer code for the token
2163   */

2164  private int scanToken()
2165    throws QueryParseException
2166  {
2167    if (_token > 0) {
2168      int value = _token;
2169      _token = -1;
2170      return value;
2171    }
2172
2173    int sign = 1;
2174    int ch;
2175
2176    for (ch = read(); Character.isWhitespace((char) ch); ch = read()) {
2177    }
2178
2179    switch (ch) {
2180    case -1:
2181    case '.':
2182    case '*':
2183    case '/':
2184    case ',':
2185    case '+':
2186    case '-':
2187    case '[':
2188    case ']':
2189      return ch;
2190
2191    case '(':
2192      _depth++;
2193      return ch;
2194
2195    case ')':
2196      _depth--;
2197      return ch;
2198
2199    case '=':
2200      if ((ch = read()) == '>')
2201        return EXTERNAL_DOT;
2202      else {
2203        unread(ch);
2204        return EQ;
2205      }
2206
2207    case '!':
2208      if ((ch = read()) == '=')
2209        return NE;
2210      else {
2211        unread(ch);
2212        return '!';
2213      }
2214
2215    case '<':
2216      if ((ch = read()) == '=')
2217        return LE;
2218      else if (ch == '>')
2219        return NE;
2220      else {
2221        unread(ch);
2222        return LT;
2223      }
2224
2225    case '>':
2226      if ((ch = read()) == '=')
2227        return GE;
2228      else {
2229        unread(ch);
2230        return GT;
2231      }
2232
2233    case '?':
2234      CharBuffer cb = CharBuffer.allocate();
2235      int index = 0;
2236      for (ch = read(); ch >= '0' && ch <= '9'; ch = read()) {
2237        cb.append((char) ch);
2238        index = 10 * index + ch - '0';
2239      }
2240      unread(ch);
2241
2242      _lexeme = cb.close();
2243
2244      if (_lexeme.length() == 0) {
2245        _lexeme = String.valueOf(++_parameterCount);
2246      }
2247      else if (index <= 0)
2248        throw error(L.l("`{0}' must refer to a positive argument",
2249                        "?" + _lexeme));
2250
2251      return ARG;
2252
2253    case ':':
2254      if (Character.isJavaIdentifierStart((char) (ch = read()))) {
2255        cb = CharBuffer.allocate();
2256
2257        for (; ch > 0 && Character.isJavaIdentifierPart((char) ch); ch = read())
2258          cb.append((char) ch);
2259
2260        unread(ch);
2261
2262        _lexeme = cb.close();
2263
2264        _parameterCount++;
2265      }
2266      else
2267        throw error(L.l("`{0}' must be a valid parameter identifier",
2268                        ":" + ((char) ch)));
2269
2270      return NAMED_ARG;
2271
2272    case '|':
2273      if ((ch = read()) == '|')
2274        return CONCAT_OP;
2275      else
2276        throw error(L.l("unexpected char at {0}", String.valueOf((char) ch)));
2277
2278      // @@ is useless?
2279
case '@':
2280      if ((ch = read()) != '@')
2281        throw error(L.l("`@' expected at {0}", charName(ch)));
2282      return scanToken();
2283    }
2284
2285    if (Character.isJavaIdentifierStart((char) ch)) {
2286      CharBuffer cb = CharBuffer.allocate();
2287
2288      for (; ch > 0 && Character.isJavaIdentifierPart((char) ch); ch = read())
2289        cb.append((char) ch);
2290
2291      unread(ch);
2292
2293      _lexeme = cb.close();
2294      String JavaDoc lower = _lexeme.toLowerCase();
2295
2296      int token = _reserved.get(lower);
2297
2298      if (token > 0)
2299        return token;
2300      else
2301        return IDENTIFIER;
2302    }
2303    else if (ch >= '0' && ch <= '9') {
2304      CharBuffer cb = CharBuffer.allocate();
2305
2306      int type = INTEGER;
2307
2308      if (sign < 0)
2309        cb.append('-');
2310
2311      for (; ch >= '0' && ch <= '9'; ch = read())
2312        cb.append((char) ch);
2313
2314      if (ch == '.') {
2315        type = DOUBLE;
2316
2317        cb.append('.');
2318        for (ch = read(); ch >= '0' && ch <= '9'; ch = read())
2319          cb.append((char) ch);
2320      }
2321
2322      if (ch == 'e' || ch == 'E') {
2323        type = DOUBLE;
2324
2325        cb.append('e');
2326        if ((ch = read()) == '+' || ch == '-') {
2327          cb.append((char) ch);
2328          ch = read();
2329        }
2330
2331        if (! (ch >= '0' && ch <= '9'))
2332          throw error(L.l("exponent needs digits at {0}",
2333                          charName(ch)));
2334
2335        for (; ch >= '0' && ch <= '9'; ch = read())
2336          cb.append((char) ch);
2337      }
2338
2339      if (ch == 'F' || ch == 'D')
2340        type = DOUBLE;
2341      else if (ch == 'L') {
2342        type = LONG;
2343      }
2344      else
2345        unread(ch);
2346
2347      _lexeme = cb.close();
2348
2349      return type;
2350    }
2351    else if (ch == '\'') {
2352      CharBuffer cb = CharBuffer.allocate();
2353
2354      cb.append("'");
2355      for (ch = read(); ch >= 0; ch = read()) {
2356        if (ch == '\'') {
2357          if ((ch = read()) == '\'')
2358            cb.append("''");
2359          else {
2360            unread(ch);
2361            break;
2362          }
2363        }
2364        else
2365          cb.append((char) ch);
2366      }
2367      cb.append("'");
2368
2369      _lexeme = cb.close();
2370
2371      return STRING;
2372    }
2373
2374    throw error(L.l("unexpected char at {0}", "" + (char) ch));
2375  }
2376
2377  /**
2378   * Returns the next character.
2379   */

2380  private int read()
2381  {
2382    if (_parseIndex < _sql.length())
2383      return _sql.charAt(_parseIndex++);
2384    else
2385      return -1;
2386  }
2387
2388  /**
2389   * Unread the last character.
2390   */

2391  private void unread(int ch)
2392  {
2393    if (ch >= 0)
2394      _parseIndex--;
2395  }
2396
2397  /**
2398   * Creates an error.
2399   */

2400  public QueryParseException error(String JavaDoc msg)
2401  {
2402    msg += "\nin \"" + _sql + "\"";
2403
2404    return new QueryParseException(msg);
2405  }
2406
2407  /**
2408   * Returns the name for a character
2409   */

2410  private String JavaDoc charName(int ch)
2411  {
2412    if (ch < 0)
2413      return L.l("end of query");
2414    else
2415      return String.valueOf((char) ch);
2416  }
2417
2418  /**
2419   * Returns the name of a token
2420   */

2421  private String JavaDoc tokenName(int token)
2422  {
2423    switch (token) {
2424    case AS: return "AS";
2425    case FROM: return "FROM";
2426    case IN: return "IN";
2427    case SELECT: return "SELECT";
2428    case WHERE: return "WHERE";
2429    case OR: return "OR";
2430    case AND: return "AND";
2431    case NOT: return "NOT";
2432    case BETWEEN: return "BETWEEN";
2433    case THIS: return "THIS";
2434    case TRUE: return "FALSE";
2435    case EMPTY: return "EMPTY";
2436    case MEMBER: return "MEMBER";
2437    case OF: return "OF";
2438    case NULL: return "NULL";
2439    case ORDER: return "ORDER";
2440    case BY: return "BY";
2441    case ASC: return "ASC";
2442    case DESC: return "DESC";
2443    case LIMIT: return "LIMIT";
2444
2445    case EXTERNAL_DOT: return "=>";
2446
2447    case -1:
2448      return L.l("end of query");
2449
2450    default:
2451      if (token < 128)
2452        return "'" + String.valueOf((char) token) + "'";
2453      else
2454        return "'" + _lexeme + "'";
2455    }
2456  }
2457
2458  /**
2459   * Returns a debuggable description of the select.
2460   */

2461  public String JavaDoc toString()
2462  {
2463    return "QueryParser[]";
2464  }
2465
2466  static {
2467    _reserved = new IntMap();
2468    _reserved.put("as", AS);
2469    _reserved.put("from", FROM);
2470    _reserved.put("in", IN);
2471    _reserved.put("select", SELECT);
2472    _reserved.put("update", UPDATE);
2473    _reserved.put("delete", DELETE);
2474    _reserved.put("set", SET);
2475    _reserved.put("distinct", DISTINCT);
2476    _reserved.put("where", WHERE);
2477    _reserved.put("order", ORDER);
2478    _reserved.put("group", GROUP);
2479    _reserved.put("by", BY);
2480    _reserved.put("having", HAVING);
2481    _reserved.put("asc", ASC);
2482    _reserved.put("desc", DESC);
2483    _reserved.put("limit", LIMIT);
2484    _reserved.put("offset", OFFSET);
2485
2486    _reserved.put("join", JOIN);
2487    _reserved.put("inner", INNER);
2488    _reserved.put("left", LEFT);
2489    _reserved.put("outer", OUTER);
2490    _reserved.put("fetch", FETCH);
2491
2492    _reserved.put("or", OR);
2493    _reserved.put("and", AND);
2494    _reserved.put("not", NOT);
2495
2496    _reserved.put("length", LENGTH);
2497    _reserved.put("locate", LOCATE);
2498
2499    _reserved.put("abs", ABS);
2500    _reserved.put("sqrt", SQRT);
2501    _reserved.put("mod", MOD);
2502    _reserved.put("size", SIZE);
2503
2504    _reserved.put("max", MAX);
2505    _reserved.put("min", MIN);
2506    _reserved.put("sum", SUM);
2507
2508    _reserved.put("concat", CONCAT);
2509    _reserved.put("lower", LOWER);
2510    _reserved.put("upper", UPPER);
2511    _reserved.put("substring", SUBSTRING);
2512    _reserved.put("trim", TRIM);
2513    _reserved.put("both", BOTH);
2514    _reserved.put("leading", LEADING);
2515    _reserved.put("trailing", TRAILING);
2516
2517    _reserved.put("current_date", CURRENT_DATE);
2518    _reserved.put("current_time", CURRENT_TIME);
2519    _reserved.put("current_timestamp", CURRENT_TIMESTAMP);
2520
2521    _reserved.put("between", BETWEEN);
2522    _reserved.put("like", LIKE);
2523    _reserved.put("escape", ESCAPE);
2524    _reserved.put("is", IS);
2525
2526    _reserved.put("new", NEW);
2527
2528    _reserved.put("this", THIS);
2529    _reserved.put("true", TRUE);
2530    _reserved.put("false", FALSE);
2531    _reserved.put("unknown", UNKNOWN);
2532    _reserved.put("empty", EMPTY);
2533    _reserved.put("member", MEMBER);
2534    _reserved.put("of", OF);
2535    _reserved.put("null", NULL);
2536  }
2537}
2538
Popular Tags