KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > versant > core > jdbc > ejbql > EJBQLNodeToSqlExp


1
2 /*
3  * Copyright (c) 1998 - 2005 Versant Corporation
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  * Versant Corporation - initial API and implementation
11  */

12 package com.versant.core.jdbc.ejbql;
13
14 import com.versant.core.ejb.query.*;
15 import com.versant.core.jdbc.sql.exp.*;
16 import com.versant.core.jdbc.sql.SqlDriver;
17 import com.versant.core.jdbc.metadata.JdbcClass;
18 import com.versant.core.jdbc.metadata.JdbcField;
19 import com.versant.core.jdbc.metadata.JdbcPolyRefField;
20 import com.versant.core.jdbc.fetch.FetchSpec;
21 import com.versant.core.jdbc.fetch.FopGetOID;
22 import com.versant.core.jdbc.fetch.FetchOpDataMainRS;
23 import com.versant.core.jdbc.fetch.FopGetState;
24 import com.versant.core.metadata.ClassMetaData;
25 import com.versant.core.metadata.FieldMetaData;
26 import com.versant.core.common.BindingSupportImpl;
27 import com.versant.core.common.Debug;
28
29 import java.util.ArrayList JavaDoc;
30
31 /**
32  * Walks EJBQL Node trees to produce SqlExp trees.
33  */

