001 /*
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements. See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership. The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License. You may obtain a copy of the License at
010 *
011 * http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied. See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 *
020 */
021 package org.apache.qpid.server.filter;
022 //
023 // Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
024 //
025
026 import java.util.HashSet;
027 import java.util.List;
028 import java.util.regex.Pattern;
029
030 import org.apache.qpid.server.queue.Filterable;
031
032 /**
033 * A filter performing a comparison of two objects
034 */
035 public abstract class ComparisonExpression<E extends Exception> extends BinaryExpression<E> implements BooleanExpression<E>
036 {
037
038 public static<E extends Exception> BooleanExpression<E> createBetween(Expression<E> value, Expression left, Expression<E> right)
039 {
040 return LogicExpression.createAND(createGreaterThanEqual(value, left), createLessThanEqual(value, right));
041 }
042
043 public static<E extends Exception> BooleanExpression<E> createNotBetween(Expression<E> value, Expression<E> left, Expression<E> right)
044 {
045 return LogicExpression.createOR(createLessThan(value, left), createGreaterThan(value, right));
046 }
047
048 private static final HashSet REGEXP_CONTROL_CHARS = new HashSet();
049
050 static
051 {
052 REGEXP_CONTROL_CHARS.add(new Character('.'));
053 REGEXP_CONTROL_CHARS.add(new Character('\\'));
054 REGEXP_CONTROL_CHARS.add(new Character('['));
055 REGEXP_CONTROL_CHARS.add(new Character(']'));
056 REGEXP_CONTROL_CHARS.add(new Character('^'));
057 REGEXP_CONTROL_CHARS.add(new Character('$'));
058 REGEXP_CONTROL_CHARS.add(new Character('?'));
059 REGEXP_CONTROL_CHARS.add(new Character('*'));
060 REGEXP_CONTROL_CHARS.add(new Character('+'));
061 REGEXP_CONTROL_CHARS.add(new Character('{'));
062 REGEXP_CONTROL_CHARS.add(new Character('}'));
063 REGEXP_CONTROL_CHARS.add(new Character('|'));
064 REGEXP_CONTROL_CHARS.add(new Character('('));
065 REGEXP_CONTROL_CHARS.add(new Character(')'));
066 REGEXP_CONTROL_CHARS.add(new Character(':'));
067 REGEXP_CONTROL_CHARS.add(new Character('&'));
068 REGEXP_CONTROL_CHARS.add(new Character('<'));
069 REGEXP_CONTROL_CHARS.add(new Character('>'));
070 REGEXP_CONTROL_CHARS.add(new Character('='));
071 REGEXP_CONTROL_CHARS.add(new Character('!'));
072 }
073
074 static class LikeExpression<E extends Exception> extends UnaryExpression<E> implements BooleanExpression<E>
075 {
076
077 Pattern likePattern;
078
079 /**
080 * @param right
081 */
082 public LikeExpression(Expression<E> right, String like, int escape)
083 {
084 super(right);
085
086 StringBuffer regexp = new StringBuffer(like.length() * 2);
087 regexp.append("\\A"); // The beginning of the input
088 for (int i = 0; i < like.length(); i++)
089 {
090 char c = like.charAt(i);
091 if (escape == (0xFFFF & c))
092 {
093 i++;
094 if (i >= like.length())
095 {
096 // nothing left to escape...
097 break;
098 }
099
100 char t = like.charAt(i);
101 regexp.append("\\x");
102 regexp.append(Integer.toHexString(0xFFFF & t));
103 }
104 else if (c == '%')
105 {
106 regexp.append(".*?"); // Do a non-greedy match
107 }
108 else if (c == '_')
109 {
110 regexp.append("."); // match one
111 }
112 else if (REGEXP_CONTROL_CHARS.contains(new Character(c)))
113 {
114 regexp.append("\\x");
115 regexp.append(Integer.toHexString(0xFFFF & c));
116 }
117 else
118 {
119 regexp.append(c);
120 }
121 }
122
123 regexp.append("\\z"); // The end of the input
124
125 likePattern = Pattern.compile(regexp.toString(), Pattern.DOTALL);
126 }
127
128 /**
129 * org.apache.activemq.filter.UnaryExpression#getExpressionSymbol()
130 */
131 public String getExpressionSymbol()
132 {
133 return "LIKE";
134 }
135
136 /**
137 * org.apache.activemq.filter.Expression#evaluate(MessageEvaluationContext)
138 */
139 public Object evaluate(Filterable<E> message) throws E
140 {
141
142 Object rv = this.getRight().evaluate(message);
143
144 if (rv == null)
145 {
146 return null;
147 }
148
149 if (!(rv instanceof String))
150 {
151 return
152 Boolean.FALSE;
153 // throw new RuntimeException("LIKE can only operate on String identifiers. LIKE attemped on: '" + rv.getClass());
154 }
155
156 return likePattern.matcher((String) rv).matches() ? Boolean.TRUE : Boolean.FALSE;
157 }
158
159 public boolean matches(Filterable<E> message) throws E
160 {
161 Object object = evaluate(message);
162
163 return (object != null) && (object == Boolean.TRUE);
164 }
165 }
166
167 public static BooleanExpression createLike(Expression left, String right, String escape)
168 {
169 if ((escape != null) && (escape.length() != 1))
170 {
171 throw new RuntimeException(
172 "The ESCAPE string litteral is invalid. It can only be one character. Litteral used: " + escape);
173 }
174
175 int c = -1;
176 if (escape != null)
177 {
178 c = 0xFFFF & escape.charAt(0);
179 }
180
181 return new LikeExpression(left, right, c);
182 }
183
184 public static BooleanExpression createNotLike(Expression left, String right, String escape)
185 {
186 return UnaryExpression.createNOT(createLike(left, right, escape));
187 }
188
189 public static BooleanExpression createInFilter(Expression left, List elements)
190 {
191
192 if (!(left instanceof PropertyExpression))
193 {
194 throw new RuntimeException("Expected a property for In expression, got: " + left);
195 }
196
197 return UnaryExpression.createInExpression((PropertyExpression) left, elements, false);
198
199 }
200
201 public static BooleanExpression createNotInFilter(Expression left, List elements)
202 {
203
204 if (!(left instanceof PropertyExpression))
205 {
206 throw new RuntimeException("Expected a property for In expression, got: " + left);
207 }
208
209 return UnaryExpression.createInExpression((PropertyExpression) left, elements, true);
210
211 }
212
213 public static BooleanExpression createIsNull(Expression left)
214 {
215 return doCreateEqual(left, ConstantExpression.NULL);
216 }
217
218 public static BooleanExpression createIsNotNull(Expression left)
219 {
220 return UnaryExpression.createNOT(doCreateEqual(left, ConstantExpression.NULL));
221 }
222
223 public static BooleanExpression createNotEqual(Expression left, Expression right)
224 {
225 return UnaryExpression.createNOT(createEqual(left, right));
226 }
227
228 public static BooleanExpression createEqual(Expression left, Expression right)
229 {
230 checkEqualOperand(left);
231 checkEqualOperand(right);
232 checkEqualOperandCompatability(left, right);
233
234 return doCreateEqual(left, right);
235 }
236
237 private static<E extends Exception> BooleanExpression<E> doCreateEqual(Expression<E> left, Expression<E> right)
238 {
239 return new EqualExpression(left, right);
240 }
241
242 public static BooleanExpression createGreaterThan(final Expression left, final Expression right)
243 {
244 checkLessThanOperand(left);
245 checkLessThanOperand(right);
246
247 return new ComparisonExpression(left, right)
248 {
249 protected boolean asBoolean(int answer)
250 {
251 return answer > 0;
252 }
253
254 public String getExpressionSymbol()
255 {
256 return ">";
257 }
258 };
259 }
260
261 public static BooleanExpression createGreaterThanEqual(final Expression left, final Expression right)
262 {
263 checkLessThanOperand(left);
264 checkLessThanOperand(right);
265
266 return new ComparisonExpression(left, right)
267 {
268 protected boolean asBoolean(int answer)
269 {
270 return answer >= 0;
271 }
272
273 public String getExpressionSymbol()
274 {
275 return ">=";
276 }
277 };
278 }
279
280 public static BooleanExpression createLessThan(final Expression left, final Expression right)
281 {
282 checkLessThanOperand(left);
283 checkLessThanOperand(right);
284
285 return new ComparisonExpression(left, right)
286 {
287
288 protected boolean asBoolean(int answer)
289 {
290 return answer < 0;
291 }
292
293 public String getExpressionSymbol()
294 {
295 return "<";
296 }
297
298 };
299 }
300
301 public static BooleanExpression createLessThanEqual(final Expression left, final Expression right)
302 {
303 checkLessThanOperand(left);
304 checkLessThanOperand(right);
305
306 return new ComparisonExpression(left, right)
307 {
308
309 protected boolean asBoolean(int answer)
310 {
311 return answer <= 0;
312 }
313
314 public String getExpressionSymbol()
315 {
316 return "<=";
317 }
318 };
319 }
320
321 /**
322 * Only Numeric expressions can be used in >, >=, < or <= expressions.s
323 *
324 * @param expr
325 */
326 public static void checkLessThanOperand(Expression expr)
327 {
328 if (expr instanceof ConstantExpression)
329 {
330 Object value = ((ConstantExpression) expr).getValue();
331 if (value instanceof Number)
332 {
333 return;
334 }
335
336 // Else it's boolean or a String..
337 throw new RuntimeException("Value '" + expr + "' cannot be compared.");
338 }
339
340 if (expr instanceof BooleanExpression)
341 {
342 throw new RuntimeException("Value '" + expr + "' cannot be compared.");
343 }
344 }
345
346 /**
347 * Validates that the expression can be used in == or <> expression.
348 * Cannot not be NULL TRUE or FALSE litterals.
349 *
350 * @param expr
351 */
352 public static void checkEqualOperand(Expression expr)
353 {
354 if (expr instanceof ConstantExpression)
355 {
356 Object value = ((ConstantExpression) expr).getValue();
357 if (value == null)
358 {
359 throw new RuntimeException("'" + expr + "' cannot be compared.");
360 }
361 }
362 }
363
364 /**
365 *
366 * @param left
367 * @param right
368 */
369 private static void checkEqualOperandCompatability(Expression left, Expression right)
370 {
371 if ((left instanceof ConstantExpression) && (right instanceof ConstantExpression))
372 {
373 if ((left instanceof BooleanExpression) && !(right instanceof BooleanExpression))
374 {
375 throw new RuntimeException("'" + left + "' cannot be compared with '" + right + "'");
376 }
377 }
378 }
379
380 /**
381 * @param left
382 * @param right
383 */
384 public ComparisonExpression(Expression left, Expression right)
385 {
386 super(left, right);
387 }
388
389 public Object evaluate(Filterable<E> message) throws E
390 {
391 Comparable lv = (Comparable) left.evaluate(message);
392 if (lv == null)
393 {
394 return null;
395 }
396
397 Comparable rv = (Comparable) right.evaluate(message);
398 if (rv == null)
399 {
400 return null;
401 }
402
403 return compare(lv, rv);
404 }
405
406 protected Boolean compare(Comparable lv, Comparable rv)
407 {
408 Class lc = lv.getClass();
409 Class rc = rv.getClass();
410 // If the the objects are not of the same type,
411 // try to convert up to allow the comparison.
412 if (lc != rc)
413 {
414 if (lc == Byte.class)
415 {
416 if (rc == Short.class)
417 {
418 lv = new Short(((Number) lv).shortValue());
419 }
420 else if (rc == Integer.class)
421 {
422 lv = new Integer(((Number) lv).intValue());
423 }
424 else if (rc == Long.class)
425 {
426 lv = new Long(((Number) lv).longValue());
427 }
428 else if (rc == Float.class)
429 {
430 lv = new Float(((Number) lv).floatValue());
431 }
432 else if (rc == Double.class)
433 {
434 lv = new Double(((Number) lv).doubleValue());
435 }
436 else
437 {
438 return Boolean.FALSE;
439 }
440 }
441 else if (lc == Short.class)
442 {
443 if (rc == Integer.class)
444 {
445 lv = new Integer(((Number) lv).intValue());
446 }
447 else if (rc == Long.class)
448 {
449 lv = new Long(((Number) lv).longValue());
450 }
451 else if (rc == Float.class)
452 {
453 lv = new Float(((Number) lv).floatValue());
454 }
455 else if (rc == Double.class)
456 {
457 lv = new Double(((Number) lv).doubleValue());
458 }
459 else
460 {
461 return Boolean.FALSE;
462 }
463 }
464 else if (lc == Integer.class)
465 {
466 if (rc == Long.class)
467 {
468 lv = new Long(((Number) lv).longValue());
469 }
470 else if (rc == Float.class)
471 {
472 lv = new Float(((Number) lv).floatValue());
473 }
474 else if (rc == Double.class)
475 {
476 lv = new Double(((Number) lv).doubleValue());
477 }
478 else
479 {
480 return Boolean.FALSE;
481 }
482 }
483 else if (lc == Long.class)
484 {
485 if (rc == Integer.class)
486 {
487 rv = new Long(((Number) rv).longValue());
488 }
489 else if (rc == Float.class)
490 {
491 lv = new Float(((Number) lv).floatValue());
492 }
493 else if (rc == Double.class)
494 {
495 lv = new Double(((Number) lv).doubleValue());
496 }
497 else
498 {
499 return Boolean.FALSE;
500 }
501 }
502 else if (lc == Float.class)
503 {
504 if (rc == Integer.class)
505 {
506 rv = new Float(((Number) rv).floatValue());
507 }
508 else if (rc == Long.class)
509 {
510 rv = new Float(((Number) rv).floatValue());
511 }
512 else if (rc == Double.class)
513 {
514 lv = new Double(((Number) lv).doubleValue());
515 }
516 else
517 {
518 return Boolean.FALSE;
519 }
520 }
521 else if (lc == Double.class)
522 {
523 if (rc == Integer.class)
524 {
525 rv = new Double(((Number) rv).doubleValue());
526 }
527 else if (rc == Long.class)
528 {
529 rv = new Double(((Number) rv).doubleValue());
530 }
531 else if (rc == Float.class)
532 {
533 rv = new Float(((Number) rv).doubleValue());
534 }
535 else
536 {
537 return Boolean.FALSE;
538 }
539 }
540 else
541 {
542 return Boolean.FALSE;
543 }
544 }
545
546 return asBoolean(lv.compareTo(rv)) ? Boolean.TRUE : Boolean.FALSE;
547 }
548
549 protected abstract boolean asBoolean(int answer);
550
551 public boolean matches(Filterable<E> message) throws E
552 {
553 Object object = evaluate(message);
554
555 return (object != null) && (object == Boolean.TRUE);
556 }
557
558 private static class EqualExpression<E extends Exception> extends ComparisonExpression<E>
559 {
560 public EqualExpression(final Expression<E> left, final Expression<E> right)
561 {
562 super(left, right);
563 }
564
565 public Object evaluate(Filterable<E> message) throws E
566 {
567 Object lv = left.evaluate(message);
568 Object rv = right.evaluate(message);
569
570 // Iff one of the values is null
571 if ((lv == null) ^ (rv == null))
572 {
573 return Boolean.FALSE;
574 }
575
576 if ((lv == rv) || lv.equals(rv))
577 {
578 return Boolean.TRUE;
579 }
580
581 if ((lv instanceof Comparable) && (rv instanceof Comparable))
582 {
583 return compare((Comparable) lv, (Comparable) rv);
584 }
585
586 return Boolean.FALSE;
587 }
588
589 protected boolean asBoolean(int answer)
590 {
591 return answer == 0;
592 }
593
594 public String getExpressionSymbol()
595 {
596 return "=";
597 }
598 }
599 }
|