summaryrefslogtreecommitdiff
path: root/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java')
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java599
1 files changed, 599 insertions, 0 deletions
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java
new file mode 100644
index 0000000000..aad9d41174
--- /dev/null
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java
@@ -0,0 +1,599 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.filter;
+//
+// Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
+//
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.qpid.server.queue.Filterable;
+
+/**
+ * A filter performing a comparison of two objects
+ */
+public abstract class ComparisonExpression extends BinaryExpression implements BooleanExpression
+{
+
+ public static BooleanExpression createBetween(Expression value, Expression left, Expression right)
+ {
+ return LogicExpression.createAND(createGreaterThanEqual(value, left), createLessThanEqual(value, right));
+ }
+
+ public static BooleanExpression createNotBetween(Expression value, Expression left, Expression right)
+ {
+ return LogicExpression.createOR(createLessThan(value, left), createGreaterThan(value, right));
+ }
+
+ private static final HashSet<Character> REGEXP_CONTROL_CHARS = new HashSet<Character>();
+
+ static
+ {
+ REGEXP_CONTROL_CHARS.add('.');
+ REGEXP_CONTROL_CHARS.add('\\');
+ REGEXP_CONTROL_CHARS.add('[');
+ REGEXP_CONTROL_CHARS.add(']');
+ REGEXP_CONTROL_CHARS.add('^');
+ REGEXP_CONTROL_CHARS.add('$');
+ REGEXP_CONTROL_CHARS.add('?');
+ REGEXP_CONTROL_CHARS.add('*');
+ REGEXP_CONTROL_CHARS.add('+');
+ REGEXP_CONTROL_CHARS.add('{');
+ REGEXP_CONTROL_CHARS.add('}');
+ REGEXP_CONTROL_CHARS.add('|');
+ REGEXP_CONTROL_CHARS.add('(');
+ REGEXP_CONTROL_CHARS.add(')');
+ REGEXP_CONTROL_CHARS.add(':');
+ REGEXP_CONTROL_CHARS.add('&');
+ REGEXP_CONTROL_CHARS.add('<');
+ REGEXP_CONTROL_CHARS.add('>');
+ REGEXP_CONTROL_CHARS.add('=');
+ REGEXP_CONTROL_CHARS.add('!');
+ }
+
+ static class LikeExpression extends UnaryExpression implements BooleanExpression
+ {
+
+ Pattern likePattern;
+
+ /**
+ * @param right
+ */
+ public LikeExpression(Expression right, String like, int escape)
+ {
+ super(right);
+
+ StringBuffer regexp = new StringBuffer(like.length() * 2);
+ regexp.append("\\A"); // The beginning of the input
+ for (int i = 0; i < like.length(); i++)
+ {
+ char c = like.charAt(i);
+ if (escape == (0xFFFF & c))
+ {
+ i++;
+ if (i >= like.length())
+ {
+ // nothing left to escape...
+ break;
+ }
+
+ char t = like.charAt(i);
+ regexp.append("\\x");
+ regexp.append(Integer.toHexString(0xFFFF & t));
+ }
+ else if (c == '%')
+ {
+ regexp.append(".*?"); // Do a non-greedy match
+ }
+ else if (c == '_')
+ {
+ regexp.append("."); // match one
+ }
+ else if (REGEXP_CONTROL_CHARS.contains(c))
+ {
+ regexp.append("\\x");
+ regexp.append(Integer.toHexString(0xFFFF & c));
+ }
+ else
+ {
+ regexp.append(c);
+ }
+ }
+
+ regexp.append("\\z"); // The end of the input
+
+ likePattern = Pattern.compile(regexp.toString(), Pattern.DOTALL);
+ }
+
+ /**
+ * org.apache.activemq.filter.UnaryExpression#getExpressionSymbol()
+ */
+ public String getExpressionSymbol()
+ {
+ return "LIKE";
+ }
+
+ /**
+ * org.apache.activemq.filter.Expression#evaluate(MessageEvaluationContext)
+ */
+ public Object evaluate(Filterable message)
+ {
+
+ Object rv = this.getRight().evaluate(message);
+
+ if (rv == null)
+ {
+ return null;
+ }
+
+ if (!(rv instanceof String))
+ {
+ return
+ Boolean.FALSE;
+ // throw new RuntimeException("LIKE can only operate on String identifiers. LIKE attemped on: '" + rv.getClass());
+ }
+
+ return likePattern.matcher((String) rv).matches() ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public boolean matches(Filterable message)
+ {
+ Object object = evaluate(message);
+
+ return (object != null) && (object == Boolean.TRUE);
+ }
+ }
+
+ public static BooleanExpression createLike(Expression left, String right, String escape)
+ {
+ if ((escape != null) && (escape.length() != 1))
+ {
+ throw new RuntimeException(
+ "The ESCAPE string litteral is invalid. It can only be one character. Litteral used: " + escape);
+ }
+
+ int c = -1;
+ if (escape != null)
+ {
+ c = 0xFFFF & escape.charAt(0);
+ }
+
+ return new LikeExpression(left, right, c);
+ }
+
+ public static BooleanExpression createNotLike(Expression left, String right, String escape)
+ {
+ return UnaryExpression.createNOT(createLike(left, right, escape));
+ }
+
+ public static BooleanExpression createInFilter(Expression left, List elements)
+ {
+
+ if (!(left instanceof PropertyExpression))
+ {
+ throw new RuntimeException("Expected a property for In expression, got: " + left);
+ }
+
+ return UnaryExpression.createInExpression((PropertyExpression) left, elements, false);
+
+ }
+
+ public static BooleanExpression createNotInFilter(Expression left, List elements)
+ {
+
+ if (!(left instanceof PropertyExpression))
+ {
+ throw new RuntimeException("Expected a property for In expression, got: " + left);
+ }
+
+ return UnaryExpression.createInExpression((PropertyExpression) left, elements, true);
+
+ }
+
+ public static BooleanExpression createIsNull(Expression left)
+ {
+ return doCreateEqual(left, ConstantExpression.NULL);
+ }
+
+ public static BooleanExpression createIsNotNull(Expression left)
+ {
+ return UnaryExpression.createNOT(doCreateEqual(left, ConstantExpression.NULL));
+ }
+
+ public static BooleanExpression createNotEqual(Expression left, Expression right)
+ {
+ return UnaryExpression.createNOT(createEqual(left, right));
+ }
+
+ public static BooleanExpression createEqual(Expression left, Expression right)
+ {
+ checkEqualOperand(left);
+ checkEqualOperand(right);
+ checkEqualOperandCompatability(left, right);
+
+ return doCreateEqual(left, right);
+ }
+
+ private static BooleanExpression doCreateEqual(Expression left, Expression right)
+ {
+ return new EqualExpression(left, right);
+ }
+
+ public static BooleanExpression createGreaterThan(final Expression left, final Expression right)
+ {
+ checkLessThanOperand(left);
+ checkLessThanOperand(right);
+
+ return new ComparisonExpression(left, right)
+ {
+ protected boolean asBoolean(int answer)
+ {
+ return answer > 0;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return ">";
+ }
+ };
+ }
+
+ public static BooleanExpression createGreaterThanEqual(final Expression left, final Expression right)
+ {
+ checkLessThanOperand(left);
+ checkLessThanOperand(right);
+
+ return new ComparisonExpression(left, right)
+ {
+ protected boolean asBoolean(int answer)
+ {
+ return answer >= 0;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return ">=";
+ }
+ };
+ }
+
+ public static BooleanExpression createLessThan(final Expression left, final Expression right)
+ {
+ checkLessThanOperand(left);
+ checkLessThanOperand(right);
+
+ return new ComparisonExpression(left, right)
+ {
+
+ protected boolean asBoolean(int answer)
+ {
+ return answer < 0;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "<";
+ }
+
+ };
+ }
+
+ public static BooleanExpression createLessThanEqual(final Expression left, final Expression right)
+ {
+ checkLessThanOperand(left);
+ checkLessThanOperand(right);
+
+ return new ComparisonExpression(left, right)
+ {
+
+ protected boolean asBoolean(int answer)
+ {
+ return answer <= 0;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "<=";
+ }
+ };
+ }
+
+ /**
+ * Only Numeric expressions can be used in >, >=, < or <= expressions.s
+ *
+ * @param expr
+ */
+ public static void checkLessThanOperand(Expression expr)
+ {
+ if (expr instanceof ConstantExpression)
+ {
+ Object value = ((ConstantExpression) expr).getValue();
+ if (value instanceof Number)
+ {
+ return;
+ }
+
+ // Else it's boolean or a String..
+ throw new RuntimeException("Value '" + expr + "' cannot be compared.");
+ }
+
+ if (expr instanceof BooleanExpression)
+ {
+ throw new RuntimeException("Value '" + expr + "' cannot be compared.");
+ }
+ }
+
+ /**
+ * Validates that the expression can be used in == or <> expression.
+ * Cannot not be NULL TRUE or FALSE litterals.
+ *
+ * @param expr
+ */
+ public static void checkEqualOperand(Expression expr)
+ {
+ if (expr instanceof ConstantExpression)
+ {
+ Object value = ((ConstantExpression) expr).getValue();
+ if (value == null)
+ {
+ throw new RuntimeException("'" + expr + "' cannot be compared.");
+ }
+ }
+ }
+
+ /**
+ *
+ * @param left
+ * @param right
+ */
+ private static void checkEqualOperandCompatability(Expression left, Expression right)
+ {
+ if ((left instanceof ConstantExpression) && (right instanceof ConstantExpression))
+ {
+ if ((left instanceof BooleanExpression) && !(right instanceof BooleanExpression))
+ {
+ throw new RuntimeException("'" + left + "' cannot be compared with '" + right + "'");
+ }
+ }
+ }
+
+ /**
+ * @param left
+ * @param right
+ */
+ public ComparisonExpression(Expression left, Expression right)
+ {
+ super(left, right);
+ }
+
+ public Object evaluate(Filterable message)
+ {
+ Comparable lv = (Comparable) left.evaluate(message);
+ if (lv == null)
+ {
+ return null;
+ }
+
+ Comparable rv = (Comparable) right.evaluate(message);
+ if (rv == null)
+ {
+ return null;
+ }
+
+ return compare(lv, rv);
+ }
+
+ protected Boolean compare(Comparable lv, Comparable rv)
+ {
+ Class lc = lv.getClass();
+ Class rc = rv.getClass();
+ // If the the objects are not of the same type,
+ // try to convert up to allow the comparison.
+ if (lc != rc)
+ {
+ if (lc == Byte.class)
+ {
+ if (rc == Short.class)
+ {
+ lv = ((Number) lv).shortValue();
+ }
+ else if (rc == Integer.class)
+ {
+ lv = ((Number) lv).intValue();
+ }
+ else if (rc == Long.class)
+ {
+ lv = ((Number) lv).longValue();
+ }
+ else if (rc == Float.class)
+ {
+ lv = ((Number) lv).floatValue();
+ }
+ else if (rc == Double.class)
+ {
+ lv = ((Number) lv).doubleValue();
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+ else if (lc == Short.class)
+ {
+ if (rc == Integer.class)
+ {
+ lv = ((Number) lv).intValue();
+ }
+ else if (rc == Long.class)
+ {
+ lv = ((Number) lv).longValue();
+ }
+ else if (rc == Float.class)
+ {
+ lv = ((Number) lv).floatValue();
+ }
+ else if (rc == Double.class)
+ {
+ lv = ((Number) lv).doubleValue();
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+ else if (lc == Integer.class)
+ {
+ if (rc == Long.class)
+ {
+ lv = ((Number) lv).longValue();
+ }
+ else if (rc == Float.class)
+ {
+ lv = ((Number) lv).floatValue();
+ }
+ else if (rc == Double.class)
+ {
+ lv = ((Number) lv).doubleValue();
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+ else if (lc == Long.class)
+ {
+ if (rc == Integer.class)
+ {
+ rv = ((Number) rv).longValue();
+ }
+ else if (rc == Float.class)
+ {
+ lv = ((Number) lv).floatValue();
+ }
+ else if (rc == Double.class)
+ {
+ lv = ((Number) lv).doubleValue();
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+ else if (lc == Float.class)
+ {
+ if (rc == Integer.class)
+ {
+ rv = ((Number) rv).floatValue();
+ }
+ else if (rc == Long.class)
+ {
+ rv = ((Number) rv).floatValue();
+ }
+ else if (rc == Double.class)
+ {
+ lv = ((Number) lv).doubleValue();
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+ else if (lc == Double.class)
+ {
+ if (rc == Integer.class)
+ {
+ rv = ((Number) rv).doubleValue();
+ }
+ else if (rc == Long.class)
+ {
+ rv = ((Number) rv).doubleValue();
+ }
+ else if (rc == Float.class)
+ {
+ rv = ((Number) rv).doubleValue();
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+ }
+
+ return asBoolean(lv.compareTo(rv)) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ protected abstract boolean asBoolean(int answer);
+
+ public boolean matches(Filterable message)
+ {
+ Object object = evaluate(message);
+
+ return (object != null) && (object == Boolean.TRUE);
+ }
+
+ private static class EqualExpression extends ComparisonExpression
+ {
+ public EqualExpression(final Expression left, final Expression right)
+ {
+ super(left, right);
+ }
+
+ public Object evaluate(Filterable message)
+ {
+ Object lv = left.evaluate(message);
+ Object rv = right.evaluate(message);
+
+ // Iff one of the values is null
+ if ((lv == null) ^ (rv == null))
+ {
+ return Boolean.FALSE;
+ }
+
+ if ((lv == rv) || lv.equals(rv))
+ {
+ return Boolean.TRUE;
+ }
+
+ if ((lv instanceof Comparable) && (rv instanceof Comparable))
+ {
+ return compare((Comparable) lv, (Comparable) rv);
+ }
+
+ return Boolean.FALSE;
+ }
+
+ protected boolean asBoolean(int answer)
+ {
+ return answer == 0;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "=";
+ }
+ }
+}