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