KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > exp > Expression


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.exp;
21
22 import java.io.PrintWriter JavaDoc;
23 import java.io.Reader JavaDoc;
24 import java.io.Serializable JavaDoc;
25 import java.io.StringReader JavaDoc;
26 import java.io.StringWriter JavaDoc;
27 import java.util.Collection JavaDoc;
28 import java.util.Collections JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.LinkedList JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Map JavaDoc;
33
34 import org.apache.commons.collections.Transformer;
35 import org.apache.cayenne.exp.parser.ExpressionParser;
36 import org.apache.cayenne.exp.parser.ParseException;
37 import org.apache.cayenne.util.ConversionUtil;
38 import org.apache.cayenne.util.Util;
39 import org.apache.cayenne.util.XMLEncoder;
40 import org.apache.cayenne.util.XMLSerializable;
41
42 /**
43  * Superclass of Cayenne expressions that defines basic API for expressions use.
44  */

45 public abstract class Expression implements Serializable JavaDoc, XMLSerializable {
46
47     /**
48      * A value that a Transformer might return to indicate that a node has to be pruned
49      * from the expression during the transformation.
50      *
51      * @since 1.2
52      */

53     public final static Object JavaDoc PRUNED_NODE = new Object JavaDoc();
54
55     public static final int AND = 0;
56     public static final int OR = 1;
57     public static final int NOT = 2;
58     public static final int EQUAL_TO = 3;
59     public static final int NOT_EQUAL_TO = 4;
60     public static final int LESS_THAN = 5;
61     public static final int GREATER_THAN = 6;
62     public static final int LESS_THAN_EQUAL_TO = 7;
63     public static final int GREATER_THAN_EQUAL_TO = 8;
64     public static final int BETWEEN = 9;
65     public static final int IN = 10;
66     public static final int LIKE = 11;
67     public static final int LIKE_IGNORE_CASE = 12;
68     public static final int ADD = 16;
69     public static final int SUBTRACT = 17;
70     public static final int MULTIPLY = 18;
71     public static final int DIVIDE = 19;
72     public static final int NEGATIVE = 20;
73     public static final int TRUE = 21;
74     public static final int FALSE = 22;
75
76     /**
77      * Expression describes a path relative to an ObjEntity. OBJ_PATH expression is
78      * resolved relative to some root ObjEntity. Path expression components are separated
79      * by "." (dot). Path can point to either one of these:
80      * <ul>
81      * <li><i>An attribute of root ObjEntity.</i> For entity Gallery OBJ_PATH expression
82      * "galleryName" will point to ObjAttribute "galleryName"
83      * <li><i>Another ObjEntity related to root ObjEntity via a chain of relationships.</i>
84      * For entity Gallery OBJ_PATH expression "paintingArray.toArtist" will point to
85      * ObjEntity "Artist"
86      * <li><i>ObjAttribute of another ObjEntity related to root ObjEntity via a chain of
87      * relationships.</i> For entity Gallery OBJ_PATH expression
88      * "paintingArray.toArtist.artistName" will point to ObjAttribute "artistName"
89      * </ul>
90      */

91     public static final int OBJ_PATH = 26;
92
93     /**
94      * Expression describes a path relative to a DbEntity. DB_PATH expression is resolved
95      * relative to some root DbEntity. Path expression components are separated by "."
96      * (dot). Path can point to either one of these:
97      * <ul>
98      * <li><i>An attribute of root DbEntity.</i> For entity GALLERY, DB_PATH expression
99      * "GALLERY_NAME" will point to a DbAttribute "GALLERY_NAME". </li>
100      * <li><i>Another DbEntity related to root DbEntity via a chain of relationships.</i>
101      * For entity GALLERY DB_PATH expression "paintingArray.toArtist" will point to
102      * DbEntity "ARTIST". </li>
103      * <li><i>DbAttribute of another ObjEntity related to root DbEntity via a chain of
104      * relationships.</i> For entity GALLERY DB_PATH expression
105      * "paintingArray.toArtist.ARTIST_NAME" will point to DbAttribute "ARTIST_NAME". </li>
106      * </ul>
107      */

108     public static final int DB_PATH = 27;
109
110     /**
111      * Interpreted as a comma-separated list of literals.
112      */

113     public static final int LIST = 28;
114
115     public static final int NOT_BETWEEN = 35;
116     public static final int NOT_IN = 36;
117     public static final int NOT_LIKE = 37;
118     public static final int NOT_LIKE_IGNORE_CASE = 38;
119
120     protected int type;
121
122     /**
123      * Parses string, converting it to Expression. If string does not represent a
124      * semantically correct expression, an ExpressionException is thrown.
125      *
126      * @since 1.1
127      */

128     // TODO: cache expression strings, since this operation is pretty slow
129
public static Expression fromString(String JavaDoc expressionString) {
130         if (expressionString == null) {
131             throw new NullPointerException JavaDoc("Null expression string.");
132         }
133
134         Reader JavaDoc reader = new StringReader JavaDoc(expressionString);
135         try {
136             return new ExpressionParser(reader).expression();
137         }
138         catch (ParseException ex) {
139             throw new ExpressionException(ex.getMessage(), ex);
140         }
141         catch (Throwable JavaDoc th) {
142             // another common error is TokenManagerError
143
throw new ExpressionException(th.getMessage(), th);
144         }
145     }
146
147     /**
148      * Returns String label for this expression. Used for debugging.
149      */

150     public String JavaDoc expName() {
151         switch (type) {
152             case AND:
153                 return "AND";
154             case OR:
155                 return "OR";
156             case NOT:
157                 return "NOT";
158             case EQUAL_TO:
159                 return "=";
160             case NOT_EQUAL_TO:
161                 return "<>";
162             case LESS_THAN:
163                 return "<";
164             case LESS_THAN_EQUAL_TO:
165                 return "<=";
166             case GREATER_THAN:
167                 return ">";
168             case GREATER_THAN_EQUAL_TO:
169                 return ">=";
170             case BETWEEN:
171                 return "BETWEEN";
172             case IN:
173                 return "IN";
174             case LIKE:
175                 return "LIKE";
176             case LIKE_IGNORE_CASE:
177                 return "LIKE_IGNORE_CASE";
178             case OBJ_PATH:
179                 return "OBJ_PATH";
180             case DB_PATH:
181                 return "DB_PATH";
182             case LIST:
183                 return "LIST";
184             case NOT_BETWEEN:
185                 return "NOT BETWEEN";
186             case NOT_IN:
187                 return "NOT IN";
188             case NOT_LIKE:
189                 return "NOT LIKE";
190             case NOT_LIKE_IGNORE_CASE:
191                 return "NOT LIKE IGNORE CASE";
192             default:
193                 return "other";
194         }
195     }
196
197     public boolean equals(Object JavaDoc object) {
198         if (!(object instanceof Expression)) {
199             return false;
200         }
201
202         Expression e = (Expression) object;
203
204         if (e.getType() != getType() || e.getOperandCount() != getOperandCount()) {
205             return false;
206         }
207
208         // compare operands
209
int len = e.getOperandCount();
210         for (int i = 0; i < len; i++) {
211             if (!Util.nullSafeEquals(e.getOperand(i), getOperand(i))) {
212                 return false;
213             }
214         }
215
216         return true;
217     }
218
219     /**
220      * Returns a type of expression. Most common types are defined as public static fields
221      * of this interface.
222      */

223     public int getType() {
224         return type;
225     }
226
227     public void setType(int type) {
228         this.type = type;
229     }
230
231     /**
232      * A shortcut for <code>expWithParams(params, true)</code>.
233      */

234     public Expression expWithParameters(Map JavaDoc parameters) {
235         return expWithParameters(parameters, true);
236     }
237
238     /**
239      * Creates and returns a new Expression instance using this expression as a prototype.
240      * All ExpressionParam operands are substituted with the values in the
241      * <code>params</code> map.
242      * <p>
243      * <i>Null values in the <code>params</code> map should be explicitly created in the
244      * map for the corresponding key. </i>
245      * </p>
246      *
247      * @param parameters a map of parameters, with each key being a string name of an
248      * expression parameter, and value being the value that should be used in
249      * the final expression.
250      * @param pruneMissing If <code>true</code>, subexpressions that rely on missing
251      * parameters will be pruned from the resulting tree. If <code>false</code>,
252      * any missing values will generate an exception.
253      * @return Expression resulting from the substitution of parameters with real values,
254      * or null if the whole expression was pruned, due to the missing parameters.
255      */

256     public Expression expWithParameters(final Map JavaDoc parameters, final boolean pruneMissing) {
257
258         // create transformer for named parameters
259
Transformer transformer = new Transformer() {
260
261             public Object JavaDoc transform(Object JavaDoc object) {
262                 if (!(object instanceof ExpressionParameter)) {
263                     return object;
264                 }
265
266                 String JavaDoc name = ((ExpressionParameter) object).getName();
267                 if (!parameters.containsKey(name)) {
268                     if (pruneMissing) {
269                         return PRUNED_NODE;
270                     }
271                     else {
272                         throw new ExpressionException("Missing required parameter: $"
273                                 + name);
274                     }
275                 }
276                 else {
277                     Object JavaDoc value = parameters.get(name);
278
279                     // wrap lists (for now); also support null parameters
280
return (value != null)
281                             ? ExpressionFactory.wrapPathOperand(value)
282                             : null;
283                 }
284             }
285         };
286
287         return transform(transformer);
288     }
289
290     /**
291      * Creates a new expression that joins this object with another expression, using
292      * specified join type. It is very useful for incrementally building chained
293      * expressions, like long AND or OR statements.
294      */

295     public Expression joinExp(int type, Expression exp) {
296         Expression join = ExpressionFactory.expressionOfType(type);
297         join.setOperand(0, this);
298         join.setOperand(1, exp);
299         join.flattenTree();
300         return join;
301     }
302
303     /**
304      * Chains this expression with another expression using "and".
305      */

306     public Expression andExp(Expression exp) {
307         return joinExp(Expression.AND, exp);
308     }
309
310     /**
311      * Chains this expression with another expression using "or".
312      */

313     public Expression orExp(Expression exp) {
314         return joinExp(Expression.OR, exp);
315     }
316
317     /**
318      * Returns a logical NOT of current expression.
319      *
320      * @since 1.0.6
321      */

322     public abstract Expression notExp();
323
324     /**
325      * Returns a count of operands of this expression. In real life there are unary (count ==
326      * 1), binary (count == 2) and ternary (count == 3) expressions.
327      */

328     public abstract int getOperandCount();
329
330     /**
331      * Returns a value of operand at <code>index</code>. Operand indexing starts at 0.
332      */

333     public abstract Object JavaDoc getOperand(int index);
334
335     /**
336      * Sets a value of operand at <code>index</code>. Operand indexing starts at 0.
337      */

338     public abstract void setOperand(int index, Object JavaDoc value);
339
340     /**
341      * Calculates expression value with object as a context for path expressions.
342      *
343      * @since 1.1
344      */

345     public abstract Object JavaDoc evaluate(Object JavaDoc o);
346
347     /**
348      * Calculates expression boolean value with object as a context for path expressions.
349      *
350      * @since 1.1
351      */

352     public boolean match(Object JavaDoc o) {
353         return ConversionUtil.toBoolean(evaluate(o));
354     }
355
356     /**
357      * Returns a list of objects that match the expression.
358      */

359     public List JavaDoc filterObjects(List JavaDoc objects) {
360         if (objects == null || objects.size() == 0) {
361             return Collections.EMPTY_LIST;
362         }
363
364         return (List JavaDoc) filter(objects, new LinkedList JavaDoc());
365     }
366
367     /**
368      * Adds objects matching this expression from the source collection to the target
369      * collection.
370      *
371      * @since 1.1
372      */

373     public Collection JavaDoc filter(Collection JavaDoc source, Collection JavaDoc target) {
374         Iterator JavaDoc it = source.iterator();
375         while (it.hasNext()) {
376             Object JavaDoc o = it.next();
377             if (match(o)) {
378                 target.add(o);
379             }
380         }
381
382         return target;
383     }
384
385     /**
386      * Clones this expression.
387      *
388      * @since 1.1
389      */

390     public Expression deepCopy() {
391         return transform(null);
392     }
393
394     /**
395      * Creates a copy of this expression node, without copying children.
396      *
397      * @since 1.1
398      */

399     public abstract Expression shallowCopy();
400
401     /**
402      * Returns true if this node should be pruned from expression tree in the event a
403      * child is removed.
404      *
405      * @since 1.1
406      */

407     protected abstract boolean pruneNodeForPrunedChild(Object JavaDoc prunedChild);
408
409     /**
410      * Restructures expression to make sure that there are no children of the same type as
411      * this expression.
412      *
413      * @since 1.1
414      */

415     protected abstract void flattenTree();
416
417     /**
418      * Traverses itself and child expressions, notifying visitor via callback methods as
419      * it goes. This is an Expression-specific implementation of the "Visitor" design
420      * pattern.
421      *
422      * @since 1.1
423      */

424     public void traverse(TraversalHandler visitor) {
425         if (visitor == null) {
426             throw new NullPointerException JavaDoc("Null Visitor.");
427         }
428
429         traverse(null, visitor);
430     }
431
432     /**
433      * Traverses itself and child expressions, notifying visitor via callback methods as
434      * it goes.
435      *
436      * @since 1.1
437      */

438     protected void traverse(Expression parentExp, TraversalHandler visitor) {
439
440         visitor.startNode(this, parentExp);
441
442         // recursively traverse each child
443
int count = getOperandCount();
444         for (int i = 0; i < count; i++) {
445             Object JavaDoc child = getOperand(i);
446
447             if (child instanceof Expression) {
448                 Expression childExp = (Expression) child;
449                 childExp.traverse(this, visitor);
450             }
451             else {
452                 visitor.objectNode(child, this);
453             }
454
455             visitor.finishedChild(this, i, i < count - 1);
456         }
457
458         visitor.endNode(this, parentExp);
459     }
460
461     /**
462      * Creates a transformed copy of this expression, applying transformation provided by
463      * Transformer to all its nodes. Null transformer will result in an identical deep
464      * copy of this expression.
465      * <p>
466      * To force a node and its children to be pruned from the copy, Transformer should
467      * return Expression.PRUNED_NODE. Otherwise an expectation is that if a node is an
468      * Expression it must be transformed to null or another Expression. Any other object
469      * type would result in a ExpressionException.
470      *
471      * @since 1.1
472      */

473     public Expression transform(Transformer transformer) {
474         Object JavaDoc transformed = transformExpression(transformer);
475
476         if (transformed == PRUNED_NODE || transformed == null) {
477             return null;
478         }
479         else if (transformed instanceof Expression) {
480             return (Expression) transformed;
481         }
482
483         throw new ExpressionException("Invalid transformed expression: " + transformed);
484     }
485
486     /**
487      * A recursive method called from "transform" to do the actual transformation.
488      *
489      * @return null, Expression.PRUNED_NODE or transformed expression.
490      * @since 1.2
491      */

492     protected Object JavaDoc transformExpression(Transformer transformer) {
493         Expression copy = shallowCopy();
494         int count = getOperandCount();
495         for (int i = 0, j = 0; i < count; i++) {
496             Object JavaDoc operand = getOperand(i);
497             Object JavaDoc transformedChild;
498
499             if (operand instanceof Expression) {
500                 transformedChild = ((Expression) operand)
501                         .transformExpression(transformer);
502             }
503             else if (transformer != null) {
504                 transformedChild = transformer.transform(operand);
505             }
506             else {
507                 transformedChild = operand;
508             }
509
510             // prune null children only if there is a transformer and it indicated so
511
boolean prune = transformer != null && transformedChild == PRUNED_NODE;
512
513             if (!prune) {
514                 copy.setOperand(j, transformedChild);
515                 j++;
516             }
517
518             if (prune && pruneNodeForPrunedChild(operand)) {
519                 // bail out early...
520
return PRUNED_NODE;
521             }
522         }
523
524         // all the children are processed, only now transform this copy
525
return (transformer != null) ? (Expression) transformer.transform(copy) : copy;
526     }
527
528     /**
529      * Encodes itself, wrapping the string into XML CDATA section.
530      *
531      * @since 1.1
532      */

533     public void encodeAsXML(XMLEncoder encoder) {
534         encoder.print("<![CDATA[");
535         encodeAsString(encoder.getPrintWriter());
536         encoder.print("]]>");
537     }
538
539     /**
540      * Stores a String representation of Expression using a provided PrintWriter.
541      *
542      * @since 1.1
543      */

544     public abstract void encodeAsString(PrintWriter JavaDoc pw);
545
546     public String JavaDoc toString() {
547         StringWriter JavaDoc buffer = new StringWriter JavaDoc();
548         PrintWriter JavaDoc pw = new PrintWriter JavaDoc(buffer);
549         encodeAsString(pw);
550         pw.close();
551         buffer.flush();
552         return buffer.toString();
553     }
554 }
555
Popular Tags