KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.access.trans;
21
22 import java.util.Iterator JavaDoc;
23 import java.util.List JavaDoc;
24
25 import org.apache.cayenne.ObjectId;
26 import org.apache.cayenne.Persistent;
27 import org.apache.cayenne.exp.Expression;
28 import org.apache.cayenne.exp.TraversalHandler;
29 import org.apache.cayenne.map.DbAttribute;
30 import org.apache.cayenne.map.DbRelationship;
31 import org.apache.cayenne.map.EntityInheritanceTree;
32 import org.apache.cayenne.map.ObjEntity;
33 import org.apache.cayenne.query.QualifiedQuery;
34 import org.apache.cayenne.query.Query;
35 import org.apache.cayenne.query.SelectQuery;
36 import org.apache.commons.collections.IteratorUtils;
37
38 /**
39  * Translates query qualifier to SQL. Used as a helper class by query translators.
40  *
41  * @author Andrus Adamchik
42  */

43 public class QualifierTranslator
44     extends QueryAssemblerHelper
45     implements TraversalHandler {
46
47     protected StringBuffer JavaDoc qualBuf = new StringBuffer JavaDoc();
48
49     protected boolean translateParentQual;
50     protected DataObjectMatchTranslator objectMatchTranslator;
51     protected boolean matchingObject;
52
53     public QualifierTranslator() {
54         this(null);
55     }
56
57     public QualifierTranslator(QueryAssembler queryAssembler) {
58         super(queryAssembler);
59     }
60
61     /** Translates query qualifier to SQL WHERE clause.
62      * Qualifier is obtained from <code>queryAssembler</code> object.
63      */

64     public String JavaDoc doTranslation() {
65         qualBuf.setLength(0);
66
67         Expression rootNode = extractQualifier();
68         if (rootNode == null) {
69             return null;
70         }
71
72         // build SQL where clause string based on expression
73
// (using '?' for object values)
74
rootNode.traverse(this);
75         return qualBuf.length() > 0 ? qualBuf.toString() : null;
76     }
77
78     protected Expression extractQualifier() {
79         Query q = queryAssembler.getQuery();
80
81         Expression qualifier =
82             (isTranslateParentQual())
83                 ? ((SelectQuery) q).getParentQualifier()
84                 : ((QualifiedQuery) q).getQualifier();
85
86         // append Entity qualifiers, taking inheritance into account
87
ObjEntity entity = getObjEntity();
88
89         if (entity != null) {
90             EntityInheritanceTree tree =
91                 queryAssembler.getEntityResolver().lookupInheritanceTree(
92                     entity);
93             Expression entityQualifier =
94                 (tree != null)
95                     ? tree.qualifierForEntityAndSubclasses()
96                     : entity.getDeclaredQualifier();
97             if (entityQualifier != null) {
98                 qualifier =
99                     (qualifier != null)
100                         ? qualifier.andExp(entityQualifier)
101                         : entityQualifier;
102             }
103         }
104
105         return qualifier;
106     }
107
108     /**
109      * Called before processing an expression to initialize
110      * objectMatchTranslator if needed.
111      */

112     protected void detectObjectMatch(Expression exp) {
113         // On demand initialization of
114
// objectMatchTranslator is not possible since there may be null
115
// object values that would not allow to detect the need for
116
// such translator in the right time (e.g.: null = dbpath)
117

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

319     public void endNode(Expression node, Expression parentNode) {
320
321         // check if we need to use objectMatchTranslator to finish building the expression
322
if (node.getOperandCount() == 2 && matchingObject) {
323             appendObjectMatch();
324         }
325
326         if (parenthesisNeeded(node, parentNode)) {
327             qualBuf.append(')');
328         }
329
330         if (node.getType() == Expression.LIKE_IGNORE_CASE
331             || node.getType() == Expression.NOT_LIKE_IGNORE_CASE) {
332             qualBuf.append(')');
333         }
334     }
335
336     public void objectNode(Object JavaDoc leaf, Expression parentNode) {
337         if (parentNode.getType() == Expression.OBJ_PATH) {
338             appendObjPath(qualBuf, parentNode);
339         }
340         else if (parentNode.getType() == Expression.DB_PATH) {
341             appendDbPath(qualBuf, parentNode);
342         }
343         else if (parentNode.getType() == Expression.LIST) {
344             appendList(parentNode, paramsDbType(parentNode));
345         }
346         else {
347             appendLiteral(qualBuf, leaf, paramsDbType(parentNode), parentNode);
348         }
349     }
350
351     protected boolean parenthesisNeeded(Expression node, Expression parentNode) {
352         if (parentNode == null)
353             return false;
354
355         // only unary expressions can go w/o parenthesis
356
if (node.getOperandCount() > 1)
357             return true;
358
359         if (node.getType() == Expression.OBJ_PATH)
360             return false;
361
362         if (node.getType() == Expression.DB_PATH)
363             return false;
364         
365         return true;
366     }
367
368     private final void appendList(Expression listExpr, DbAttribute paramDesc) {
369         Iterator JavaDoc it = null;
370         Object JavaDoc list = listExpr.getOperand(0);
371         if (list instanceof List JavaDoc) {
372             it = ((List JavaDoc) list).iterator();
373         }
374         else if (list instanceof Object JavaDoc[]) {
375             it = IteratorUtils.arrayIterator((Object JavaDoc[]) list);
376         }
377         else {
378             String JavaDoc className = (list != null) ? list.getClass().getName() : "<null>";
379             throw new IllegalArgumentException JavaDoc(
380                 "Unsupported type for the list expressions: " + className);
381         }
382
383         // process first element outside the loop
384
// (unroll loop to avoid condition checking
385
if (it.hasNext())
386             appendLiteral(qualBuf, it.next(), paramDesc, listExpr);
387         else
388             return;
389
390         while (it.hasNext()) {
391             qualBuf.append(", ");
392             appendLiteral(qualBuf, it.next(), paramDesc, listExpr);
393         }
394     }
395
396     /**
397      * Returns <code>true</code> if this translator will translate
398      * parent qualifier on call to <code>doTranslation</code>.
399      *
400      * @return boolean
401      */

402     public boolean isTranslateParentQual() {
403         return translateParentQual;
404     }
405
406     /**
407      * Configures translator to translate
408      * parent or main qualifier on call to <code>doTranslation</code>.
409      *
410      * @param translateParentQual The translateParentQual to set
411      */

412     public void setTranslateParentQual(boolean translateParentQual) {
413         this.translateParentQual = translateParentQual;
414     }
415
416     public ObjEntity getObjEntity() {
417         if (isTranslateParentQual()) {
418             SelectQuery query = (SelectQuery) queryAssembler.getQuery();
419             return queryAssembler.getEntityResolver().getObjEntity(
420                 query.getParentObjEntityName());
421         }
422         else {
423             return super.getObjEntity();
424         }
425     }
426
427     protected void appendLiteral(
428         StringBuffer JavaDoc buf,
429         Object JavaDoc val,
430         DbAttribute attr,
431         Expression parentExpression) {
432
433         if (!matchingObject) {
434             super.appendLiteral(buf, val, attr, parentExpression);
435         }
436         else if (val == null || (val instanceof Persistent)) {
437             objectMatchTranslator.setDataObject((Persistent) val);
438         }
439         else if(val instanceof ObjectId) {
440             objectMatchTranslator.setObjectId((ObjectId) val);
441         }
442         else {
443             throw new IllegalArgumentException JavaDoc("Attempt to use literal other than DataObject during object match.");
444         }
445     }
446
447     protected void processRelTermination(StringBuffer JavaDoc buf, DbRelationship rel) {
448
449         if (!matchingObject) {
450             super.processRelTermination(buf, rel);
451         }
452         else {
453             if (rel.isToMany()) {
454                 // append joins
455
queryAssembler.dbRelationshipAdded(rel);
456             }
457             objectMatchTranslator.setRelationship(rel);
458         }
459     }
460 }
461
Popular Tags