View Javadoc

1   /*** 
2    * 
3    * Copyright 2004 Protique Ltd
4    * Copyright 2004 Hiram Chirino
5    * 
6    * Licensed under the Apache License, Version 2.0 (the "License"); 
7    * you may not use this file except in compliance with the License. 
8    * You may obtain a copy of the License at 
9    * 
10   * http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS, 
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
15   * See the License for the specific language governing permissions and 
16   * limitations under the License. 
17   * 
18   **/
19  package org.codehaus.activemq.filter;
20  
21  import javax.jms.JMSException;
22  import javax.jms.Message;
23  import java.util.HashSet;
24  import java.util.List;
25  import java.util.regex.Pattern;
26  
27  /***
28   * A filter performing a comparison of two objects
29   * 
30   * @version $Revision: 1.1 $
31   */
32  public abstract class ComparisonExpression extends BinaryExpression implements BooleanExpression {
33  
34      public static BooleanExpression createBetween(Expression value, Expression left, Expression right) {
35          return LogicExpression.createAND(createGreaterThanEqual(value, left), createLessThanEqual(value, right));
36      }
37  
38      public static BooleanExpression createNotBetween(Expression value, Expression left, Expression right) {
39          return LogicExpression.createOR(createLessThan(value, left), createGreaterThan(value, right));
40      }
41  
42      static final private HashSet REGEXP_CONTROL_CHARS = new HashSet();
43  
44      static {
45          REGEXP_CONTROL_CHARS.add(new Character('.'));
46          REGEXP_CONTROL_CHARS.add(new Character('//'));
47          REGEXP_CONTROL_CHARS.add(new Character('['));
48          REGEXP_CONTROL_CHARS.add(new Character(']'));
49          REGEXP_CONTROL_CHARS.add(new Character('^'));
50          REGEXP_CONTROL_CHARS.add(new Character('$'));
51          REGEXP_CONTROL_CHARS.add(new Character('?'));
52          REGEXP_CONTROL_CHARS.add(new Character('*'));
53          REGEXP_CONTROL_CHARS.add(new Character('+'));
54          REGEXP_CONTROL_CHARS.add(new Character('{'));
55          REGEXP_CONTROL_CHARS.add(new Character('}'));
56          REGEXP_CONTROL_CHARS.add(new Character('|'));
57          REGEXP_CONTROL_CHARS.add(new Character('('));
58          REGEXP_CONTROL_CHARS.add(new Character(')'));
59          REGEXP_CONTROL_CHARS.add(new Character(':'));
60          REGEXP_CONTROL_CHARS.add(new Character('&'));
61          REGEXP_CONTROL_CHARS.add(new Character('<'));
62          REGEXP_CONTROL_CHARS.add(new Character('>'));
63          REGEXP_CONTROL_CHARS.add(new Character('='));
64          REGEXP_CONTROL_CHARS.add(new Character('!'));
65      }
66  
67      static class LikeExpression extends UnaryExpression implements BooleanExpression {
68  
69          Pattern likePattern;
70  
71          /***
72           * @param left
73           */
74          public LikeExpression(Expression right, String like, int escape) {
75              super(right);
76  
77              StringBuffer regexp = new StringBuffer(like.length() * 2);
78              regexp.append("//A"); // The beginning of the input
79              for (int i = 0; i < like.length(); i++) {
80                  char c = like.charAt(i);
81                  if (escape == (0xFFFF & c)) {
82                      i++;
83                      if (i >= like.length()) {
84                          // nothing left to escape...
85                          break;
86                      }
87  
88                      char t = like.charAt(i);
89                      regexp.append("//x");
90                      regexp.append(Integer.toHexString(0xFFFF & t));
91                  }
92                  else if (c == '%') {
93                      regexp.append(".*?"); // Do a non-greedy match 
94                  }
95                  else if (c == '_') {
96                      regexp.append("."); // match one 
97                  }
98                  else if (REGEXP_CONTROL_CHARS.contains(new Character(c))) {
99                      regexp.append("//x");
100                     regexp.append(Integer.toHexString(0xFFFF & c));
101                 }
102                 else {
103                     regexp.append(c);
104                 }
105             }
106             regexp.append("//z"); // The end of the input
107 
108             System.out.println("regexp: " + like + ": " + regexp);
109             likePattern = Pattern.compile(regexp.toString(), Pattern.DOTALL);
110         }
111 
112         /***
113          * @see org.codehaus.activemq.filter.UnaryExpression#getExpressionSymbol()
114          */
115         public String getExpressionSymbol() {
116             return "LIKE";
117         }
118 
119         /***
120          * @see org.codehaus.activemq.filter.Expression#evaluate(javax.jms.Message)
121          */
122         public Object evaluate(Message message) throws JMSException {
123 
124             Object rv = this.getRight().evaluate(message);
125 
126             if (rv == null) {
127                 return null;
128             }
129 
130             if (!(rv instanceof String)) {
131                 throw new RuntimeException("LIKE can only operate on String identifiers.  LIKE attemped on: '" + rv.getClass());
132             }
133 
134             return likePattern.matcher((String) rv).matches() ? Boolean.TRUE : Boolean.FALSE;
135         }
136 
137     }
138 
139     public static BooleanExpression createLike(Expression left, String right, String escape) {
140         if (escape != null && escape.length() != 1) {
141             throw new RuntimeException("The ESCAPE string litteral is invalid.  It can only be one character.  Litteral used: " + escape);
142         }
143         int c = -1;
144         if (escape != null) {
145             c = 0xFFFF & escape.charAt(0);
146         }
147 
148         return new LikeExpression(left, right, c);
149     }
150 
151     public static BooleanExpression createNotLike(Expression left, String right, String escape) {
152         return UnaryExpression.createNOT(createLike(left, right, escape));
153     }
154 
155     public static BooleanExpression createInFilter(Expression left, List elements) {
156         if (elements.isEmpty()) {
157             return ConstantExpression.FALSE;
158         }
159 
160         BooleanExpression answer = createEqual(left, (Expression) elements.get(0));
161         for (int i = 1; i < elements.size(); i++) {
162             answer = LogicExpression.createOR(answer, createEqual(left, (Expression) elements.get(i)));
163         }
164         return answer;
165     }
166 
167     public static BooleanExpression createNotInFilter(Expression left, List elements) {
168         if (elements.isEmpty()) {
169             return ConstantExpression.TRUE;
170         }
171 
172         BooleanExpression answer = createEqual(left, (Expression) elements.get(0));
173         for (int i = 1; i < elements.size(); i++) {
174             answer = LogicExpression.createOR(answer, createEqual(left, (Expression) elements.get(i)));
175         }
176         return UnaryExpression.createNOT(answer);
177     }
178 
179     public static BooleanExpression createIsNull(Expression left) {
180         return createEqual(left, ConstantExpression.NULL);
181     }
182 
183     public static BooleanExpression createIsNotNull(Expression left) {
184         return createNotEqual(left, ConstantExpression.NULL);
185     }
186 
187     public static BooleanExpression createNotEqual(Expression left, Expression right) {
188         return UnaryExpression.createNOT(createEqual(left, right));
189     }
190 
191     public static BooleanExpression createEqual(Expression left, Expression right) {
192         return new ComparisonExpression(left, right) {
193 
194             public Object evaluate(Message message) throws JMSException {
195                 Object obj1 = left.evaluate(message);
196                 Object obj2 = right.evaluate(message);
197 
198                 Comparable lv = obj1 instanceof Comparable ? (Comparable) obj1 : null;
199                 Comparable rv = obj2 instanceof Comparable ? (Comparable) obj2 : null;
200                 // Iff one of the values is null
201                 if (lv == null ^ rv == null) {
202                     return Boolean.FALSE;
203                 }
204                 if (lv == rv) {
205                     return Boolean.TRUE;
206                 }
207                 return compare(lv, rv);
208             }
209 
210             protected boolean asBoolean(int answer) {
211                 return answer == 0;
212             }
213 
214             public String getExpressionSymbol() {
215                 return "=";
216             }
217         };
218     }
219 
220     public static BooleanExpression createGreaterThan(final Expression left, final Expression right) {
221         return new ComparisonExpression(left, right) {
222             protected boolean asBoolean(int answer) {
223                 return answer > 0;
224             }
225 
226             public String getExpressionSymbol() {
227                 return ">";
228             }
229         };
230     }
231 
232     public static BooleanExpression createGreaterThanEqual(final Expression left, final Expression right) {
233         return new ComparisonExpression(left, right) {
234             protected boolean asBoolean(int answer) {
235                 return answer >= 0;
236             }
237 
238             public String getExpressionSymbol() {
239                 return ">=";
240             }
241         };
242     }
243 
244     public static BooleanExpression createLessThan(final Expression left, final Expression right) {
245         return new ComparisonExpression(left, right) {
246 
247             protected boolean asBoolean(int answer) {
248                 return answer < 0;
249             }
250 
251             public String getExpressionSymbol() {
252                 return "<";
253             }
254 
255         };
256     }
257 
258     public static BooleanExpression createLessThanEqual(final Expression left, final Expression right) {
259         return new ComparisonExpression(left, right) {
260 
261             protected boolean asBoolean(int answer) {
262                 return answer <= 0;
263             }
264 
265             public String getExpressionSymbol() {
266                 return "<=";
267             }
268         };
269     }
270 
271     /***
272      * @param left
273      * @param right
274      */
275     public ComparisonExpression(Expression left, Expression right) {
276         super(left, right);
277     }
278 
279     public Object evaluate(Message message) throws JMSException {
280         Comparable lv = (Comparable) left.evaluate(message);
281         if (lv == null) {
282             return null;
283         }
284         Comparable rv = (Comparable) right.evaluate(message);
285         if (rv == null) {
286             return null;
287         }
288         return compare(lv, rv);
289     }
290 
291     protected Boolean compare(Comparable lv, Comparable rv) {
292         Class lc = lv.getClass();
293         Class rc = rv.getClass();
294         // If the the objects are not of the same type,
295         // try to convert up to allow the comparison.
296         if (lc != rc) {
297             if (lc == Integer.class) {
298                 if (rc == Long.class) {
299                     lv = new Long(((Integer) lv).longValue());
300                 }
301                 else if (rc == Double.class) {
302                     lv = new Double(((Long) lv).doubleValue());
303                 }
304                 else {
305                     throw new RuntimeException("You cannot compare a '" + lc.getName() + "' and a '" + rc.getName()
306                             + "'");
307                 }
308             }
309             if (lc == Long.class) {
310                 if (rc == Integer.class) {
311                     rv = new Long(((Integer) rv).longValue());
312                 }
313                 else if (rc == Double.class) {
314                     lv = new Double(((Long) lv).doubleValue());
315                 }
316                 else {
317                     throw new RuntimeException("You cannot compare a '" + lc.getName() + "' and a '" + rc.getName()
318                             + "'");
319                 }
320             }
321             if (lc == Double.class) {
322                 if (rc == Integer.class) {
323                     rv = new Double(((Integer) rv).doubleValue());
324                 }
325                 else if (rc == Long.class) {
326                     rv = new Double(((Long) rv).doubleValue());
327                 }
328                 else {
329                     throw new RuntimeException("You cannot compare a '" + lc.getName() + "' and a '" + rc.getName()
330                             + "'");
331                 }
332             }
333         }
334         return asBoolean(lv.compareTo(rv)) ? Boolean.TRUE : Boolean.FALSE;
335     }
336 
337     protected abstract boolean asBoolean(int answer);
338 }