1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.gridsystems.beanfilter;
18
19 import java.util.ArrayList;
20 import java.util.BitSet;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26 import java.util.regex.PatternSyntaxException;
27
28
29
30
31
32
33
34
35 public class CompareNode extends EvalNode implements FilterParserConstants {
36
37
38
39
40 private static final BitSet COMPARATORS = new BitSet();
41
42
43
44
45 private static final Set<Class<?>> PRIMITIVE_NUMBER_CLASSES = new HashSet<Class<?>>();
46
47
48
49
50 private static final Map<Class<?>, Operation[]> LEFTRIGHTOPERANDS;
51
52 static {
53 COMPARATORS.set(EQ);
54 COMPARATORS.set(NE);
55 COMPARATORS.set(LT);
56 COMPARATORS.set(LE);
57 COMPARATORS.set(GT);
58 COMPARATORS.set(GE);
59 COMPARATORS.set(MATCHES);
60 COMPARATORS.set(LIKE);
61
62 LEFTRIGHTOPERANDS = new HashMap<Class<?>, Operation[]>();
63
64 LEFTRIGHTOPERANDS.put(HashSet.class, new Operation[]{
65 new Operation(HashSet.class, EQ, NE, CONTAINS, IN)
66 });
67 LEFTRIGHTOPERANDS.put(String.class, new Operation[] {
68 new Operation(String.class, EQ, NE, GT, LT, GE, LE, MATCHES, LIKE),
69 new Operation(List.class, IN)
70 });
71 LEFTRIGHTOPERANDS.put(HashSet.class, new Operation[]{
72 new Operation(HashSet.class, EQ, NE, CONTAINS, IN)
73 });
74 LEFTRIGHTOPERANDS.put(Number.class, new Operation[] {
75 new Operation(Number.class, EQ, NE, GT, LT, GE, LE),
76 new Operation(List.class, IN)
77 });
78 LEFTRIGHTOPERANDS.put(Boolean.class, new Operation[] {
79 new Operation(Boolean.class, EQ, NE),
80 new Operation(boolean.class, EQ, NE),
81 new Operation(List.class, IN)
82 });
83 LEFTRIGHTOPERANDS.put(boolean.class, new Operation[] {
84 new Operation(Boolean.class, EQ, NE),
85 new Operation(boolean.class, EQ, NE),
86 new Operation(List.class, IN)
87 });
88
89 PRIMITIVE_NUMBER_CLASSES.add(int.class);
90 PRIMITIVE_NUMBER_CLASSES.add(long.class);
91 PRIMITIVE_NUMBER_CLASSES.add(float.class);
92 PRIMITIVE_NUMBER_CLASSES.add(double.class);
93 PRIMITIVE_NUMBER_CLASSES.add(short.class);
94 PRIMITIVE_NUMBER_CLASSES.add(byte.class);
95 }
96
97
98
99
100
101
102 private static class Operation {
103
104
105
106
107 final Class<?> right;
108
109
110
111
112 final List<Integer> operations;
113
114
115
116
117
118
119
120 public Operation(Class<?> right, int ... operators) {
121 this.right = right;
122 operations = new ArrayList<Integer>();
123 for (int i = 0; i < operators.length; i++) {
124 operations.add(operators[i]);
125 }
126 }
127
128
129
130
131
132
133
134 public boolean isAllowed(Class<?> right, int operation) {
135 return this.right.equals(right) && operations.contains(operation);
136 }
137
138 }
139
140
141
142
143 private EvalValue lval;
144
145
146
147
148 private EvalValue rval;
149
150
151
152
153 private int op;
154
155
156
157
158
159
160
161
162
163
164 public CompareNode(EvalValue lval, int op, EvalValue rval, Class<?> parentClass)
165 throws EvalException {
166 super(lval.getLinePos(), lval.getCharPos());
167 if (!COMPARATORS.get(op)) {
168 String operation;
169 try {
170 operation = tokenImage[op];
171 } catch (IndexOutOfBoundsException e) {
172 operation = "?";
173 }
174 Object[] eparams = { lval.getLinePos(), lval.getCharPos(), operation };
175 throw new EvalException("FTR006", eparams);
176 }
177 this.lval = lval;
178 this.rval = rval;
179 this.op = op;
180
181 checkSyntax(parentClass);
182 }
183
184
185
186
187
188
189 private void checkSyntax(Class<?> parentClass) throws EvalException {
190 Class<?> lClass = lval.getClassValue(parentClass);
191 Class<?> rClass = rval.getClassValue(parentClass);
192
193 if (isNumber(lClass)) {
194 lClass = Number.class;
195 }
196 if (isNumber(rClass)) {
197 rClass = Number.class;
198 }
199
200 Operation[] operations = LEFTRIGHTOPERANDS.get(lClass);
201
202 if (operations != null) {
203 for (int i = 0; i < operations.length; i++) {
204 Operation oper = operations[i];
205 if (oper.isAllowed(rClass, this.op)) {
206 return;
207 }
208 }
209 } else {
210 switch (op) {
211 case EQ:
212 case NE:
213 if ((lClass != null) && (rClass != null) && !lClass.equals(rClass)) {
214
215
216 Object[] params = {
217 lval.toString(), lClass, lval.getLinePos(), lval.getCharPos(),
218 rval.toString(), rClass, rval.getLinePos(), rval.getCharPos()
219 };
220 throw new EvalException("FTR008", params);
221 }
222 break;
223
224 default:
225
226 String operation = "?";
227 try {
228 operation = tokenImage[op];
229 } catch (IndexOutOfBoundsException e) {
230
231 }
232 Object[] params = { lval.getLinePos(), lval.getCharPos(), operation };
233 throw new EvalException("FTR006", params);
234 }
235 }
236 }
237
238
239
240
241
242
243 private static boolean isNumber(Class<?> number) {
244 if (number == null) {
245 return false;
246 }
247 if (Number.class.isAssignableFrom(number)) {
248 return true;
249 }
250 return PRIMITIVE_NUMBER_CLASSES.contains(number);
251 }
252
253
254
255
256 @Override public boolean eval(Object src) throws EvalException {
257 Object lobj = lval.getValue(src);
258 Object robj = rval.getValue(src);
259
260 if (lobj instanceof HashSet || robj instanceof HashSet) {
261 CollectionNode tempNode = new CollectionNode(lval, op, rval);
262 return tempNode.eval(src);
263 }
264
265 return evaluateOperation(this.lval, this.rval, lobj, this.op, robj);
266 }
267
268
269
270
271
272
273
274
275
276
277 public static boolean evaluateOperation(EvalValue lval, EvalValue rval,
278 Object lobj, int op, Object robj) throws EvalException {
279 switch (op) {
280 case EQ:
281 return eq(lobj, robj);
282 case NE:
283 return !eq(lobj, robj);
284 case LE:
285 return comp(lval, rval, lobj, robj, true, true);
286 case LT:
287 return comp(lval, rval, lobj, robj, true, false);
288 case GT:
289 return comp(lval, rval, lobj, robj, false, false);
290 case GE:
291 return comp(lval, rval, lobj, robj, false, true);
292 case MATCHES:
293 return match(lval, rval, lobj, robj);
294 case LIKE:
295 return like(lval, rval, lobj, robj);
296 default:
297 return false;
298 }
299 }
300
301
302
303
304
305
306
307 public EvalValue getLeftValue() {
308 return lval;
309 }
310
311
312
313
314
315
316 public EvalValue getRightValue() {
317 return rval;
318 }
319
320
321
322
323
324
325 public int getOp() {
326 return op;
327 }
328
329
330
331
332
333
334
335
336 private static boolean eq(Object o1, Object o2) {
337 if (o1 == null) {
338 if (o2 == null) {
339 return true;
340 }
341 return false;
342 } else {
343 if (o2 == null) {
344 return false;
345 } else {
346 if (o1 instanceof Number && o2 instanceof Number) {
347 return (isInt(o1) && isInt(o2))
348 ? ((Number)o1).longValue() == ((Number)o2).longValue()
349 : ((Number)o1).doubleValue() == ((Number)o2).doubleValue();
350 } else {
351 return o1.equals(o2);
352 }
353 }
354 }
355 }
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371 @SuppressWarnings("unchecked")
372 private static boolean comp(EvalValue lval, EvalValue rval, Object o1, Object o2,
373 boolean less, boolean eq) throws EvalException {
374
375 if (o1 == null || o2 == null) {
376 return false;
377 }
378
379 int sign = (less) ? -1 : 1;
380 int value = (eq) ? 0 : 1;
381
382 if (o1 instanceof Number && o2 instanceof Number) {
383 Number n1 = (Number)o1;
384 Number n2 = (Number)o2;
385 if (isInt(n1) && isInt(n2)) {
386 long diff = n1.longValue() - n2.longValue();
387 return (sign * diff) >= value;
388 } else {
389 double dif = n1.doubleValue() - n2.doubleValue();
390 long diff = dif == 0.0 ? 0 : (dif > 0.0 ? sign : -sign);
391 return diff >= value;
392 }
393 } else if (o1 instanceof Comparable && o1.getClass().equals(o2.getClass())) {
394 int diff = ((Comparable)o1).compareTo(o2);
395 return (sign * diff) >= value;
396 }
397 throw new EvalException("FTR006", lval.getLinePos(), lval.getCharPos(), "");
398 }
399
400
401
402
403
404
405
406
407
408
409
410 private static boolean match(EvalValue lval, EvalValue rval, Object o1, Object o2)
411 throws EvalException {
412
413 if (o1 == null || o2 == null) {
414 return false;
415 }
416 String regex = o2.toString();
417 try {
418 return o1.toString().matches(regex);
419 } catch (PatternSyntaxException e) {
420 throw new EvalException("FTR007", rval.getLinePos(), rval.getCharPos(), regex);
421 }
422 }
423
424
425
426
427
428
429
430
431
432
433
434 private static boolean like(EvalValue lval, EvalValue rval, Object o1, Object o2)
435 throws EvalException {
436
437 if (o1 == null || o2 == null) {
438 return false;
439 }
440 StringBuffer sb = new StringBuffer();
441 String src = o2.toString();
442 for (int i = 0; i < src.length(); i++) {
443 char c = src.charAt(i);
444 switch (c) {
445 case '.':
446 case '\\':
447 sb.append('\\');
448 break;
449 case '?':
450 case '*':
451 sb.append('.');
452 default:
453 break;
454 }
455 sb.append(c);
456 }
457 return match(lval, rval, o1, sb);
458 }
459
460
461
462
463
464
465
466 private static boolean isInt(Object obj) {
467 if (obj == null) {
468 return false;
469 }
470 return isNumber(obj.getClass());
471 }
472
473
474
475
476 @Override public String toString() {
477 String oper = tokenImage[op];
478 if (oper.startsWith("\"")) {
479 oper = oper.substring(1, oper.length() - 1);
480 }
481 return lval + " " + oper + " " + rval;
482 }
483 }