| /* |
| * |
| * 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.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; |
| |
| /** |
| * A filter performing a comparison of two objects |
| */ |
| public abstract class ComparisonExpression<T> extends BinaryExpression<T> implements BooleanExpression<T> |
| { |
| |
| public static <E> BooleanExpression<E> createBetween(Expression<E> value, Expression<E> left, Expression<E> right) |
| { |
| return LogicExpression.createAND(createGreaterThanEqual(value, left), createLessThanEqual(value, right)); |
| } |
| |
| public static <E> BooleanExpression<E> createNotBetween(Expression<E> value, Expression<E> left, Expression<E> 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<E> extends UnaryExpression<E> implements BooleanExpression<E> |
| { |
| |
| private Pattern likePattern; |
| |
| public LikeExpression(Expression<E> right, String like, int escape) |
| { |
| super(right); |
| |
| StringBuilder regexp = new StringBuilder(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(E message) |
| { |
| |
| Object rv = this.getRight().evaluate(message); |
| |
| if (rv == null) |
| { |
| return null; |
| } |
| |
| if (!(rv instanceof String)) |
| { |
| return |
| Boolean.FALSE; |
| } |
| |
| return likePattern.matcher((String) rv).matches() ? Boolean.TRUE : Boolean.FALSE; |
| } |
| |
| public boolean matches(E message) |
| { |
| Object object = evaluate(message); |
| |
| return (object != null) && (object == Boolean.TRUE); |
| } |
| } |
| |
| public static <E> BooleanExpression<E> createLike(Expression<E> left, String right, String escape) |
| { |
| if ((escape != null) && (escape.length() != 1)) |
| { |
| throw new SelectorParsingException( |
| "The ESCAPE string literal 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 <E> BooleanExpression<E> createNotLike(Expression<E> left, String right, String escape) |
| { |
| return UnaryExpression.createNOT(createLike(left, right, escape)); |
| } |
| |
| public static <E> BooleanExpression<E> createInFilter(Expression<E> left, List<?> elements, boolean allowNonJms) |
| { |
| |
| if (!(allowNonJms || left instanceof PropertyExpression)) |
| { |
| throw new SelectorParsingException("Expected a property for In expression, got: " + left); |
| } |
| |
| return UnaryExpression.createInExpression(left, elements, false, allowNonJms); |
| |
| } |
| |
| public static <E> BooleanExpression<E> createNotInFilter(Expression<E> left, List<?> elements, boolean allowNonJms) |
| { |
| |
| if (!(allowNonJms || left instanceof PropertyExpression)) |
| { |
| throw new SelectorParsingException("Expected a property for In expression, got: " + left); |
| } |
| |
| return UnaryExpression.createInExpression(left, elements, true, allowNonJms); |
| |
| } |
| |
| public static <E> BooleanExpression<E> createIsNull(Expression<E> left) |
| { |
| return doCreateEqual(left, ConstantExpression.<E>NULL()); |
| } |
| |
| public static <E> BooleanExpression<E> createIsNotNull(Expression<E> left) |
| { |
| return UnaryExpression.createNOT(doCreateEqual(left, ConstantExpression.<E>NULL())); |
| } |
| |
| public static <E> BooleanExpression<E> createNotEqual(Expression<E> left, Expression<E> right) |
| { |
| return UnaryExpression.createNOT(createEqual(left, right)); |
| } |
| |
| public static <E> BooleanExpression<E> createEqual(Expression<E> left, Expression<E> right) |
| { |
| checkEqualOperand(left); |
| checkEqualOperand(right); |
| checkEqualOperandCompatability(left, right); |
| |
| return doCreateEqual(left, right); |
| } |
| |
| private static <E> BooleanExpression<E> doCreateEqual(Expression<E> left, Expression<E> right) |
| { |
| return new EqualExpression<>(left, right); |
| } |
| |
| public static <E> BooleanExpression<E> createGreaterThan(final Expression<E> left, final Expression<E> right) |
| { |
| checkLessThanOperand(left); |
| checkLessThanOperand(right); |
| |
| return new ComparisonExpression<E>(left, right) |
| { |
| protected boolean asBoolean(int answer) |
| { |
| return answer > 0; |
| } |
| |
| public String getExpressionSymbol() |
| { |
| return ">"; |
| } |
| }; |
| } |
| |
| public static <E> BooleanExpression<E> createGreaterThanEqual(final Expression<E> left, final Expression<E> right) |
| { |
| checkLessThanOperand(left); |
| checkLessThanOperand(right); |
| |
| return new ComparisonExpression<E>(left, right) |
| { |
| protected boolean asBoolean(int answer) |
| { |
| return answer >= 0; |
| } |
| |
| public String getExpressionSymbol() |
| { |
| return ">="; |
| } |
| }; |
| } |
| |
| public static <E> BooleanExpression<E> createLessThan(final Expression<E> left, final Expression<E> right) |
| { |
| checkLessThanOperand(left); |
| checkLessThanOperand(right); |
| |
| return new ComparisonExpression<E>(left, right) |
| { |
| |
| protected boolean asBoolean(int answer) |
| { |
| return answer < 0; |
| } |
| |
| public String getExpressionSymbol() |
| { |
| return "<"; |
| } |
| |
| }; |
| } |
| |
| public static <E> BooleanExpression<E> createLessThanEqual(final Expression<E> left, final Expression<E> right) |
| { |
| checkLessThanOperand(left); |
| checkLessThanOperand(right); |
| |
| return new ComparisonExpression<E>(left, right) |
| { |
| |
| protected boolean asBoolean(int answer) |
| { |
| return answer <= 0; |
| } |
| |
| public String getExpressionSymbol() |
| { |
| return "<="; |
| } |
| }; |
| } |
| |
| /** |
| * Only Numeric expressions can be used in {@literal >, >=, < or <=} expressions. |
| * |
| * @param expr expression to check |
| */ |
| public static <E> void checkLessThanOperand(Expression<E> expr) |
| { |
| if (expr instanceof ConstantExpression) |
| { |
| Object value = ((ConstantExpression) expr).getValue(); |
| if (value instanceof Number) |
| { |
| return; |
| } |
| |
| // Else it's boolean or a String.. |
| throw new SelectorParsingException("Value '" + expr + "' cannot be compared."); |
| } |
| |
| if (expr instanceof BooleanExpression) |
| { |
| throw new SelectorParsingException("Value '" + expr + "' cannot be compared."); |
| } |
| } |
| |
| /** |
| * Validates that the expression can be used in {@literal == or <>} expression. |
| * Cannot not be NULL TRUE or FALSE literals. |
| * |
| * @param expr expression to check |
| */ |
| public static <E> void checkEqualOperand(Expression<E> expr) |
| { |
| if (expr instanceof ConstantExpression) |
| { |
| Object value = ((ConstantExpression) expr).getValue(); |
| if (value == null) |
| { |
| throw new SelectorParsingException("'" + expr + "' cannot be compared."); |
| } |
| } |
| } |
| |
| private static <E> void checkEqualOperandCompatability(Expression<E> left, Expression<E> right) |
| { |
| if ((left instanceof ConstantExpression) && (right instanceof ConstantExpression)) |
| { |
| if ((left instanceof BooleanExpression) && !(right instanceof BooleanExpression)) |
| { |
| throw new SelectorParsingException("'" + left + "' cannot be compared with '" + right + "'"); |
| } |
| } |
| } |
| |
| public ComparisonExpression(Expression<T> left, Expression<T> right) |
| { |
| super(left, right); |
| } |
| |
| public Object evaluate(T message) |
| { |
| Comparable lv = (Comparable) getLeft().evaluate(message); |
| if (lv == null) |
| { |
| return null; |
| } |
| |
| Comparable rv = (Comparable) getRight().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 if (lv instanceof Enum) |
| { |
| if (rv instanceof String) |
| { |
| try |
| { |
| rv = Enum.valueOf(lc, (String) rv); |
| } |
| catch (IllegalArgumentException e) |
| { |
| return Boolean.FALSE; |
| } |
| } |
| else |
| { |
| return Boolean.FALSE; |
| } |
| } |
| else if (lv instanceof String) |
| { |
| if (rv instanceof Enum) |
| { |
| lv = Enum.valueOf(rc, (String) lv); |
| } |
| 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(T message) |
| { |
| Object object = evaluate(message); |
| |
| return (object != null) && (object == Boolean.TRUE); |
| } |
| |
| private static class EqualExpression<E> extends ComparisonExpression<E> |
| { |
| public EqualExpression(final Expression<E> left, final Expression<E> right) |
| { |
| super(left, right); |
| } |
| |
| public Object evaluate(E message) |
| { |
| Object lv = getLeft().evaluate(message); |
| Object rv = getRight().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 "="; |
| } |
| } |
| } |