001 /** 002 * 003 * Copyright 2004 Protique Ltd 004 * Copyright 2004 Hiram Chirino 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 * 018 **/ 019 package org.activemq.filter; 020 021 import java.util.HashSet; 022 import java.util.List; 023 import java.util.regex.Pattern; 024 025 import javax.jms.JMSException; 026 import javax.jms.Message; 027 028 /** 029 * A filter performing a comparison of two objects 030 * 031 * @version $Revision: 1.1.1.1 $ 032 */ 033 public abstract class ComparisonExpression extends BinaryExpression implements BooleanExpression { 034 035 public static BooleanExpression createBetween(Expression value, Expression left, Expression right) { 036 return LogicExpression.createAND(createGreaterThanEqual(value, left), createLessThanEqual(value, right)); 037 } 038 039 public static BooleanExpression createNotBetween(Expression value, Expression left, Expression right) { 040 return LogicExpression.createOR(createLessThan(value, left), createGreaterThan(value, right)); 041 } 042 043 static final private HashSet REGEXP_CONTROL_CHARS = new HashSet(); 044 045 static { 046 REGEXP_CONTROL_CHARS.add(new Character('.')); 047 REGEXP_CONTROL_CHARS.add(new Character('\\')); 048 REGEXP_CONTROL_CHARS.add(new Character('[')); 049 REGEXP_CONTROL_CHARS.add(new Character(']')); 050 REGEXP_CONTROL_CHARS.add(new Character('^')); 051 REGEXP_CONTROL_CHARS.add(new Character('$')); 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 } 067 068 static class LikeExpression extends UnaryExpression implements BooleanExpression { 069 070 Pattern likePattern; 071 072 /** 073 * @param left 074 */ 075 public LikeExpression(Expression right, String like, int escape) { 076 super(right); 077 078 StringBuffer regexp = new StringBuffer(like.length() * 2); 079 regexp.append("\\A"); // The beginning of the input 080 for (int i = 0; i < like.length(); i++) { 081 char c = like.charAt(i); 082 if (escape == (0xFFFF & c)) { 083 i++; 084 if (i >= like.length()) { 085 // nothing left to escape... 086 break; 087 } 088 089 char t = like.charAt(i); 090 regexp.append("\\x"); 091 regexp.append(Integer.toHexString(0xFFFF & t)); 092 } 093 else if (c == '%') { 094 regexp.append(".*?"); // Do a non-greedy match 095 } 096 else if (c == '_') { 097 regexp.append("."); // match one 098 } 099 else if (REGEXP_CONTROL_CHARS.contains(new Character(c))) { 100 regexp.append("\\x"); 101 regexp.append(Integer.toHexString(0xFFFF & c)); 102 } 103 else { 104 regexp.append(c); 105 } 106 } 107 regexp.append("\\z"); // The end of the input 108 109 System.out.println("regexp: " + like + ": " + regexp); 110 likePattern = Pattern.compile(regexp.toString(), Pattern.DOTALL); 111 } 112 113 /** 114 * @see org.activemq.filter.UnaryExpression#getExpressionSymbol() 115 */ 116 public String getExpressionSymbol() { 117 return "LIKE"; 118 } 119 120 /** 121 * @see org.activemq.filter.Expression#evaluate(javax.jms.Message) 122 */ 123 public Object evaluate(Message message) throws JMSException { 124 125 Object rv = this.getRight().evaluate(message); 126 127 if (rv == null) { 128 return null; 129 } 130 131 if (!(rv instanceof String)) { 132 return Boolean.FALSE; 133 //throw new RuntimeException("LIKE can only operate on String identifiers. LIKE attemped on: '" + rv.getClass()); 134 } 135 136 return likePattern.matcher((String) rv).matches() ? Boolean.TRUE : Boolean.FALSE; 137 } 138 139 } 140 141 public static BooleanExpression createLike(Expression left, String right, String escape) { 142 if (escape != null && escape.length() != 1) { 143 throw new RuntimeException("The ESCAPE string litteral is invalid. It can only be one character. Litteral used: " + escape); 144 } 145 int c = -1; 146 if (escape != null) { 147 c = 0xFFFF & escape.charAt(0); 148 } 149 150 return new LikeExpression(left, right, c); 151 } 152 153 public static BooleanExpression createNotLike(Expression left, String right, String escape) { 154 return UnaryExpression.createNOT(createLike(left, right, escape)); 155 } 156 157 public static BooleanExpression createInFilter(Expression left, List elements) { 158 159 if( !(left instanceof PropertyExpression) ) 160 throw new RuntimeException("Expected a property for In expression, got: "+left); 161 return UnaryExpression.createInExpression((PropertyExpression)left, elements, false); 162 163 } 164 165 public static BooleanExpression createNotInFilter(Expression left, List elements) { 166 167 if( !(left instanceof PropertyExpression) ) 168 throw new RuntimeException("Expected a property for In expression, got: "+left); 169 return UnaryExpression.createInExpression((PropertyExpression)left, elements, true); 170 171 } 172 173 public static BooleanExpression createIsNull(Expression left) { 174 return doCreateEqual(left, ConstantExpression.NULL); 175 } 176 177 public static BooleanExpression createIsNotNull(Expression left) { 178 return UnaryExpression.createNOT(doCreateEqual(left, ConstantExpression.NULL)); 179 } 180 181 public static BooleanExpression createNotEqual(Expression left, Expression right) { 182 return UnaryExpression.createNOT(createEqual(left, right)); 183 } 184 185 public static BooleanExpression createEqual(Expression left, Expression right) { 186 checkEqualOperand(left); 187 checkEqualOperand(right); 188 checkEqualOperandCompatability(left, right); 189 return doCreateEqual(left, right); 190 } 191 192 private static BooleanExpression doCreateEqual(Expression left, Expression right) { 193 return new ComparisonExpression(left, right) { 194 195 public Object evaluate(Message message) throws JMSException { 196 Object obj1 = left.evaluate(message); 197 Object obj2 = right.evaluate(message); 198 199 // Iff one of the values is null 200 if (obj1 == null ^ obj2 == null) { 201 return Boolean.FALSE; 202 } 203 if (obj1 == obj2 || obj1.equals(obj2)) { 204 return Boolean.TRUE; 205 } 206 Comparable lv = obj1 instanceof Comparable ? (Comparable) obj1 : null; 207 Comparable rv = obj2 instanceof Comparable ? (Comparable) obj2 : null; 208 if( lv==null || rv==null ) 209 return Boolean.FALSE; 210 return compare(lv, rv); 211 } 212 213 protected boolean asBoolean(int answer) { 214 return answer == 0; 215 } 216 217 public String getExpressionSymbol() { 218 return "="; 219 } 220 }; 221 } 222 223 public static BooleanExpression createGreaterThan(final Expression left, final Expression right) { 224 checkLessThanOperand(left); 225 checkLessThanOperand(right); 226 return new ComparisonExpression(left, right) { 227 protected boolean asBoolean(int answer) { 228 return answer > 0; 229 } 230 231 public String getExpressionSymbol() { 232 return ">"; 233 } 234 }; 235 } 236 237 public static BooleanExpression createGreaterThanEqual(final Expression left, final Expression right) { 238 checkLessThanOperand(left); 239 checkLessThanOperand(right); 240 return new ComparisonExpression(left, right) { 241 protected boolean asBoolean(int answer) { 242 return answer >= 0; 243 } 244 245 public String getExpressionSymbol() { 246 return ">="; 247 } 248 }; 249 } 250 251 public static BooleanExpression createLessThan(final Expression left, final Expression right) { 252 checkLessThanOperand(left); 253 checkLessThanOperand(right); 254 return new ComparisonExpression(left, right) { 255 256 protected boolean asBoolean(int answer) { 257 return answer < 0; 258 } 259 260 public String getExpressionSymbol() { 261 return "<"; 262 } 263 264 }; 265 } 266 267 public static BooleanExpression createLessThanEqual(final Expression left, final Expression right) { 268 checkLessThanOperand(left); 269 checkLessThanOperand(right); 270 return new ComparisonExpression(left, right) { 271 272 protected boolean asBoolean(int answer) { 273 return answer <= 0; 274 } 275 276 public String getExpressionSymbol() { 277 return "<="; 278 } 279 }; 280 } 281 282 /** 283 * Only Numeric expressions can be used in >, >=, < or <= expressions.s 284 * 285 * @param expr 286 */ 287 public static void checkLessThanOperand(Expression expr ) { 288 if( expr instanceof ConstantExpression ) { 289 Object value = ((ConstantExpression)expr).getValue(); 290 if( value instanceof Number ) 291 return; 292 293 // Else it's boolean or a String.. 294 throw new RuntimeException("Value '"+expr+"' cannot be compared."); 295 } 296 if( expr instanceof BooleanExpression ) { 297 throw new RuntimeException("Value '"+expr+"' cannot be compared."); 298 } 299 } 300 301 /** 302 * Validates that the expression can be used in == or <> expression. 303 * Cannot not be NULL TRUE or FALSE litterals. 304 * 305 * @param expr 306 */ 307 public static void checkEqualOperand(Expression expr ) { 308 if( expr instanceof ConstantExpression ) { 309 Object value = ((ConstantExpression)expr).getValue(); 310 if( value == null ) 311 throw new RuntimeException("'"+expr+"' cannot be compared."); 312 } 313 } 314 315 /** 316 * 317 * @param left 318 * @param right 319 */ 320 private static void checkEqualOperandCompatability(Expression left, Expression right) { 321 if( left instanceof ConstantExpression && right instanceof ConstantExpression ) { 322 if( left instanceof BooleanExpression && !(right instanceof BooleanExpression) ) 323 throw new RuntimeException("'"+left+"' cannot be compared with '"+right+"'"); 324 } 325 } 326 327 328 329 /** 330 * @param left 331 * @param right 332 */ 333 public ComparisonExpression(Expression left, Expression right) { 334 super(left, right); 335 } 336 337 public Object evaluate(Message message) throws JMSException { 338 Comparable lv = (Comparable) left.evaluate(message); 339 if (lv == null) { 340 return null; 341 } 342 Comparable rv = (Comparable) right.evaluate(message); 343 if (rv == null) { 344 return null; 345 } 346 return compare(lv, rv); 347 } 348 349 protected Boolean compare(Comparable lv, Comparable rv) { 350 Class lc = lv.getClass(); 351 Class rc = rv.getClass(); 352 // If the the objects are not of the same type, 353 // try to convert up to allow the comparison. 354 if (lc != rc) { 355 if (lc == Integer.class) { 356 if (rc == Long.class) { 357 lv = new Long(((Number) lv).longValue()); 358 } 359 else if (rc == Float.class) { 360 lv = new Float(((Number) lv).floatValue()); 361 } 362 else if (rc == Double.class) { 363 lv = new Double(((Number) lv).doubleValue()); 364 } 365 else { 366 return Boolean.FALSE; 367 } 368 } 369 else if (lc == Long.class) { 370 if (rc == Integer.class) { 371 rv = new Long(((Number) rv).longValue()); 372 } 373 else if (rc == Float.class) { 374 lv = new Float(((Number) lv).floatValue()); 375 } 376 else if (rc == Double.class) { 377 lv = new Double(((Number) lv).doubleValue()); 378 } 379 else { 380 return Boolean.FALSE; 381 } 382 } 383 else if (lc == Float.class) { 384 if (rc == Integer.class) { 385 rv = new Float(((Number) rv).floatValue()); 386 } 387 else if (rc == Long.class) { 388 rv = new Float(((Number) rv).floatValue()); 389 } 390 else if (rc == Double.class) { 391 lv = new Double(((Number) lv).doubleValue()); 392 } 393 else { 394 return Boolean.FALSE; 395 } 396 } 397 else if (lc == Double.class) { 398 if (rc == Integer.class) { 399 rv = new Double(((Number) rv).doubleValue()); 400 } 401 else if (rc == Long.class) { 402 rv = new Double(((Number) rv).doubleValue()); 403 } 404 else if (rc == Float.class) { 405 rv = new Float(((Number) rv).doubleValue()); 406 } 407 else { 408 return Boolean.FALSE; 409 } 410 } 411 else 412 return Boolean.FALSE; 413 } 414 return asBoolean(lv.compareTo(rv)) ? Boolean.TRUE : Boolean.FALSE; 415 } 416 417 protected abstract boolean asBoolean(int answer); 418 }