View Javadoc
1   /*
2   Copyright (C) 2004 Grid Systems, S.A.
3   
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License, version 2, as
6   published by the Free Software Foundation.
7   
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12  
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  */
17  package com.gridsystems.beanfilter;
18  
19  import java.util.Collection;
20  import java.util.HashSet;
21  import java.util.Set;
22  
23  /**
24   * Collection operations node (contains, in).
25   *
26   * @author Rodrigo Ruiz
27   * @version 1.0
28   */
29  public class CollectionNode extends EvalNode implements  FilterParserConstants {
30    /**
31     * Left operand.
32     */
33    private EvalValue lval;
34  
35    /**
36     * Right operand.
37     */
38    private EvalValue rval;
39  
40    /**
41     * Operation type.
42     */
43    private int op;
44  
45    /**
46     * Creates a new instance.
47     *
48     * @param lval  The left operand
49     * @param op    The operation
50     * @param rval  The right operand
51     */
52    public CollectionNode(EvalValue lval, int op, EvalValue rval) {
53      super(lval.getLinePos(), lval.getCharPos());
54      this.lval = lval;
55      this.rval = rval;
56      this.op = op;
57    }
58  
59    /**
60     * {@inheritDoc}
61     */
62    @Override public boolean eval(Object src) throws EvalException {
63      Object o1 = lval.getValue(src);
64      Object o2 = rval.getValue(src);
65  
66      // Check nulls
67      if (((o1 == null) && !(o2 instanceof Set))
68        || ((o2 == null) && !(o1 instanceof Set))) {
69        switch (op) {
70          case CONTAINS:
71          case IN:
72            // continue executing code.
73            break;
74          case EQ:
75            return o1 == o2;
76          case NE:
77            return o1 != o2;
78          default:
79            return false;
80        }
81      }
82  
83      Set<Object> s1 = toHashSet(o1);
84      Set<Object> s2 = toHashSet(o2);
85      Collection<Object> c1 = deref(s1, src);
86      Collection<Object> c2 = deref(s2, src);
87  
88      switch (op) {
89        case CONTAINS:
90          return c1.containsAll(c2);
91        case IN:
92          return c2.containsAll(c1);
93  
94        case EQ:
95          if ((s1 instanceof AllItemsHashSet) || (s2 instanceof AllItemsHashSet)) {
96            return c1.equals(c2);
97          } else {
98            return operateOneByOne(s1, s2, c1, c2);
99          }
100       case NE:
101         if ((s1 instanceof AllItemsHashSet) || (s2 instanceof AllItemsHashSet)) {
102           return operateOneByOne(s1, s2, c1, c2);
103         } else {
104           return !c1.equals(c2);
105         }
106 
107       default:
108         return operateOneByOne(s1, s2, c1, c2);
109     }
110   }
111 
112   /**
113    * Compare two sets element by element.
114    * @param s1 First Set
115    * @param s2 Second Set
116    * @param c1 First Collection
117    * @param c2 Second Collection
118    * @return Result of comparison
119    * @throws EvalException If error.
120    */
121   private boolean operateOneByOne(Set<Object> s1, Set<Object> s2,
122     Collection<Object> c1, Collection<Object> c2) throws EvalException {
123     boolean existsFalse = false;
124     boolean existsTrue = false;
125     Object[] list1 = c1.toArray();
126     Object[] list2 = c2.toArray();
127     if ((list1.length == 0) || (list2.length == 0)) {
128       switch (op) {
129         case EQ:
130           return list1.length == list2.length;
131         case LE:
132           return list1.length <= list2.length;
133         case GE:
134           return list1.length >= list2.length;
135         case NE:
136           return list1.length != list2.length;
137         case LT:
138           return list1.length < list2.length;
139         case GT:
140           return list1.length > list2.length;
141         default:
142           return false;
143       }
144     }
145     for (int i = 0; i < list1.length; i++) {
146       for (int j = 0; j < list2.length; j++) {
147         if (CompareNode.evaluateOperation(lval, rval, list1[i], op, list2[j])) {
148           existsTrue = true;
149         } else {
150           existsFalse = true;
151         }
152       }
153     }
154     if ((s1 instanceof AllItemsHashSet) || (s2 instanceof AllItemsHashSet)) {
155       return !existsFalse;
156     } else {
157       return existsTrue;
158     }
159   }
160 
161   /**
162    * Converts any EvalValue contained in the set into its value.
163    *
164    * @param set  The set to process
165    * @param src  The data source, for dynamic items
166    * @return  A set containing no EvalValue instances
167    * @throws EvalException If an error occurs getting dereferencing an item
168    */
169   private Set<Object> deref(Set<Object> set, Object src) throws EvalException {
170     Set<Object> set2 = new HashSet<Object>();
171     for (Object item : set) {
172       if (item instanceof EvalValue) {
173         item = ((EvalValue)item).getValue(src);
174       }
175       set2.add(normalize(item));
176     }
177     return set2;
178   }
179 
180   /**
181    * Gets a normalized version of the specified object. The "normalization" converts
182    * compatible objects into comparable ones. For example, it will convert all integer
183    * values to instances of Long, so comparisons and containment operations will not
184    * fail.
185    *
186    * @param obj  The source object
187    * @return  The converted object
188    */
189   private Object normalize(Object obj) {
190     if (obj instanceof Integer || obj instanceof Short || obj instanceof Byte) {
191       long longValue = ((Number)obj).longValue();
192       return longValue;
193     } else if (obj instanceof Float) {
194       double doubleValue = ((Number)obj).doubleValue();
195       return doubleValue;
196     } else {
197       return obj;
198     }
199   }
200 
201   /**
202    * Converts an object into a set, if it is not yet.
203    *
204    * @param obj  The object to convert
205    * @return  A set containing the object, or object items if it is an array
206    *          or collection, or the object if it is already a set
207    */
208   @SuppressWarnings("unchecked")
209   private HashSet<Object> toHashSet(Object obj) {
210     if (obj == null) {
211       HashSet<Object> hash = new AllItemsHashSet();
212       hash.add(null);
213       return hash;
214     } else if (obj instanceof HashSet) {
215       return (HashSet<Object>)obj;
216     } else {
217       HashSet<Object> set;
218       if (obj.getClass().isArray()) {
219         set = ArrayValue.arrayToHash(obj, ArrayValue.ArrayType.ALL_ITEMS);
220       } else {
221         set = new AnyItemsHashSet();
222         set.add(obj);
223       }
224       return set;
225     }
226   }
227 
228   /**
229    * {@inheritDoc}
230    */
231   @Override public String toString() {
232     String oper = tokenImage[op];
233     if (oper.startsWith("\"")) {
234       oper = oper.substring(1, oper.length() - 1);
235     }
236     return lval + " " + oper + " " + rval;
237   }
238 }