KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectstyle > cayenne > access > trans > QualifierTranslator


1 /* ====================================================================
2  *
3  * The ObjectStyle Group Software License, version 1.1
4  * ObjectStyle Group - http://objectstyle.org/
5  *
6  * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
7  * of the software. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if any,
22  * must include the following acknowlegement:
23  * "This product includes software developed by independent contributors
24  * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
29  * or promote products derived from this software without prior written
30  * permission. For written permission, email
31  * "andrus at objectstyle dot org".
32  *
33  * 5. Products derived from this software may not be called "ObjectStyle"
34  * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
35  * names without prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals and hosted on ObjectStyle Group web site. For more
53  * information on the ObjectStyle Group, please see
54  * <http://objectstyle.org/>.
55  */

56 package org.objectstyle.cayenne.access.trans;
57
58 import java.util.Iterator JavaDoc;
59 import java.util.List JavaDoc;
60
61 import org.apache.commons.collections.IteratorUtils;
62 import org.objectstyle.cayenne.DataObject;
63 import org.objectstyle.cayenne.exp.Expression;
64 import org.objectstyle.cayenne.exp.TraversalHandler;
65 import org.objectstyle.cayenne.map.DbAttribute;
66 import org.objectstyle.cayenne.map.DbRelationship;
67 import org.objectstyle.cayenne.map.EntityInheritanceTree;
68 import org.objectstyle.cayenne.map.ObjEntity;
69 import org.objectstyle.cayenne.query.QualifiedQuery;
70 import org.objectstyle.cayenne.query.Query;
71 import org.objectstyle.cayenne.query.SelectQuery;
72
73 /**
74  * Translates query qualifier to SQL. Used as a helper class by query translators.
75  *
76  * @author Andrei Adamchik
77  */

