KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > ejb > ql > QLParser


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.ejb.ql;
31
32 import com.caucho.bytecode.JClass;
33 import com.caucho.bytecode.JMethod;
34 import com.caucho.config.ConfigException;
35 import com.caucho.config.LineConfigException;
36 import com.caucho.ejb.cfg.EjbConfig;
37 import com.caucho.ejb.cfg.EjbEntityBean;
38 import com.caucho.ejb.cfg.FunctionSignature;
39 import com.caucho.util.CharBuffer;
40 import com.caucho.util.IntMap;
41 import com.caucho.util.L10N;
42 import com.caucho.util.Log;
43
44 import javax.ejb.EJBLocalObject JavaDoc;
45 import javax.ejb.EntityBean JavaDoc;
46 import javax.ejb.FinderException JavaDoc;
47 import java.util.ArrayList JavaDoc;
48 import java.util.HashMap JavaDoc;
49 import java.util.logging.Logger JavaDoc;
50
51 /**
52  * Contains the parser for EJB-QL queries and stores the parsed expressions.
53  *
54  * <p>The expression tree is rooted at Expr.
55  */

56 public class QLParser extends Query {
57   static final Logger JavaDoc log = Log.open(QLParser.class);
58   static final L10N L = new L10N(QLParser.class);
59   
60   final static int IDENTIFIER = 128;
61   final static int INTEGER = IDENTIFIER + 1;
62   final static int LONG = INTEGER + 1;
63   final static int DOUBLE = LONG + 1;
64   final static int STRING = DOUBLE + 1;
65   final static int TRUE = STRING + 1;
66   final static int FALSE = TRUE + 1;
67   final static int UNKNOWN = FALSE + 1;
68   final static int MEMBER = UNKNOWN + 1;
69   final static int OF = MEMBER + 1;
70   final static int EMPTY = OF + 1;
71   final static int NULL = EMPTY + 1;
72   
73   final static int FROM = NULL + 1;
74   final static int IN = FROM + 1;
75   final static int SELECT = IN + 1;
76   final static int DISTINCT = SELECT + 1;
77   final static int WHERE = SELECT + 1;
78   final static int AS = WHERE + 1;
79   final static int ORDER = AS + 1;
80   final static int BY = ORDER + 1;
81   final static int ASC = BY + 1;
82   final static int DESC = ASC + 1;
83   final static int LIMIT = DESC + 1;
84   final static int OFFSET = LIMIT + 1;
85   
86   final static int BETWEEN = OFFSET + 1;
87   final static int LIKE = BETWEEN + 1;
88   final static int ESCAPE = LIKE + 1;
89   final static int IS = ESCAPE + 1;
90   
91   final static int EQ = IS + 1;
92   final static int NE = EQ + 1;
93   final static int LT = NE + 1;
94   final static int LE = LT + 1;
95   final static int GT = LE + 1;
96   final static int GE = GT + 1;
97   
98   final static int AND = GE + 1;
99   final static int OR = AND + 1;
100   final static int NOT = OR + 1;
101
102   final static int EXTERNAL_DOT = NOT + 1;
103   
104   final static int ARG = EXTERNAL_DOT + 1;
105   final static int THIS = ARG + 1;
106
107   private static IntMap _reserved;
108
109   private String JavaDoc _location;
110   
111   // The owning bean
112
private EjbEntityBean _bean;
113   
114   // The target bean
115
private EjbEntityBean _target;
116
117   // the functions
118
private ArrayList JavaDoc<FunctionSignature> _functions;
119   
120   // Method name to generate
121
private String JavaDoc _methodName;
122   // Return class
123
private JClass _returnType;
124   // Return ejb
125
private String JavaDoc _returnEJB;
126   // EJB-QL
127
private String JavaDoc _query;
128
129   // list of the identifier
130
private ArrayList JavaDoc<FromItem> _fromList;
131   // list of the identifiers
132
private ArrayList JavaDoc<PathExpr> _fromIds = new ArrayList JavaDoc<PathExpr>();
133   
134   // list of the relation links
135
//private ArrayList<LinkItem> _linkList;
136
// select expression
137
private Expr _selectExpr;
138   // is distinct (set)
139
private boolean _isDistinct;
140   
141   // from table
142
private String JavaDoc _fromTable;
143   // from identifier
144
private String JavaDoc _fromId;
145   // this expression
146
private IdExpr _thisExpr;
147   // where expression
148
private Expr _whereExpr;
149   // arguments
150
private ArrayList JavaDoc<Expr> _argList;
151   // order by expression
152
private ArrayList JavaDoc<Expr> _orderExprList;
153   // order by ascending/descending
154
private ArrayList JavaDoc<Boolean JavaDoc> _orderAscendingList;
155   // order by limit max
156
private Expr _limitMax;
157   // order by limit offset
158
private Expr _limitOffset;
159
160   private AndExpr _andExpr;
161   private boolean _isWhere;
162
163   private HashMap JavaDoc<String JavaDoc,PathExpr> _idMap;
164   private HashMap JavaDoc<Expr,Expr> _pathMap;
165   
166   // parse index
167
private int _parseIndex;
168   // current token
169
private int _token;
170   // temp for parsing
171
private String JavaDoc lexeme;
172
173   private int _unique;
174
175   private String JavaDoc _booleanTrue = "1";
176   private String JavaDoc _booleanFalse = "0";
177
178   private boolean _addArgToQuery = true;
179
180   private boolean _queryLoadsBean = true;
181
182   private int _maxArg;
183
184   private QLParser(EjbEntityBean bean)
185   {
186     _bean = bean;
187
188     // _functions = FunExpr.getStandardFunctions();
189
_functions = bean.getConfig().getFunctions();
190   }
191   
192   /**
193    * Creates a new select method.
194    *
195    * @param bean the owning persistent bean
196    * @param methodName the method name to implement
197    * @param method the method signature
198    */

199   public QLParser(EjbEntityBean bean,
200           String JavaDoc methodName,
201           JMethod method,
202           JClass returnType)
203     throws ConfigException
204   {
205     this(bean);
206
207     setMethod(method);
208     
209     _methodName = methodName;
210     _returnType = returnType;
211
212     JClass []exn = method.getExceptionTypes();
213     for (int i = 0; i < exn.length; i++)
214       if (exn[i].isAssignableTo(FinderException JavaDoc.class))
215         return;
216
217     throw new ConfigException(L.l("{0}: `{1}' must throw javax.ejb.FinderException.",
218                   method.getDeclaringClass().getName(),
219                   getFullMethodName(method)));
220   }
221
222   public static void parseOrderBy(EjbEntityBean bean,
223                   String JavaDoc orderBy,
224                   ArrayList JavaDoc<String JavaDoc> orderList,
225                   ArrayList JavaDoc<Boolean JavaDoc> orderAscendingList)
226     throws ConfigException
227   {
228     QLParser query = new QLParser(bean);
229
230     query.parseOrderBy(orderBy, orderList, orderAscendingList);
231   }
232
233   /**
234    * Sets the location.
235    */

236   public void setLocation(String JavaDoc location)
237   {
238     _location = location;
239   }
240   
241   /**
242    * Returns the owning bean
243    */

244   public EjbEntityBean getPersistentBean()
245   {
246     return _bean;
247   }
248
249   /**
250    * Gets a persistent bean by its type.
251    */

252   public EjbEntityBean getBeanByName(String JavaDoc ejbName)
253   {
254     // return _bean.getManager().getBeanInfoByName(ejbName);
255
throw new UnsupportedOperationException JavaDoc();
256   }
257
258   /**
259    * Gets a persistent bean by its type.
260    */

261   public EjbEntityBean getBeanByType(JClass type)
262   {
263     //return _bean.getManager().getBeanInfoByRemote(type);
264
throw new UnsupportedOperationException JavaDoc();
265   }
266
267   /**
268    * Returns the name of the select method.
269    */

270   public String JavaDoc getName()
271   {
272     return _methodName;
273   }
274
275   /**
276    * Returns the function map.
277    */

278   public ArrayList JavaDoc<FunctionSignature> getFunctions()
279   {
280     return _functions;
281   }
282
283   /**
284    * Sets the function map.
285    */

286   public void setFunctions(ArrayList JavaDoc<FunctionSignature> functions)
287   {
288     _functions = functions;
289   }
290
291   /**
292    * Sets the boolean true.
293    */

294   public void setBooleanTrue(String JavaDoc literal)
295   {
296     _booleanTrue = literal;
297   }
298
299   /**
300    * Sets the boolean false.
301    */

302   public void setBooleanFalse(String JavaDoc literal)
303   {
304     _booleanFalse = literal;
305   }
306   
307   /**
308    * Returns the return type of the select method.
309    */

310   public JClass getReturnType()
311   {
312     return _returnType;
313   }
314   
315   /**
316    * Returns the return type of the select method.
317    */

318   public String JavaDoc getReturnEJB()
319   {
320     return _selectExpr.getReturnEJB();
321   }
322   
323   /**
324    * Sets the return type of the select method
325    */

326   void setReturnType(JClass returnType)
327   {
328     _returnType = returnType;
329   }
330
331   /**
332    * Returns the EJB-QL string
333    */

334   public String JavaDoc getQuery()
335   {
336     return _query;
337   }
338
339   /**
340    * Gets the select expression
341    */

342   public Expr getSelectExpr()
343   {
344     return _selectExpr;
345   }
346
347   public boolean isDistinct()
348   {
349     return _isDistinct;
350   }
351
352   public boolean getQueryLoadsBean()
353   {
354     return _queryLoadsBean;
355   }
356
357   public void setQueryLoadsBean(boolean loadBean)
358   {
359     _queryLoadsBean = loadBean;
360   }
361   
362   /**
363    * Gets the this expression.
364    */

365   IdExpr getThisExpr()
366   {
367     return _thisExpr;
368   }
369   
370   /**
371    * Gets the where expression.
372    */

373   Expr getWhereExpr()
374   {
375     return _whereExpr;
376   }
377   
378   /**
379    * Gets the order by expression.
380    */

381   ArrayList JavaDoc<Expr> getOrderExprList()
382   {
383     return _orderExprList;
384   }
385
386   /**
387    * Returns true if the order is ascending.
388    */

389   ArrayList JavaDoc<Boolean JavaDoc> getAscendingList()
390   {
391     return _orderAscendingList;
392   }
393
394   /**
395    * Returns any limit max expression
396    */

397   Expr getLimitMax()
398   {
399     return _limitMax;
400   }
401
402   /**
403    * Returns any limit offset expression
404    */

405   Expr getLimitOffset()
406   {
407     return _limitOffset;
408   }
409
410   /**
411    * Gets the from table
412    */

413   ArrayList JavaDoc<FromItem> getFromList()
414   {
415     return _fromList;
416   }
417
418   void addFromItem(String JavaDoc id, String JavaDoc table)
419   {
420     _fromList.add(new FromItem(id, table));
421   }
422
423   public int getUnique()
424   {
425     return _unique++;
426   }
427   
428   /**
429    * Adds a relation expression
430    */

431   /*
432   public void addLink(String tableA, String columnA,
433                       String tableB, String columnB,
434               boolean isCommon)
435   {
436     if (isCommon)
437       addCommonLink(tableA, columnA, tableB, columnB);
438     else
439       addLink(tableA, columnA, tableB, columnB);
440   }
441   */

442   
443   /**
444    * Adds a relation expression
445    */

446   /*
447   public void addLink(String tableA, String columnA,
448                       String tableB, String columnB)
449   {
450     if (_andExpr != null) {
451       LinkExpr expr = new LinkExpr(tableA, columnA, tableB, columnB);
452
453       _andExpr.add(expr);
454
455       return;
456     }
457
458     addCommonLink(tableA, columnA, tableB, columnB);
459   }
460   */

461   
462   /**
463    * Adds a relation expression
464    */

465   /*
466   public void addCommonLink(String tableA, String columnA,
467                 String tableB, String columnB)
468   {
469     if (_linkList == null)
470       _linkList = new ArrayList<LinkItem>();
471
472     LinkItem item = new LinkItem(tableA, columnA, tableB, columnB);
473
474     if (! _linkList.contains(item))
475       _linkList.add(item);
476   }
477   */

478
479   /**
480    * Returns the auxiliary relation expressions
481    */

482   /*
483   ArrayList<LinkItem> getRelations()
484   {
485     return _linkList;
486   }
487   */

488
489   /**
490    * Adds a select method argument
491    */

492   public void addArg(Expr expr)
493   {
494     _argList.add(expr);
495   }
496
497   /**
498    * Gets the select method arguments in SQL order.
499    */

500   ArrayList JavaDoc<Expr> getArgList()
501   {
502     return _argList;
503   }
504   
505   /**
506    * Parses the select method's query.
507    *
508    * @param query the source query string.
509    */

510   public void parseOrderBy(String JavaDoc orderBy,
511                            ArrayList JavaDoc<String JavaDoc> orderList,
512                            ArrayList JavaDoc<Boolean JavaDoc> orderAscendingList)
513     throws ConfigException
514   {
515     _query = orderBy;
516
517     _parseIndex = 0;
518     _unique = 0;
519     _token = -1;
520
521     int token = -1;
522     
523     do {
524       token = scanToken();
525
526       if (token == IDENTIFIER)
527         orderList.add(lexeme.toString());
528       else
529         throw error(L.l("unexpected token `{0}' in order-by",
530                         tokenName(token)));
531
532       token = scanToken();
533       if (token == DESC) {
534         token = scanToken();
535         orderAscendingList.add(Boolean.FALSE);
536       }
537       else if (token == ASC) {
538         token = scanToken();
539         orderAscendingList.add(Boolean.TRUE);
540       }
541       else
542         orderAscendingList.add(Boolean.TRUE);
543     } while (token == ',');
544
545     if (token >= 0)
546       throw error(L.l("extra token {0} at end of order-by",
547                       tokenName(peekToken())));
548   }
549   
550   /**
551    * Parses the select method's query.
552    *
553    * @param query the source query string.
554    */

555   public EjbQuery parseQuery(String JavaDoc query)
556     throws ConfigException
557   {
558     int token;
559
560     _query = query;
561     _fromList = new ArrayList JavaDoc<FromItem>();
562     _pathMap = new HashMap JavaDoc<Expr,Expr>();
563     _idMap = new HashMap JavaDoc<String JavaDoc,PathExpr>();
564     _argList = new ArrayList JavaDoc<Expr>();
565     //_linkList = new ArrayList<LinkItem>();
566

567     _parseIndex = 0;
568     _unique = 0;
569     _token = -1;
570
571     setConfig(_bean.getConfig());
572     
573     // First pass parses the from
574
for (; (token = peekToken()) >= 0 && token != FROM; token = scanToken()) {
575     }
576     
577     if (token != FROM)
578       throw error(L.l("expected FROM at {0}",
579                       tokenName(token)));
580
581     scanToken();
582
583     parseFrom();
584
585     token = peekToken();
586     if (token >= 0 && token != WHERE && token != ORDER)
587       throw error(L.l("expected WHERE or ORDER at {0}",
588                       tokenName(token)));
589
590     _parseIndex = 0;
591     _token = -1;
592     token = scanToken();
593
594     if (token != SELECT)
595       throw error(L.l("expected SELECT at {0}", tokenName(token)));
596     
597     if (peekToken() == DISTINCT) {
598       scanToken();
599       _isDistinct = true;
600     }
601
602     _selectExpr = parseExpr();
603
604     /*
605     if (_selectExpr instanceof CollectionExpr) {
606       CollectionExpr expr = (CollectionExpr) _selectExpr;
607       
608       _selectExpr = new CollectionIdExpr(this, "foo", expr);
609     }
610     */

611
612     if (_selectExpr instanceof PathExpr)
613       ((PathExpr) _selectExpr).setUsesField();
614
615     /*
616     if (! (selectExpr instanceof PathExpr) &&
617         ! (selectExpr instanceof FieldExpr))
618       throw error(L.l("`{0}' is an illegal SELECT expression. Only path expressions are allowed in SELECT.", selectExpr));
619     */

620     
621     token = peekToken();
622     if (token != FROM)
623       throw error(L.l("expected FROM at {0}",
624                       tokenName(token)));
625
626     // skip over the from since it's been parsed
627
for (;
628          (token = peekToken()) >= 0 && token != WHERE && token != ORDER;
629          token = scanToken()) {
630     }
631
632     _addArgToQuery = true;
633     
634     if ((token = scanToken()) == WHERE) {
635       _isWhere = true;
636       _whereExpr = parseExpr();
637       _isWhere = false;
638       token = scanToken();
639     }
640
641     if (_whereExpr != null && ! _whereExpr.isBoolean())
642       throw error(L.l("WHERE must be a boolean expression at {0}",
643                       _whereExpr));
644
645     _addArgToQuery = false;
646     
647     int oldMaxArg = _maxArg;
648     if (token == ORDER) {
649       if (peekToken() == BY)
650         scanToken();
651       if (_orderExprList == null)
652         _orderExprList = new ArrayList JavaDoc<Expr>();
653       
654       if (_orderAscendingList == null)
655         _orderAscendingList = new ArrayList JavaDoc<Boolean JavaDoc>();
656
657       do {
658         _orderExprList.add(parseExpr());
659
660         token = peekToken();
661         if (token == DESC) {
662           token = scanToken();
663           _orderAscendingList.add(Boolean.FALSE);
664         }
665         else if (token == ASC) {
666           token = scanToken();
667           _orderAscendingList.add(Boolean.TRUE);
668         }
669         else
670           _orderAscendingList.add(Boolean.TRUE);
671       } while ((token = scanToken()) == ',');
672     }
673
674     if (token == OFFSET) {
675       _limitOffset = parseExpr();
676       token = scanToken();
677
678       if (! _limitOffset.getJavaType().getName().equals("int"))
679         throw error(L.l("OFFSET `{0}' must be an integer expression",
680                         _limitMax));
681     }
682
683     if (token == LIMIT) {
684       _limitMax = parseExpr();
685       token = scanToken();
686
687       if (! _limitMax.getJavaType().getName().equals("int"))
688         throw error(L.l("LIMIT `{0}' must be an integer expression",
689                         _limitMax));
690     }
691
692     if (token >= 0)
693       throw error(L.l("extra token {0} at end of query",
694                       tokenName(peekToken())));
695     _maxArg = oldMaxArg;
696
697     EjbSelectQuery ejbQuery = new EjbSelectQuery(_query);
698
699     ejbQuery.setDistinct(_isDistinct);
700     ejbQuery.setFromList(_fromIds);
701     ejbQuery.setSelectExpr(_selectExpr);
702     ejbQuery.setWhereExpr(_whereExpr);
703
704     ejbQuery.setMaxArg(_maxArg);
705     ejbQuery.setThisExpr(_thisExpr);
706
707     ejbQuery.setOrderBy(_orderExprList, _orderAscendingList);
708     
709     ejbQuery.setOffset(_limitOffset);
710     ejbQuery.setLimit(_limitMax);
711
712     return ejbQuery;
713   }
714
715   /**
716    * Parses the FROM block. parseFrom's effect is to populate the
717    * core identifiers.
718    *
719    * <pre>
720    * from-list ::= from-list ',' from-item
721    * ::= from-item
722    *
723    * from-item ::= IDENTIFIER AS? IDENTIFIER
724    * ::= IN(collection-path) AS? IDENTIFIER
725    * </pre>
726    */

727   private void parseFrom()
728     throws ConfigException
729   {
730     boolean moreTables = true;
731
732     while (moreTables) {
733       int token = scanToken();
734
735       if (token == IN) {
736         if (scanToken() != '(')
737           throw error(L.l("expected `(' at {0} while parsing IN(<collection-path>).",
738                           tokenName(token)));
739
740         parseFromCollection();
741       }
742       else if (token == IDENTIFIER) {
743         String JavaDoc id = lexeme;
744         if (peekToken() == AS)
745           scanToken();
746         
747         parseFromTable(id);
748       }
749       else
750         throw error(L.l("expected identifier at {0} while parsing FROM",
751                         tokenName(token)));
752
753       if (peekToken() == ',') {
754         scanToken();
755         moreTables = true;
756       }
757       else
758         moreTables = false;
759     }
760   }
761
762   /**
763    * Parses a single-valued table identifier.
764    */

765   private void parseFromTable(String JavaDoc table)
766     throws ConfigException
767   {
768     int token = scanToken();
769     
770     if (token != IDENTIFIER) {
771       throw error(L.l("expected identifier at {0} while parsing FROM {1} [AS] var", tokenName(token), table));
772     }
773
774     String JavaDoc name = lexeme;
775
776     EjbConfig ejbConfig = _bean.getConfig();
777
778     EjbEntityBean entity = ejbConfig.findEntityBySchema(table);
779
780     if (entity == null)
781       throw error(L.l("`{0}' is an unknown entity-bean schema in `FROM {0} AS {1}'",
782                       table, name));
783
784     // _bean.addBeanDepend(info.getEJBName());
785

786     IdExpr id = new IdExpr(this, name, entity);
787
788     addIdentifier(name, id);
789     addFromItem(name, entity.getSQLTable());
790
791     _fromIds.add(id);
792   }
793
794   /**
795    * Parses a collection-valued table identifier.
796    */

797   private void parseFromCollection()
798     throws ConfigException
799   {
800     Expr expr = parseDotExpr();
801
802     if (scanToken() != ')')
803       throw error(L.l("expected `)' at {0} while parsing IN(<collection-path>).",
804                       tokenName(_token)));
805     
806     if (! (expr instanceof CollectionExpr))
807       throw error(L.l("expected <collection-path> expression at `{0}'", expr));
808
809     CollectionExpr collectionExpr = (CollectionExpr) expr;
810
811     if (peekToken() == AS)
812       scanToken();
813
814     int token = scanToken();
815     if (token != IDENTIFIER)
816       throw error(L.l("expected identifier expression at {0} while parsing `IN({1}) AS id'",
817                       tokenName(token), expr));
818       
819     String JavaDoc name = lexeme;
820
821     CollectionIdExpr idExpr;
822     idExpr = new CollectionIdExpr(this, name, collectionExpr);
823
824     addIdentifier(name, idExpr);
825     
826     _fromIds.add(idExpr);
827   }
828   
829   /**
830    * Parses the next expression.
831    *
832    * <pre>
833    * expr ::= or-expr
834    * </pre>
835    *
836    * @return the parsed expression
837    */

838   private Expr parseExpr()
839     throws ConfigException
840   {
841     return parseOrExpr();
842   }
843
844   /**
845    * Parses an or expression.
846    *
847    * <pre>
848    * or-expr ::= or-expr AND and-expr
849    * ::= and-expr
850    * </pre>
851    *
852    * @return the parsed expression
853    */

854   private Expr parseOrExpr()
855     throws ConfigException
856   {
857     Expr expr = parseAndExpr();
858     
859     int token = peekToken();
860     while ((token = peekToken()) == OR) {
861       scanToken();
862
863       expr = new BinaryExpr(this, token, expr, parseAndExpr());
864     }
865
866     return expr;
867   }
868
869   /**
870    * Parses an and expression.
871    *
872    * <pre>
873    * and-expr ::= and-expr AND not-expr
874    * ::= not-expr
875    * </pre>
876    *
877    * @return the parsed expression
878    */

879   private Expr parseAndExpr()
880     throws ConfigException
881   {
882     AndExpr oldAnd = _andExpr;
883     
884     AndExpr andExpr = new AndExpr(this);
885
886     if (_isWhere)
887       _andExpr = andExpr;
888     
889     Expr expr = parseNotExpr();
890
891     andExpr.add(expr);
892     
893     int token = peekToken();
894     while ((token = peekToken()) == AND) {
895       scanToken();
896
897       expr = parseNotExpr();
898
899       andExpr.add(expr);
900     }
901
902     _andExpr = oldAnd;
903
904     return andExpr.getSingleExpr();
905   }
906
907   /**
908    * Parses a not expression.
909    *
910    * <pre>
911    * not-expr ::= NOT? cmp-expr
912    * </pre>
913    *
914    * @return the parsed expression
915    */

916   private Expr parseNotExpr()
917     throws ConfigException
918   {
919     int token = peekToken();
920     if (token == NOT) {
921       scanToken();
922
923       Expr expr = parseCmpExpr();
924       
925       return new UnaryExpr(NOT, expr);
926     }
927     else
928       return parseCmpExpr();
929   }
930
931   /**
932    * Parses a comparison expression.
933    *
934    * <pre>
935    * cmp-expr ::= add-expr '=' add-expr is-term?
936    * ::= add-expr 'NOT'? 'BETWEEN' add-expr 'AND' add-expr is-term?
937    * ::= add-expr 'NOT'? 'LIKE' string ('ESCAPE' string)? is-term?
938    * ::= add-expr 'NOT'? 'IN' ('lit-1', ..., 'lit-n')
939    * ::= add-expr
940    * </pre>
941    *
942    * @return the parsed expression
943    */

944   private Expr parseCmpExpr()
945     throws ConfigException
946   {
947     int token = peekToken();
948     boolean isNot = false;
949     
950     Expr expr = parseArithmeticExpr();
951     
952     token = peekToken();
953     
954     if (token == NOT) {
955       scanToken();
956       isNot = true;
957       token = peekToken();
958     }
959     
960     if (token >= EQ && token <= GE) {
961       scanToken();
962       
963       return parseIs(new BinaryExpr(this, token, expr, parseAddExpr()));
964     }
965     else if (token == BETWEEN) {
966       scanToken();
967       
968       Expr a = parseArithmeticExpr();
969
970       if ((token = scanToken()) != AND)
971         throw error(L.l("Expected 'AND' at {0}", tokenName(token)));
972
973       Expr b = parseArithmeticExpr();
974
975       return parseIs(new BetweenExpr(expr, a, b, isNot));
976     }
977     else if (token == LIKE) {
978       scanToken();
979
980       Expr pattern = parseArithmeticExpr();
981       
982       String JavaDoc escape = null;
983       if (peekToken() == ESCAPE) {
984         scanToken();
985         
986         if ((token = scanToken()) != STRING)
987           throw error(L.l("Expected string at {0}", tokenName(token)));
988
989         escape = lexeme.toString();
990       }
991
992       return parseIs(new LikeExpr(expr, pattern, escape, isNot));
993     }
994     else if (token == IN) {
995       scanToken();
996       token = scanToken();
997
998       if (token != '(')
999         throw error(L.l("Expected '(' after IN at {0}", tokenName(token)));
1000
1001      ArrayList JavaDoc<Expr> args = new ArrayList JavaDoc<Expr>();
1002      while ((token = peekToken()) > 0 && token != ')') {
1003        Expr arg = parseArithmeticExpr();
1004
1005        args.add(arg);
1006
1007        token = peekToken();
1008        if (token == ',') {
1009          scanToken();
1010          token = peekToken();
1011        }
1012      }
1013
1014      if (peekToken() != ')')
1015        throw error(L.l("Expected ')' after IN at {0}", tokenName(token)));
1016
1017      scanToken();
1018
1019      return new InExpr(this, expr, args, isNot);
1020    }
1021    else if (token == IS) {
1022      scanToken();
1023
1024      if (isNot)
1025        throw error(L.l("'NOT IS' is an invalid expression."));
1026
1027      token = scanToken();
1028      if (token == NOT) {
1029        isNot = true;
1030        token = scanToken();
1031      }
1032
1033      if (token == NULL)
1034        return parseIs(new IsExpr(this, expr, NULL, isNot));
1035      else if (token == EMPTY) {
1036        if (! (expr instanceof CollectionExpr))
1037          throw error(L.l("IS EMPTY requires collection path at `{0}'",
1038                          expr));
1039        return parseIs(new EmptyExpr(expr, isNot));
1040      }
1041      else
1042        throw error(L.l("`{0}' unexpected after IS.", tokenName(token)));
1043    }
1044    else if (token == MEMBER) {
1045      scanToken();
1046      
1047      token = peekToken();
1048      if (token == OF)
1049        token = scanToken();
1050
1051      Expr collection = parseDotExpr();
1052        
1053      return parseIs(new MemberExpr(isNot, expr, collection));
1054    }
1055    else
1056      return expr;
1057  }
1058
1059  /**
1060   * Parses an 'IS' term
1061   *
1062   * <pre>
1063   * is-term ::= IS NOT? (TRUE|FALSE|UNKNOWN)
1064   * </pre>
1065   */

1066  private Expr parseIs(Expr base)
1067    throws ConfigException
1068  {
1069    if (peekToken() != IS)
1070      return base;
1071
1072    scanToken();
1073    boolean isNot = peekToken() == NOT;
1074    if (isNot)
1075      scanToken();
1076
1077    int token = scanToken();
1078    if (token == UNKNOWN)
1079      return new IsExpr(this, base, NULL, isNot);
1080    else if (token == TRUE)
1081      return isNot ? new UnaryExpr(NOT, base) : base;
1082    else if (token == FALSE)
1083      return isNot ? base : new UnaryExpr(NOT, base);
1084
1085    throw error(L.l("expected TRUE or FALSE at {0}", tokenName(token)));
1086  }
1087
1088  /**
1089   * Parses an arithmetic expression.
1090   *
1091   * <pre>
1092   * arithmetic-expr ::= add-expr
1093   * </pre>
1094   */

1095  private Expr parseArithmeticExpr()
1096    throws ConfigException
1097  {
1098    return parseAddExpr();
1099  }
1100
1101  /**
1102   * Parses an addition expression.
1103   *
1104   * <pre>
1105   * add-expr ::= add-expr ('+' | '-') mul-expr
1106   * ::= mul-expr
1107   * </pre>
1108   *
1109   * @return the parsed expression
1110   */

1111  private Expr parseAddExpr()
1112    throws ConfigException
1113  {
1114    Expr expr = parseMulExpr();
1115    
1116    int token = peekToken();
1117    while ((token = peekToken()) == '-' || token == '+') {
1118      scanToken();
1119
1120      expr = new BinaryExpr(this, token, expr, parseMulExpr());
1121    }
1122
1123    return expr;
1124  }
1125
1126  /**
1127   * Parses a multiplication/division expression.
1128   *
1129   * <pre>
1130   * mul-expr ::= mul-expr ('*' | '/') unary-expr
1131   * ::= unary-expr
1132   * </pre>
1133   *
1134   * @return the parsed expression
1135   */

1136  private Expr parseMulExpr()
1137    throws ConfigException
1138  {
1139    Expr expr = parseUnaryExpr();
1140    
1141    int token = peekToken();
1142    while ((token = peekToken()) == '*' || token == '/') {
1143      scanToken();
1144
1145      expr = new BinaryExpr(this, token, expr, parseUnaryExpr());
1146    }
1147
1148    return expr;
1149  }
1150
1151  /**
1152   * Parses a unary +/-
1153   *
1154   * <pre>
1155   * unary-expr ::= ('+'|'-')? path-expr
1156   * </pre>
1157   *
1158   * @return the parsed expression
1159   */

1160  private Expr parseUnaryExpr()
1161    throws ConfigException
1162  {
1163    int token = peekToken();
1164
1165    if (token == '+' || token == '-') {
1166      scanToken();
1167      return new UnaryExpr(token, parseRefExpr());
1168    }
1169    else
1170      return parseRefExpr();
1171  }
1172
1173  /**
1174   * Parses a path expression.
1175   *
1176   * <pre>
1177   * ref-expr ::= path-expr '=>' IDENTIFIER
1178   * ::= path-expr
1179   * </pre>
1180   *
1181   * @return the parsed expression
1182   */

1183  private Expr parseRefExpr()
1184    throws ConfigException
1185  {
1186    Expr expr = parseDotExpr();
1187    
1188    int token;
1189    if ((token = peekToken()) == EXTERNAL_DOT) {
1190      scanToken();
1191
1192      token = scanToken();
1193
1194      if (token != IDENTIFIER)
1195        throw error(L.l("expected field identifier at {0}",
1196                        tokenName(token)));
1197
1198      expr = expr.newField(lexeme);
1199      expr.evalTypes();
1200
1201      if (! expr.isExternal())
1202        throw error(L.l("`{0}' must refer to an external entity bean", expr));
1203    }
1204
1205    return expr;
1206  }
1207
1208  /**
1209   * Parses a path expression.
1210   *
1211   * <pre>
1212   * path-expr ::= path-expr '.' IDENTIFIER
1213   * ::= term
1214   * </pre>
1215   *
1216   * @return the parsed expression
1217   */

1218  private Expr parseDotExpr()
1219    throws ConfigException
1220  {
1221    Expr expr = parseTerm();
1222    
1223    int token;
1224    while ((token = peekToken()) == '.') {
1225      scanToken();
1226
1227      token = scanToken();
1228
1229      if (token != IDENTIFIER)
1230        throw error(L.l("expected field identifier at {0}",
1231                        tokenName(token)));
1232
1233      expr.evalTypes();
1234      if (expr.isExternal())
1235        throw error(L.l("`{0}' must not refer to an external entity bean", expr));
1236      Expr field = expr.newField(lexeme);
1237      expr = field;
1238
1239      Expr equiv = _pathMap.get(field);
1240      if (equiv != null)
1241        expr = equiv;
1242      else
1243        _pathMap.put(field, field);
1244    }
1245
1246    return expr;
1247  }
1248
1249  /**
1250   * Parses a term
1251   *
1252   * <pre>
1253   * term ::= IDENTIFIER | INTEGER | LONG | DOUBLE | STRING
1254   * ::= THIS '.' IDENTIFIER
1255   * ::= IDENTIFIER '(' args ')'
1256   * ::= '(' args ')'
1257   * </pre>
1258   */

1259  private Expr parseTerm()
1260    throws ConfigException
1261  {
1262    int token = scanToken();
1263
1264    switch (token) {
1265    case IDENTIFIER:
1266      String JavaDoc name = lexeme.toString();
1267      if (peekToken() != '(')
1268        return getIdentifier(name);
1269      else
1270        return parseFunction(name);
1271
1272    case FALSE:
1273      return new LiteralExpr(_booleanFalse, boolean.class);
1274      
1275    case TRUE:
1276      return new LiteralExpr(_booleanTrue, boolean.class);
1277
1278    case INTEGER:
1279      return new LiteralExpr(lexeme, int.class);
1280
1281    case LONG:
1282      return new LiteralExpr(lexeme, long.class);
1283
1284    case DOUBLE:
1285      return new LiteralExpr(lexeme, double.class);
1286
1287    case STRING:
1288      return new LiteralExpr(lexeme, String JavaDoc.class);
1289
1290    case ARG:
1291    {
1292      ArgExpr arg = new ArgExpr(this, Integer.parseInt(lexeme));
1293      if (_addArgToQuery)
1294        addArg(arg);
1295      return arg;
1296    }
1297
1298    case THIS:
1299    {
1300      if (_thisExpr == null) {
1301        _thisExpr = new IdExpr(this, "caucho_this", _bean);
1302        addFromItem("caucho_this", _bean.getSQLTable());
1303    _fromIds.add(_thisExpr);
1304        _argList.add(0, new ThisExpr(this, _bean));
1305      }
1306
1307      return _thisExpr;
1308    }
1309
1310    case '(':
1311      Expr expr = parseExpr();
1312      if ((token = scanToken()) != ')')
1313        throw error(L.l("expected `)' at {0}", tokenName(token)));
1314
1315      return expr;
1316        
1317
1318    default:
1319      throw error(L.l("expected term at {0}", tokenName(token)));
1320    }
1321  }
1322
1323  /**
1324   * Parses a function
1325   *
1326   * <pre>
1327   * function ::= IDENTIFIER '(' expr (',' expr)* ')'
1328   * ::= IDENTIFIER '(' ')'
1329   * </pre>
1330   */

1331  private Expr parseFunction(String JavaDoc name)
1332    throws ConfigException
1333  {
1334    ArrayList JavaDoc<Expr> args = new ArrayList JavaDoc<Expr>();
1335
1336    int token;
1337    if ((token = scanToken()) != '(')
1338      throw error(L.l("expected `(' at {0} while parsing function {1}()", tokenName(token), name));
1339
1340    while ((token = peekToken()) != ')' && token > 0) {
1341      Expr expr = parseExpr();
1342
1343      args.add(expr);
1344
1345      if ((token = peekToken()) == ',')
1346        scanToken();
1347    }
1348
1349    if (token != ')')
1350      throw error(L.l("expected `)' at {0} while parsing function {1}", tokenName(token), name));
1351
1352    scanToken();
1353
1354    if (name.equalsIgnoreCase("object")) {
1355      if (args.size() != 1)
1356        throw error(L.l("OBJECT() requires a single argument"));
1357      
1358      Expr expr = args.get(0);
1359
1360      if (! expr.getJavaType().isAssignableTo(EntityBean.class) &&
1361          ! expr.getJavaType().isAssignableTo(EJBLocalObject JavaDoc.class))
1362        throw error(L.l("OBJECT({0}) requires an entity bean as its argument at `{1}'",
1363                        expr, expr.getJavaType()));
1364
1365      return expr;
1366    }
1367    else {
1368      try {
1369        return new FunExpr(name, args, _functions);
1370      } catch (ConfigException e) {
1371        throw error(e.getMessage());
1372      }
1373    }
1374  }
1375
1376  /**
1377   * Adds a new identifier
1378   *
1379   * @param name the name of the identifier
1380   *
1381   * @return the IdExpr corresponding to the identifier
1382   */

1383  void addIdentifier(String JavaDoc name, PathExpr expr)
1384    throws ConfigException
1385  {
1386    Expr oldExpr = _idMap.get(name);
1387    
1388    if (oldExpr != null)
1389      throw error(L.l("`{0}' is defined twice", name));
1390
1391    _idMap.put(name, expr);
1392  }
1393
1394  /**
1395   * Adds a new identifier
1396   *
1397   * @param name the name of the identifier
1398   *
1399   * @return the IdExpr corresponding to the identifier
1400   */

1401  PathExpr getIdentifier(String JavaDoc name)
1402    throws ConfigException
1403  {
1404    PathExpr expr = _idMap.get(name);
1405    
1406    if (expr == null)
1407      throw error(L.l("`{0}' is an unknown identifier", name));
1408
1409    return expr;
1410  }
1411
1412  /**
1413   * Peeks the next token
1414   *
1415   * @return integer code for the token
1416   */

1417  private int peekToken()
1418    throws ConfigException
1419  {
1420    if (_token > 0)
1421      return _token;
1422
1423    _token = scanToken();
1424
1425    return _token;
1426  }
1427  
1428  /**
1429   * Scan the next token. If the lexeme is a string, its string
1430   * representation is in "lexeme".
1431   *
1432   * @return integer code for the token
1433   */

1434  private int scanToken()
1435    throws ConfigException
1436  {
1437    if (_token > 0) {
1438      int value = _token;
1439      _token = -1;
1440      return value;
1441    }
1442
1443    int sign = 1;
1444    int ch;
1445
1446    for (ch = read(); Character.isWhitespace((char) ch); ch = read()) {
1447    }
1448
1449    switch (ch) {
1450    case -1:
1451    case '(':
1452    case ')':
1453    case '.':
1454    case '*':
1455    case '/':
1456    case ',':
1457      return ch;
1458      
1459    case '+':
1460      if ((ch = read()) >= '0' && ch <= '9')
1461        break;
1462      else {
1463        unread(ch);
1464        return '+';
1465      }
1466        
1467    case '-':
1468      if ((ch = read()) >= '0' && ch <= '9') {
1469        sign = -1;
1470        break;
1471      }
1472      else {
1473        unread(ch);
1474        return '-';
1475      }
1476      
1477    case '=':
1478      if ((ch = read()) == '>')
1479        return EXTERNAL_DOT;
1480      else {
1481        unread(ch);
1482        return EQ;
1483      }
1484
1485    case '<':
1486      if ((ch = read()) == '=')
1487        return LE;
1488      else if (ch == '>')
1489        return NE;
1490      else {
1491        unread(ch);
1492        return LT;
1493      }
1494
1495    case '>':
1496      if ((ch = read()) == '=')
1497        return GE;
1498      else {
1499        unread(ch);
1500        return GT;
1501      }
1502
1503    case '?':
1504      CharBuffer cb = CharBuffer.allocate();
1505      int index = 0;
1506      for (ch = read(); ch >= '0' && ch <= '9'; ch = read()) {
1507        cb.append((char) ch);
1508        index = 10 * index + ch - '0';
1509      }
1510      unread(ch);
1511
1512      lexeme = cb.close();
1513
1514      if (index <= 0)
1515        throw error(L.l("`{0}' must refer to a positive argument",
1516                        "?" + lexeme));
1517
1518      if (_maxArg < index)
1519    _maxArg = index;
1520      
1521      return ARG;
1522
1523      // @@ is useless?
1524
case '@':
1525      if ((ch = read()) != '@')
1526        throw error(L.l("`@' expected at {0}", charName(ch)));
1527      return scanToken();
1528    }
1529
1530    if (Character.isJavaIdentifierStart((char) ch)) {
1531      CharBuffer cb = CharBuffer.allocate();
1532
1533      for (; ch > 0 && Character.isJavaIdentifierPart((char) ch); ch = read())
1534        cb.append((char) ch);
1535
1536      unread(ch);
1537
1538      lexeme = cb.close();
1539      String JavaDoc lower = lexeme.toLowerCase();
1540
1541      int token = _reserved.get(lower);
1542
1543      if (token > 0)
1544        return token;
1545      else
1546        return IDENTIFIER;
1547    }
1548    else if (ch >= '0' && ch <= '9') {
1549      CharBuffer cb = CharBuffer.allocate();
1550
1551      int type = INTEGER;
1552      
1553      if (sign < 0)
1554        cb.append('-');
1555
1556      for (; ch >= '0' && ch <= '9'; ch = read())
1557        cb.append((char) ch);
1558
1559      if (ch == '.') {
1560        type = DOUBLE;
1561        
1562        cb.append('.');
1563        for (ch = read(); ch >= '0' && ch <= '9'; ch = read())
1564          cb.append((char) ch);
1565      }
1566
1567      if (ch == 'e' || ch == 'E') {
1568        type = DOUBLE;
1569
1570        cb.append('e');
1571        if ((ch = read()) == '+' || ch == '-') {
1572          cb.append((char) ch);
1573          ch = read();
1574        }
1575        
1576        if (! (ch >= '0' && ch <= '9'))
1577          throw error(L.l("exponent needs digits at {0}",
1578                          charName(ch)));
1579          
1580        for (; ch >= '0' && ch <= '9'; ch = read())
1581          cb.append((char) ch);
1582      }
1583
1584      if (ch == 'F' || ch == 'D')
1585        type = DOUBLE;
1586      else if (ch == 'L') {
1587        type = LONG;
1588      }
1589      else
1590        unread(ch);
1591
1592      lexeme = cb.close();
1593
1594      return type;
1595    }
1596    else if (ch == '\'') {
1597      CharBuffer cb = CharBuffer.allocate();
1598
1599      cb.append("'");
1600      for (ch = read(); ch >= 0; ch = read()) {
1601        if (ch == '\'') {
1602          if ((ch = read()) == '\'')
1603            cb.append("''");
1604          else {
1605            unread(ch);
1606            break;
1607          }
1608        }
1609        else
1610          cb.append((char) ch);
1611      }
1612      cb.append("'");
1613
1614      lexeme = cb.close();
1615
1616      return STRING;
1617    }
1618
1619    throw error(L.l("unexpected char at {0}", "" + (char) ch));
1620  }
1621
1622  /**
1623   * Returns the next character.
1624   */

1625  private int read()
1626  {
1627    if (_parseIndex < _query.length())
1628      return _query.charAt(_parseIndex++);
1629    else
1630      return -1;
1631  }
1632
1633  /**
1634   * Unread the last character.
1635   */

1636  private void unread(int ch)
1637  {
1638    if (ch >= 0)
1639      _parseIndex--;
1640  }
1641
1642  /**
1643   * Returns a full method name with arguments.
1644   */

1645  private String JavaDoc getFullMethodName(JMethod method)
1646  {
1647    return method.getFullName();
1648  }
1649  
1650  /**
1651   * Returns a full method name with arguments.
1652   */

1653  private String JavaDoc getFullMethodName(String JavaDoc methodName, JClass []params)
1654  {
1655    String JavaDoc name = methodName + "(";
1656
1657    for (int i = 0; i < params.length; i++) {
1658      if (i != 0)
1659        name += ", ";
1660
1661      name += params[i].getPrintName();
1662    }
1663
1664    return name + ")";
1665  }
1666
1667  /**
1668   * Returns a printable version of a class.
1669   */

1670  private String JavaDoc getClassName(Class JavaDoc cl)
1671  {
1672    if (cl.isArray())
1673      return getClassName(cl.getComponentType()) + "[]";
1674    else if (cl.getName().startsWith("java")) {
1675      int p = cl.getName().lastIndexOf('.');
1676
1677      return cl.getName().substring(p + 1);
1678    }
1679    else
1680      return cl.getName();
1681  }
1682
1683  /**
1684   * Creates an error.
1685   */

1686  public ConfigException error(String JavaDoc msg)
1687  {
1688    msg += "\nin \"" + _query + "\"";
1689    /*
1690    if (_qlConfig != null)
1691      return new SelectLineParseException(_qlConfig.getFilename() + ":" +
1692                      _qlConfig.getLine() + ": " +
1693                      msg);
1694    */

1695    if (_location != null)
1696      return new LineConfigException(_location + msg);
1697    else
1698      return new ConfigException(msg);
1699  }
1700
1701  /**
1702   * Returns the name for a character
1703   */

1704  private String JavaDoc charName(int ch)
1705  {
1706    if (ch < 0)
1707      return L.l("end of query");
1708    else
1709      return String.valueOf((char) ch);
1710  }
1711  
1712  /**
1713   * Returns the name of a token
1714   */

1715  private String JavaDoc tokenName(int token)
1716  {
1717    switch (token) {
1718    case AS: return "AS";
1719    case FROM: return "FROM";
1720    case IN: return "IN";
1721    case SELECT: return "SELECT";
1722    case WHERE: return "WHERE";
1723    case OR: return "OR";
1724    case AND: return "AND";
1725    case NOT: return "NOT";
1726    case BETWEEN: return "BETWEEN";
1727    case THIS: return "THIS";
1728    case TRUE: return "FALSE";
1729    case EMPTY: return "EMPTY";
1730    case MEMBER: return "MEMBER";
1731    case OF: return "OF";
1732    case NULL: return "NULL";
1733    case ORDER: return "ORDER";
1734    case BY: return "BY";
1735    case ASC: return "ASC";
1736    case DESC: return "DESC";
1737    case LIMIT: return "LIMIT";
1738      
1739    case EXTERNAL_DOT: return "=>";
1740
1741    case -1:
1742      return L.l("end of query");
1743      
1744    default:
1745      if (token < 128)
1746        return "'" + String.valueOf((char) token) + "'";
1747      else
1748        return "'" + lexeme + "'";
1749    }
1750  }
1751
1752  public static ArrayList JavaDoc<FunctionSignature> getStandardFunctions()
1753  {
1754    return FunExpr.getStandardFunctions();
1755  }
1756
1757  /**
1758   * Returns a debuggable description of the select.
1759   */

1760  public String JavaDoc toString()
1761  {
1762    return "QLParser[" + getMethod() + "]";
1763  }
1764
1765  public boolean equals(Object JavaDoc b)
1766  {
1767    if (! (b instanceof QLParser))
1768      return false;
1769
1770    QLParser bSel = (QLParser) b;
1771
1772    if (_bean != bSel._bean)
1773      return false;
1774
1775    return methodEquals(getMethod(), bSel.getMethod());
1776  }
1777
1778  static boolean methodEquals(JMethod a, JMethod b)
1779  {
1780    if (! a.getName().equals(b.getName()))
1781      return false;
1782    
1783    JClass []aParam = a.getParameterTypes();
1784    JClass []bParam = b.getParameterTypes();
1785    
1786    if (aParam.length != bParam.length)
1787      return false;
1788
1789    for (int i = 0; i < aParam.length; i++) {
1790      if (! aParam[i].equals(bParam[i]))
1791        return false;
1792    }
1793    
1794    return true;
1795  }
1796
1797  static class FromItem {
1798    String JavaDoc _id;
1799    String JavaDoc _table;
1800
1801    FromItem(String JavaDoc id, String JavaDoc table)
1802    {
1803      _id = id;
1804      _table = table;
1805    }
1806  }
1807  
1808  static class LinkItem {
1809    String JavaDoc columnA;
1810    String JavaDoc tableA;
1811    
1812    String JavaDoc columnB;
1813    String JavaDoc tableB;
1814
1815    LinkItem(String JavaDoc tableA, String JavaDoc columnA,
1816             String JavaDoc tableB, String JavaDoc columnB)
1817    {
1818      this.columnA = columnA;
1819      this.tableA = tableA;
1820      this.columnB = columnB;
1821      this.tableB = tableB;
1822    }
1823
1824    public boolean equals(Object JavaDoc o)
1825    {
1826      if (! (o instanceof LinkItem))
1827        return false;
1828
1829      LinkItem link = (LinkItem) o;
1830
1831      if (tableA.equals(link.tableA) && columnA.equals(link.columnA) &&
1832          tableB.equals(link.tableB) && columnB.equals(link.columnB))
1833        return true;
1834      else if (tableA.equals(link.tableB) && columnA.equals(link.columnB) &&
1835          tableB.equals(link.tableA) && columnB.equals(link.columnA))
1836        return true;
1837      else
1838        return false;
1839    }
1840  }
1841
1842  static {
1843    _reserved = new IntMap();
1844    _reserved.put("as", AS);
1845    _reserved.put("from", FROM);
1846    _reserved.put("in", IN);
1847    _reserved.put("select", SELECT);
1848    _reserved.put("distinct", DISTINCT);
1849    _reserved.put("where", WHERE);
1850    _reserved.put("order", ORDER);
1851    _reserved.put("by", BY);
1852    _reserved.put("asc", ASC);
1853    _reserved.put("desc", DESC);
1854    _reserved.put("limit", LIMIT);
1855    _reserved.put("offset", OFFSET);
1856    
1857    _reserved.put("or", OR);
1858    _reserved.put("and", AND);
1859    _reserved.put("not", NOT);
1860    
1861    _reserved.put("between", BETWEEN);
1862    _reserved.put("like", LIKE);
1863    _reserved.put("escape", ESCAPE);
1864    _reserved.put("is", IS);
1865    
1866    _reserved.put("this", THIS);
1867    _reserved.put("true", TRUE);
1868    _reserved.put("false", FALSE);
1869    _reserved.put("unknown", UNKNOWN);
1870    _reserved.put("empty", EMPTY);
1871    _reserved.put("member", MEMBER);
1872    _reserved.put("of", OF);
1873    _reserved.put("null", NULL);
1874  }
1875}
1876
Popular Tags