You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rocketmq.apache.org by do...@apache.org on 2017/06/08 07:54:29 UTC

[08/50] [abbrv] incubator-rocketmq git commit: [ROCKETMQ-121]Support message filtering based on SQL92 closes apache/incubator-rocketmq#82

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq/blob/58f1574b/filter/src/main/java/org/apache/rocketmq/filter/expression/Expression.java
----------------------------------------------------------------------
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/Expression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/Expression.java
new file mode 100644
index 0000000..3e6d9b3
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/Expression.java
@@ -0,0 +1,38 @@
+/*
+ * 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.rocketmq.filter.expression;
+
+/**
+ * Interface of expression.
+ * <p>
+ * This class was taken from ActiveMQ org.apache.activemq.filter.Expression,
+ * but the parameter is changed to an interface.
+ * </p>
+ *
+ * @see org.apache.rocketmq.filter.expression.EvaluationContext
+ */
+public interface Expression {
+
+    /**
+     * Calculate express result with context.
+     *
+     * @param context context of evaluation
+     * @return the value of this expression
+     */
+    Object evaluate(EvaluationContext context) throws Exception;
+}

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq/blob/58f1574b/filter/src/main/java/org/apache/rocketmq/filter/expression/LogicExpression.java
----------------------------------------------------------------------
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/LogicExpression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/LogicExpression.java
new file mode 100644
index 0000000..1062bb8
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/LogicExpression.java
@@ -0,0 +1,94 @@
+/*
+ * 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.rocketmq.filter.expression;
+
+/**
+ * A filter performing a comparison of two objects
+ * <p>
+ * This class was taken from ActiveMQ org.apache.activemq.filter.LogicExpression,
+ * </p>
+ */
+public abstract class LogicExpression extends BinaryExpression implements BooleanExpression {
+
+    /**
+     * @param left
+     * @param right
+     */
+    public LogicExpression(BooleanExpression left, BooleanExpression right) {
+        super(left, right);
+    }
+
+    public static BooleanExpression createOR(BooleanExpression lvalue, BooleanExpression rvalue) {
+        return new LogicExpression(lvalue, rvalue) {
+
+            public Object evaluate(EvaluationContext context) throws Exception {
+
+                Boolean lv = (Boolean) left.evaluate(context);
+                if (lv != null && lv.booleanValue()) {
+                    return Boolean.TRUE;
+                }
+                Boolean rv = (Boolean) right.evaluate(context);
+                if (rv != null && rv.booleanValue()) {
+                    return Boolean.TRUE;
+                }
+                if (lv == null || rv == null) {
+                    return null;
+                }
+                return Boolean.FALSE;
+            }
+
+            public String getExpressionSymbol() {
+                return "||";
+            }
+        };
+    }
+
+    public static BooleanExpression createAND(BooleanExpression lvalue, BooleanExpression rvalue) {
+        return new LogicExpression(lvalue, rvalue) {
+
+            public Object evaluate(EvaluationContext context) throws Exception {
+
+                Boolean lv = (Boolean) left.evaluate(context);
+
+                if (lv != null && !lv.booleanValue()) {
+                    return Boolean.FALSE;
+                }
+                Boolean rv = (Boolean) right.evaluate(context);
+                if (rv != null && !rv.booleanValue()) {
+                    return Boolean.FALSE;
+                }
+                if (lv == null || rv == null) {
+                    return null;
+                }
+                return Boolean.TRUE;
+            }
+
+            public String getExpressionSymbol() {
+                return "&&";
+            }
+        };
+    }
+
+    public abstract Object evaluate(EvaluationContext context) throws Exception;
+
+    public boolean matches(EvaluationContext context) throws Exception {
+        Object object = evaluate(context);
+        return object != null && object == Boolean.TRUE;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq/blob/58f1574b/filter/src/main/java/org/apache/rocketmq/filter/expression/MQFilterException.java
----------------------------------------------------------------------
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/MQFilterException.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/MQFilterException.java
new file mode 100644
index 0000000..676a17b
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/MQFilterException.java
@@ -0,0 +1,46 @@
+/*
+ * 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.rocketmq.filter.expression;
+
+/**
+ * Exception.
+ */
+public class MQFilterException extends Exception {
+    private static final long serialVersionUID = 1L;
+    private final int responseCode;
+    private final String errorMessage;
+
+    public MQFilterException(String errorMessage, Throwable cause) {
+        super(cause);
+        this.responseCode = -1;
+        this.errorMessage = errorMessage;
+    }
+
+    public MQFilterException(int responseCode, String errorMessage) {
+        this.responseCode = responseCode;
+        this.errorMessage = errorMessage;
+    }
+
+    public int getResponseCode() {
+        return responseCode;
+    }
+
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq/blob/58f1574b/filter/src/main/java/org/apache/rocketmq/filter/expression/NowExpression.java
----------------------------------------------------------------------
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/NowExpression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/NowExpression.java
new file mode 100644
index 0000000..d76caca
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/NowExpression.java
@@ -0,0 +1,36 @@
+/*
+ * 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.rocketmq.filter.expression;
+
+/**
+ * Current time expression.Just for test.
+ */
+public class NowExpression extends ConstantExpression {
+    public NowExpression() {
+        super("now");
+    }
+
+    @Override
+    public Object evaluate(EvaluationContext context) throws Exception {
+        return new Long(System.currentTimeMillis());
+    }
+
+    public Object getValue() {
+        return new Long(System.currentTimeMillis());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq/blob/58f1574b/filter/src/main/java/org/apache/rocketmq/filter/expression/PropertyExpression.java
----------------------------------------------------------------------
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/PropertyExpression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/PropertyExpression.java
new file mode 100644
index 0000000..b9657b0
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/PropertyExpression.java
@@ -0,0 +1,70 @@
+/*
+ * 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.rocketmq.filter.expression;
+
+/**
+ * Represents a property expression
+ * <p>
+ * This class was taken from ActiveMQ org.apache.activemq.filter.PropertyExpression,
+ * but more simple and no transfer between expression and message property.
+ * </p>
+ */
+public class PropertyExpression implements Expression {
+    private final String name;
+
+    public PropertyExpression(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public Object evaluate(EvaluationContext context) throws Exception {
+        return context.get(name);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * @see Object#toString()
+     */
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    /**
+     * @see Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+    /**
+     * @see Object#equals(Object)
+     */
+    @Override
+    public boolean equals(Object o) {
+
+        if (o == null || !this.getClass().equals(o.getClass())) {
+            return false;
+        }
+        return name.equals(((PropertyExpression) o).name);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq/blob/58f1574b/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryExpression.java
----------------------------------------------------------------------
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryExpression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryExpression.java
new file mode 100644
index 0000000..0519f4d
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryExpression.java
@@ -0,0 +1,267 @@
+/*
+ * 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.rocketmq.filter.expression;
+
+import org.apache.rocketmq.filter.constant.UnaryType;
+
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An expression which performs an operation on two expression values
+ * <p>
+ * This class was taken from ActiveMQ org.apache.activemq.filter.UnaryExpression,
+ * but:
+ * 1. remove XPath and XQuery expression;
+ * 2. Add constant UnaryType to distinguish different unary expression;
+ * 3. Extract UnaryInExpression to an independent class.
+ * </p>
+ */
+public abstract class UnaryExpression implements Expression {
+
+    private static final BigDecimal BD_LONG_MIN_VALUE = BigDecimal.valueOf(Long.MIN_VALUE);
+    protected Expression right;
+
+    public UnaryType unaryType;
+
+    public UnaryExpression(Expression left) {
+        this.right = left;
+    }
+
+    public UnaryExpression(Expression left, UnaryType unaryType) {
+        this.setUnaryType(unaryType);
+        this.right = left;
+    }
+
+    public static Expression createNegate(Expression left) {
+        return new UnaryExpression(left, UnaryType.NEGATE) {
+            public Object evaluate(EvaluationContext context) throws Exception {
+                Object rvalue = right.evaluate(context);
+                if (rvalue == null) {
+                    return null;
+                }
+                if (rvalue instanceof Number) {
+                    return negate((Number) rvalue);
+                }
+                return null;
+            }
+
+            public String getExpressionSymbol() {
+                return "-";
+            }
+        };
+    }
+
+    public static BooleanExpression createInExpression(PropertyExpression right, List<Object> elements,
+                                                       final boolean not) {
+
+        // Use a HashSet if there are many elements.
+        Collection<Object> t;
+        if (elements.size() == 0) {
+            t = null;
+        } else if (elements.size() < 5) {
+            t = elements;
+        } else {
+            t = new HashSet<Object>(elements);
+        }
+        final Collection inList = t;
+
+        return new UnaryInExpression(right, UnaryType.IN, inList, not) {
+            public Object evaluate(EvaluationContext context) throws Exception {
+
+                Object rvalue = right.evaluate(context);
+                if (rvalue == null) {
+                    return null;
+                }
+                if (rvalue.getClass() != String.class) {
+                    return null;
+                }
+
+                if ((inList != null && inList.contains(rvalue)) ^ not) {
+                    return Boolean.TRUE;
+                } else {
+                    return Boolean.FALSE;
+                }
+
+            }
+
+            public String toString() {
+                StringBuffer answer = new StringBuffer();
+                answer.append(right);
+                answer.append(" ");
+                answer.append(getExpressionSymbol());
+                answer.append(" ( ");
+
+                int count = 0;
+                for (Iterator i = inList.iterator(); i.hasNext(); ) {
+                    Object o = (Object) i.next();
+                    if (count != 0) {
+                        answer.append(", ");
+                    }
+                    answer.append(o);
+                    count++;
+                }
+
+                answer.append(" )");
+                return answer.toString();
+            }
+
+            public String getExpressionSymbol() {
+                if (not) {
+                    return "NOT IN";
+                } else {
+                    return "IN";
+                }
+            }
+        };
+    }
+
+    abstract static class BooleanUnaryExpression extends UnaryExpression implements BooleanExpression {
+        public BooleanUnaryExpression(Expression left, UnaryType unaryType) {
+            super(left, unaryType);
+        }
+
+        public boolean matches(EvaluationContext context) throws Exception {
+            Object object = evaluate(context);
+            return object != null && object == Boolean.TRUE;
+        }
+    }
+
+    public static BooleanExpression createNOT(BooleanExpression left) {
+        return new BooleanUnaryExpression(left, UnaryType.NOT) {
+            public Object evaluate(EvaluationContext context) throws Exception {
+                Boolean lvalue = (Boolean) right.evaluate(context);
+                if (lvalue == null) {
+                    return null;
+                }
+                return lvalue.booleanValue() ? Boolean.FALSE : Boolean.TRUE;
+            }
+
+            public String getExpressionSymbol() {
+                return "NOT";
+            }
+        };
+    }
+
+    public static BooleanExpression createBooleanCast(Expression left) {
+        return new BooleanUnaryExpression(left, UnaryType.BOOLEANCAST) {
+            public Object evaluate(EvaluationContext context) throws Exception {
+                Object rvalue = right.evaluate(context);
+                if (rvalue == null) {
+                    return null;
+                }
+                if (!rvalue.getClass().equals(Boolean.class)) {
+                    return Boolean.FALSE;
+                }
+                return ((Boolean) rvalue).booleanValue() ? Boolean.TRUE : Boolean.FALSE;
+            }
+
+            public String toString() {
+                return right.toString();
+            }
+
+            public String getExpressionSymbol() {
+                return "";
+            }
+        };
+    }
+
+    private static Number negate(Number left) {
+        Class clazz = left.getClass();
+        if (clazz == Integer.class) {
+            return new Integer(-left.intValue());
+        } else if (clazz == Long.class) {
+            return new Long(-left.longValue());
+        } else if (clazz == Float.class) {
+            return new Float(-left.floatValue());
+        } else if (clazz == Double.class) {
+            return new Double(-left.doubleValue());
+        } else if (clazz == BigDecimal.class) {
+            // We ussually get a big deciamal when we have Long.MIN_VALUE
+            // constant in the
+            // Selector. Long.MIN_VALUE is too big to store in a Long as a
+            // positive so we store it
+            // as a Big decimal. But it gets Negated right away.. to here we try
+            // to covert it back
+            // to a Long.
+            BigDecimal bd = (BigDecimal) left;
+            bd = bd.negate();
+
+            if (BD_LONG_MIN_VALUE.compareTo(bd) == 0) {
+                return Long.valueOf(Long.MIN_VALUE);
+            }
+            return bd;
+        } else {
+            throw new RuntimeException("Don't know how to negate: " + left);
+        }
+    }
+
+    public Expression getRight() {
+        return right;
+    }
+
+    public void setRight(Expression expression) {
+        right = expression;
+    }
+
+    public UnaryType getUnaryType() {
+        return unaryType;
+    }
+
+    public void setUnaryType(UnaryType unaryType) {
+        this.unaryType = unaryType;
+    }
+
+    /**
+     * @see Object#toString()
+     */
+    public String toString() {
+        return "(" + getExpressionSymbol() + " " + right.toString() + ")";
+    }
+
+    /**
+     * @see Object#hashCode()
+     */
+    public int hashCode() {
+        return toString().hashCode();
+    }
+
+    /**
+     * @see Object#equals(Object)
+     */
+    public boolean equals(Object o) {
+
+        if (o == null || !this.getClass().equals(o.getClass())) {
+            return false;
+        }
+        return toString().equals(o.toString());
+
+    }
+
+    /**
+     * Returns the symbol that represents this binary expression. For example,
+     * addition is represented by "+"
+     *
+     * @return
+     */
+    public abstract String getExpressionSymbol();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq/blob/58f1574b/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryInExpression.java
----------------------------------------------------------------------
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryInExpression.java b/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryInExpression.java
new file mode 100644
index 0000000..7d9083c
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/expression/UnaryInExpression.java
@@ -0,0 +1,61 @@
+/*
+ * 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.rocketmq.filter.expression;
+
+import org.apache.rocketmq.filter.constant.UnaryType;
+
+import java.util.Collection;
+
+/**
+ * In expression.
+ */
+abstract public class UnaryInExpression extends UnaryExpression implements BooleanExpression {
+
+    private boolean not;
+
+    private Collection inList;
+
+    public UnaryInExpression(Expression left, UnaryType unaryType,
+                             Collection inList, boolean not) {
+        super(left, unaryType);
+        this.setInList(inList);
+        this.setNot(not);
+
+    }
+
+    public boolean matches(EvaluationContext context) throws Exception {
+        Object object = evaluate(context);
+        return object != null && object == Boolean.TRUE;
+    }
+
+    public boolean isNot() {
+        return not;
+    }
+
+    public void setNot(boolean not) {
+        this.not = not;
+    }
+
+    public Collection getInList() {
+        return inList;
+    }
+
+    public void setInList(Collection inList) {
+        this.inList = inList;
+    }
+};

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq/blob/58f1574b/filter/src/main/java/org/apache/rocketmq/filter/parser/ParseException.java
----------------------------------------------------------------------
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/parser/ParseException.java b/filter/src/main/java/org/apache/rocketmq/filter/parser/ParseException.java
new file mode 100644
index 0000000..2ccccaf
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/parser/ParseException.java
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 5.0 */
+/* JavaCCOptions:KEEP_LINE_COL=null */
+package org.apache.rocketmq.filter.parser;
+
+/**
+ * This exception is thrown when parse errors are encountered.
+ * You can explicitly create objects of this exception type by
+ * calling the method generateParseException in the generated
+ * parser.
+ * <p/>
+ * You can modify this class to customize your error reporting
+ * mechanisms so long as you retain the public fields.
+ */
+public class ParseException extends Exception {
+
+    /**
+     * The version identifier for this Serializable class.
+     * Increment only if the <i>serialized</i> form of the
+     * class changes.
+     */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * This constructor is used by the method "generateParseException"
+     * in the generated parser.  Calling this constructor generates
+     * a new object of this type with the fields "currentToken",
+     * "expectedTokenSequences", and "TOKEN_IMAGE" set.
+     */
+    public ParseException(Token currentTokenVal,
+                          int[][] expectedTokenSequencesVal,
+                          String[] tokenImageVal
+    ) {
+        super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal));
+        currentToken = currentTokenVal;
+        expectedTokenSequences = expectedTokenSequencesVal;
+        tokenImage = tokenImageVal;
+    }
+
+    /**
+     * The following constructors are for use by you for whatever
+     * purpose you can think of.  Constructing the exception in this
+     * manner makes the exception behave in the normal way - i.e., as
+     * documented in the class "Throwable".  The fields "errorToken",
+     * "expectedTokenSequences", and "TOKEN_IMAGE" do not contain
+     * relevant information.  The JavaCC generated code does not use
+     * these constructors.
+     */
+
+    public ParseException() {
+        super();
+    }
+
+    /**
+     * Constructor with message.
+     */
+    public ParseException(String message) {
+        super(message);
+    }
+
+    /**
+     * This is the last token that has been consumed successfully.  If
+     * this object has been created due to a parse error, the token
+     * followng this token will (therefore) be the first error token.
+     */
+    public Token currentToken;
+
+    /**
+     * Each entry in this array is an array of integers.  Each array
+     * of integers represents a sequence of tokens (by their ordinal
+     * values) that is expected at this point of the parse.
+     */
+    public int[][] expectedTokenSequences;
+
+    /**
+     * This is a reference to the "TOKEN_IMAGE" array of the generated
+     * parser within which the parse error occurred.  This array is
+     * defined in the generated ...Constants interface.
+     */
+    public String[] tokenImage;
+
+    /**
+     * It uses "currentToken" and "expectedTokenSequences" to generate a parse
+     * error message and returns it.  If this object has been created
+     * due to a parse error, and you do not catch it (it gets thrown
+     * from the parser) the correct error message
+     * gets displayed.
+     */
+    private static String initialise(Token currentToken,
+                                     int[][] expectedTokenSequences,
+                                     String[] tokenImage) {
+        String eol = System.getProperty("line.separator", "\n");
+        StringBuffer expected = new StringBuffer();
+        int maxSize = 0;
+        for (int i = 0; i < expectedTokenSequences.length; i++) {
+            if (maxSize < expectedTokenSequences[i].length) {
+                maxSize = expectedTokenSequences[i].length;
+            }
+            for (int j = 0; j < expectedTokenSequences[i].length; j++) {
+                expected.append(tokenImage[expectedTokenSequences[i][j]]).append(' ');
+            }
+            if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
+                expected.append("...");
+            }
+            expected.append(eol).append("    ");
+        }
+        String retval = "Encountered \"";
+        Token tok = currentToken.next;
+        for (int i = 0; i < maxSize; i++) {
+            if (i != 0)
+                retval += " ";
+            if (tok.kind == 0) {
+                retval += tokenImage[0];
+                break;
+            }
+            retval += " " + tokenImage[tok.kind];
+            retval += " \"";
+            retval += add_escapes(tok.image);
+            retval += " \"";
+            tok = tok.next;
+        }
+        retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+        retval += "." + eol;
+        if (expectedTokenSequences.length == 1) {
+            retval += "Was expecting:" + eol + "    ";
+        } else {
+            retval += "Was expecting one of:" + eol + "    ";
+        }
+        retval += expected.toString();
+        return retval;
+    }
+
+    /**
+     * The end of line string for this machine.
+     */
+    protected String eol = System.getProperty("line.separator", "\n");
+
+    /**
+     * Used to convert raw characters to their escaped version
+     * when these raw version cannot be used as part of an ASCII
+     * string literal.
+     */
+    static String add_escapes(String str) {
+        StringBuffer retval = new StringBuffer();
+        char ch;
+        for (int i = 0; i < str.length(); i++) {
+            switch (str.charAt(i)) {
+                case 0:
+                    continue;
+                case '\b':
+                    retval.append("\\b");
+                    continue;
+                case '\t':
+                    retval.append("\\t");
+                    continue;
+                case '\n':
+                    retval.append("\\n");
+                    continue;
+                case '\f':
+                    retval.append("\\f");
+                    continue;
+                case '\r':
+                    retval.append("\\r");
+                    continue;
+                case '\"':
+                    retval.append("\\\"");
+                    continue;
+                case '\'':
+                    retval.append("\\\'");
+                    continue;
+                case '\\':
+                    retval.append("\\\\");
+                    continue;
+                default:
+                    if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+                        String s = "0000" + Integer.toString(ch, 16);
+                        retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+                    } else {
+                        retval.append(ch);
+                    }
+                    continue;
+            }
+        }
+        return retval.toString();
+    }
+
+}
+/* JavaCC - OriginalChecksum=4c829b0daa2c9af00ddafe2441eb9097 (do not edit this line) */

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq/blob/58f1574b/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.java
----------------------------------------------------------------------
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.java b/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.java
new file mode 100644
index 0000000..74e5501
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.java
@@ -0,0 +1,1354 @@
+/*
+ * 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.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. SelectorParser.java */
+package org.apache.rocketmq.filter.parser;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.apache.rocketmq.filter.expression.BooleanExpression;
+import org.apache.rocketmq.filter.expression.ComparisonExpression;
+import org.apache.rocketmq.filter.expression.ConstantExpression;
+import org.apache.rocketmq.filter.expression.Expression;
+import org.apache.rocketmq.filter.expression.LogicExpression;
+import org.apache.rocketmq.filter.expression.MQFilterException;
+import org.apache.rocketmq.filter.expression.PropertyExpression;
+import org.apache.rocketmq.filter.expression.UnaryExpression;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+
+/**
+ * JMS Selector Parser generated by JavaCC
+ * <p/>
+ * Do not edit this .java file directly - it is autogenerated from SelectorParser.jj
+ */
+public class SelectorParser implements SelectorParserConstants {
+
+    private static final Cache<String, Object> PARSE_CACHE = CacheBuilder.newBuilder().maximumSize(100).build();
+    //    private static final String CONVERT_STRING_EXPRESSIONS_PREFIX = "convert_string_expressions:";
+
+    public static BooleanExpression parse(String sql) throws MQFilterException {
+        //        sql = "("+sql+")";
+        Object result = PARSE_CACHE.getIfPresent(sql);
+        if (result instanceof MQFilterException) {
+            throw (MQFilterException) result;
+        } else if (result instanceof BooleanExpression) {
+            return (BooleanExpression) result;
+        } else {
+
+            //            boolean convertStringExpressions = false;
+            //            if( sql.startsWith(CONVERT_STRING_EXPRESSIONS_PREFIX)) {
+            //                convertStringExpressions = true;
+            //                sql = sql.substring(CONVERT_STRING_EXPRESSIONS_PREFIX.length());
+            //            }
+            //
+            //            if( convertStringExpressions ) {
+            //                ComparisonExpression.CONVERT_STRING_EXPRESSIONS.set(true);
+            //            }
+            ComparisonExpression.CONVERT_STRING_EXPRESSIONS.set(true);
+            try {
+
+                BooleanExpression e = new SelectorParser(sql).parse();
+                PARSE_CACHE.put(sql, e);
+                return e;
+            } catch (MQFilterException t) {
+                PARSE_CACHE.put(sql, t);
+                throw t;
+            } finally {
+                ComparisonExpression.CONVERT_STRING_EXPRESSIONS.remove();
+                //                if( convertStringExpressions ) {
+                //                    ComparisonExpression.CONVERT_STRING_EXPRESSIONS.remove();
+                //                }
+            }
+        }
+    }
+
+    public static void clearCache() {
+        PARSE_CACHE.cleanUp();
+    }
+
+    private String sql;
+
+    protected SelectorParser(String sql) {
+        this(new StringReader(sql));
+        this.sql = sql;
+    }
+
+    protected BooleanExpression parse() throws MQFilterException {
+        try {
+            return this.JmsSelector();
+        } catch (Throwable e) {
+            throw new MQFilterException("Invalid MessageSelector. ", e);
+        }
+    }
+
+    private BooleanExpression asBooleanExpression(Expression value) throws ParseException {
+        if (value instanceof BooleanExpression) {
+            return (BooleanExpression) value;
+        }
+        if (value instanceof PropertyExpression) {
+            return UnaryExpression.createBooleanCast(value);
+        }
+        throw new ParseException("Expression will not result in a boolean value: " + value);
+    }
+
+    // ----------------------------------------------------------------------------
+    // Grammer
+    // ----------------------------------------------------------------------------
+    final public BooleanExpression JmsSelector() throws ParseException {
+        Expression left = null;
+        left = orExpression();
+        {
+            if (true)
+                return asBooleanExpression(left);
+        }
+        throw new Error("Missing return statement in function");
+    }
+
+    final public Expression orExpression() throws ParseException {
+        Expression left;
+        Expression right;
+        left = andExpression();
+        label_1:
+        while (true) {
+            switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+                case OR:
+                    break;
+                default:
+                    jjLa1[0] = jjGen;
+                    break label_1;
+            }
+            jj_consume_token(OR);
+            right = andExpression();
+            left = LogicExpression.createOR(asBooleanExpression(left), asBooleanExpression(right));
+        }
+        {
+            if (true)
+                return left;
+        }
+        throw new Error("Missing return statement in function");
+    }
+
+    final public Expression andExpression() throws ParseException {
+        Expression left;
+        Expression right;
+        left = equalityExpression();
+        label_2:
+        while (true) {
+            switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+                case AND:
+                    break;
+                default:
+                    jjLa1[1] = jjGen;
+                    break label_2;
+            }
+            jj_consume_token(AND);
+            right = equalityExpression();
+            left = LogicExpression.createAND(asBooleanExpression(left), asBooleanExpression(right));
+        }
+        {
+            if (true)
+                return left;
+        }
+        throw new Error("Missing return statement in function");
+    }
+
+    final public Expression equalityExpression() throws ParseException {
+        Expression left;
+        Expression right;
+        left = comparisonExpression();
+        label_3:
+        while (true) {
+            switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+                case IS:
+                case 22:
+                case 23:
+                    break;
+                default:
+                    jjLa1[2] = jjGen;
+                    break label_3;
+            }
+            switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+                case 22:
+                    jj_consume_token(22);
+                    right = comparisonExpression();
+                    left = ComparisonExpression.createEqual(left, right);
+                    break;
+                case 23:
+                    jj_consume_token(23);
+                    right = comparisonExpression();
+                    left = ComparisonExpression.createNotEqual(left, right);
+                    break;
+                default:
+                    jjLa1[3] = jjGen;
+                    if (jj_2_1(2)) {
+                        jj_consume_token(IS);
+                        jj_consume_token(NULL);
+                        left = ComparisonExpression.createIsNull(left);
+                    } else {
+                        switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+                            case IS:
+                                jj_consume_token(IS);
+                                jj_consume_token(NOT);
+                                jj_consume_token(NULL);
+                                left = ComparisonExpression.createIsNotNull(left);
+                                break;
+                            default:
+                                jjLa1[4] = jjGen;
+                                jj_consume_token(-1);
+                                throw new ParseException();
+                        }
+                    }
+            }
+        }
+        {
+            if (true)
+                return left;
+        }
+        throw new Error("Missing return statement in function");
+    }
+
+    final public Expression comparisonExpression() throws ParseException {
+        Expression left;
+        Expression right;
+        Expression low;
+        Expression high;
+        String t, u;
+        boolean not;
+        ArrayList list;
+        left = unaryExpr();
+        label_4:
+        while (true) {
+            switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+                case NOT:
+                case BETWEEN:
+                case IN:
+                case 24:
+                case 25:
+                case 26:
+                case 27:
+                    break;
+                default:
+                    jjLa1[5] = jjGen;
+                    break label_4;
+            }
+            switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+                case 24:
+                    jj_consume_token(24);
+                    right = unaryExpr();
+                    left = ComparisonExpression.createGreaterThan(left, right);
+                    break;
+                case 25:
+                    jj_consume_token(25);
+                    right = unaryExpr();
+                    left = ComparisonExpression.createGreaterThanEqual(left, right);
+                    break;
+                case 26:
+                    jj_consume_token(26);
+                    right = unaryExpr();
+                    left = ComparisonExpression.createLessThan(left, right);
+                    break;
+                case 27:
+                    jj_consume_token(27);
+                    right = unaryExpr();
+                    left = ComparisonExpression.createLessThanEqual(left, right);
+                    break;
+                case BETWEEN:
+                    jj_consume_token(BETWEEN);
+                    low = unaryExpr();
+                    jj_consume_token(AND);
+                    high = unaryExpr();
+                    left = ComparisonExpression.createBetween(left, low, high);
+                    break;
+                default:
+                    jjLa1[8] = jjGen;
+                    if (jj_2_2(2)) {
+                        jj_consume_token(NOT);
+                        jj_consume_token(BETWEEN);
+                        low = unaryExpr();
+                        jj_consume_token(AND);
+                        high = unaryExpr();
+                        left = ComparisonExpression.createNotBetween(left, low, high);
+                    } else {
+                        switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+                            case IN:
+                                jj_consume_token(IN);
+                                jj_consume_token(28);
+                                t = stringLitteral();
+                                list = new ArrayList();
+                                list.add(t);
+                                label_5:
+                                while (true) {
+                                    switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+                                        case 29:
+                                            break;
+                                        default:
+                                            jjLa1[6] = jjGen;
+                                            break label_5;
+                                    }
+                                    jj_consume_token(29);
+                                    t = stringLitteral();
+                                    list.add(t);
+                                }
+                                jj_consume_token(30);
+                                left = ComparisonExpression.createInFilter(left, list);
+                                break;
+                            default:
+                                jjLa1[9] = jjGen;
+                                if (jj_2_3(2)) {
+                                    jj_consume_token(NOT);
+                                    jj_consume_token(IN);
+                                    jj_consume_token(28);
+                                    t = stringLitteral();
+                                    list = new ArrayList();
+                                    list.add(t);
+                                    label_6:
+                                    while (true) {
+                                        switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+                                            case 29:
+                                                break;
+                                            default:
+                                                jjLa1[7] = jjGen;
+                                                break label_6;
+                                        }
+                                        jj_consume_token(29);
+                                        t = stringLitteral();
+                                        list.add(t);
+                                    }
+                                    jj_consume_token(30);
+                                    left = ComparisonExpression.createNotInFilter(left, list);
+                                } else {
+                                    jj_consume_token(-1);
+                                    throw new ParseException();
+                                }
+                        }
+                    }
+            }
+        }
+        {
+            if (true)
+                return left;
+        }
+        throw new Error("Missing return statement in function");
+    }
+
+    final public Expression unaryExpr() throws ParseException {
+        String s = null;
+        Expression left = null;
+        if (jj_2_4(2147483647)) {
+            jj_consume_token(31);
+            left = unaryExpr();
+        } else {
+            switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+                case 32:
+                    jj_consume_token(32);
+                    left = unaryExpr();
+                    left = UnaryExpression.createNegate(left);
+                    break;
+                case NOT:
+                    jj_consume_token(NOT);
+                    left = unaryExpr();
+                    left = UnaryExpression.createNOT(asBooleanExpression(left));
+                    break;
+                case TRUE:
+                case FALSE:
+                case NULL:
+                case DECIMAL_LITERAL:
+                case FLOATING_POINT_LITERAL:
+                case STRING_LITERAL:
+                case ID:
+                case 28:
+                    left = primaryExpr();
+                    break;
+                default:
+                    jjLa1[10] = jjGen;
+                    jj_consume_token(-1);
+                    throw new ParseException();
+            }
+        }
+        {
+            if (true)
+                return left;
+        }
+        throw new Error("Missing return statement in function");
+    }
+
+    final public Expression primaryExpr() throws ParseException {
+        Expression left = null;
+        switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+            case TRUE:
+            case FALSE:
+            case NULL:
+            case DECIMAL_LITERAL:
+            case FLOATING_POINT_LITERAL:
+            case STRING_LITERAL:
+                left = literal();
+                break;
+            case ID:
+                left = variable();
+                break;
+            case 28:
+                jj_consume_token(28);
+                left = orExpression();
+                jj_consume_token(30);
+                break;
+            default:
+                jjLa1[11] = jjGen;
+                jj_consume_token(-1);
+                throw new ParseException();
+        }
+        {
+            if (true)
+                return left;
+        }
+        throw new Error("Missing return statement in function");
+    }
+
+    final public ConstantExpression literal() throws ParseException {
+        Token t;
+        String s;
+        ConstantExpression left = null;
+        switch ((jjNtk == -1) ? jj_ntk() : jjNtk) {
+            case STRING_LITERAL:
+                s = stringLitteral();
+                left = new ConstantExpression(s);
+                break;
+            case DECIMAL_LITERAL:
+                t = jj_consume_token(DECIMAL_LITERAL);
+                left = ConstantExpression.createFromDecimal(t.image);
+                break;
+            case FLOATING_POINT_LITERAL:
+                t = jj_consume_token(FLOATING_POINT_LITERAL);
+                left = ConstantExpression.createFloat(t.image);
+                break;
+            case TRUE:
+                jj_consume_token(TRUE);
+                left = ConstantExpression.TRUE;
+                break;
+            case FALSE:
+                jj_consume_token(FALSE);
+                left = ConstantExpression.FALSE;
+                break;
+            case NULL:
+                jj_consume_token(NULL);
+                left = ConstantExpression.NULL;
+                break;
+            default:
+                jjLa1[12] = jjGen;
+                jj_consume_token(-1);
+                throw new ParseException();
+        }
+        {
+            if (true)
+                return left;
+        }
+        throw new Error("Missing return statement in function");
+    }
+
+    final public String stringLitteral() throws ParseException {
+        Token t;
+        StringBuffer rc = new StringBuffer();
+        boolean first = true;
+        t = jj_consume_token(STRING_LITERAL);
+        // Decode the sting value.
+        String image = t.image;
+        for (int i = 1; i < image.length() - 1; i++) {
+            char c = image.charAt(i);
+            if (c == '\'')
+                i++;
+            rc.append(c);
+        }
+        {
+            if (true)
+                return rc.toString();
+        }
+        throw new Error("Missing return statement in function");
+    }
+
+    final public PropertyExpression variable() throws ParseException {
+        Token t;
+        PropertyExpression left = null;
+        t = jj_consume_token(ID);
+        left = new PropertyExpression(t.image);
+        {
+            if (true)
+                return left;
+        }
+        throw new Error("Missing return statement in function");
+    }
+
+    private boolean jj_2_1(int xla) {
+        jjLa = xla;
+        jjLastpos = jjScanpos = token;
+        try {
+            return !jj_3_1();
+        } catch (LookaheadSuccess ls) {
+            return true;
+        } finally {
+            jj_save(0, xla);
+        }
+    }
+
+    private boolean jj_2_2(int xla) {
+        jjLa = xla;
+        jjLastpos = jjScanpos = token;
+        try {
+            return !jj_3_2();
+        } catch (LookaheadSuccess ls) {
+            return true;
+        } finally {
+            jj_save(1, xla);
+        }
+    }
+
+    private boolean jj_2_3(int xla) {
+        jjLa = xla;
+        jjLastpos = jjScanpos = token;
+        try {
+            return !jj_3_3();
+        } catch (LookaheadSuccess ls) {
+            return true;
+        } finally {
+            jj_save(2, xla);
+        }
+    }
+
+    private boolean jj_2_4(int xla) {
+        jjLa = xla;
+        jjLastpos = jjScanpos = token;
+        try {
+            return !jj_3_4();
+        } catch (LookaheadSuccess ls) {
+            return true;
+        } finally {
+            jj_save(3, xla);
+        }
+    }
+
+    private boolean jj_3R_7() {
+        Token xsp;
+        xsp = jjScanpos;
+        if (jj_3R_8()) {
+            jjScanpos = xsp;
+            if (jj_3R_9()) {
+                jjScanpos = xsp;
+                if (jj_3R_10()) {
+                    jjScanpos = xsp;
+                    if (jj_3R_11())
+                        return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_43() {
+        if (jj_scan_token(29))
+            return true;
+        if (jj_3R_27())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_24() {
+        if (jj_scan_token(NULL))
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_35() {
+        if (jj_scan_token(IS))
+            return true;
+        if (jj_scan_token(NOT))
+            return true;
+        if (jj_scan_token(NULL))
+            return true;
+        return false;
+    }
+
+    private boolean jj_3_1() {
+        if (jj_scan_token(IS))
+            return true;
+        if (jj_scan_token(NULL))
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_23() {
+        if (jj_scan_token(FALSE))
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_34() {
+        if (jj_scan_token(23))
+            return true;
+        if (jj_3R_30())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_22() {
+        if (jj_scan_token(TRUE))
+            return true;
+        return false;
+    }
+
+    private boolean jj_3_3() {
+        if (jj_scan_token(NOT))
+            return true;
+        if (jj_scan_token(IN))
+            return true;
+        if (jj_scan_token(28))
+            return true;
+        if (jj_3R_27())
+            return true;
+        Token xsp;
+        while (true) {
+            xsp = jjScanpos;
+            if (jj_3R_43()) {
+                jjScanpos = xsp;
+                break;
+            }
+        }
+        if (jj_scan_token(30))
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_31() {
+        Token xsp;
+        xsp = jjScanpos;
+        if (jj_3R_33()) {
+            jjScanpos = xsp;
+            if (jj_3R_34()) {
+                jjScanpos = xsp;
+                if (jj_3_1()) {
+                    jjScanpos = xsp;
+                    if (jj_3R_35())
+                        return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_33() {
+        if (jj_scan_token(22))
+            return true;
+        if (jj_3R_30())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_42() {
+        if (jj_scan_token(29))
+            return true;
+        if (jj_3R_27())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_21() {
+        if (jj_scan_token(FLOATING_POINT_LITERAL))
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_20() {
+        if (jj_scan_token(DECIMAL_LITERAL))
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_28() {
+        if (jj_3R_30())
+            return true;
+        Token xsp;
+        while (true) {
+            xsp = jjScanpos;
+            if (jj_3R_31()) {
+                jjScanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_41() {
+        if (jj_scan_token(IN))
+            return true;
+        if (jj_scan_token(28))
+            return true;
+        if (jj_3R_27())
+            return true;
+        Token xsp;
+        while (true) {
+            xsp = jjScanpos;
+            if (jj_3R_42()) {
+                jjScanpos = xsp;
+                break;
+            }
+        }
+        if (jj_scan_token(30))
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_19() {
+        if (jj_3R_27())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_29() {
+        if (jj_scan_token(AND))
+            return true;
+        if (jj_3R_28())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_16() {
+        Token xsp;
+        xsp = jjScanpos;
+        if (jj_3R_19()) {
+            jjScanpos = xsp;
+            if (jj_3R_20()) {
+                jjScanpos = xsp;
+                if (jj_3R_21()) {
+                    jjScanpos = xsp;
+                    if (jj_3R_22()) {
+                        jjScanpos = xsp;
+                        if (jj_3R_23()) {
+                            jjScanpos = xsp;
+                            if (jj_3R_24())
+                                return true;
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3_2() {
+        if (jj_scan_token(NOT))
+            return true;
+        if (jj_scan_token(BETWEEN))
+            return true;
+        if (jj_3R_7())
+            return true;
+        if (jj_scan_token(AND))
+            return true;
+        if (jj_3R_7())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_40() {
+        if (jj_scan_token(BETWEEN))
+            return true;
+        if (jj_3R_7())
+            return true;
+        if (jj_scan_token(AND))
+            return true;
+        if (jj_3R_7())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_25() {
+        if (jj_3R_28())
+            return true;
+        Token xsp;
+        while (true) {
+            xsp = jjScanpos;
+            if (jj_3R_29()) {
+                jjScanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_39() {
+        if (jj_scan_token(27))
+            return true;
+        if (jj_3R_7())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_15() {
+        if (jj_scan_token(28))
+            return true;
+        if (jj_3R_18())
+            return true;
+        if (jj_scan_token(30))
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_14() {
+        if (jj_3R_17())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_38() {
+        if (jj_scan_token(26))
+            return true;
+        if (jj_3R_7())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_13() {
+        if (jj_3R_16())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_26() {
+        if (jj_scan_token(OR))
+            return true;
+        if (jj_3R_25())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_17() {
+        if (jj_scan_token(ID))
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_37() {
+        if (jj_scan_token(25))
+            return true;
+        if (jj_3R_7())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_12() {
+        Token xsp;
+        xsp = jjScanpos;
+        if (jj_3R_13()) {
+            jjScanpos = xsp;
+            if (jj_3R_14()) {
+                jjScanpos = xsp;
+                if (jj_3R_15())
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_32() {
+        Token xsp;
+        xsp = jjScanpos;
+        if (jj_3R_36()) {
+            jjScanpos = xsp;
+            if (jj_3R_37()) {
+                jjScanpos = xsp;
+                if (jj_3R_38()) {
+                    jjScanpos = xsp;
+                    if (jj_3R_39()) {
+                        jjScanpos = xsp;
+                        if (jj_3R_40()) {
+                            jjScanpos = xsp;
+                            if (jj_3_2()) {
+                                jjScanpos = xsp;
+                                if (jj_3R_41()) {
+                                    jjScanpos = xsp;
+                                    if (jj_3_3())
+                                        return true;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_36() {
+        if (jj_scan_token(24))
+            return true;
+        if (jj_3R_7())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_11() {
+        if (jj_3R_12())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_18() {
+        if (jj_3R_25())
+            return true;
+        Token xsp;
+        while (true) {
+            xsp = jjScanpos;
+            if (jj_3R_26()) {
+                jjScanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3_4() {
+        if (jj_scan_token(31))
+            return true;
+        if (jj_3R_7())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_10() {
+        if (jj_scan_token(NOT))
+            return true;
+        if (jj_3R_7())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_9() {
+        if (jj_scan_token(32))
+            return true;
+        if (jj_3R_7())
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_27() {
+        if (jj_scan_token(STRING_LITERAL))
+            return true;
+        return false;
+    }
+
+    private boolean jj_3R_30() {
+        if (jj_3R_7())
+            return true;
+        Token xsp;
+        while (true) {
+            xsp = jjScanpos;
+            if (jj_3R_32()) {
+                jjScanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_8() {
+        if (jj_scan_token(31))
+            return true;
+        if (jj_3R_7())
+            return true;
+        return false;
+    }
+
+    /**
+     * Generated Token Manager.
+     */
+    public SelectorParserTokenManager tokenSource;
+    SimpleCharStream jjInputStream;
+    /**
+     * Current token.
+     */
+    public Token token;
+    /**
+     * Next token.
+     */
+    public Token jjNt;
+    private int jjNtk;
+    private Token jjScanpos, jjLastpos;
+    private int jjLa;
+    private int jjGen;
+    final private int[] jjLa1 = new int[13];
+    static private int[] jjLa10;
+    static private int[] jjLa11;
+
+    static {
+        jj_la1_init_0();
+        jj_la1_init_1();
+    }
+
+    private static void jj_la1_init_0() {
+        jjLa10 = new int[]{0x400, 0x200, 0xc10000, 0xc00000, 0x10000, 0xf001900, 0x20000000, 0x20000000, 0xf000800,
+            0x1000, 0x1036e100, 0x1036e000, 0x16e000};
+    }
+
+    private static void jj_la1_init_1() {
+        jjLa11 = new int[]{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0};
+    }
+
+    final private JJCalls[] jj2Rtns = new JJCalls[4];
+    private boolean jjRescan = false;
+    private int jjGc = 0;
+
+    /**
+     * Constructor with InputStream.
+     */
+    public SelectorParser(java.io.InputStream stream) {
+        this(stream, null);
+    }
+
+    /**
+     * Constructor with InputStream and supplied encoding
+     */
+    public SelectorParser(java.io.InputStream stream, String encoding) {
+        try {
+            jjInputStream = new SimpleCharStream(stream, encoding, 1, 1);
+        } catch (java.io.UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+        tokenSource = new SelectorParserTokenManager(jjInputStream);
+        token = new Token();
+        jjNtk = -1;
+        jjGen = 0;
+        for (int i = 0; i < 13; i++)
+            jjLa1[i] = -1;
+        for (int i = 0; i < jj2Rtns.length; i++)
+            jj2Rtns[i] = new JJCalls();
+    }
+
+    /**
+     * Reinitialise.
+     */
+    public void ReInit(java.io.InputStream stream) {
+        ReInit(stream, null);
+    }
+
+    /**
+     * Reinitialise.
+     */
+    public void ReInit(java.io.InputStream stream, String encoding) {
+        try {
+            jjInputStream.ReInit(stream, encoding, 1, 1);
+        } catch (java.io.UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+        tokenSource.ReInit(jjInputStream);
+        token = new Token();
+        jjNtk = -1;
+        jjGen = 0;
+        for (int i = 0; i < 13; i++)
+            jjLa1[i] = -1;
+        for (int i = 0; i < jj2Rtns.length; i++)
+            jj2Rtns[i] = new JJCalls();
+    }
+
+    /**
+     * Constructor.
+     */
+    public SelectorParser(java.io.Reader stream) {
+        jjInputStream = new SimpleCharStream(stream, 1, 1);
+        tokenSource = new SelectorParserTokenManager(jjInputStream);
+        token = new Token();
+        jjNtk = -1;
+        jjGen = 0;
+        for (int i = 0; i < 13; i++)
+            jjLa1[i] = -1;
+        for (int i = 0; i < jj2Rtns.length; i++)
+            jj2Rtns[i] = new JJCalls();
+    }
+
+    /**
+     * Reinitialise.
+     */
+    public void ReInit(java.io.Reader stream) {
+        jjInputStream.ReInit(stream, 1, 1);
+        tokenSource.ReInit(jjInputStream);
+        token = new Token();
+        jjNtk = -1;
+        jjGen = 0;
+        for (int i = 0; i < 13; i++)
+            jjLa1[i] = -1;
+        for (int i = 0; i < jj2Rtns.length; i++)
+            jj2Rtns[i] = new JJCalls();
+    }
+
+    /**
+     * Constructor with generated Token Manager.
+     */
+    public SelectorParser(SelectorParserTokenManager tm) {
+        tokenSource = tm;
+        token = new Token();
+        jjNtk = -1;
+        jjGen = 0;
+        for (int i = 0; i < 13; i++)
+            jjLa1[i] = -1;
+        for (int i = 0; i < jj2Rtns.length; i++)
+            jj2Rtns[i] = new JJCalls();
+    }
+
+    /**
+     * Reinitialise.
+     */
+    public void ReInit(SelectorParserTokenManager tm) {
+        tokenSource = tm;
+        token = new Token();
+        jjNtk = -1;
+        jjGen = 0;
+        for (int i = 0; i < 13; i++)
+            jjLa1[i] = -1;
+        for (int i = 0; i < jj2Rtns.length; i++)
+            jj2Rtns[i] = new JJCalls();
+    }
+
+    private Token jj_consume_token(int kind) throws ParseException {
+        Token oldToken;
+        if ((oldToken = token).next != null)
+            token = token.next;
+        else
+            token = token.next = tokenSource.getNextToken();
+        jjNtk = -1;
+        if (token.kind == kind) {
+            jjGen++;
+            if (++jjGc > 100) {
+                jjGc = 0;
+                for (int i = 0; i < jj2Rtns.length; i++) {
+                    JJCalls c = jj2Rtns[i];
+                    while (c != null) {
+                        if (c.gen < jjGen)
+                            c.first = null;
+                        c = c.next;
+                    }
+                }
+            }
+            return token;
+        }
+        token = oldToken;
+        jjKind = kind;
+        throw generateParseException();
+    }
+
+    static private final class LookaheadSuccess extends java.lang.Error {
+    }
+
+    final private LookaheadSuccess jjLs = new LookaheadSuccess();
+
+    private boolean jj_scan_token(int kind) {
+        if (jjScanpos == jjLastpos) {
+            jjLa--;
+            if (jjScanpos.next == null) {
+                jjLastpos = jjScanpos = jjScanpos.next = tokenSource.getNextToken();
+            } else {
+                jjLastpos = jjScanpos = jjScanpos.next;
+            }
+        } else {
+            jjScanpos = jjScanpos.next;
+        }
+        if (jjRescan) {
+            int i = 0;
+            Token tok = token;
+            while (tok != null && tok != jjScanpos) {
+                i++;
+                tok = tok.next;
+            }
+            if (tok != null)
+                jj_add_error_token(kind, i);
+        }
+        if (jjScanpos.kind != kind)
+            return true;
+        if (jjLa == 0 && jjScanpos == jjLastpos)
+            throw jjLs;
+        return false;
+    }
+
+    /**
+     * Get the next Token.
+     */
+    final public Token getNextToken() {
+        if (token.next != null)
+            token = token.next;
+        else
+            token = token.next = tokenSource.getNextToken();
+        jjNtk = -1;
+        jjGen++;
+        return token;
+    }
+
+    /**
+     * Get the specific Token.
+     */
+    final public Token getToken(int index) {
+        Token t = token;
+        for (int i = 0; i < index; i++) {
+            if (t.next != null)
+                t = t.next;
+            else
+                t = t.next = tokenSource.getNextToken();
+        }
+        return t;
+    }
+
+    private int jj_ntk() {
+        if ((jjNt = token.next) == null)
+            return jjNtk = (token.next = tokenSource.getNextToken()).kind;
+        else
+            return jjNtk = jjNt.kind;
+    }
+
+    private java.util.List<int[]> jjExpentries = new java.util.ArrayList<int[]>();
+    private int[] jjExpentry;
+    private int jjKind = -1;
+    private int[] jjLasttokens = new int[100];
+    private int jjEndpos;
+
+    private void jj_add_error_token(int kind, int pos) {
+        if (pos >= 100)
+            return;
+        if (pos == jjEndpos + 1) {
+            jjLasttokens[jjEndpos++] = kind;
+        } else if (jjEndpos != 0) {
+            jjExpentry = new int[jjEndpos];
+            for (int i = 0; i < jjEndpos; i++) {
+                jjExpentry[i] = jjLasttokens[i];
+            }
+            jj_entries_loop:
+            for (java.util.Iterator<?> it = jjExpentries.iterator(); it.hasNext(); ) {
+                int[] oldentry = (int[]) (it.next());
+                if (oldentry.length == jjExpentry.length) {
+                    for (int i = 0; i < jjExpentry.length; i++) {
+                        if (oldentry[i] != jjExpentry[i]) {
+                            continue jj_entries_loop;
+                        }
+                    }
+                    jjExpentries.add(jjExpentry);
+                    break jj_entries_loop;
+                }
+            }
+            if (pos != 0)
+                jjLasttokens[(jjEndpos = pos) - 1] = kind;
+        }
+    }
+
+    /**
+     * Generate ParseException.
+     */
+    public ParseException generateParseException() {
+        jjExpentries.clear();
+        boolean[] la1tokens = new boolean[33];
+        if (jjKind >= 0) {
+            la1tokens[jjKind] = true;
+            jjKind = -1;
+        }
+        for (int i = 0; i < 13; i++) {
+            if (jjLa1[i] == jjGen) {
+                for (int j = 0; j < 32; j++) {
+                    if ((jjLa10[i] & (1 << j)) != 0) {
+                        la1tokens[j] = true;
+                    }
+                    if ((jjLa11[i] & (1 << j)) != 0) {
+                        la1tokens[32 + j] = true;
+                    }
+                }
+            }
+        }
+        for (int i = 0; i < 33; i++) {
+            if (la1tokens[i]) {
+                jjExpentry = new int[1];
+                jjExpentry[0] = i;
+                jjExpentries.add(jjExpentry);
+            }
+        }
+        jjEndpos = 0;
+        jj_rescan_token();
+        jj_add_error_token(0, 0);
+        int[][] exptokseq = new int[jjExpentries.size()][];
+        for (int i = 0; i < jjExpentries.size(); i++) {
+            exptokseq[i] = jjExpentries.get(i);
+        }
+        return new ParseException(token, exptokseq, TOKEN_IMAGE);
+    }
+
+    /**
+     * Enable tracing.
+     */
+    final public void enable_tracing() {
+    }
+
+    /**
+     * Disable tracing.
+     */
+    final public void disable_tracing() {
+    }
+
+    private void jj_rescan_token() {
+        jjRescan = true;
+        for (int i = 0; i < 4; i++) {
+            try {
+                JJCalls p = jj2Rtns[i];
+                do {
+                    if (p.gen > jjGen) {
+                        jjLa = p.arg;
+                        jjLastpos = jjScanpos = p.first;
+                        switch (i) {
+                            case 0:
+                                jj_3_1();
+                                break;
+                            case 1:
+                                jj_3_2();
+                                break;
+                            case 2:
+                                jj_3_3();
+                                break;
+                            case 3:
+                                jj_3_4();
+                                break;
+                        }
+                    }
+                    p = p.next;
+                } while (p != null);
+            } catch (LookaheadSuccess ls) {
+            }
+        }
+        jjRescan = false;
+    }
+
+    private void jj_save(int index, int xla) {
+        JJCalls p = jj2Rtns[index];
+        while (p.gen > jjGen) {
+            if (p.next == null) {
+                p = p.next = new JJCalls();
+                break;
+            }
+            p = p.next;
+        }
+        p.gen = jjGen + xla - jjLa;
+        p.first = token;
+        p.arg = xla;
+    }
+
+    static final class JJCalls {
+        int gen;
+        Token first;
+        int arg;
+        JJCalls next;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-rocketmq/blob/58f1574b/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.jj
----------------------------------------------------------------------
diff --git a/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.jj b/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.jj
new file mode 100644
index 0000000..5d1a4a7
--- /dev/null
+++ b/filter/src/main/java/org/apache/rocketmq/filter/parser/SelectorParser.jj
@@ -0,0 +1,524 @@
+/*
+ * 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.
+ */
+
+/*
+ * This file was taken from ActiveMQ activemq-client/src/main/grammar/SelectorParser.jj.
+ *
+ * There are some modifications:
+ * 1. Convert string expressions default;
+ * 2. HEX_LITERAL and OCTAL_LITERAL were removed;
+ * 3. LIKE, ESCAPE, XPATH and XQUERY were removed;
+ * 4. Computation expressions were removed;
+ */
+
+// ----------------------------------------------------------------------------
+// OPTIONS
+// ----------------------------------------------------------------------------
+options {
+  STATIC = false;
+  UNICODE_INPUT = true;
+
+  //ERROR_REPORTING = false;
+}
+
+// ----------------------------------------------------------------------------
+// PARSER
+// ----------------------------------------------------------------------------
+
+PARSER_BEGIN(SelectorParser)
+
+package org.apache.rocketmq.filter.parser;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.apache.rocketmq.filter.expression.BooleanExpression;
+import org.apache.rocketmq.filter.expression.ComparisonExpression;
+import org.apache.rocketmq.filter.expression.ConstantExpression;
+import org.apache.rocketmq.filter.expression.Expression;
+import org.apache.rocketmq.filter.expression.LogicExpression;
+import org.apache.rocketmq.filter.expression.MQFilterException;
+import org.apache.rocketmq.filter.expression.PropertyExpression;
+import org.apache.rocketmq.filter.expression.UnaryExpression;
+import org.apache.rocketmq.filter.util.LRUCache;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+
+/**
+ * JMS Selector Parser generated by JavaCC
+ *
+ * Do not edit this .java file directly - it is autogenerated from SelectorParser.jj
+ */
+public class SelectorParser {
+
+    private static final Cache<String, Object> PARSE_CACHE = CacheBuilder.newBuilder().maximumSize(100).build();
+//    private static final String CONVERT_STRING_EXPRESSIONS_PREFIX = "convert_string_expressions:";
+
+    public static BooleanExpression parse(String sql) throws MQFilterException {
+//        sql = "("+sql+")";
+        Object result = PARSE_CACHE.getIfPresent(sql);
+        if (result instanceof MQFilterException) {
+            throw (MQFilterException) result;
+        } else if (result instanceof BooleanExpression) {
+            return (BooleanExpression) result;
+        } else {
+
+//            boolean convertStringExpressions = false;
+//            if( sql.startsWith(CONVERT_STRING_EXPRESSIONS_PREFIX)) {
+//                convertStringExpressions = true;
+//                sql = sql.substring(CONVERT_STRING_EXPRESSIONS_PREFIX.length());
+//            }
+//
+//            if( convertStringExpressions ) {
+//                ComparisonExpression.CONVERT_STRING_EXPRESSIONS.set(true);
+//            }
+            ComparisonExpression.CONVERT_STRING_EXPRESSIONS.set(true);
+            try {
+
+                BooleanExpression e = new SelectorParser(sql).parse();
+                PARSE_CACHE.put(sql, e);
+                return e;
+            } catch (MQFilterException t) {
+                PARSE_CACHE.put(sql, t);
+                throw t;
+            } finally {
+                ComparisonExpression.CONVERT_STRING_EXPRESSIONS.remove();
+//                if( convertStringExpressions ) {
+//                    ComparisonExpression.CONVERT_STRING_EXPRESSIONS.remove();
+//                }
+            }
+        }
+    }
+
+    public static void clearCache() {
+        PARSE_CACHE.cleanUp();
+    }
+
+    private String sql;
+
+    protected SelectorParser(String sql) {
+        this(new StringReader(sql));
+        this.sql = sql;
+    }
+
+    protected BooleanExpression parse() throws MQFilterException {
+        try {
+            return this.JmsSelector();
+        }
+        catch (Throwable e) {
+            throw new MQFilterException("Invalid MessageSelector. ", e);
+        }
+    }
+
+    private BooleanExpression asBooleanExpression(Expression value) throws ParseException  {
+        if (value instanceof BooleanExpression) {
+            return (BooleanExpression) value;
+        }
+        if (value instanceof PropertyExpression) {
+            return UnaryExpression.createBooleanCast( value );
+        }
+        throw new ParseException("Expression will not result in a boolean value: " + value);
+    }
+
+}
+
+PARSER_END(SelectorParser)
+
+// ----------------------------------------------------------------------------
+// Tokens
+// ----------------------------------------------------------------------------
+
+/* White Space */
+SPECIAL_TOKEN :
+{
+  " " | "\t" | "\n" | "\r" | "\f"
+}
+
+/* Comments */
+SKIP:
+{
+  <LINE_COMMENT: "--" (~["\n","\r"])* ("\n"|"\r"|"\r\n") >
+}
+
+SKIP:
+{
+  <BLOCK_COMMENT: "/*" (~["*"])* "*" ("*" | (~["*","/"] (~["*"])* "*"))* "/">
+}
+
+/* Reserved Words */
+TOKEN [IGNORE_CASE] :
+{
+    <  NOT     : "NOT">
+  | <  AND     : "AND">
+  | <  OR      : "OR">
+  | <  BETWEEN : "BETWEEN">
+  | <  IN      : "IN">
+  | <  TRUE    : "TRUE" >
+  | <  FALSE   : "FALSE" >
+  | <  NULL    : "NULL" >
+  | <  IS      : "IS" >
+}
+
+/* Literals */
+TOKEN [IGNORE_CASE] :
+
+{
+
+    < DECIMAL_LITERAL: "0" | ["1"-"9"] (["0"-"9"])* (["l","L"])? >
+  | < FLOATING_POINT_LITERAL:
+          (["0"-"9"])+ "." (["0"-"9"])* (<EXPONENT>)? // matches: 5.5 or 5. or 5.5E10 or 5.E10
+        | "." (["0"-"9"])+ (<EXPONENT>)?              // matches: .5 or .5E10
+        | (["0"-"9"])+ <EXPONENT>                     // matches: 5E10
+    >
+  | < #EXPONENT: "E" (["+","-"])? (["0"-"9"])+ >
+  | < STRING_LITERAL: "'" ( ("''") | ~["'"] )*  "'" >
+}
+
+TOKEN [IGNORE_CASE] :
+{
+    < ID : ["a"-"z", "_", "$"] (["a"-"z","0"-"9","_", "$"])* >
+}
+
+// ----------------------------------------------------------------------------
+// Grammer
+// ----------------------------------------------------------------------------
+BooleanExpression JmsSelector() :
+{
+    Expression left=null;
+}
+{
+    (
+        left = orExpression()
+    )
+    {
+        return asBooleanExpression(left);
+    }
+
+}
+
+Expression orExpression() :
+{
+    Expression left;
+    Expression right;
+}
+{
+    (
+        left = andExpression()
+        (
+            <OR> right = andExpression()
+            {
+                left = LogicExpression.createOR(asBooleanExpression(left), asBooleanExpression(right));
+            }
+        )*
+    )
+    {
+        return left;
+    }
+
+}
+
+
+Expression andExpression() :
+{
+    Expression left;
+    Expression right;
+}
+{
+    (
+        left = equalityExpression()
+        (
+            <AND> right = equalityExpression()
+            {
+                left = LogicExpression.createAND(asBooleanExpression(left), asBooleanExpression(right));
+            }
+        )*
+    )
+    {
+        return left;
+    }
+}
+
+Expression equalityExpression() :
+{
+    Expression left;
+    Expression right;
+}
+{
+    (
+        left = comparisonExpression()
+        (
+
+            "=" right = comparisonExpression()
+            {
+                left = ComparisonExpression.createEqual(left, right);
+            }
+            |
+            "<>" right = comparisonExpression()
+            {
+                left = ComparisonExpression.createNotEqual(left, right);
+            }
+            |
+            LOOKAHEAD(2)
+            <IS> <NULL>
+            {
+                left = ComparisonExpression.createIsNull(left);
+            }
+            |
+            <IS> <NOT> <NULL>
+            {
+                left = ComparisonExpression.createIsNotNull(left);
+            }
+        )*
+    )
+    {
+        return left;
+    }
+}
+
+Expression comparisonExpression() :
+{
+    Expression left;
+    Expression right;
+    Expression low;
+    Expression high;
+    String t, u;
+    boolean not;
+    ArrayList list;
+}
+{
+    (
+        left = unaryExpr()
+        (
+
+                ">" right = unaryExpr()
+                {
+                    left = ComparisonExpression.createGreaterThan(left, right);
+                }
+            |
+                ">=" right = unaryExpr()
+                {
+                    left = ComparisonExpression.createGreaterThanEqual(left, right);
+                }
+            |
+                "<" right = unaryExpr()
+                {
+                    left = ComparisonExpression.createLessThan(left, right);
+                }
+            |
+                "<=" right = unaryExpr()
+                {
+                    left = ComparisonExpression.createLessThanEqual(left, right);
+                }
+            |
+                <BETWEEN> low = unaryExpr() <AND> high = unaryExpr()
+                {
+                    left = ComparisonExpression.createBetween(left, low, high);
+                }
+            |
+                LOOKAHEAD(2)
+                <NOT> <BETWEEN> low = unaryExpr() <AND> high = unaryExpr()
+                {
+                    left = ComparisonExpression.createNotBetween(left, low, high);
+                }
+            |
+                <IN>
+                "("
+                    t = stringLitteral()
+                    {
+                        list = new ArrayList();
+                        list.add( t );
+                    }
+                    (
+                        ","
+                        t = stringLitteral()
+                        {
+                            list.add( t );
+                        }
+
+                    )*
+                ")"
+                {
+                   left = ComparisonExpression.createInFilter(left, list);
+                }
+            |
+                LOOKAHEAD(2)
+                <NOT> <IN>
+                "("
+                    t = stringLitteral()
+                    {
+                        list = new ArrayList();
+                        list.add( t );
+                    }
+                    (
+                        ","
+                        t = stringLitteral()
+                        {
+                            list.add( t );
+                        }
+
+                    )*
+                ")"
+                {
+                   left = ComparisonExpression.createNotInFilter(left, list);
+                }
+
+        )*
+    )
+    {
+        return left;
+    }
+}
+
+Expression unaryExpr() :
+{
+    String s=null;
+    Expression left=null;
+}
+{
+    (
+        LOOKAHEAD( "+" unaryExpr() )
+        "+" left=unaryExpr()
+        |
+        "-" left=unaryExpr()
+        {
+            left = UnaryExpression.createNegate(left);
+        }
+        |
+        <NOT> left=unaryExpr()
+        {
+            left = UnaryExpression.createNOT( asBooleanExpression(left) );
+        }
+        |
+        left = primaryExpr()
+    )
+    {
+        return left;
+    }
+
+}
+
+Expression primaryExpr() :
+{
+    Expression left=null;
+}
+{
+    (
+        left = literal()
+        |
+        left = variable()
+        |
+        "(" left = orExpression() ")"
+    )
+    {
+        return left;
+    }
+}
+
+
+
+ConstantExpression literal() :
+{
+    Token t;
+    String s;
+    ConstantExpression left=null;
+}
+{
+    (
+        (
+            s = stringLitteral()
+            {
+                left = new ConstantExpression(s);
+            }
+        )
+        |
+        (
+            t = <DECIMAL_LITERAL>
+            {
+                left = ConstantExpression.createFromDecimal(t.image);
+            }
+        )
+        |
+        (
+            t = <FLOATING_POINT_LITERAL>
+            {
+                left = ConstantExpression.createFloat(t.image);
+            }
+        )
+        |
+        (
+            <TRUE>
+            {
+                left = ConstantExpression.TRUE;
+            }
+        )
+        |
+        (
+            <FALSE>
+            {
+                left = ConstantExpression.FALSE;
+            }
+        )
+        |
+        (
+            <NULL>
+            {
+                left = ConstantExpression.NULL;
+            }
+        )
+    )
+    {
+        return left;
+    }
+}
+
+String stringLitteral() :
+{
+    Token t;
+    StringBuffer rc = new StringBuffer();
+    boolean first=true;
+}
+{
+    t = <STRING_LITERAL>
+    {
+        // Decode the sting value.
+        String image = t.image;
+        for( int i=1; i < image.length()-1; i++ ) {
+            char c = image.charAt(i);
+            if( c == '\'' )
+                i++;
+            rc.append(c);
+        }
+        return rc.toString();
+    }
+}
+
+PropertyExpression variable() :
+{
+    Token t;
+    PropertyExpression left=null;
+}
+{
+    (
+        t = <ID>
+        {
+            left = new PropertyExpression(t.image);
+        }
+    )
+    {
+        return left;
+    }
+}