KickJava   Java API By Example, From Geeks To Geeks.

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


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.exp;
57
58 import java.io.PrintWriter JavaDoc;
59 import java.io.Reader JavaDoc;
60 import java.io.Serializable JavaDoc;
61 import java.io.StringReader JavaDoc;
62 import java.io.StringWriter JavaDoc;
63 import java.util.Collection JavaDoc;
64 import java.util.Collections JavaDoc;
65 import java.util.Iterator JavaDoc;
66 import java.util.LinkedList JavaDoc;
67 import java.util.List JavaDoc;
68 import java.util.Map JavaDoc;
69
70 import org.apache.commons.collections.Transformer;
71 import org.objectstyle.cayenne.exp.parser.ExpressionParser;
72 import org.objectstyle.cayenne.exp.parser.ParseException;
73 import org.objectstyle.cayenne.util.ConversionUtil;
74 import org.objectstyle.cayenne.util.Util;
75 import org.objectstyle.cayenne.util.XMLEncoder;
76 import org.objectstyle.cayenne.util.XMLSerializable;
77
78 /**
79  * Superclass of Cayenne expressions that defines basic
80  * API for expressions use.
81  */

82 public abstract class Expression implements Serializable JavaDoc, XMLSerializable {
83     
84     private final static Object JavaDoc nullValue = new Object JavaDoc();
85
86     public static final int AND = 0;
87     public static final int OR = 1;
88     public static final int NOT = 2;
89     public static final int EQUAL_TO = 3;
90     public static final int NOT_EQUAL_TO = 4;
91     public static final int LESS_THAN = 5;
92     public static final int GREATER_THAN = 6;
93     public static final int LESS_THAN_EQUAL_TO = 7;
94     public static final int GREATER_THAN_EQUAL_TO = 8;
95     public static final int BETWEEN = 9;
96     public static final int IN = 10;
97     public static final int LIKE = 11;
98     public static final int LIKE_IGNORE_CASE = 12;
99     public static final int EXISTS = 15;
100     public static final int ADD = 16;
101     public static final int SUBTRACT = 17;
102     public static final int MULTIPLY = 18;
103     public static final int DIVIDE = 19;
104     public static final int NEGATIVE = 20;
105     public static final int POSITIVE = 21;
106
107     /**
108      * Currently not supported in Cayenne.
109      */

110     public static final int ALL = 22;
111
112     /**
113      * Currently not supported in Cayenne.
114      */

115     public static final int SOME = 23;
116
117     /**
118      * Currently not supported in Cayenne.
119      */

120     public static final int ANY = 24;
121
122     /**
123      * Expression interpreted as raw SQL.
124      * No translations will be done for this kind of expressions.
125      */

126     public static final int RAW_SQL = 25;
127
128     /**
129      * Expression describes a path relative to an ObjEntity.
130      * OBJ_PATH expression is resolved relative to some root ObjEntity. Path expression components
131      * are separated by "." (dot). Path can point to either one of these:
132      * <ul>
133      * <li><i>An attribute of root ObjEntity.</i>
134      * For entity Gallery OBJ_PATH expression "galleryName" will point to ObjAttribute "galleryName"
135      * <li><i>Another ObjEntity related to root ObjEntity via a chain of relationships.</i>
136      * For entity Gallery OBJ_PATH expression "paintingArray.toArtist" will point to ObjEntity "Artist"
137      * <li><i>ObjAttribute of another ObjEntity related to root ObjEntity via a chain of relationships.</i>
138      * For entity Gallery OBJ_PATH expression "paintingArray.toArtist.artistName" will point to ObjAttribute "artistName"
139      * </ul>
140      */

141     public static final int OBJ_PATH = 26;
142
143     /**
144      * Expression describes a path relative to a DbEntity.
145      * DB_PATH expression is resolved relative to some root DbEntity.
146      * Path expression components are separated by "." (dot). Path can
147      * point to either one of these:
148      * <ul>
149      * <li><i>An attribute of root DbEntity.</i>
150      * For entity GALLERY, DB_PATH expression "GALLERY_NAME" will point
151      * to a DbAttribute "GALLERY_NAME".
152      * </li>
153      *
154      * <li><i>Another DbEntity related to root DbEntity via a chain of relationships.</i>
155      * For entity GALLERY DB_PATH expression "paintingArray.toArtist" will point to
156      * DbEntity "ARTIST".
157      * </li>
158      *
159      * <li><i>DbAttribute of another ObjEntity related to root DbEntity via a chain
160      * of relationships.</i>
161      * For entity GALLERY DB_PATH expression "paintingArray.toArtist.ARTIST_NAME" will point
162      * to DbAttribute "ARTIST_NAME".
163      * </li>
164      * </ul>
165      */

166     public static final int DB_PATH = 27;
167
168     /**
169      * Interpreted as a comma-separated list of literals.
170      */

171     public static final int LIST = 28;
172
173     /**
174      * Currently not supported in Cayenne.
175      */

176     public static final int SUBQUERY = 29;
177
178     /**
179      * Currently not supported in Cayenne.
180      */

181     public static final int COUNT = 30;
182     
183     /**
184      * Currently not supported in Cayenne.
185      */

186     public static final int AVG = 31;
187
188     /**
189      * Currently not supported in Cayenne.
190      */

191     public static final int SUM = 32;
192
193     /**
194      * Currently not supported in Cayenne.
195      */

196     public static final int MAX = 33;
197
198     /**
199      * Currently not supported in Cayenne.
200      */

201     public static final int MIN = 34;
202
203     public static final int NOT_BETWEEN = 35;
204     public static final int NOT_IN = 36;
205     public static final int NOT_LIKE = 37;
206     public static final int NOT_LIKE_IGNORE_CASE = 38;
207
208     protected int type;
209
210     /**
211      * Parses string, converting it to Expression. If string does
212      * not represent a semantically correct expression, an ExpressionException
213      * is thrown.
214      *
215      * @since 1.1
216      */

217     // TODO: cache expression strings, since this operation is pretty slow
218
public static Expression fromString(String JavaDoc expressionString) {
219         if (expressionString == null) {
220             throw new NullPointerException JavaDoc("Null expression string.");
221         }
222
223         Reader JavaDoc reader = new StringReader JavaDoc(expressionString);
224         try {
225             return new ExpressionParser(reader).expression();
226         }
227         catch (ParseException ex) {
228             throw new ExpressionException(ex.getMessage(), ex);
229         }
230         catch (Throwable JavaDoc th) {
231             // another common error is TokenManagerError
232
throw new ExpressionException(th.getMessage(), th);
233         }
234     }
235
236     /**
237      * Returns String label for this expression. Used for debugging.
238      */

239     public String JavaDoc expName() {
240         switch (type) {
241             case AND :
242                 return "AND";
243             case OR :
244                 return "OR";
245             case NOT :
246                 return "NOT";
247             case EQUAL_TO :
248                 return "=";
249             case NOT_EQUAL_TO :
250                 return "<>";
251             case LESS_THAN :
252                 return "<";
253             case LESS_THAN_EQUAL_TO :
254                 return "<=";
255             case GREATER_THAN :
256                 return ">";
257             case GREATER_THAN_EQUAL_TO :
258                 return ">=";
259             case BETWEEN :
260                 return "BETWEEN";
261             case IN :
262                 return "IN";
263             case LIKE :
264                 return "LIKE";
265             case LIKE_IGNORE_CASE :
266                 return "LIKE_IGNORE_CASE";
267             case EXISTS :
268                 return "EXISTS";
269             case OBJ_PATH :
270                 return "OBJ_PATH";
271             case DB_PATH :
272                 return "DB_PATH";
273             case LIST :
274                 return "LIST";
275             case NOT_BETWEEN :
276                 return "NOT BETWEEN";
277             case NOT_IN :
278                 return "NOT IN";
279             case NOT_LIKE :
280                 return "NOT LIKE";
281             case NOT_LIKE_IGNORE_CASE :
282                 return "NOT LIKE IGNORE CASE";
283             default :
284                 return "other";
285         }
286     }
287
288     public boolean equals(Object JavaDoc object) {
289         if (!(object instanceof Expression)) {
290             return false;
291         }
292
293         Expression e = (Expression) object;
294
295         if (e.getType() != getType() || e.getOperandCount() != getOperandCount()) {
296             return false;
297         }
298
299         // compare operands
300
int len = e.getOperandCount();
301         for (int i = 0; i < len; i++) {
302             if (!Util.nullSafeEquals(e.getOperand(i), getOperand(i))) {
303                 return false;
304             }
305         }
306
307         return true;
308     }
309
310     /**
311      * Returns a type of expression. Most common types are defined
312      * as public static fields of this interface.
313      */

314     public int getType() {
315         return type;
316     }
317
318     public void setType(int type) {
319         this.type = type;
320     }
321
322     /**
323      * A shortcut for <code>expWithParams(params, true)</code>.
324      */

325     public Expression expWithParameters(Map JavaDoc parameters) {
326         return expWithParameters(parameters, true);
327     }
328
329     /**
330      * Creates and returns a new Expression instance using this expression as a
331      * prototype. All ExpressionParam operands are substituted with the values
332      * in the <code>params</code> map.
333      *
334      * <p><i>Null values in the <code>params</code> map should be
335      * explicitly created in the map for the corresponding key.
336      * </i></p>
337      *
338      * @param parameters a map of parameters, with each key being a string name of
339      * an expression parameter, and value being the value that should be used in
340      * the final expression.
341      * @param pruneMissing If <code>true</code>, subexpressions that rely
342      * on missing parameters will be pruned from the resulting tree. If
343      * <code>false</code>, any missing values will generate an exception.
344      *
345      * @return Expression resulting from the substitution of parameters with
346      * real values, or null if the whole expression was pruned, due to the
347      * missing parameters.
348      */

349     public Expression expWithParameters(
350         final Map JavaDoc parameters,
351         final boolean pruneMissing) {
352
353         // create transformer for named parameters
354
Transformer transformer = new Transformer() {
355             public Object JavaDoc transform(Object JavaDoc object) {
356                 if (!(object instanceof ExpressionParameter)) {
357                     return object;
358                 }
359
360                 String JavaDoc name = ((ExpressionParameter) object).getName();
361                 if (!parameters.containsKey(name)) {
362                     if (pruneMissing) {
363                         return null;
364                     }
365                     else {
366                         throw new ExpressionException(
367                             "Missing required parameter: $" + name);
368                     }
369                 }
370                 else {
371                     Object JavaDoc value = parameters.get(name);
372
373                     // wrap lists (for now); also support null parameters
374
return (value != null)
375                             ? ExpressionFactory.wrapPathOperand(value)
376                             : nullValue;
377                 }
378             }
379         };
380
381         return transform(transformer);
382     }
383
384     /**
385      * Creates a new expression that joins this object
386      * with another expression, using specified join type.
387      * It is very useful for incrementally building chained expressions,
388      * like long AND or OR statements.
389      */

390     public Expression joinExp(int type, Expression exp) {
391         Expression join = ExpressionFactory.expressionOfType(type);
392         join.setOperand(0, this);
393         join.setOperand(1, exp);
394         join.flattenTree();
395         return join;
396     }
397
398     /**
399      * Chains this expression with another expression using "and".
400      */

401     public Expression andExp(Expression exp) {
402         return joinExp(Expression.AND, exp);
403     }
404
405     /**
406      * Chains this expression with another expression using "or".
407      */

408     public Expression orExp(Expression exp) {
409         return joinExp(Expression.OR, exp);
410     }
411
412     /**
413      * Returns a logical NOT of current expression.
414      *
415      * @since 1.0.6
416      */

417     public Expression notExp() {
418         Expression exp = ExpressionFactory.expressionOfType(Expression.NOT);
419         exp.setOperand(0, this);
420         return exp;
421     }
422
423     /**
424      * Returns a count of operands of this expression. In real life there are
425      * unary (count == 1), binary (count == 2) and ternary (count == 3)
426      * expressions.
427      */

428     public abstract int getOperandCount();
429
430     /**
431      * Returns a value of operand at <code>index</code>.
432      * Operand indexing starts at 0.
433      */

434     public abstract Object JavaDoc getOperand(int index);
435
436     /**
437      * Sets a value of operand at <code>index</code>.
438      * Operand indexing starts at 0.
439      */

440     public abstract void setOperand(int index, Object JavaDoc value);
441
442     /**
443      * Calculates expression value with object as a context for
444      * path expressions.
445      *
446      * @since 1.1
447      */

448     public Object JavaDoc evaluate(Object JavaDoc o) {
449         return ASTCompiler.compile(this).evaluateASTChain(o);
450     }
451
452     /**
453      * Calculates expression boolean value with object as a context for
454      * path expressions.
455      *
456      * @since 1.1
457      */

458     public boolean match(Object JavaDoc o) {
459         return ConversionUtil.toBoolean(evaluate(o));
460     }
461
462     /**
463      * Returns a list of objects that match the expression.
464      */

465     public List JavaDoc filterObjects(List JavaDoc objects) {
466         if (objects == null || objects.size() == 0) {
467             return Collections.EMPTY_LIST;
468         }
469         
470         return (List JavaDoc) filter(objects, new LinkedList JavaDoc());
471     }
472     
473     /**
474      * Adds objects matching this expression from the source collection
475      * to the target collection.
476      *
477      * @since 1.1
478      */

479     public Collection JavaDoc filter(Collection JavaDoc source, Collection JavaDoc target) {
480         Iterator JavaDoc it = source.iterator();
481         while (it.hasNext()) {
482             Object JavaDoc o = it.next();
483             if (match(o)) {
484                 target.add(o);
485             }
486         }
487         
488         return target;
489     }
490
491     /**
492      * Clones this expression.
493      *
494      * @since 1.1
495      */

496     public Expression deepCopy() {
497         return transform(null);
498     }
499
500     /**
501      * Creates a copy of this expression node, without copying children.
502      *
503      * @since 1.1
504      */

505     public abstract Expression shallowCopy();
506
507     /**
508      * Returns true if this node should be pruned from expression tree
509      * in the event a child is removed.
510      *
511      * @since 1.1
512      */

513     protected abstract boolean pruneNodeForPrunedChild(Object JavaDoc prunedChild);
514
515     /**
516      * Restructures expression to make sure that there are no children of the
517      * same type as this expression.
518      *
519      * @since 1.1
520      */

521     protected abstract void flattenTree();
522
523     /**
524      * Traverses itself and child expressions, notifying visitor via callback
525      * methods as it goes. This is an Expression-specific implementation of
526      * the "Visitor" design pattern.
527      *
528      * @since 1.1
529      */

530     public void traverse(TraversalHandler visitor) {
531         if (visitor == null) {
532             throw new NullPointerException JavaDoc("Null Visitor.");
533         }
534
535         traverse(null, visitor);
536     }
537
538     /**
539      * Traverses itself and child expressions, notifying visitor via callback
540      * methods as it goes.
541      *
542      * @since 1.1
543      */

544     protected void traverse(Expression parentExp, TraversalHandler visitor) {
545
546         visitor.startNode(this, parentExp);
547
548         // recursively traverse each child
549
int count = getOperandCount();
550         for (int i = 0; i < count; i++) {
551             Object JavaDoc child = getOperand(i);
552
553             if (child instanceof Expression) {
554                 Expression childExp = (Expression) child;
555                 childExp.traverse(this, visitor);
556             }
557             else {
558                 visitor.objectNode(child, this);
559             }
560
561             visitor.finishedChild(this, i, i < count - 1);
562         }
563
564         visitor.endNode(this, parentExp);
565     }
566
567     /**
568      * Creates a transformed copy of this expression, applying
569      * transformation provided by Transformer to all its nodes.
570      * Null transformer will result in an identical deep copy of
571      * this expression.
572      *
573      * <p>To force a node and its children to be pruned from the
574      * copy, Transformer should return null for a given node.
575      *
576      * <p>There is one limitation on what Transformer is expected to do:
577      * if a node is an Expression it must be transformed to null
578      * or another Expression. Any other object type would result in an
579      * exception.
580      *
581      *
582      * @since 1.1
583      */

584     public Expression transform(Transformer transformer) {
585
586         Expression copy = shallowCopy();
587         int count = getOperandCount();
588         for (int i = 0, j = 0; i < count; i++) {
589             Object JavaDoc operand = getOperand(i);
590             Object JavaDoc transformedChild = operand;
591
592             if (operand instanceof Expression) {
593                 transformedChild = ((Expression) operand).transform(transformer);
594             }
595             else if (transformer != null) {
596                 transformedChild = transformer.transform(operand);
597             }
598
599             if (transformedChild != null) {
600                 Object JavaDoc value = (transformedChild != nullValue) ? transformedChild : null;
601                 copy.setOperand(j, value);
602                 j++;
603             }
604             else if (pruneNodeForPrunedChild(operand)) {
605                 // bail out early...
606
return null;
607             }
608         }
609
610         // all the children are processed, only now transform this copy
611
return (transformer != null) ? (Expression) transformer.transform(copy) : copy;
612     }
613
614     /**
615      * Encodes itself, wrapping the string into XML CDATA section.
616      * @since 1.1
617      */

618     public void encodeAsXML(XMLEncoder encoder) {
619         encoder.print("<![CDATA[");
620         encodeAsString(encoder.getPrintWriter());
621         encoder.print("]]>");
622     }
623
624     /**
625      * Stores a String representation of Expression using a provided
626      * PrintWriter.
627      *
628      * @since 1.1
629      */

630     public abstract void encodeAsString(PrintWriter JavaDoc pw);
631
632     /**
633      * Convenience method to log nested expressions. Used mainly for debugging.
634      * Called from "toString".
635      *
636      * @deprecated Since 1.1 <code>encode</code> is used to recursively
637      * print expressions.
638      */

639     protected void toStringBuffer(StringBuffer JavaDoc buf) {
640         for (int i = 0; i < getOperandCount(); i++) {
641             if (i > 0 || getOperandCount() == 1) {
642                 buf.append(" ").append(expName()).append(" ");
643             }
644
645             Object JavaDoc op = getOperand(i);
646             if (op == null) {
647                 buf.append("<null>");
648             }
649             else if (op instanceof String JavaDoc) {
650                 buf.append("'").append(op).append("'");
651             }
652             else if (op instanceof Expression) {
653                 buf.append('(');
654                 ((Expression) op).toStringBuffer(buf);
655                 buf.append(')');
656             }
657             else {
658                 buf.append(String.valueOf(op));
659             }
660         }
661
662     }
663
664     public String JavaDoc toString() {
665         StringWriter JavaDoc buffer = new StringWriter JavaDoc();
666         PrintWriter JavaDoc pw = new PrintWriter JavaDoc(buffer);
667         encodeAsString(pw);
668         pw.close();
669         buffer.flush();
670         return buffer.toString();
671     }
672 }
673
Popular Tags