78 public class QualifierTranslator
79     extends QueryAssemblerHelper
80     implements TraversalHandler {
81
82     protected StringBuffer JavaDoc qualBuf = new StringBuffer JavaDoc();
83
84     protected boolean translateParentQual;
85     protected DataObjectMatchTranslator objectMatchTranslator;
86     protected boolean matchingObject;
87
88     public QualifierTranslator() {
89         this(null);
90     }
91
92     public QualifierTranslator(QueryAssembler queryAssembler) {
93         super(queryAssembler);
94     }
95
96     /** Translates query qualifier to SQL WHERE clause.
97      * Qualifier is obtained from <code>queryAssembler</code> object.
98      */

99     public String JavaDoc doTranslation() {
100         qualBuf.setLength(0);
101
102         Expression rootNode = extractQualifier();
103         if (rootNode == null) {
104             return null;
105         }
106
107         // build SQL where clause string based on expression
108
// (using '?' for object values)
109
rootNode.traverse(this);
110         return qualBuf.length() > 0 ? qualBuf.toString() : null;
111     }
112
113     protected Expression extractQualifier() {
114         Query q = queryAssembler.getQuery();
115
116         Expression qualifier =
117             (isTranslateParentQual())
118                 ? ((SelectQuery) q).getParentQualifier()
119                 : ((QualifiedQuery) q).getQualifier();
120
121         // append Entity qualifiers, taking inheritance into account
122
ObjEntity entity = getObjEntity();
123
124         if (entity != null) {
125             EntityInheritanceTree tree =
126                 queryAssembler.getEntityResolver().lookupInheritanceTree(
127                     entity);
128             Expression entityQualifier =
129                 (tree != null)
130                     ? tree.qualifierForEntityAndSubclasses()
131                     : entity.getDeclaredQualifier();
132             if (entityQualifier != null) {
133                 qualifier =
134                     (qualifier != null)
135                         ? qualifier.andExp(entityQualifier)
136                         : entityQualifier;
137             }
138         }
139
140         return qualifier;
141     }
142
143     /**
144      * Called before processing an expression to initialize
145      * objectMatchTranslator if needed.
146      */

147     protected void detectObjectMatch(Expression exp) {
148         // On demand initialization of
149
// objectMatchTranslator is not possible since there may be null
150
// object values that would not allow to detect the need for
151
// such translator in the right time (e.g.: null = dbpath)
152

153         matchingObject = false;
154
155         if (exp.getOperandCount() != 2) {
156             // only binary expressions are supported
157
return;
158         }
159
160         // check if there are DataObjects among direct children of the Expression
161
for (int i = 0; i < 2; i++) {
162             Object JavaDoc op = exp.getOperand(i);
163             if (op instanceof DataObject) {
164                 matchingObject = true;
165
166                 if (objectMatchTranslator == null) {
167                     objectMatchTranslator = new DataObjectMatchTranslator();
168                 }
169                 else {
170                     objectMatchTranslator.reset();
171                 }
172                 break;
173             }
174         }
175     }
176
177     protected void appendObjectMatch() {
178         if (!matchingObject || objectMatchTranslator == null) {
179             throw new IllegalStateException JavaDoc("An invalid attempt to append object match.");
180         }
181
182         // turn off special handling, so that all the methods behave as a superclass's impl.
183
matchingObject = false;
184
185         boolean first = true;
186         DbRelationship relationship = objectMatchTranslator.getRelationship();
187         Iterator JavaDoc it = objectMatchTranslator.keys();
188         while (it.hasNext()) {
189             if (first) {
190                 first = false;
191             }
192             else {
193                 qualBuf.append(" AND ");
194             }
195
196             String JavaDoc key = (String JavaDoc) it.next();
197             DbAttribute attr = objectMatchTranslator.getAttribute(key);
198             Object JavaDoc val = objectMatchTranslator.getValue(key);
199            
200             processColumn(qualBuf, attr, relationship);
201             qualBuf.append(objectMatchTranslator.getOperation());
202             appendLiteral(qualBuf, val, attr, objectMatchTranslator.getExpression());
203         }
204
205         objectMatchTranslator.reset();
206     }
207
208     /** Opportunity to insert an operation */
209     public void finishedChild(Expression node, int childIndex, boolean hasMoreChildren) {
210
211         if (!hasMoreChildren) {
212             return;
213         }
214
215         StringBuffer JavaDoc buf = (matchingObject) ? new StringBuffer JavaDoc() : qualBuf;
216
217         switch (node.getType()) {
218             case Expression.AND :
219                 buf.append(" AND ");
220                 break;
221             case Expression.OR :
222                 buf.append(" OR ");
223                 break;
224             case Expression.EQUAL_TO :
225                 // translate NULL as IS NULL
226
if (childIndex == 0
227                     && node.getOperandCount() == 2
228                     && node.getOperand(1) == null) {
229                     buf.append(" IS ");
230                 }
231                 else {
232                     buf.append(" = ");
233                 }
234                 break;
235             case Expression.NOT_EQUAL_TO :
236                 // translate NULL as IS NOT NULL
237
if (childIndex == 0
238                     && node.getOperandCount() == 2
239                     && node.getOperand(1) == null) {
240                     buf.append(" IS NOT ");
241                 }
242                 else {
243                     buf.append(" <> ");
244                 }
245                 break;
246             case Expression.LESS_THAN :
247                 buf.append(" < ");
248                 break;
249             case Expression.GREATER_THAN :
250                 buf.append(" > ");
251                 break;
252             case Expression.LESS_THAN_EQUAL_TO :
253                 buf.append(" <= ");
254                 break;
255             case Expression.GREATER_THAN_EQUAL_TO :
256                 buf.append(" >= ");
257                 break;
258             case Expression.IN :
259                 buf.append(" IN ");
260                 break;
261             case Expression.NOT_IN :
262                 buf.append(" NOT IN ");
263                 break;
264             case Expression.LIKE :
265                 buf.append(" LIKE ");
266                 break;
267             case Expression.NOT_LIKE :
268                 buf.append(" NOT LIKE ");
269                 break;
270             case Expression.LIKE_IGNORE_CASE :
271                 buf.append(") LIKE UPPER(");
272                 break;
273             case Expression.NOT_LIKE_IGNORE_CASE :
274                 buf.append(") NOT LIKE UPPER(");
275                 break;
276             case Expression.ADD :
277                 buf.append(" + ");
278                 break;
279             case Expression.SUBTRACT :
280                 buf.append(" - ");
281                 break;
282             case Expression.MULTIPLY :
283                 buf.append(" * ");
284                 break;
285             case Expression.DIVIDE :
286                 buf.append(" / ");
287                 break;
288             case Expression.BETWEEN :
289                 if (childIndex == 0)
290                     buf.append(" BETWEEN ");
291                 else if (childIndex == 1)
292                     buf.append(" AND ");
293                 break;
294             case Expression.NOT_BETWEEN :
295                 if (childIndex == 0)
296                     buf.append(" NOT BETWEEN ");
297                 else if (childIndex == 1)
298                     buf.append(" AND ");
299                 break;
300         }
301
302         if (matchingObject) {
303             objectMatchTranslator.setOperation(buf.toString());
304             objectMatchTranslator.setExpression(node);
305         }
306     }
307
308     public void startNode(Expression node, Expression parentNode) {
309         int count = node.getOperandCount();
310
311         if (count == 2) {
312             // binary nodes are the only ones that currently require this
313
detectObjectMatch(node);
314         }
315
316         if (parenthesisNeeded(node, parentNode)) {
317             qualBuf.append('(');
318         }
319
320         if (count == 1) {
321             if (node.getType() == Expression.NEGATIVE)
322                 qualBuf.append('-');
323             // ignore POSITIVE - it is a NOOP
324
// else if(node.getType() == Expression.POSITIVE)
325
// qualBuf.append('+');
326
else if (node.getType() == Expression.NOT)
327                 qualBuf.append("NOT ");
328             else if (node.getType() == Expression.EXISTS)
329                 qualBuf.append("EXISTS ");
330             else if (node.getType() == Expression.ALL)
331                 qualBuf.append("ALL ");
332             else if (node.getType() == Expression.SOME)
333                 qualBuf.append("SOME ");
334             else if (node.getType() == Expression.ANY)
335                 qualBuf.append("ANY ");
336         }
337         else if (
338             node.getType() == Expression.LIKE_IGNORE_CASE
339                 || node.getType() == Expression.NOT_LIKE_IGNORE_CASE) {
340             qualBuf.append("UPPER(");
341         }
342     }
343
344     /**
345      * @since 1.1
346      */

347     public void endNode(Expression node, Expression parentNode) {
348
349         // check if we need to use objectMatchTranslator to finish building the expression
350
if (node.getOperandCount() == 2 && matchingObject) {
351             appendObjectMatch();
352         }
353
354         if (parenthesisNeeded(node, parentNode)) {
355             qualBuf.append(')');
356         }
357
358         if (node.getType() == Expression.LIKE_IGNORE_CASE
359             || node.getType() == Expression.NOT_LIKE_IGNORE_CASE) {
360             qualBuf.append(')');
361         }
362     }
363
364     public void objectNode(Object JavaDoc leaf, Expression parentNode) {
365         if (parentNode.getType() == Expression.RAW_SQL) {
366             appendRawSql(leaf);
367         }
368         else if (parentNode.getType() == Expression.OBJ_PATH) {
369             appendObjPath(qualBuf, parentNode);
370         }
371         else if (parentNode.getType() == Expression.DB_PATH) {
372             appendDbPath(qualBuf, parentNode);
373         }
374         else if (parentNode.getType() == Expression.LIST) {
375             appendList(parentNode, paramsDbType(parentNode));
376         }
377         else {
378             appendLiteral(qualBuf, leaf, paramsDbType(parentNode), parentNode);
379         }
380     }
381
382     protected boolean parenthesisNeeded(Expression node, Expression parentNode) {
383         if (parentNode == null)
384             return false;
385
386         // only unary expressions can go w/o parenthesis
387
if (node.getOperandCount() > 1)
388             return true;
389
390         if (node.getType() == Expression.OBJ_PATH)
391             return false;
392
393         if (node.getType() == Expression.DB_PATH)
394             return false;
395
396         return true;
397     }
398
399     private void appendRawSql(Object JavaDoc sql) {
400         if (sql != null) {
401             qualBuf.append(sql);
402         }
403     }
404
405     private final void appendList(Expression listExpr, DbAttribute paramDesc) {
406         Iterator JavaDoc it = null;
407         Object JavaDoc list = listExpr.getOperand(0);
408         if (list instanceof List JavaDoc) {
409             it = ((List JavaDoc) list).iterator();
410         }
411         else if (list instanceof Object JavaDoc[]) {
412             it = IteratorUtils.arrayIterator((Object JavaDoc[]) list);
413         }
414         else {
415             String JavaDoc className = (list != null) ? list.getClass().getName() : "<null>";
416             throw new IllegalArgumentException JavaDoc(
417                 "Unsupported type for the list expressions: " + className);
418         }
419
420         // process first element outside the loop
421
// (unroll loop to avoid condition checking
422
if (it.hasNext())
423             appendLiteral(qualBuf, it.next(), paramDesc, listExpr);
424         else
425             return;
426
427         while (it.hasNext()) {
428             qualBuf.append(", ");
429             appendLiteral(qualBuf, it.next(), paramDesc, listExpr);
430         }
431     }
432
433     /**
434      * Returns <code>true</code> if this translator will translate
435      * parent qualifier on call to <code>doTranslation</code>.
436      *
437      * @return boolean
438      */

439     public boolean isTranslateParentQual() {
440         return translateParentQual;
441     }
442
443     /**
444      * Configures translator to translate
445      * parent or main qualifier on call to <code>doTranslation</code>.
446      *
447      * @param translateParentQual The translateParentQual to set
448      */

449     public void setTranslateParentQual(boolean translateParentQual) {
450         this.translateParentQual = translateParentQual;
451     }
452
453     public ObjEntity getObjEntity() {
454         if (isTranslateParentQual()) {
455             SelectQuery query = (SelectQuery) queryAssembler.getQuery();
456             return queryAssembler.getEntityResolver().getObjEntity(
457                 query.getParentObjEntityName());
458         }
459         else {
460             return super.getObjEntity();
461         }
462     }
463
464     protected void appendLiteral(
465         StringBuffer JavaDoc buf,
466         Object JavaDoc val,
467         DbAttribute attr,
468         Expression parentExpression) {
469
470         if (!matchingObject) {
471             super.appendLiteral(buf, val, attr, parentExpression);
472         }
473         else if (val == null || (val instanceof DataObject)) {
474             objectMatchTranslator.setDataObject((DataObject) val);
475         }
476         else {
477             throw new IllegalArgumentException JavaDoc("Attempt to use literal other than DataObject during object match.");
478         }
479     }
480
481     protected void processRelTermination(StringBuffer JavaDoc buf, DbRelationship rel) {
482
483         if (!matchingObject) {
484             super.processRelTermination(buf, rel);
485         }
486         else {
487             if (rel.isToMany()) {
488                 // append joins
489
queryAssembler.dbRelationshipAdded(rel);
490             }
491             objectMatchTranslator.setRelationship(rel);
492         }
493     }
494 }
Popular Tags