34 public class EJBQLNodeToSqlExp extends NodeVisitorAdapter {
35
36     private ResolveContext rc;
37     private SqlDriver sqlDriver;
38
39     private static final Object JavaDoc[] EMPTY = new Object JavaDoc[0];
40
41     public EJBQLNodeToSqlExp(ResolveContext rc, SqlDriver sqlDriver) {
42         this.rc = rc;
43         this.sqlDriver = sqlDriver;
44     }
45
46     /**
47      * Throw exception if we hit any unhandled nodes.
48      */

49     protected Object JavaDoc defaultArrive(Node node, Object JavaDoc msg) {
50         throw rc.createUserException("Not implemented: " + node.getClass(),
51                 node);
52     }
53
54     /**
55      * Convert resolved Node tree to a SqlExp tree. Returns nul if node is null.
56      */

57     public SqlExp toSqlExp(Node node, Object JavaDoc msg) {
58         if (node == null) {
59             return null;
60         } else {
61             return (SqlExp)node.arrive(this, msg);
62         }
63     }
64
65     private RuntimeException JavaDoc notImplemented() {
66         return notImplemented("not implemented");
67     }
68
69     private RuntimeException JavaDoc notImplemented(String JavaDoc msg) {
70         return BindingSupportImpl.getInstance().internal(msg);
71     }
72
73     /**
74      * Invoke arrive on each entry in the list. Returns Object[0] if list is
75      * null.
76      */

77     private Object JavaDoc[] invokeArriveOnList(Node list, Object JavaDoc msg) {
78         if (list == null) {
79             return EMPTY;
80         }
81         if (list.getNext() == null) { // 1 node only
82
return new Object JavaDoc[]{toSqlExp(list, msg)};
83         } else {
84             ArrayList JavaDoc a = new ArrayList JavaDoc();
85             for (; list != null; list = list.getNext()) {
86                 a.add(toSqlExp(list, msg));
87             }
88             return a.toArray();
89         }
90     }
91
92     /**
93      * SELECT .. FROM .. WHERE .. GROUP BY .. HAVING .. ORDER BY expression
94      * that is not a subquery.
95      */

96     public Object JavaDoc arriveSelectNode(SelectNode node, Object JavaDoc msg) {
97         Object JavaDoc[] fromList = invokeArriveOnList(node.getFromList(), msg);
98         SelectExp se = (SelectExp)fromList[0];
99         if (fromList.length > 1) {
100             // todo combine into a single SelextExp
101
throw notImplemented();
102         }
103         se.fetchSpec = new FetchSpec(se, sqlDriver);
104         invokeArriveOnList(node.getSelectList(), msg);
105         se.whereExp = toSqlExp(node.getWhere(), msg);
106         return se;
107     }
108
109     /**
110      * Declaration of identification variable in the FROM list e.g.
111      * 'FROM Order AS o, Product AS p'.
112      */

113     public Object JavaDoc arriveIdentificationVarNode(IdentificationVarNode node,
114             Object JavaDoc msg) {
115         // create SelectExp's linked with joins for whole nav tree
116
NavRoot root = node.getNavRoot();
117         ClassMetaData cmd = root.getNavClassMetaData();
118         JdbcClass jdbcClass = (JdbcClass)cmd.storeClass;
119         SelectExp se = new SelectExp();
120         se.table = jdbcClass.table;
121         root.storeObject = se;
122         return se;
123     }
124
125     /**
126      * Path (e.g. o.customer.name, o).
127      */

128     public Object JavaDoc arrivePathNode(PathNode node, Object JavaDoc msg) {
129         switch (node.getParentType()) {
130             case PathNode.SELECT:
131                 return pathNodeSelect(node);
132             case PathNode.WHERE:
133                 return pathNodeWhere(node);
134             case PathNode.GROUP_BY:
135             case PathNode.ORDER_BY:
136             case PathNode.AGGREGATE:
137             case PathNode.CONSTRUCTOR:
138             case PathNode.JOIN:
139             case PathNode.COLLECTION_MEMBER:
140         }
141         return super.arrivePathNode(node, msg);
142     }
143
144     private Object JavaDoc pathNodeSelect(PathNode node) {
145         FieldMetaData fmd = node.getFmd();
146         if (fmd == null) { // complete object
147
return arriveObjectNodeImp(node.getNavBase());
148         } else { // single field
149
throw notImplemented();
150         }
151     }
152
153     public Object JavaDoc arriveObjectNode(ObjectNode node, Object JavaDoc msg) {
154         return arriveObjectNodeImp(node.getNavBase());
155     }
156
157     private Object JavaDoc arriveObjectNodeImp(NavBase navBase) {
158         SelectExp se = (SelectExp)navBase.storeObject;
159         FetchSpec fetchSpec = se.fetchSpec;
160         ClassMetaData cmd = navBase.getNavClassMetaData();
161         FopGetOID fopGetOid = new FopGetOID(fetchSpec,
162                 FetchOpDataMainRS.INSTANCE, cmd);
163         FopGetState fopGetState = new FopGetState(fetchSpec,
164                 fopGetOid.getOutputData(), cmd.fetchGroups[0], true);
165         fetchSpec.addFetchOp(fopGetOid, true);
166         fetchSpec.addFetchOp(fopGetState, true);
167         // one of the above lines must change to false when we figure out
168
// how to link the State to the OID cleanly (OID field on State??)
169
return se;
170     }
171
172     /**
173      * Get the SelectExp for node. This will create it and any joins required
174      * if it does not already exist. This actually creates all of the joins
175      * for the nodes NavRoot.
176      */

177     private SelectExp getNodeSelectExp(PathNode node) {
178         NavBase nav = node.getNavBase();
179         SelectExp se = (SelectExp)nav.storeObject;
180         if (se == null) {
181             createJoins(nav.getRoot());
182             se = (SelectExp)nav.storeObject;
183             if (Debug.DEBUG) {
184                 if (se == null) {
185                     throw BindingSupportImpl.getInstance().internal(
186                             "nav.storeObject == null: " + node);
187                 }
188             }
189         }
190         return se;
191     }
192
193     /**
194      * Create all the joins for a tree of navigations starting at navRoot.
195      */

196     private void createJoins(NavBase from) {
197         SelectExp fromSE = (SelectExp)from.storeObject;
198         for (NavField to = from.getChildren(); to != null; to = to.getNext()) {
199             if (to.storeObject != null) {
200                 continue;
201             }
202             JdbcField jdbcField = (JdbcField)to.getFmd().storeField;
203             JdbcClass targetClass = (JdbcClass)to.getNavClassMetaData().storeClass;
204             SelectExp se = new SelectExp();
205             se.table = targetClass.table;
206             se.jdbcField = jdbcField;
207             if (jdbcField instanceof JdbcPolyRefField) {
208                 JdbcPolyRefField pf = (JdbcPolyRefField)jdbcField;
209                 Join j = fromSE.addJoin(pf.refCols, se.table.pk, se);
210                 // add a condition to check that the class-id column of
211
// the polyref matches the type used in the cast
212
j.appendJoinExp(
213                         pf.createClassIdMatchExp(fromSE, targetClass.cmd));
214             } else {
215                 fromSE.addJoin(jdbcField.mainTableCols, se.table.pk, se);
216             }
217             se.outer = to.isOuter();
218             to.storeObject = se;
219             createJoins(to);
220         }
221     }
222
223     private Object JavaDoc pathNodeWhere(PathNode node) {
224         FieldMetaData fmd = node.getFmd();
225         if (fmd != null) {
226             JdbcField jdbcField = (JdbcField)fmd.storeField;
227             SelectExp se = getNodeSelectExp(node);
228             ColumnExp columnExp = jdbcField.toColumnExp(
229                     SelectExp.createJoinToSuperTable(se, jdbcField), true);
230             return columnExp;
231         } else {
232             throw notImplemented();
233         }
234     }
235
236     public Object JavaDoc arriveCompNode(CompNode node, Object JavaDoc msg) {
237         SqlExp left = toSqlExp(node.getLeft(), msg);
238         SqlExp right = toSqlExp(node.getRight(), left);
239         int op = node.getOp();
240         if (op == CompNode.EQ || op == CompNode.NE) {
241             if (op == CompNode.EQ) {
242                 op = BinaryOpExp.EQUAL;
243             } else {
244                 op = BinaryOpExp.NOT_EQUAL;
245             }
246             return SqlExp.createBinaryOpExp(left, op, right);
247         } else {
248             if (left.next == null && right.next == null) {
249                 switch (op) {
250                     case CompNode.GT: op = BinaryOpExp.GT; break;
251                     case CompNode.LT: op = BinaryOpExp.LT; break;
252                     case CompNode.GE: op = BinaryOpExp.GE; break;
253                     case CompNode.LE: op = BinaryOpExp.LE; break;
254                     default:
255                         throw BindingSupportImpl.getInstance().internal(
256                                 "Unknown op: " + op);
257                 }
258                 return new BinaryOpExp(left, op, right);
259             }
260             throw BindingSupportImpl.getInstance().runtime(
261                 "Expressions consisting of multiple columns may not be compared " +
262                 "with >, <, >= or <=\n");
263         }
264     }
265
266     public Object JavaDoc arriveLiteralNode(LiteralNode node, Object JavaDoc msg) {
267         String JavaDoc value;
268         int t;
269         switch (node.getType()) {
270             case LiteralNode.DOUBLE:
271                 t = LiteralExp.TYPE_OTHER;
272                 value = sqlDriver.toSqlLiteral(node.getDoubleValue());
273                 break;
274             case LiteralNode.LONG:
275                 t = LiteralExp.TYPE_OTHER;
276                 value = sqlDriver.toSqlLiteral(node.getLongValue());
277                 break;
278             case LiteralNode.STRING:
279                 t = LiteralExp.TYPE_STRING;
280                 value = node.getStringValue();
281                 break;
282             case LiteralNode.BOOLEAN:
283                 t = LiteralExp.TYPE_OTHER;
284                 value = sqlDriver.toSqlLiteral(node.getBooleanValue());
285                 break;
286             default:
287                 throw BindingSupportImpl.getInstance().internal(
288                     "Unknown literal type: " + node.getType());
289         }
290         return new LiteralExp(t, value);
291     }
292
293     public Object JavaDoc arriveParameterNode(ParameterNode node, Object JavaDoc msg) {
294         if (msg != null && !(msg instanceof SqlExp)) {
295             throw BindingSupportImpl.getInstance().internal(
296                     "expected SqlExp for msg");
297         }
298         SqlExp leftSibling = (SqlExp)msg;
299         SqlParamUsage u = new SqlParamUsage();
300         node.getUsage().storeObject = u;
301         if (leftSibling instanceof ColumnExp) {
302             ColumnExp left = (ColumnExp)leftSibling;
303             u.jdbcField = left.jdbcField;
304             u.col = left.col;
305             // Create extra ? in the SQL if our left sibling has multiple
306
// columns. Example: a reference to a composite pk class
307
// compared to a parameter.
308
SqlExp pos = u.expList = new ParamExp(u.jdbcType, u);
309             for (;;) {
310                 if ((left = (ColumnExp)left.next) == null) break;
311                 pos = pos.next = new ParamExp(left.getJdbcType(), u);
312             }
313         } else {
314             if (leftSibling.next != null) {
315                 throw BindingSupportImpl.getInstance().internal(
316                     "Expression on left has more than one column and no " +
317                     "field information");
318             }
319             u.expList = new ParamExp(leftSibling.getJdbcType(), u);
320         }
321         if (u.jdbcField == null) {
322             u.jdbcType = leftSibling.getJdbcType();
323             u.javaTypeCode = leftSibling.getJavaTypeCode();
324             u.classIndex = leftSibling.getClassIndex();
325         }
326         return u.expList;
327     }
328
329     public Object JavaDoc arriveAndNode(AndNode node, Object JavaDoc msg) {
330         // Build the list of expressions to be AND'ed together.
331
SqlExp prev = null, first = null;
332         for (Node cn = node.getArgsList(); cn != null; cn = cn.getNext()) {
333             SqlExp e = toSqlExp(cn, prev);
334             if (first == null) {
335                 first = e;
336             } else if ( prev != null ) {
337                 prev.next = e;
338             }
339             prev = e;
340         }
341
342         // if there is now only one entry in the list then return it otherwise
343
// create a new AndExp for the list
344
if (first.next == null) {
345             return first;
346         } else {
347             return new AndExp(first);
348         }
349     }
350
351     public Object JavaDoc arriveOrNode(OrNode node, Object JavaDoc msg) {
352         // Build the list of expressions to be OR'ed together.
353
SqlExp prev = null, first = null;
354         for (Node cn = node.getArgsList(); cn != null; cn = cn.getNext()) {
355             SqlExp e = toSqlExp(cn, prev);
356             if (first == null) {
357                 first = e;
358             } else if ( prev != null ) {
359                 prev.next = e;
360             }
361             prev = e;
362         }
363
364         // if there is now only one entry in the list then return it otherwise
365
// create a new OrExp for the list
366
if (first.next == null) {
367             return first;
368         } else {
369             return new OrExp(first);
370         }
371     }
372
373     public Object JavaDoc arriveAddNode(AddNode node, Object JavaDoc msg) {
374         SqlExp left = processAddNodeChild(node.getLeft());
375         SqlExp right = processAddNodeChild(node.getRight());
376         left.next = right;
377         return new AddExp(left, new int[]{node.getOp()});
378     }
379
380     private SqlExp processAddNodeChild(Node c) {
381         SqlExp e = toSqlExp(c, null);
382         if (e.next != null) {
383             throw BindingSupportImpl.getInstance().runtime(
384                 "Expressions consisting of multiple columns may not be used" +
385                 "with + or -");
386         }
387         return e;
388     }
389
390     public Object JavaDoc arriveMultiplyNode(MultiplyNode node, Object JavaDoc msg) {
391         SqlExp left = processMultiplyNodeChild(node.getLeft());
392         SqlExp right = processMultiplyNodeChild(node.getLeft());
393         left.next = right;
394         return new AddExp(left, new int[]{node.getOp()});
395     }
396
397     private SqlExp processMultiplyNodeChild(Node c) {
398         SqlExp e = toSqlExp(c, null);
399         if (e.next != null) {
400             throw BindingSupportImpl.getInstance().runtime(
401                 "Expressions consisting of multiple columns may not be used" +
402                 "with * or /");
403         }
404         return e;
405     }
406
407     public Object JavaDoc arriveParenNode(ParenNode node, Object JavaDoc msg) {
408         SqlExp e = toSqlExp(node, msg);
409         if (e instanceof ParenExp) {
410             return e;
411         } else {
412             return new ParenExp(e);
413         }
414     }
415
416     public Object JavaDoc arriveBetweenNode(BetweenNode node, Object JavaDoc msg) {
417         SqlExp arg = toSqlExp(node.getArg(), null);
418         SqlExp from = toSqlExp(node.getFrom(), null);
419         SqlExp to = toSqlExp(node.getTo(), null);
420         arg.next = from;
421         from.next = to;
422         return new BetweenExp(arg);
423     }
424
425     public Object JavaDoc arriveNotNode(NotNode node, Object JavaDoc msg) {
426         SqlExp arg = toSqlExp(node.getExp(), null);
427         return new UnaryOpExp(arg, UnaryOpExp.OP_NOT);
428     }
429
430     public Object JavaDoc arriveLikeNode(LikeNode node, Object JavaDoc msg) {
431         SqlExp arg = toSqlExp(node.getPath(), null);
432         SqlExp pattern = toSqlExp(node.getPattern(), null);
433         if (node.getEscape() != null) {
434             throw notImplemented(
435                     "Escape character argument to LIKE is not implemented");
436         }
437         return new BinaryOpExp(arg, BinaryOpExp.LIKE, pattern);
438     }
439
440     public Object JavaDoc arriveUnaryMinusNode(UnaryMinusNode node, Object JavaDoc msg) {
441         SqlExp e = toSqlExp(node, msg);
442         if (e.next != null) {
443             throw BindingSupportImpl.getInstance().runtime(
444                 "Expressions consisting of multiple columns may not be used" +
445                 "with unary -");
446         }
447         return new UnaryOpExp(e, UnaryOpExp.OP_MINUS);
448     }
449
450 }
451
452
Popular Tags