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");
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
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(".*?");
94 }
95 else if (c == '_') {
96 regexp.append(".");
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");
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
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
295
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 }