You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ta...@apache.org on 2014/09/26 21:52:17 UTC

[1/2] Create our own selector parser lib based on apollo-selector

Repository: qpid-jms
Updated Branches:
  refs/heads/master 4c17e5e47 -> a5fe3018d


http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/XalanXPathEvaluator.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/XalanXPathEvaluator.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/XalanXPathEvaluator.java
new file mode 100644
index 0000000..b09961e
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/XalanXPathEvaluator.java
@@ -0,0 +1,116 @@
+/**
+ * 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.jms.selector.filter;
+
+import org.apache.xpath.CachedXPathAPI;
+import org.apache.xpath.objects.XObject;
+import org.w3c.dom.Document;
+import org.w3c.dom.traversal.NodeIterator;
+import org.xml.sax.InputSource;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+
+public class XalanXPathEvaluator implements XPathExpression.XPathEvaluator {
+    public static final String DOCUMENT_BUILDER_FACTORY_FEATURE = "org.apache.activemq.apollo.documentBuilderFactory.feature";
+    private final String xpath;
+
+    public XalanXPathEvaluator(String xpath) {
+        this.xpath = xpath;
+    }
+
+    public boolean evaluate(Filterable m) throws FilterException {
+        String stringBody = m.getBodyAs(String.class);
+        if (stringBody!=null) {
+            return evaluate(stringBody);
+        } 
+        return false;
+    }
+
+    protected boolean evaluate(String text) {
+        return evaluate(new InputSource(new StringReader(text)));
+    }
+
+    protected boolean evaluate(InputSource inputSource) {
+        try {
+            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+            factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
+            factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+            factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+            setupFeatures(factory);
+            factory.setNamespaceAware(true);
+            factory.setIgnoringElementContentWhitespace(true);
+            factory.setIgnoringComments(true);
+            DocumentBuilder dbuilder = factory.newDocumentBuilder();
+            Document doc = dbuilder.parse(inputSource);
+
+            //An XPath expression could return a true or false value instead of a node.
+            //eval() is a better way to determine the boolean value of the exp.
+            //For compliance with legacy behavior where selecting an empty node returns true,
+            //selectNodeIterator is attempted in case of a failure.
+
+            CachedXPathAPI cachedXPathAPI = new CachedXPathAPI();
+            XObject result = cachedXPathAPI.eval(doc, xpath);
+            if (result.bool())
+            	return true;
+            else {
+            	NodeIterator iterator = cachedXPathAPI.selectNodeIterator(doc, xpath);
+            	return (iterator.nextNode() != null);
+            }
+
+        } catch (Throwable e) {
+            return false;
+        }
+    }
+
+    protected void setupFeatures(DocumentBuilderFactory factory) {
+        Properties properties = System.getProperties();
+        List<String> features = new ArrayList<String>();
+        for (Map.Entry<Object, Object> prop : properties.entrySet()) {
+            String key = (String) prop.getKey();
+            if (key.startsWith(DOCUMENT_BUILDER_FACTORY_FEATURE)) {
+                String uri = key.split(DOCUMENT_BUILDER_FACTORY_FEATURE + ":")[1];
+                Boolean value = Boolean.valueOf((String)prop.getValue());
+                try {
+                    factory.setFeature(uri, value);
+                    features.add("feature " + uri + " value " + value);
+                } catch (ParserConfigurationException e) {
+                    throw new RuntimeException("DocumentBuilderFactory doesn't support the feature " + uri + " with value " + value + ", due to " + e);
+                }
+            }
+        }
+        if (features.size() > 0) {
+            StringBuffer featureString = new StringBuffer();
+            // just log the configured feature
+            for (String feature : features) {
+                if (featureString.length() != 0) {
+                    featureString.append(", ");
+                }
+                featureString.append(feature);
+            }
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/package.html
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/package.html b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/package.html
new file mode 100755
index 0000000..836ddd9
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/package.html
@@ -0,0 +1,27 @@
+<!--
+    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.
+-->
+<html>
+<head>
+</head>
+<body>
+
+<p>
+	Filter implementations for wildcards & JMS selectors
+</p>
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/javacc/HyphenatedParser.jj
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/javacc/HyphenatedParser.jj b/qpid-jms-selector/src/main/javacc/HyphenatedParser.jj
new file mode 100755
index 0000000..5966027
--- /dev/null
+++ b/qpid-jms-selector/src/main/javacc/HyphenatedParser.jj
@@ -0,0 +1,563 @@
+/**
+ * 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.
+ */
+
+// ----------------------------------------------------------------------------
+// OPTIONS
+// ----------------------------------------------------------------------------
+options {
+  STATIC = false;
+  UNICODE_INPUT = true;
+  // some performance optimizations
+  ERROR_REPORTING = false;
+}
+
+// ----------------------------------------------------------------------------
+// PARSER
+// ----------------------------------------------------------------------------
+
+PARSER_BEGIN(HyphenatedParser)
+/**
+ * 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.jms.selector.hyphenated;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.qpid.jms.selector.filter.*;
+
+/**
+ * JMS Selector Parser generated by JavaCC
+ *
+ * Do not edit this .java file directly - it is autogenerated from HyphenatedParser.jj
+ */
+public class HyphenatedParser {
+    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(HyphenatedParser)
+
+// ----------------------------------------------------------------------------
+// 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">
+  | <  LIKE    : "LIKE">
+  | <  ESCAPE  : "ESCAPE">
+  | <  IN      : "IN">
+  | <  IS      : "IS">
+  | <  TRUE    : "TRUE" >
+  | <  FALSE   : "FALSE" >
+  | <  NULL    : "NULL" >
+  | <  XPATH   : "XPATH" >
+  | <  XQUERY  : "XQUERY" >
+}
+
+/* Literals */
+TOKEN [IGNORE_CASE] :
+{
+
+    < DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* (["l","L"])? >
+  | < HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ >
+  | < OCTAL_LITERAL: "0" (["0"-"7"])* >
+  | < 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 = addExpression()
+        (
+
+                ">" right = addExpression()
+                {
+                    left = ComparisonExpression.createGreaterThan(left, right);
+                }
+            |
+                ">=" right = addExpression()
+                {
+                    left = ComparisonExpression.createGreaterThanEqual(left, right);
+                }
+            |
+                "<" right = addExpression()
+                {
+                    left = ComparisonExpression.createLessThan(left, right);
+                }
+            |
+                "<=" right = addExpression()
+                {
+                    left = ComparisonExpression.createLessThanEqual(left, right);
+                }
+           |
+                {
+                    u=null;
+                }
+                <LIKE> t = stringLitteral()
+                    [ <ESCAPE> u = stringLitteral() ]
+                {
+                    left = ComparisonExpression.createLike(left, t, u);
+                }
+           |
+                LOOKAHEAD(2)
+                {
+                    u=null;
+                }
+                <NOT> <LIKE> t = stringLitteral() [ <ESCAPE> u = stringLitteral() ]
+                {
+                    left = ComparisonExpression.createNotLike(left, t, u);
+                }
+            |
+                <BETWEEN> low = addExpression() <AND> high = addExpression()
+                {
+                    left = ComparisonExpression.createBetween(left, low, high);
+                }
+            |
+                LOOKAHEAD(2)
+                <NOT> <BETWEEN> low = addExpression() <AND> high = addExpression()
+                {
+                    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 addExpression() :
+{
+    Expression left;
+    Expression right;
+}
+{
+    left = multExpr()
+    (
+        LOOKAHEAD( ("+"|"-") multExpr())
+        (
+            "+" right = multExpr()
+            {
+                left = ArithmeticExpression.createPlus(left, right);
+            }
+            |
+            "-" right = multExpr()
+            {
+                left = ArithmeticExpression.createMinus(left, right);
+            }
+        )
+
+    )*
+    {
+        return left;
+    }
+}
+
+Expression multExpr() :
+{
+    Expression left;
+    Expression right;
+}
+{
+    left = unaryExpr()
+    (
+        "*" right = unaryExpr()
+        {
+            left = ArithmeticExpression.createMultiply(left, right);
+        }
+        |
+        "/" right = unaryExpr()
+        {
+            left = ArithmeticExpression.createDivide(left, right);
+        }
+        |
+        "%" right = unaryExpr()
+        {
+            left = ArithmeticExpression.createMod(left, right);
+        }
+
+    )*
+    {
+        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) );
+        }
+        |
+        <XPATH> s=stringLitteral()
+        {
+            left = UnaryExpression.createXPath( s );
+        }
+        |
+        <XQUERY> s=stringLitteral()
+        {
+            left = UnaryExpression.createXQuery( s );
+        }
+        |
+        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 = <HEX_LITERAL>
+            {
+                left = ConstantExpression.createFromHex(t.image);
+            }
+        )
+        |
+        (
+            t = <OCTAL_LITERAL>
+            {
+                left = ConstantExpression.createFromOctal(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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/javacc/StrictParser.jj
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/javacc/StrictParser.jj b/qpid-jms-selector/src/main/javacc/StrictParser.jj
new file mode 100755
index 0000000..dfe8dab
--- /dev/null
+++ b/qpid-jms-selector/src/main/javacc/StrictParser.jj
@@ -0,0 +1,563 @@
+/**
+ * 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.
+ */
+
+// ----------------------------------------------------------------------------
+// OPTIONS
+// ----------------------------------------------------------------------------
+options {
+  STATIC = false;
+  UNICODE_INPUT = true;
+  // some performance optimizations
+  ERROR_REPORTING = false;
+}
+
+// ----------------------------------------------------------------------------
+// PARSER
+// ----------------------------------------------------------------------------
+
+PARSER_BEGIN(StrictParser)
+/**
+ * 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.jms.selector.strict;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.qpid.jms.selector.filter.*;
+
+/**
+ * JMS Selector Parser generated by JavaCC
+ *
+ * Do not edit this .java file directly - it is autogenerated from StrictParser.jj
+ */
+public class StrictParser {
+    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(StrictParser)
+
+// ----------------------------------------------------------------------------
+// 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">
+  | <  LIKE    : "LIKE">
+  | <  ESCAPE  : "ESCAPE">
+  | <  IN      : "IN">
+  | <  IS      : "IS">
+  | <  TRUE    : "TRUE" >
+  | <  FALSE   : "FALSE" >
+  | <  NULL    : "NULL" >
+  | <  XPATH   : "XPATH" >
+  | <  XQUERY  : "XQUERY" >
+}
+
+/* Literals */
+TOKEN [IGNORE_CASE] :
+{
+
+    < DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* (["l","L"])? >
+  | < HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ >
+  | < OCTAL_LITERAL: "0" (["0"-"7"])* >
+  | < 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 = addExpression()
+        (
+
+                ">" right = addExpression()
+                {
+                    left = ComparisonExpression.createGreaterThan(left, right);
+                }
+            |
+                ">=" right = addExpression()
+                {
+                    left = ComparisonExpression.createGreaterThanEqual(left, right);
+                }
+            |
+                "<" right = addExpression()
+                {
+                    left = ComparisonExpression.createLessThan(left, right);
+                }
+            |
+                "<=" right = addExpression()
+                {
+                    left = ComparisonExpression.createLessThanEqual(left, right);
+                }
+           |
+                {
+                    u=null;
+                }
+                <LIKE> t = stringLitteral()
+                    [ <ESCAPE> u = stringLitteral() ]
+                {
+                    left = ComparisonExpression.createLike(left, t, u);
+                }
+           |
+                LOOKAHEAD(2)
+                {
+                    u=null;
+                }
+                <NOT> <LIKE> t = stringLitteral() [ <ESCAPE> u = stringLitteral() ]
+                {
+                    left = ComparisonExpression.createNotLike(left, t, u);
+                }
+            |
+                <BETWEEN> low = addExpression() <AND> high = addExpression()
+                {
+                    left = ComparisonExpression.createBetween(left, low, high);
+                }
+            |
+                LOOKAHEAD(2)
+                <NOT> <BETWEEN> low = addExpression() <AND> high = addExpression()
+                {
+                    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 addExpression() :
+{
+    Expression left;
+    Expression right;
+}
+{
+    left = multExpr()
+    (
+        LOOKAHEAD( ("+"|"-") multExpr())
+        (
+            "+" right = multExpr()
+            {
+                left = ArithmeticExpression.createPlus(left, right);
+            }
+            |
+            "-" right = multExpr()
+            {
+                left = ArithmeticExpression.createMinus(left, right);
+            }
+        )
+
+    )*
+    {
+        return left;
+    }
+}
+
+Expression multExpr() :
+{
+    Expression left;
+    Expression right;
+}
+{
+    left = unaryExpr()
+    (
+        "*" right = unaryExpr()
+        {
+            left = ArithmeticExpression.createMultiply(left, right);
+        }
+        |
+        "/" right = unaryExpr()
+        {
+            left = ArithmeticExpression.createDivide(left, right);
+        }
+        |
+        "%" right = unaryExpr()
+        {
+            left = ArithmeticExpression.createMod(left, right);
+        }
+
+    )*
+    {
+        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) );
+        }
+        |
+        <XPATH> s=stringLitteral()
+        {
+            left = UnaryExpression.createXPath( s );
+        }
+        |
+        <XQUERY> s=stringLitteral()
+        {
+            left = UnaryExpression.createXQuery( s );
+        }
+        |
+        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 = <HEX_LITERAL>
+            {
+                left = ConstantExpression.createFromHex(t.image);
+            }
+        )
+        |
+        (
+            t = <OCTAL_LITERAL>
+            {
+                left = ConstantExpression.createFromOctal(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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/test/java/org/apache/activemq/apollo/selector/SelectorParserTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/test/java/org/apache/activemq/apollo/selector/SelectorParserTest.java b/qpid-jms-selector/src/test/java/org/apache/activemq/apollo/selector/SelectorParserTest.java
new file mode 100755
index 0000000..e48fa4e
--- /dev/null
+++ b/qpid-jms-selector/src/test/java/org/apache/activemq/apollo/selector/SelectorParserTest.java
@@ -0,0 +1,74 @@
+/**
+ * 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.activemq.apollo.selector;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.jms.selector.SelectorParser;
+import org.apache.qpid.jms.selector.filter.BooleanExpression;
+import org.apache.qpid.jms.selector.filter.ComparisonExpression;
+import org.apache.qpid.jms.selector.filter.Expression;
+import org.apache.qpid.jms.selector.filter.LogicExpression;
+import org.apache.qpid.jms.selector.filter.PropertyExpression;
+import org.apache.qpid.jms.selector.filter.XPathExpression;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @version $Revision: 1.2 $
+ */
+public class SelectorParserTest extends TestCase {
+    private static final Logger LOG = LoggerFactory.getLogger(SelectorParserTest.class);
+
+    public void testParseXPath() throws Exception {
+        BooleanExpression filter = parse("XPATH '//title[@lang=''eng'']'");
+        assertTrue("Created XPath expression", filter instanceof XPathExpression);
+        LOG.info("Expression: " + filter);
+    }
+
+    public void testParseWithParensAround() throws Exception {
+        String[] values = {"x = 1 and y = 2", "(x = 1) and (y = 2)", "((x = 1) and (y = 2))"};
+
+        for (int i = 0; i < values.length; i++) {
+            String value = values[i];
+            LOG.info("Parsing: " + value);
+
+            BooleanExpression andExpression = parse(value);
+            assertTrue("Created LogicExpression expression", andExpression instanceof LogicExpression);
+            LogicExpression logicExpression = (LogicExpression)andExpression;
+            Expression left = logicExpression.getLeft();
+            Expression right = logicExpression.getRight();
+
+            assertTrue("Left is a binary filter", left instanceof ComparisonExpression);
+            assertTrue("Right is a binary filter", right instanceof ComparisonExpression);
+            ComparisonExpression leftCompare = (ComparisonExpression)left;
+            ComparisonExpression rightCompare = (ComparisonExpression)right;
+            assertPropertyExpression("left", leftCompare.getLeft(), "x");
+            assertPropertyExpression("right", rightCompare.getLeft(), "y");
+        }
+    }
+
+    protected void assertPropertyExpression(String message, Expression expression, String expected) {
+        assertTrue(message + ". Must be PropertyExpression", expression instanceof PropertyExpression);
+        PropertyExpression propExp = (PropertyExpression)expression;
+        assertEquals(message + ". Property name", expected, propExp.getName());
+    }
+
+    protected BooleanExpression parse(String text) throws Exception {
+        return SelectorParser.parse(text);
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/test/java/org/apache/activemq/apollo/selector/SelectorTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/test/java/org/apache/activemq/apollo/selector/SelectorTest.java b/qpid-jms-selector/src/test/java/org/apache/activemq/apollo/selector/SelectorTest.java
new file mode 100755
index 0000000..398548f
--- /dev/null
+++ b/qpid-jms-selector/src/test/java/org/apache/activemq/apollo/selector/SelectorTest.java
@@ -0,0 +1,484 @@
+/**
+ * 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.activemq.apollo.selector;
+
+import java.util.HashMap;
+
+import junit.framework.TestCase;
+
+import org.apache.qpid.jms.selector.SelectorParser;
+import org.apache.qpid.jms.selector.filter.BooleanExpression;
+import org.apache.qpid.jms.selector.filter.FilterException;
+import org.apache.qpid.jms.selector.filter.Filterable;
+
+/**
+ * @version $Revision: 1.7 $
+ */
+@SuppressWarnings("unchecked")
+public class SelectorTest extends TestCase {
+		
+    class MockMessage implements Filterable {
+
+    	HashMap<String, Object> properties = new HashMap<String, Object>();
+		private String text;
+		private Object destination;
+		private String messageId;
+		private String type;
+		private Object localConnectionId;
+    	
+		public void setDestination(Object destination) {
+			this.destination=destination;
+		}
+		public void setJMSMessageID(String messageId) {
+			this.messageId = messageId;
+		}
+		public void setJMSType(String type) {
+			this.type = type;
+		}
+		public void setText(String text) {
+			this.text = text;
+		}
+
+		public void setBooleanProperty(String key, boolean value) {
+			properties.put(key, value);
+		}
+
+		public void setStringProperty(String key, String value) {
+			properties.put(key, value);
+		}
+
+		public void setByteProperty(String key, byte value) {
+			properties.put(key, value);
+		}
+
+		public void setDoubleProperty(String key, double value) {
+			properties.put(key, value);
+		}
+
+		public void setFloatProperty(String key, float value) {
+			properties.put(key, value);
+		}
+
+		public void setLongProperty(String key, long value) {
+			properties.put(key, value);
+		}
+
+		public void setIntProperty(String key, int value) {
+			properties.put(key, value);
+		}
+
+		public void setShortProperty(String key, short value) {
+			properties.put(key, value);
+		}
+
+		public void setObjectProperty(String key, Object value) {
+			properties.put(key, value);
+		}
+
+		public <T> T getBodyAs(Class<T> type) throws FilterException {
+			if( type == String.class ) {
+				return type.cast(text);
+			}
+			return null;
+		}
+
+        public Object getProperty(String name) {
+            if( "JMSType".equals(name) ) {
+                return type;
+            }
+            if( "JMSMessageID".equals(name) ) {
+                return messageId;
+            }
+            return properties.get(name);
+        }
+
+        public <T> T getDestination() {
+			return (T)destination;
+		}
+
+		public Object getLocalConnectionId() {
+			return localConnectionId;
+		}
+
+    	
+    }
+
+
+    public void testBooleanSelector() throws Exception {
+        MockMessage message = createMessage();
+
+        assertSelector(message, "(trueProp OR falseProp) AND trueProp", true);
+        assertSelector(message, "(trueProp OR falseProp) AND falseProp", false);
+
+    }
+
+    public void testXPathSelectors() throws Exception {
+        MockMessage message = new MockMessage();
+
+        message.setJMSType("xml");
+        message.setText("<root><a key='first' num='1'/><b key='second' num='2'>b</b></root>");
+
+        assertSelector(message, "XPATH 'root/a'", true);
+        assertSelector(message, "XPATH '//root/b'", true);
+        assertSelector(message, "XPATH 'root/c'", false);
+        assertSelector(message, "XPATH '//root/b/text()=\"b\"'", true);
+        assertSelector(message, "XPATH '//root/b=\"b\"'", true);
+        assertSelector(message, "XPATH '//root/b=\"c\"'", false);
+        assertSelector(message, "XPATH '//root/b!=\"c\"'", true);
+        
+        assertSelector(message, "XPATH '//root/*[@key=''second'']'", true);
+        assertSelector(message, "XPATH '//root/*[@key=''third'']'", false);
+        assertSelector(message, "XPATH '//root/a[@key=''first'']'", true);
+        assertSelector(message, "XPATH '//root/a[@num=1]'", true);
+        assertSelector(message, "XPATH '//root/a[@key=''second'']'", false);
+        
+        assertSelector(message, "XPATH '/root/*[@key=''first'' or @key=''third'']'", true);
+        assertSelector(message, "XPATH '//root/*[@key=''third'' or @key=''forth'']'", false);
+
+        assertSelector(message, "XPATH '/root/b=''b'' and /root/b[@key=''second'']'", true);
+        assertSelector(message, "XPATH '/root/b=''b'' and /root/b[@key=''first'']'", false);
+        
+        assertSelector(message, "XPATH 'not(//root/a)'", false);
+        assertSelector(message, "XPATH 'not(//root/c)'", true);
+        assertSelector(message, "XPATH '//root/a[not(@key=''first'')]'", false);
+        assertSelector(message, "XPATH '//root/a[not(not(@key=''first''))]'", true);
+        
+        assertSelector(message, "XPATH 'string(//root/b)'", true);
+        assertSelector(message, "XPATH 'string(//root/a)'", false);
+        
+        assertSelector(message, "XPATH 'sum(//@num) < 10'", true);
+        assertSelector(message, "XPATH 'sum(//@num) > 10'", false);
+        
+        assertSelector(message, "XPATH '//root/a[@num > 1]'", false);
+        assertSelector(message, "XPATH '//root/b[@num > 1]'", true);  
+        
+    }
+
+    public void testJMSPropertySelectors() throws Exception {
+        MockMessage message = createMessage();
+        message.setJMSType("selector-test");
+        message.setJMSMessageID("id:test:1:1:1:1");
+
+        assertSelector(message, "JMSType = 'selector-test'", true);
+        assertSelector(message, "JMSType = 'crap'", false);
+
+        assertSelector(message, "JMSMessageID = 'id:test:1:1:1:1'", true);
+        assertSelector(message, "JMSMessageID = 'id:not-test:1:1:1:1'", false);
+
+        message = createMessage();
+        message.setJMSType("1001");
+
+        assertSelector(message, "JMSType='1001'", true);
+        assertSelector(message, "JMSType='1001' OR JMSType='1002'", true);
+        assertSelector(message, "JMSType = 'crap'", false);
+    }
+
+    public void testBasicSelectors() throws Exception {
+        MockMessage message = createMessage();
+
+        assertSelector(message, "name = 'James'", true);
+        assertSelector(message, "rank > 100", true);
+        assertSelector(message, "rank >= 123", true);
+        assertSelector(message, "rank >= 124", false);
+
+    }
+
+    public void testPropertyTypes() throws Exception {
+        MockMessage message = createMessage();
+        assertSelector(message, "byteProp = 123", true);
+        assertSelector(message, "byteProp = 10", false);
+        assertSelector(message, "byteProp2 = 33", true);
+        assertSelector(message, "byteProp2 = 10", false);
+
+        assertSelector(message, "shortProp = 123", true);
+        assertSelector(message, "shortProp = 10", false);
+
+        assertSelector(message, "shortProp = 123", true);
+        assertSelector(message, "shortProp = 10", false);
+
+        assertSelector(message, "intProp = 123", true);
+        assertSelector(message, "intProp = 10", false);
+
+        assertSelector(message, "longProp = 123", true);
+        assertSelector(message, "longProp = 10", false);
+
+        assertSelector(message, "floatProp = 123", true);
+        assertSelector(message, "floatProp = 10", false);
+
+        assertSelector(message, "doubleProp = 123", true);
+        assertSelector(message, "doubleProp = 10", false);
+    }
+
+    public void testAndSelectors() throws Exception {
+        MockMessage message = createMessage();
+
+        assertSelector(message, "name = 'James' and rank < 200", true);
+        assertSelector(message, "name = 'James' and rank > 200", false);
+        assertSelector(message, "name = 'Foo' and rank < 200", false);
+        assertSelector(message, "unknown = 'Foo' and anotherUnknown < 200", false);
+    }
+
+    public void testOrSelectors() throws Exception {
+        MockMessage message = createMessage();
+
+        assertSelector(message, "name = 'James' or rank < 200", true);
+        assertSelector(message, "name = 'James' or rank > 200", true);
+        assertSelector(message, "name = 'Foo' or rank < 200", true);
+        assertSelector(message, "name = 'Foo' or rank > 200", false);
+        assertSelector(message, "unknown = 'Foo' or anotherUnknown < 200", false);
+    }
+
+    public void testPlus() throws Exception {
+        MockMessage message = createMessage();
+
+        assertSelector(message, "rank + 2 = 125", true);
+        assertSelector(message, "(rank + 2) = 125", true);
+        assertSelector(message, "125 = (rank + 2)", true);
+        assertSelector(message, "rank + version = 125", true);
+        assertSelector(message, "rank + 2 < 124", false);
+        assertSelector(message, "name + '!' = 'James!'", true);
+    }
+
+    public void testMinus() throws Exception {
+        MockMessage message = createMessage();
+
+        assertSelector(message, "rank - 2 = 121", true);
+        assertSelector(message, "rank - version = 121", true);
+        assertSelector(message, "rank - 2 > 122", false);
+    }
+
+    public void testMultiply() throws Exception {
+        MockMessage message = createMessage();
+
+        assertSelector(message, "rank * 2 = 246", true);
+        assertSelector(message, "rank * version = 246", true);
+        assertSelector(message, "rank * 2 < 130", false);
+    }
+
+    public void testDivide() throws Exception {
+        MockMessage message = createMessage();
+
+        assertSelector(message, "rank / version = 61.5", true);
+        assertSelector(message, "rank / 3 > 100.0", false);
+        assertSelector(message, "rank / 3 > 100", false);
+        assertSelector(message, "version / 2 = 1", true);
+
+    }
+
+    public void testBetween() throws Exception {
+        MockMessage message = createMessage();
+
+        assertSelector(message, "rank between 100 and 150", true);
+        assertSelector(message, "rank between 10 and 120", false);
+    }
+
+    public void testIn() throws Exception {
+        MockMessage message = createMessage();
+
+        assertSelector(message, "name in ('James', 'Bob', 'Gromit')", true);
+        assertSelector(message, "name in ('Bob', 'James', 'Gromit')", true);
+        assertSelector(message, "name in ('Gromit', 'Bob', 'James')", true);
+
+        assertSelector(message, "name in ('Gromit', 'Bob', 'Cheddar')", false);
+        assertSelector(message, "name not in ('Gromit', 'Bob', 'Cheddar')", true);
+    }
+
+    public void testIsNull() throws Exception {
+        MockMessage message = createMessage();
+
+        assertSelector(message, "dummy is null", true);
+        assertSelector(message, "dummy is not null", false);
+        assertSelector(message, "name is not null", true);
+        assertSelector(message, "name is null", false);
+    }
+
+    public void testLike() throws Exception {
+        MockMessage message = createMessage();
+        message.setStringProperty("modelClassId", "com.whatever.something.foo.bar");
+        message.setStringProperty("modelInstanceId", "170");
+        message.setStringProperty("modelRequestError", "abc");
+        message.setStringProperty("modelCorrelatedClientId", "whatever");
+
+        assertSelector(message, "modelClassId LIKE 'com.whatever.something.%' AND modelInstanceId = '170' AND (modelRequestError IS NULL OR modelCorrelatedClientId = 'whatever')",
+                       true);
+
+        message.setStringProperty("modelCorrelatedClientId", "shouldFailNow");
+
+        assertSelector(message, "modelClassId LIKE 'com.whatever.something.%' AND modelInstanceId = '170' AND (modelRequestError IS NULL OR modelCorrelatedClientId = 'whatever')",
+                       false);
+
+        message = createMessage();
+        message.setStringProperty("modelClassId", "com.whatever.something.foo.bar");
+        message.setStringProperty("modelInstanceId", "170");
+        message.setStringProperty("modelCorrelatedClientId", "shouldNotMatch");
+
+        assertSelector(message, "modelClassId LIKE 'com.whatever.something.%' AND modelInstanceId = '170' AND (modelRequestError IS NULL OR modelCorrelatedClientId = 'whatever')",
+                       true);
+    }
+
+    /**
+     * Test cases from Mats Henricson
+     */
+    public void testMatsHenricsonUseCases() throws Exception {
+        MockMessage message = createMessage();
+        assertSelector(message, "SessionserverId=1870414179", false);
+
+        message.setLongProperty("SessionserverId", 1870414179);
+        assertSelector(message, "SessionserverId=1870414179", true);
+
+        message.setLongProperty("SessionserverId", 1234);
+        assertSelector(message, "SessionserverId=1870414179", false);
+
+        assertSelector(message, "Command NOT IN ('MirrorLobbyRequest', 'MirrorLobbyReply')", false);
+
+        message.setStringProperty("Command", "Cheese");
+        assertSelector(message, "Command NOT IN ('MirrorLobbyRequest', 'MirrorLobbyReply')", true);
+
+        message.setStringProperty("Command", "MirrorLobbyRequest");
+        assertSelector(message, "Command NOT IN ('MirrorLobbyRequest', 'MirrorLobbyReply')", false);
+        message.setStringProperty("Command", "MirrorLobbyReply");
+        assertSelector(message, "Command NOT IN ('MirrorLobbyRequest', 'MirrorLobbyReply')", false);
+    }
+
+    public void testFloatComparisons() throws Exception {
+        MockMessage message = createMessage();
+
+        // JMS 1.1 Section 3.8.1.1 : Approximate literals use the Java
+        // floating-point literal syntax.
+        // We will use the java varible x to demo valid floating point syntaxs.
+        double x;
+
+        // test decimals like x.x
+        x = 1.0;
+        x = -1.1;
+        x = 1.0E1;
+        x = 1.1E1;
+        x = -1.1E1;
+        assertSelector(message, "1.0 < 1.1", true);
+        assertSelector(message, "-1.1 < 1.0", true);
+        assertSelector(message, "1.0E1 < 1.1E1", true);
+        assertSelector(message, "-1.1E1 < 1.0E1", true);
+
+        // test decimals like x.
+        x = 1.;
+        x = 1.E1;
+        assertSelector(message, "1. < 1.1", true);
+        assertSelector(message, "-1.1 < 1.", true);
+        assertSelector(message, "1.E1 < 1.1E1", true);
+        assertSelector(message, "-1.1E1 < 1.E1", true);
+
+        // test decimals like .x
+        x = .5;
+        x = -.5;
+        x = .5E1;
+        assertSelector(message, ".1 < .5", true);
+        assertSelector(message, "-.5 < .1", true);
+        assertSelector(message, ".1E1 < .5E1", true);
+        assertSelector(message, "-.5E1 < .1E1", true);
+
+        // test exponents
+        x = 4E10;
+        x = -4E10;
+        x = 5E+10;
+        x = 5E-10;
+        assertSelector(message, "4E10 < 5E10", true);
+        assertSelector(message, "5E8 < 5E10", true);
+        assertSelector(message, "-4E10 < 2E10", true);
+        assertSelector(message, "-5E8 < 2E2", true);
+        assertSelector(message, "4E+10 < 5E+10", true);
+        assertSelector(message, "4E-10 < 5E-10", true);
+    }
+
+    public void testStringQuoteParsing() throws Exception {
+        MockMessage message = createMessage();
+        assertSelector(message, "quote = '''In God We Trust'''", true);
+    }
+
+    public void testLikeComparisons() throws Exception {
+        MockMessage message = createMessage();
+
+        assertSelector(message, "quote LIKE '''In G_d We Trust'''", true);
+        assertSelector(message, "quote LIKE '''In Gd_ We Trust'''", false);
+        assertSelector(message, "quote NOT LIKE '''In G_d We Trust'''", false);
+        assertSelector(message, "quote NOT LIKE '''In Gd_ We Trust'''", true);
+
+        assertSelector(message, "foo LIKE '%oo'", true);
+        assertSelector(message, "foo LIKE '%ar'", false);
+        assertSelector(message, "foo NOT LIKE '%oo'", false);
+        assertSelector(message, "foo NOT LIKE '%ar'", true);
+
+        assertSelector(message, "foo LIKE '!_%' ESCAPE '!'", true);
+        assertSelector(message, "quote LIKE '!_%' ESCAPE '!'", false);
+        assertSelector(message, "foo NOT LIKE '!_%' ESCAPE '!'", false);
+        assertSelector(message, "quote NOT LIKE '!_%' ESCAPE '!'", true);
+
+        assertSelector(message, "punctuation LIKE '!#$&()*+,-./:;<=>?@[\\]^`{|}~'", true);
+    }
+
+    public void testInvalidSelector() throws Exception {
+        MockMessage message = createMessage();
+        assertInvalidSelector(message, "3+5");
+        assertInvalidSelector(message, "True AND 3+5");
+        assertInvalidSelector(message, "=TEST 'test'");
+    }
+
+    protected MockMessage createMessage() {
+        MockMessage message = createMessage("FOO.BAR");
+        message.setJMSType("selector-test");
+        message.setJMSMessageID("connection:1:1:1:1");
+        message.setObjectProperty("name", "James");
+        message.setObjectProperty("location", "London");
+
+        message.setByteProperty("byteProp", (byte)123);
+        message.setByteProperty("byteProp2", (byte)33);
+        message.setShortProperty("shortProp", (short)123);
+        message.setIntProperty("intProp", (int)123);
+        message.setLongProperty("longProp", (long)123);
+        message.setFloatProperty("floatProp", (float)123);
+        message.setDoubleProperty("doubleProp", (double)123);
+
+        message.setIntProperty("rank", 123);
+        message.setIntProperty("version", 2);
+        message.setStringProperty("quote", "'In God We Trust'");
+        message.setStringProperty("foo", "_foo");
+        message.setStringProperty("punctuation", "!#$&()*+,-./:;<=>?@[\\]^`{|}~");
+        message.setBooleanProperty("trueProp", true);
+        message.setBooleanProperty("falseProp", false);
+        return message;
+    }
+
+    protected void assertInvalidSelector(MockMessage message, String text) {
+        try {
+            SelectorParser.parse(text);
+            fail("Created a valid selector");
+        } catch (FilterException e) {
+        }
+    }
+    
+    protected void assertSelector(MockMessage message, String text, boolean expected) throws FilterException {
+        BooleanExpression selector = SelectorParser.parse(text);
+        assertTrue("Created a valid selector", selector != null);
+        boolean value = selector.matches(message);
+        assertEquals("Selector for: " + text, expected, value);
+    }
+
+    protected MockMessage createMessage(String subject) {
+    	MockMessage message = new MockMessage();
+        message.setDestination(subject);
+        return message;
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/test/resources/log4j.properties b/qpid-jms-selector/src/test/resources/log4j.properties
new file mode 100755
index 0000000..8df5a9e
--- /dev/null
+++ b/qpid-jms-selector/src/test/resources/log4j.properties
@@ -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.
+## ---------------------------------------------------------------------------
+
+#
+# The logging properties used during tests..
+#
+log4j.rootLogger=INFO, out, stdout
+
+log4j.logger.org.apache.qpid.jms=DEBUG
+
+# Tune the TestPeer as needed for debugging.
+log4j.logger.org.apache.qpid.jms.test.testpeer=TRACE
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] - %-5p %-30.30c{1} - %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] - %-5p %-30.30c{1} - %m%n
+log4j.appender.out.file=target/activemq-test.log
+log4j.appender.out.append=true


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[2/2] git commit: Create our own selector parser lib based on apollo-selector

Posted by ta...@apache.org.
Create our own selector parser lib based on apollo-selector

Project: http://git-wip-us.apache.org/repos/asf/qpid-jms/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-jms/commit/a5fe3018
Tree: http://git-wip-us.apache.org/repos/asf/qpid-jms/tree/a5fe3018
Diff: http://git-wip-us.apache.org/repos/asf/qpid-jms/diff/a5fe3018

Branch: refs/heads/master
Commit: a5fe3018d12021f4b61cc63e791e70bf63910361
Parents: 4c17e5e
Author: Timothy Bish <ta...@gmail.com>
Authored: Fri Sep 26 15:52:05 2014 -0400
Committer: Timothy Bish <ta...@gmail.com>
Committed: Fri Sep 26 15:52:05 2014 -0400

----------------------------------------------------------------------
 pom.xml                                         |  31 +-
 qpid-jms-client/pom.xml                         |   6 +-
 .../java/org/apache/qpid/jms/JmsSession.java    |   6 +-
 .../qpid-jms-activemq-tests/pom.xml             |  10 +-
 qpid-jms-selector/pom.xml                       | 129 +++++
 .../org/apache/qpid/jms/selector/LRUCache.java  |  91 +++
 .../qpid/jms/selector/SelectorParser.java       | 100 ++++
 .../selector/filter/ArithmeticExpression.java   | 204 +++++++
 .../jms/selector/filter/BinaryExpression.java   |  96 ++++
 .../jms/selector/filter/BooleanExpression.java  |  36 ++
 .../selector/filter/ComparisonExpression.java   | 478 ++++++++++++++++
 .../jms/selector/filter/ConstantExpression.java | 163 ++++++
 .../qpid/jms/selector/filter/Expression.java    |  34 ++
 .../jms/selector/filter/FilterException.java    |  40 ++
 .../qpid/jms/selector/filter/Filterable.java    |  53 ++
 .../jms/selector/filter/LogicExpression.java    |  88 +++
 .../jms/selector/filter/PropertyExpression.java |  67 +++
 .../jms/selector/filter/UnaryExpression.java    | 256 +++++++++
 .../jms/selector/filter/XPathExpression.java    |  75 +++
 .../jms/selector/filter/XQueryExpression.java   |  49 ++
 .../selector/filter/XalanXPathEvaluator.java    | 116 ++++
 .../qpid/jms/selector/filter/package.html       |  27 +
 .../src/main/javacc/HyphenatedParser.jj         | 563 +++++++++++++++++++
 .../src/main/javacc/StrictParser.jj             | 563 +++++++++++++++++++
 .../apollo/selector/SelectorParserTest.java     |  74 +++
 .../activemq/apollo/selector/SelectorTest.java  | 484 ++++++++++++++++
 .../src/test/resources/log4j.properties         |  38 ++
 27 files changed, 3859 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 2035569..3918759 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,8 +14,7 @@
   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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+--><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <parent>
     <groupId>org.apache</groupId>
     <artifactId>apache</artifactId>
@@ -48,15 +47,16 @@
     <slf4j-version>1.7.7</slf4j-version>
     <hawtbuf-version>1.11</hawtbuf-version>
     <activemq-version>5.11-SNAPSHOT</activemq-version>
-    <apollo-version>1.7</apollo-version>
     <proton-version>1.0-SNAPSHOT</proton-version>
     <fuse-joram-tests-version>1.0</fuse-joram-tests-version>
     <jetty-version>8.1.15.v20140411</jetty-version>
     <vertex-version>2.0.2-final</vertex-version>
     <mockito-version>1.9.5</mockito-version>
     <hamcrest-version>1.3</hamcrest-version>
+    <xalan-version>2.6.0</xalan-version>
 
     <!-- Maven Plugin Version for this Project -->
+    <maven-javacc-plugin-version>2.6</maven-javacc-plugin-version>
     <maven-surefire-plugin-version>2.16</maven-surefire-plugin-version>
     <maven-assembly-plugin-version>2.4</maven-assembly-plugin-version>
     <maven-release-plugin-version>2.4.1</maven-release-plugin-version>
@@ -64,6 +64,7 @@
     <maven-idea-plugin-version>2.5</maven-idea-plugin-version>
     <maven-compiler-plugin-version>3.1</maven-compiler-plugin-version>
     <findbugs-maven-plugin-version>3.0.0</findbugs-maven-plugin-version>
+    <build-helper-maven-plugin-version>1.8</build-helper-maven-plugin-version>
 
     <!-- Test properties -->
     <maven.test.redirectTestOutputToFile>true</maven.test.redirectTestOutputToFile>
@@ -90,6 +91,7 @@
     <module>qpid-jms-discovery</module>
     <module>qpid-jms-interop-tests</module>
     <module>qpid-jms-examples</module>
+    <module>qpid-jms-selector</module>
   </modules>
 
   <dependencyManagement>
@@ -109,6 +111,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.qpid</groupId>
+        <artifactId>qpid-jms-selector</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.qpid</groupId>
         <artifactId>proton-jms</artifactId>
         <version>${proton-version}</version>
       </dependency>
@@ -156,9 +163,9 @@
         <version>${hawtbuf-version}</version>
       </dependency>
       <dependency>
-        <groupId>org.apache.activemq</groupId>
-        <artifactId>apollo-selector</artifactId>
-        <version>${apollo-version}</version>
+        <groupId>xalan</groupId>
+        <artifactId>xalan</artifactId>
+        <version>${xalan-version}</version>
       </dependency>
       <dependency>
         <groupId>io.vertx</groupId>
@@ -217,6 +224,16 @@
           <artifactId>maven-compiler-plugin</artifactId>
           <version>${maven-compiler-plugin-version}</version>
         </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>javacc-maven-plugin</artifactId>
+          <version>${maven-javacc-plugin-version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>build-helper-maven-plugin</artifactId>
+          <version>${build-helper-maven-plugin-version}</version>
+        </plugin>
       </plugins>
     </pluginManagement>
     <plugins>
@@ -234,4 +251,4 @@
       </plugin>
     </plugins>
   </build>
-</project>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-client/pom.xml
----------------------------------------------------------------------
diff --git a/qpid-jms-client/pom.xml b/qpid-jms-client/pom.xml
index 15820ce..85f5b99 100644
--- a/qpid-jms-client/pom.xml
+++ b/qpid-jms-client/pom.xml
@@ -30,7 +30,7 @@
 
   <dependencies>
     <!-- =================================== -->
-    <!-- Required Dependencies               -->
+    <!-- Client Dependencies               -->
     <!-- =================================== -->
     <dependency>
       <groupId>org.slf4j</groupId>
@@ -45,8 +45,8 @@
       <artifactId>hawtbuf</artifactId>
     </dependency>
     <dependency>
-      <groupId>org.apache.activemq</groupId>
-      <artifactId>apollo-selector</artifactId>
+      <groupId>org.apache.qpid</groupId>
+      <artifactId>qpid-jms-selector</artifactId>
       <optional>true</optional>
     </dependency>
     <dependency>

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java
index 0c8343c..3dbbaf2 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java
@@ -59,8 +59,6 @@ import javax.jms.TopicPublisher;
 import javax.jms.TopicSession;
 import javax.jms.TopicSubscriber;
 
-import org.apache.activemq.apollo.filter.FilterException;
-import org.apache.activemq.apollo.selector.SelectorParser;
 import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
 import org.apache.qpid.jms.message.JmsMessage;
 import org.apache.qpid.jms.message.JmsMessageFactory;
@@ -71,8 +69,10 @@ import org.apache.qpid.jms.meta.JmsProducerId;
 import org.apache.qpid.jms.meta.JmsSessionId;
 import org.apache.qpid.jms.meta.JmsSessionInfo;
 import org.apache.qpid.jms.provider.Provider;
-import org.apache.qpid.jms.provider.ProviderFuture;
 import org.apache.qpid.jms.provider.ProviderConstants.ACK_TYPE;
+import org.apache.qpid.jms.provider.ProviderFuture;
+import org.apache.qpid.jms.selector.SelectorParser;
+import org.apache.qpid.jms.selector.filter.FilterException;
 
 /**
  * JMS Session implementation

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-interop-tests/qpid-jms-activemq-tests/pom.xml
----------------------------------------------------------------------
diff --git a/qpid-jms-interop-tests/qpid-jms-activemq-tests/pom.xml b/qpid-jms-interop-tests/qpid-jms-activemq-tests/pom.xml
index 0087cd0..afb51e3 100644
--- a/qpid-jms-interop-tests/qpid-jms-activemq-tests/pom.xml
+++ b/qpid-jms-interop-tests/qpid-jms-activemq-tests/pom.xml
@@ -43,16 +43,16 @@
       <groupId>org.apache.qpid</groupId>
       <artifactId>qpid-jms-discovery</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.qpid</groupId>
+      <artifactId>qpid-jms-selector</artifactId>
+      <optional>true</optional>
+    </dependency>
 
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.apache.activemq</groupId>
-      <artifactId>apollo-selector</artifactId>
-      <optional>true</optional>
-    </dependency>
 
     <!-- =================================== -->
     <!-- Testing Dependencies                -->

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/pom.xml
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/pom.xml b/qpid-jms-selector/pom.xml
new file mode 100644
index 0000000..94b8b4d
--- /dev/null
+++ b/qpid-jms-selector/pom.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.qpid</groupId>
+    <artifactId>qpid-jms-parent</artifactId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+
+  <packaging>jar</packaging>
+  <artifactId>qpid-jms-selector</artifactId>
+  <name>QpidJMS Selector</name>
+  <description>The Qpid JMS optional message selector validation library</description>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>xalan</groupId>
+      <artifactId>xalan</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>javacc-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>generate-sources</phase>
+            <configuration>
+              <sourceDirectory>${basedir}/src/main/javacc</sourceDirectory>
+              <outputDirectory>${basedir}/target/generated-sources/javacc</outputDirectory>
+            </configuration>
+            <goals>
+              <goal>javacc</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>add-source</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>add-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <source>${basedir}/target/generated-sources/javacc</source>
+              </sources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+    <pluginManagement>
+      <plugins>
+        <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence
+          on the Maven build itself. -->
+        <plugin>
+          <groupId>org.eclipse.m2e</groupId>
+          <artifactId>lifecycle-mapping</artifactId>
+          <version>1.0.0</version>
+          <configuration>
+            <lifecycleMappingMetadata>
+              <pluginExecutions>
+                <pluginExecution>
+                  <pluginExecutionFilter>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>
+                      javacc-maven-plugin
+                    </artifactId>
+                    <versionRange>[2.6,)</versionRange>
+                    <goals>
+                      <goal>javacc</goal>
+                    </goals>
+                  </pluginExecutionFilter>
+                  <action>
+                    <execute>
+                      <runOnIncremental>true</runOnIncremental>
+                    </execute>
+                  </action>
+                </pluginExecution>
+              </pluginExecutions>
+            </lifecycleMappingMetadata>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/LRUCache.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/LRUCache.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/LRUCache.java
new file mode 100755
index 0000000..6e9858d
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/LRUCache.java
@@ -0,0 +1,91 @@
+/**
+ * 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.jms.selector;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * A Simple LRU Cache
+ * 
+ * @param <K>
+ * @param <V>
+ */
+
+public class LRUCache<K, V> extends LinkedHashMap<K, V> {
+    private static final long serialVersionUID = -342098639681884413L;
+    protected int maxCacheSize = 10000;
+
+    /**
+     * Default constructor for an LRU Cache The default capacity is 10000
+     */
+    public LRUCache() {
+        this(0,10000, 0.75f, true);
+    }
+
+    /**
+     * Constructs a LRUCache with a maximum capacity
+     * 
+     * @param maximumCacheSize
+     */
+    public LRUCache(int maximumCacheSize) {
+        this(0, maximumCacheSize, 0.75f, true);
+    }
+
+    /**
+     * Constructs an empty <tt>LRUCache</tt> instance with the specified
+     * initial capacity, maximumCacheSize,load factor and ordering mode.
+     * 
+     * @param initialCapacity the initial capacity.
+     * @param maximumCacheSize
+     * @param loadFactor the load factor.
+     * @param accessOrder the ordering mode - <tt>true</tt> for access-order,
+     *                <tt>false</tt> for insertion-order.
+     * @throws IllegalArgumentException if the initial capacity is negative or
+     *                 the load factor is non-positive.
+     */
+
+    public LRUCache(int initialCapacity, int maximumCacheSize, float loadFactor, boolean accessOrder) {
+        super(initialCapacity, loadFactor, accessOrder);
+        this.maxCacheSize = maximumCacheSize;
+    }
+
+    /**
+     * @return Returns the maxCacheSize.
+     */
+    public int getMaxCacheSize() {
+        return maxCacheSize;
+    }
+
+    /**
+     * @param maxCacheSize The maxCacheSize to set.
+     */
+    public void setMaxCacheSize(int maxCacheSize) {
+        this.maxCacheSize = maxCacheSize;
+    }
+
+    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
+        if( size() > maxCacheSize ) {
+            onCacheEviction(eldest);
+            return true;
+        }
+        return false;
+    }
+
+    protected void onCacheEviction(Map.Entry<K,V> eldest) {
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/SelectorParser.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/SelectorParser.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/SelectorParser.java
new file mode 100644
index 0000000..9e68112
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/SelectorParser.java
@@ -0,0 +1,100 @@
+/**
+ * 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.jms.selector;
+
+import org.apache.qpid.jms.selector.filter.BooleanExpression;
+import org.apache.qpid.jms.selector.filter.ComparisonExpression;
+import org.apache.qpid.jms.selector.filter.FilterException;
+import org.apache.qpid.jms.selector.hyphenated.HyphenatedParser;
+import org.apache.qpid.jms.selector.strict.StrictParser;
+
+import java.io.StringReader;
+
+/**
+ */
+public class SelectorParser {
+
+    private static final LRUCache cache = new LRUCache(100);
+    private static final String CONVERT_STRING_EXPRESSIONS_PREFIX = "convert_string_expressions:";
+    private static final String HYPHENATED_PROPS_PREFIX = "hyphenated_props:";
+    private static final String NO_CONVERT_STRING_EXPRESSIONS_PREFIX = "no_convert_string_expressions:";
+    private static final String NO_HYPHENATED_PROPS_PREFIX = "no_hyphenated_props:";
+
+    public static BooleanExpression parse(String sql) throws FilterException {
+        Object result = cache.get(sql);
+        if (result instanceof FilterException) {
+            throw (FilterException) result;
+        } else if (result instanceof BooleanExpression) {
+            return (BooleanExpression) result;
+        } else {
+            String actual = sql;
+            boolean convertStringExpressions = false;
+            boolean hyphenatedProps=false;
+            while(true) {
+              if( actual.startsWith(CONVERT_STRING_EXPRESSIONS_PREFIX)) {
+                  convertStringExpressions = true;
+                  actual = actual.substring(CONVERT_STRING_EXPRESSIONS_PREFIX.length());
+                  continue;
+              }
+              if( actual.startsWith(HYPHENATED_PROPS_PREFIX)) {
+                  hyphenatedProps = true;
+                  actual = actual.substring(HYPHENATED_PROPS_PREFIX.length());
+                  continue;
+              }
+              if( actual.startsWith(NO_CONVERT_STRING_EXPRESSIONS_PREFIX)) {
+                  convertStringExpressions = false;
+                  actual = actual.substring(NO_CONVERT_STRING_EXPRESSIONS_PREFIX.length());
+                  continue;
+              }
+              if( actual.startsWith(NO_HYPHENATED_PROPS_PREFIX)) {
+                  hyphenatedProps = false;
+                  actual = actual.substring(NO_HYPHENATED_PROPS_PREFIX.length());
+                  continue;
+              }
+              break;
+            }
+
+            if( convertStringExpressions ) {
+                ComparisonExpression.CONVERT_STRING_EXPRESSIONS.set(true);
+            }
+            try {
+                BooleanExpression e = null;
+                if( hyphenatedProps ) {
+                    HyphenatedParser parser = new HyphenatedParser(new StringReader(actual));
+                    e = parser.JmsSelector();
+                } else {
+                    StrictParser parser = new StrictParser(new StringReader(actual));
+                    e = parser.JmsSelector();
+                }
+                cache.put(sql, e);
+                return e;
+            } catch (Throwable e) {
+                FilterException fe = new FilterException(actual, e);
+                cache.put(sql, fe);
+                throw fe;
+            } finally {
+                if( convertStringExpressions ) {
+                    ComparisonExpression.CONVERT_STRING_EXPRESSIONS.remove();
+                }
+            }
+        }
+    }
+
+    public static void clearCache() {
+        cache.clear();
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/ArithmeticExpression.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/ArithmeticExpression.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/ArithmeticExpression.java
new file mode 100755
index 0000000..a4700c6
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/ArithmeticExpression.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.
+ */
+package org.apache.qpid.jms.selector.filter;
+
+
+/**
+ * An expression which performs an operation on two expression values
+ * 
+ * @version $Revision: 1.2 $
+ */
+public abstract class ArithmeticExpression extends BinaryExpression {
+
+    protected static final int INTEGER = 1;
+    protected static final int LONG = 2;
+    protected static final int DOUBLE = 3;
+    boolean convertStringExpressions = false;
+
+    /**
+     * @param left
+     * @param right
+     */
+    public ArithmeticExpression(Expression left, Expression right) {
+        super(left, right);
+        convertStringExpressions = ComparisonExpression.CONVERT_STRING_EXPRESSIONS.get()!=null;
+    }
+
+    public static Expression createPlus(Expression left, Expression right) {
+        return new ArithmeticExpression(left, right) {
+            protected Object evaluate(Object lvalue, Object rvalue) {
+                if (lvalue instanceof String) {
+                    String text = (String)lvalue;
+                    String answer = text + rvalue;
+                    return answer;
+                } else {
+                    return plus(asNumber(lvalue), asNumber(rvalue));
+                }
+            }
+
+            public String getExpressionSymbol() {
+                return "+";
+            }
+        };
+    }
+
+    public static Expression createMinus(Expression left, Expression right) {
+        return new ArithmeticExpression(left, right) {
+            protected Object evaluate(Object lvalue, Object rvalue) {
+                return minus(asNumber(lvalue), asNumber(rvalue));
+            }
+
+            public String getExpressionSymbol() {
+                return "-";
+            }
+        };
+    }
+
+    public static Expression createMultiply(Expression left, Expression right) {
+        return new ArithmeticExpression(left, right) {
+
+            protected Object evaluate(Object lvalue, Object rvalue) {
+                return multiply(asNumber(lvalue), asNumber(rvalue));
+            }
+
+            public String getExpressionSymbol() {
+                return "*";
+            }
+        };
+    }
+
+    public static Expression createDivide(Expression left, Expression right) {
+        return new ArithmeticExpression(left, right) {
+
+            protected Object evaluate(Object lvalue, Object rvalue) {
+                return divide(asNumber(lvalue), asNumber(rvalue));
+            }
+
+            public String getExpressionSymbol() {
+                return "/";
+            }
+        };
+    }
+
+    public static Expression createMod(Expression left, Expression right) {
+        return new ArithmeticExpression(left, right) {
+
+            protected Object evaluate(Object lvalue, Object rvalue) {
+                return mod(asNumber(lvalue), asNumber(rvalue));
+            }
+
+            public String getExpressionSymbol() {
+                return "%";
+            }
+        };
+    }
+
+    protected Number plus(Number left, Number right) {
+        switch (numberType(left, right)) {
+        case INTEGER:
+            return new Integer(left.intValue() + right.intValue());
+        case LONG:
+            return new Long(left.longValue() + right.longValue());
+        default:
+            return new Double(left.doubleValue() + right.doubleValue());
+        }
+    }
+
+    protected Number minus(Number left, Number right) {
+        switch (numberType(left, right)) {
+        case INTEGER:
+            return new Integer(left.intValue() - right.intValue());
+        case LONG:
+            return new Long(left.longValue() - right.longValue());
+        default:
+            return new Double(left.doubleValue() - right.doubleValue());
+        }
+    }
+
+    protected Number multiply(Number left, Number right) {
+        switch (numberType(left, right)) {
+        case INTEGER:
+            return new Integer(left.intValue() * right.intValue());
+        case LONG:
+            return new Long(left.longValue() * right.longValue());
+        default:
+            return new Double(left.doubleValue() * right.doubleValue());
+        }
+    }
+
+    protected Number divide(Number left, Number right) {
+        return new Double(left.doubleValue() / right.doubleValue());
+    }
+
+    protected Number mod(Number left, Number right) {
+        return new Double(left.doubleValue() % right.doubleValue());
+    }
+
+    private int numberType(Number left, Number right) {
+        if (isDouble(left) || isDouble(right)) {
+            return DOUBLE;
+        } else if (left instanceof Long || right instanceof Long) {
+            return LONG;
+        } else {
+            return INTEGER;
+        }
+    }
+
+    private boolean isDouble(Number n) {
+        return n instanceof Float || n instanceof Double;
+    }
+
+    protected Number asNumber(Object value) {
+        if (value instanceof Number) {
+            return (Number)value;
+        } else {
+            if( convertStringExpressions && value instanceof String) {
+                String v = (String) value;
+                try {
+                    if( v.contains(".") ) {
+                        return new Double(v);
+                    } else {
+                        return new Long(v);
+                    }
+                } catch (NumberFormatException e) {
+                    throw new RuntimeException("Cannot convert value: " + value + " into a number");
+                }
+            }
+            throw new RuntimeException("Cannot convert value: " + value + " into a number");
+        }
+    }
+
+    public Object evaluate(Filterable message) throws FilterException {
+        Object lvalue = left.evaluate(message);
+        if (lvalue == null) {
+            return null;
+        }
+        Object rvalue = right.evaluate(message);
+        if (rvalue == null) {
+            return null;
+        }
+        return evaluate(lvalue, rvalue);
+    }
+
+    /**
+     * @param lvalue
+     * @param rvalue
+     * @return
+     */
+    protected abstract Object evaluate(Object lvalue, Object rvalue);
+
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/BinaryExpression.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/BinaryExpression.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/BinaryExpression.java
new file mode 100755
index 0000000..2bb1a95
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/BinaryExpression.java
@@ -0,0 +1,96 @@
+/**
+ * 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.jms.selector.filter;
+
+
+
+/**
+ * An expression which performs an operation on two expression values.
+ * 
+ * @version $Revision: 1.2 $
+ */
+public abstract class BinaryExpression implements Expression {
+    protected Expression left;
+    protected Expression right;
+
+    public BinaryExpression(Expression left, Expression right) {
+        this.left = left;
+        this.right = right;
+    }
+
+    public Expression getLeft() {
+        return left;
+    }
+
+    public Expression getRight() {
+        return right;
+    }
+
+
+    /**
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        return "(" + left.toString() + " " + getExpressionSymbol() + " " + right.toString() + ")";
+    }
+
+    /**
+     * TODO: more efficient hashCode()
+     *
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        return toString().hashCode();
+    }
+
+    /**
+     * TODO: more efficient hashCode()
+     *
+     * @see java.lang.Object#equals(java.lang.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();
+
+    /**
+     * @param expression
+     */
+    public void setRight(Expression expression) {
+        right = expression;
+    }
+
+    /**
+     * @param expression
+     */
+    public void setLeft(Expression expression) {
+        left = expression;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/BooleanExpression.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/BooleanExpression.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/BooleanExpression.java
new file mode 100755
index 0000000..3225c56
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/BooleanExpression.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.qpid.jms.selector.filter;
+
+
+
+/**
+ * A BooleanExpression is an expression that always
+ * produces a Boolean result.
+ *
+ * @version $Revision: 1.2 $
+ */
+public interface BooleanExpression extends Expression {
+    
+    /**
+     * @param message
+     * @return true if the expression evaluates to Boolean.TRUE.
+     * @throws FilterException
+     */
+    boolean matches(Filterable message) throws FilterException;
+
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/ComparisonExpression.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/ComparisonExpression.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/ComparisonExpression.java
new file mode 100755
index 0000000..4179943
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/ComparisonExpression.java
@@ -0,0 +1,478 @@
+/**
+ * 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.jms.selector.filter;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+
+/**
+ * A filter performing a comparison of two objects
+ * 
+ * @version $Revision: 1.2 $
+ */
+public abstract class ComparisonExpression extends BinaryExpression implements BooleanExpression {
+
+    public static final ThreadLocal<Boolean> CONVERT_STRING_EXPRESSIONS = new ThreadLocal<Boolean>();
+
+    boolean convertStringExpressions = false;
+    private static final Set<Character> REGEXP_CONTROL_CHARS = new HashSet<Character>();
+
+    /**
+     * @param left
+     * @param right
+     */
+    public ComparisonExpression(Expression left, Expression right) {
+        super(left, right);
+        convertStringExpressions = CONVERT_STRING_EXPRESSIONS.get()!=null;
+    }
+
+    public static BooleanExpression createBetween(Expression value, Expression left, Expression right) {
+        return LogicExpression.createAND(createGreaterThanEqual(value, left), createLessThanEqual(value, right));
+    }
+
+    public static BooleanExpression createNotBetween(Expression value, Expression left, Expression right) {
+        return LogicExpression.createOR(createLessThan(value, left), createGreaterThan(value, right));
+    }
+
+    static {
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('.'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('\\'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('['));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf(']'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('^'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('$'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('?'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('*'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('+'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('{'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('}'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('|'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('('));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf(')'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf(':'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('&'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('<'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('>'));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('='));
+        REGEXP_CONTROL_CHARS.add(Character.valueOf('!'));
+    }
+
+    static class LikeExpression extends UnaryExpression implements BooleanExpression {
+
+        Pattern likePattern;
+
+        /**
+         */
+        public LikeExpression(Expression right, String like, int escape) {
+            super(right);
+
+            StringBuffer regexp = new StringBuffer(like.length() * 2);
+            regexp.append("\\A"); // The beginning of the input
+            for (int i = 0; i < like.length(); i++) {
+                char c = like.charAt(i);
+                if (escape == (0xFFFF & c)) {
+                    i++;
+                    if (i >= like.length()) {
+                        // nothing left to escape...
+                        break;
+                    }
+
+                    char t = like.charAt(i);
+                    regexp.append("\\x");
+                    regexp.append(Integer.toHexString(0xFFFF & t));
+                } else if (c == '%') {
+                    regexp.append(".*?"); // Do a non-greedy match
+                } else if (c == '_') {
+                    regexp.append("."); // match one
+                } else if (REGEXP_CONTROL_CHARS.contains(new Character(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);
+        }
+
+        /**
+         * @see org.apache.activemq.filter.UnaryExpression#getExpressionSymbol()
+         */
+        public String getExpressionSymbol() {
+            return "LIKE";
+        }
+
+        /**
+         * @see org.apache.activemq.filter.Expression#evaluate(Filterable)
+         */
+        public Object evaluate(Filterable message) throws FilterException {
+
+            Object rv = this.getRight().evaluate(message);
+
+            if (rv == null) {
+                return null;
+            }
+
+            if (!(rv instanceof String)) {
+                return Boolean.FALSE;
+                // throw new RuntimeException("LIKE can only operate on String
+                // identifiers. LIKE attemped on: '" + rv.getClass());
+            }
+
+            return likePattern.matcher((String)rv).matches() ? Boolean.TRUE : Boolean.FALSE;
+        }
+
+        public boolean matches(Filterable message) throws FilterException {
+            Object object = evaluate(message);
+            return object != null && object == Boolean.TRUE;
+        }
+    }
+
+    public static BooleanExpression createLike(Expression left, String right, String escape) {
+        if (escape != null && escape.length() != 1) {
+            throw new RuntimeException("The ESCAPE string litteral is invalid.  It can only be one character.  Litteral used: " + escape);
+        }
+        int c = -1;
+        if (escape != null) {
+            c = 0xFFFF & escape.charAt(0);
+        }
+
+        return new LikeExpression(left, right, c);
+    }
+
+    public static BooleanExpression createNotLike(Expression left, String right, String escape) {
+        return UnaryExpression.createNOT(createLike(left, right, escape));
+    }
+
+    public static BooleanExpression createInFilter(Expression left, List elements) {
+
+        if (!(left instanceof PropertyExpression)) {
+            throw new RuntimeException("Expected a property for In expression, got: " + left);
+        }
+        return UnaryExpression.createInExpression((PropertyExpression)left, elements, false);
+
+    }
+
+    public static BooleanExpression createNotInFilter(Expression left, List elements) {
+
+        if (!(left instanceof PropertyExpression)) {
+            throw new RuntimeException("Expected a property for In expression, got: " + left);
+        }
+        return UnaryExpression.createInExpression((PropertyExpression)left, elements, true);
+
+    }
+
+    public static BooleanExpression createIsNull(Expression left) {
+        return doCreateEqual(left, ConstantExpression.NULL);
+    }
+
+    public static BooleanExpression createIsNotNull(Expression left) {
+        return UnaryExpression.createNOT(doCreateEqual(left, ConstantExpression.NULL));
+    }
+
+    public static BooleanExpression createNotEqual(Expression left, Expression right) {
+        return UnaryExpression.createNOT(createEqual(left, right));
+    }
+
+    public static BooleanExpression createEqual(Expression left, Expression right) {
+        checkEqualOperand(left);
+        checkEqualOperand(right);
+        checkEqualOperandCompatability(left, right);
+        return doCreateEqual(left, right);
+    }
+
+    private static BooleanExpression doCreateEqual(Expression left, Expression right) {
+        return new ComparisonExpression(left, right) {
+
+            public Object evaluate(Filterable message) throws FilterException {
+                Object lv = left.evaluate(message);
+                Object rv = right.evaluate(message);
+
+                // Iff one of the values is null
+                if (lv == null ^ rv == null) {
+                    return Boolean.FALSE;
+                }
+                if (lv == rv || lv.equals(rv)) {
+                    return Boolean.TRUE;
+                }
+                if (lv instanceof Comparable && rv instanceof Comparable) {
+                    return compare((Comparable)lv, (Comparable)rv);
+                }
+                return Boolean.FALSE;
+            }
+
+            protected boolean asBoolean(int answer) {
+                return answer == 0;
+            }
+
+            public String getExpressionSymbol() {
+                return "=";
+            }
+        };
+    }
+
+    public static BooleanExpression createGreaterThan(final Expression left, final Expression right) {
+        checkLessThanOperand(left);
+        checkLessThanOperand(right);
+        return new ComparisonExpression(left, right) {
+            protected boolean asBoolean(int answer) {
+                return answer > 0;
+            }
+
+            public String getExpressionSymbol() {
+                return ">";
+            }
+        };
+    }
+
+    public static BooleanExpression createGreaterThanEqual(final Expression left, final Expression right) {
+        checkLessThanOperand(left);
+        checkLessThanOperand(right);
+        return new ComparisonExpression(left, right) {
+            protected boolean asBoolean(int answer) {
+                return answer >= 0;
+            }
+
+            public String getExpressionSymbol() {
+                return ">=";
+            }
+        };
+    }
+
+    public static BooleanExpression createLessThan(final Expression left, final Expression right) {
+        checkLessThanOperand(left);
+        checkLessThanOperand(right);
+        return new ComparisonExpression(left, right) {
+
+            protected boolean asBoolean(int answer) {
+                return answer < 0;
+            }
+
+            public String getExpressionSymbol() {
+                return "<";
+            }
+
+        };
+    }
+
+    public static BooleanExpression createLessThanEqual(final Expression left, final Expression right) {
+        checkLessThanOperand(left);
+        checkLessThanOperand(right);
+        return new ComparisonExpression(left, right) {
+
+            protected boolean asBoolean(int answer) {
+                return answer <= 0;
+            }
+
+            public String getExpressionSymbol() {
+                return "<=";
+            }
+        };
+    }
+
+    /**
+     * Only Numeric expressions can be used in >, >=, < or <= expressions.s
+     * 
+     * @param expr
+     */
+    public static void checkLessThanOperand(Expression expr) {
+        if (expr instanceof ConstantExpression) {
+            Object value = ((ConstantExpression)expr).getValue();
+            if (value instanceof Number) {
+                return;
+            }
+
+            // Else it's boolean or a String..
+            throw new RuntimeException("Value '" + expr + "' cannot be compared.");
+        }
+        if (expr instanceof BooleanExpression) {
+            throw new RuntimeException("Value '" + expr + "' cannot be compared.");
+        }
+    }
+
+    /**
+     * Validates that the expression can be used in == or <> expression. Cannot
+     * not be NULL TRUE or FALSE litterals.
+     * 
+     * @param expr
+     */
+    public static void checkEqualOperand(Expression expr) {
+        if (expr instanceof ConstantExpression) {
+            Object value = ((ConstantExpression)expr).getValue();
+            if (value == null) {
+                throw new RuntimeException("'" + expr + "' cannot be compared.");
+            }
+        }
+    }
+
+    /**
+     * @param left
+     * @param right
+     */
+    private static void checkEqualOperandCompatability(Expression left, Expression right) {
+        if (left instanceof ConstantExpression && right instanceof ConstantExpression) {
+            if (left instanceof BooleanExpression && !(right instanceof BooleanExpression)) {
+                throw new RuntimeException("'" + left + "' cannot be compared with '" + right + "'");
+            }
+        }
+    }
+
+    public Object evaluate(Filterable message) throws FilterException {
+        Comparable<Comparable> lv = (Comparable)left.evaluate(message);
+        if (lv == null) {
+            return null;
+        }
+        Comparable rv = (Comparable)right.evaluate(message);
+        if (rv == null) {
+            return null;
+        }
+        return compare(lv, rv);
+    }
+
+    protected Boolean compare(Comparable lv, Comparable rv) {
+        Class<? extends Comparable> lc = lv.getClass();
+        Class<? extends Comparable> rc = rv.getClass();
+        // If the the objects are not of the same type,
+        // try to convert up to allow the comparison.
+        if (lc != rc) {
+            try {
+                if (lc == Boolean.class) {
+                    if (convertStringExpressions && rc == String.class) {
+                        rv = Boolean.valueOf((String)rv);
+                    } else {
+                        return Boolean.FALSE;
+                    }
+                } else if (lc == Byte.class) {
+                    if (rc == Short.class) {
+                        lv = Short.valueOf(((Number)lv).shortValue());
+                    } else if (rc == Integer.class) {
+                        lv = Integer.valueOf(((Number)lv).intValue());
+                    } else if (rc == Long.class) {
+                        lv = Long.valueOf(((Number)lv).longValue());
+                    } else if (rc == Float.class) {
+                        lv = new Float(((Number)lv).floatValue());
+                    } else if (rc == Double.class) {
+                        lv = new Double(((Number)lv).doubleValue());
+                    } else if (convertStringExpressions && rc == String.class) {
+                        rv = Byte.valueOf((String)rv);
+                    } else {
+                        return Boolean.FALSE;
+                    }
+                } else if (lc == Short.class) {
+                    if (rc == Integer.class) {
+                        lv = Integer.valueOf(((Number)lv).intValue());
+                    } else if (rc == Long.class) {
+                        lv = Long.valueOf(((Number)lv).longValue());
+                    } else if (rc == Float.class) {
+                        lv = new Float(((Number)lv).floatValue());
+                    } else if (rc == Double.class) {
+                        lv = new Double(((Number)lv).doubleValue());
+                    } else if (convertStringExpressions && rc == String.class) {
+                        rv = Short.valueOf((String)rv);
+                    } else {
+                        return Boolean.FALSE;
+                    }
+                } else if (lc == Integer.class) {
+                    if (rc == Long.class) {
+                        lv = Long.valueOf(((Number)lv).longValue());
+                    } else if (rc == Float.class) {
+                        lv = new Float(((Number)lv).floatValue());
+                    } else if (rc == Double.class) {
+                        lv = new Double(((Number)lv).doubleValue());
+                    } else if (convertStringExpressions && rc == String.class) {
+                        rv = Integer.valueOf((String)rv);
+                    } else {
+                        return Boolean.FALSE;
+                    }
+                } else if (lc == Long.class) {
+                    if (rc == Integer.class) {
+                        rv = Long.valueOf(((Number)rv).longValue());
+                    } else if (rc == Float.class) {
+                        lv = new Float(((Number)lv).floatValue());
+                    } else if (rc == Double.class) {
+                        lv = new Double(((Number)lv).doubleValue());
+                    } else if (convertStringExpressions && rc == String.class) {
+                        rv = Long.valueOf((String)rv);
+                    } else {
+                        return Boolean.FALSE;
+                    }
+                } else if (lc == Float.class) {
+                    if (rc == Integer.class) {
+                        rv = new Float(((Number)rv).floatValue());
+                    } else if (rc == Long.class) {
+                        rv = new Float(((Number)rv).floatValue());
+                    } else if (rc == Double.class) {
+                        lv = new Double(((Number)lv).doubleValue());
+                    } else if (convertStringExpressions && rc == String.class) {
+                        rv = Float.valueOf((String)rv);
+                    } else {
+                        return Boolean.FALSE;
+                    }
+                } else if (lc == Double.class) {
+                    if (rc == Integer.class) {
+                        rv = new Double(((Number)rv).doubleValue());
+                    } else if (rc == Long.class) {
+                        rv = new Double(((Number)rv).doubleValue());
+                    } else if (rc == Float.class) {
+                        rv = new Double(((Number)rv).doubleValue());
+                    } else if (convertStringExpressions && rc == String.class) {
+                        rv = Double.valueOf((String)rv);
+                    } else {
+                        return Boolean.FALSE;
+                    }
+                } else if (convertStringExpressions && lc == String.class) {
+
+                    if (rc == Boolean.class) {
+                        lv = Boolean.valueOf((String)lv);
+                    } else if (rc == Byte.class) {
+                        lv = Byte.valueOf((String)lv);
+                    } else if (rc == Short.class) {
+                        lv = Short.valueOf((String)lv);
+                    } else if (rc == Integer.class) {
+                        lv = Integer.valueOf((String)lv);
+                    } else if (rc == Long.class) {
+                        lv = Long.valueOf((String)lv);
+                    } else if (rc == Float.class) {
+                        lv = Float.valueOf((String)lv);
+                    } else if (rc == Double.class) {
+                        lv = Double.valueOf((String)lv);
+                    } else {
+                        return Boolean.FALSE;
+                    }
+
+                } else {
+                    return Boolean.FALSE;
+                }
+            } catch (NumberFormatException e) {
+                return Boolean.FALSE;
+            }
+        }
+        return asBoolean(lv.compareTo(rv)) ? Boolean.TRUE : Boolean.FALSE;
+    }
+
+    protected abstract boolean asBoolean(int answer);
+
+    public boolean matches(Filterable message) throws FilterException {
+        Object object = evaluate(message);
+        return object != null && object == Boolean.TRUE;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/ConstantExpression.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/ConstantExpression.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/ConstantExpression.java
new file mode 100755
index 0000000..53d300d
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/ConstantExpression.java
@@ -0,0 +1,163 @@
+/**
+ * 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.jms.selector.filter;
+
+import java.math.BigDecimal;
+
+
+/**
+ * Represents a constant expression
+ * 
+ * @version $Revision: 1.2 $
+ */
+public class ConstantExpression implements Expression {
+
+    static class BooleanConstantExpression extends ConstantExpression implements BooleanExpression {
+        public BooleanConstantExpression(Object value) {
+            super(value);
+        }
+
+        public boolean matches(Filterable message) throws FilterException {
+            Object object = evaluate(message);
+            return object != null && object == Boolean.TRUE;
+        }
+    }
+
+    public static final BooleanConstantExpression NULL = new BooleanConstantExpression(null);
+    public static final BooleanConstantExpression TRUE = new BooleanConstantExpression(Boolean.TRUE);
+    public static final BooleanConstantExpression FALSE = new BooleanConstantExpression(Boolean.FALSE);
+
+    private Object value;
+
+    public ConstantExpression(Object value) {
+        this.value = value;
+    }
+
+    public static ConstantExpression createFromDecimal(String text) {
+
+        // Strip off the 'l' or 'L' if needed.
+        if (text.endsWith("l") || text.endsWith("L")) {
+            text = text.substring(0, text.length() - 1);
+        }
+
+        Number value;
+        try {
+            value = new Long(text);
+        } catch (NumberFormatException e) {
+            // The number may be too big to fit in a long.
+            value = new BigDecimal(text);
+        }
+
+        long l = value.longValue();
+        if (Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE) {
+            value = Integer.valueOf(value.intValue());
+        }
+        return new ConstantExpression(value);
+    }
+
+    public static ConstantExpression createFromHex(String text) {
+        Number value = Long.valueOf(Long.parseLong(text.substring(2), 16));
+        long l = value.longValue();
+        if (Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE) {
+            value = Integer.valueOf(value.intValue());
+        }
+        return new ConstantExpression(value);
+    }
+
+    public static ConstantExpression createFromOctal(String text) {
+        Number value = Long.valueOf(Long.parseLong(text, 8));
+        long l = value.longValue();
+        if (Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE) {
+            value = Integer.valueOf(value.intValue());
+        }
+        return new ConstantExpression(value);
+    }
+
+    public static ConstantExpression createFloat(String text) {
+        Number value = new Double(text);
+        return new ConstantExpression(value);
+    }
+
+    public Object evaluate(Filterable message) throws FilterException {
+        return value;
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+    /**
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        if (value == null) {
+            return "NULL";
+        }
+        if (value instanceof Boolean) {
+            return ((Boolean)value).booleanValue() ? "TRUE" : "FALSE";
+        }
+        if (value instanceof String) {
+            return encodeString((String)value);
+        }
+        return value.toString();
+    }
+
+    /**
+     * TODO: more efficient hashCode()
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        return toString().hashCode();
+    }
+
+    /**
+     * TODO: more efficient hashCode()
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public boolean equals(Object o) {
+
+        if (o == null || !this.getClass().equals(o.getClass())) {
+            return false;
+        }
+        return toString().equals(o.toString());
+
+    }
+
+    /**
+     * Encodes the value of string so that it looks like it would look like when
+     * it was provided in a selector.
+     * 
+     * @param string
+     * @return
+     */
+    public static String encodeString(String s) {
+        StringBuffer b = new StringBuffer();
+        b.append('\'');
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (c == '\'') {
+                b.append(c);
+            }
+            b.append(c);
+        }
+        b.append('\'');
+        return b.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/Expression.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/Expression.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/Expression.java
new file mode 100755
index 0000000..081eddf
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/Expression.java
@@ -0,0 +1,34 @@
+/**
+ * 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.jms.selector.filter;
+
+
+
+/**
+ * Represents an expression
+ * 
+ * @version $Revision: 1.2 $
+ */
+public interface Expression {
+
+    /**
+     * @return the value of this expression
+     */
+    Object evaluate(Filterable message) throws FilterException;
+    
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/FilterException.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/FilterException.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/FilterException.java
new file mode 100644
index 0000000..6c4830e
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/FilterException.java
@@ -0,0 +1,40 @@
+/**
+ * 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.jms.selector.filter;
+
+public class FilterException extends Exception {
+
+    private static final long serialVersionUID = -6892363158919485507L;
+
+    public FilterException() {
+        super();
+    }
+
+    public FilterException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public FilterException(String message) {
+        super(message);
+    }
+
+    public FilterException(Throwable cause) {
+        super(cause);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/Filterable.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/Filterable.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/Filterable.java
new file mode 100755
index 0000000..106b0e9
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/Filterable.java
@@ -0,0 +1,53 @@
+/**
+ * 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.jms.selector.filter;
+
+/**
+ * A Filterable is the object being evaluated by the filters.  It provides
+ * access to filtered properties.
+ * 
+ * @version $Revision: 1.4 $
+ */
+public interface Filterable {
+
+    /**
+     * This method is used by message filters which do content based routing (Like the XPath
+     * based selectors).
+     *
+     * @param <T>
+     * @param type
+     * @return
+     * @throws FilterException
+     */
+    <T> T getBodyAs(Class<T> type) throws FilterException;
+
+    /**
+     * Extracts the named message property
+     *
+     * @param name
+     * @return
+     */
+    Object getProperty(String name);
+
+    /**
+     * Used by the NoLocal filter.
+     *
+     * @return a unique id for the connection that produced the message.
+     */
+    Object getLocalConnectionId();
+
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/LogicExpression.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/LogicExpression.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/LogicExpression.java
new file mode 100755
index 0000000..aabd35c
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/LogicExpression.java
@@ -0,0 +1,88 @@
+/**
+ * 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.jms.selector.filter;
+
+
+/**
+ * A filter performing a comparison of two objects
+ * 
+ * @version $Revision: 1.2 $
+ */
+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(Filterable message) throws FilterException {
+
+                Boolean lv = (Boolean)left.evaluate(message);
+                // Can we do an OR shortcut??
+                if (lv != null && lv.booleanValue()) {
+                    return Boolean.TRUE;
+                }
+
+                Boolean rv = (Boolean)right.evaluate(message);
+                return rv == null ? null : rv;
+            }
+
+            public String getExpressionSymbol() {
+                return "OR";
+            }
+        };
+    }
+
+    public static BooleanExpression createAND(BooleanExpression lvalue, BooleanExpression rvalue) {
+        return new LogicExpression(lvalue, rvalue) {
+
+            public Object evaluate(Filterable message) throws FilterException {
+
+                Boolean lv = (Boolean)left.evaluate(message);
+
+                // Can we do an AND shortcut??
+                if (lv == null) {
+                    return null;
+                }
+                if (!lv.booleanValue()) {
+                    return Boolean.FALSE;
+                }
+
+                Boolean rv = (Boolean)right.evaluate(message);
+                return rv == null ? null : rv;
+            }
+
+            public String getExpressionSymbol() {
+                return "AND";
+            }
+        };
+    }
+
+    public abstract Object evaluate(Filterable message) throws FilterException;
+
+    public boolean matches(Filterable message) throws FilterException {
+        Object object = evaluate(message);
+        return object != null && object == Boolean.TRUE;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/PropertyExpression.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/PropertyExpression.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/PropertyExpression.java
new file mode 100755
index 0000000..767aed4
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/PropertyExpression.java
@@ -0,0 +1,67 @@
+/**
+ * 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.jms.selector.filter;
+
+
+/**
+ * Represents a property expression
+ * 
+ * @version $Revision: 1.5 $
+ */
+public class PropertyExpression implements Expression {
+
+    private final String name;
+
+    public PropertyExpression(String name) {
+        this.name = name;
+    }
+
+    public Object evaluate(Filterable message) throws FilterException {
+        return message.getProperty(name);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        return name;
+    }
+
+    /**
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+    /**
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    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/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/UnaryExpression.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/UnaryExpression.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/UnaryExpression.java
new file mode 100755
index 0000000..75433f2
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/UnaryExpression.java
@@ -0,0 +1,256 @@
+/**
+ * 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.jms.selector.filter;
+
+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
+ * 
+ * @version $Revision: 1.3 $
+ */
+public abstract class UnaryExpression implements Expression {
+
+    private static final BigDecimal BD_LONG_MIN_VALUE = BigDecimal.valueOf(Long.MIN_VALUE);
+    protected Expression right;
+
+    public UnaryExpression(Expression left) {
+        this.right = left;
+    }
+
+    public static Expression createNegate(Expression left) {
+        return new UnaryExpression(left) {
+            public Object evaluate(Filterable message) throws FilterException {
+                Object rvalue = right.evaluate(message);
+                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<Object> inList = t;
+
+        return new BooleanUnaryExpression(right) {
+            public Object evaluate(Filterable message) throws FilterException {
+
+                Object rvalue = right.evaluate(message);
+                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<Object> 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) {
+            super(left);
+        }
+
+        public boolean matches(Filterable message) throws FilterException {
+            Object object = evaluate(message);
+            return object != null && object == Boolean.TRUE;
+        }
+    };
+
+    public static BooleanExpression createNOT(BooleanExpression left) {
+        return new BooleanUnaryExpression(left) {
+            public Object evaluate(Filterable message) throws FilterException {
+                Boolean lvalue = (Boolean)right.evaluate(message);
+                if (lvalue == null) {
+                    return null;
+                }
+                return lvalue.booleanValue() ? Boolean.FALSE : Boolean.TRUE;
+            }
+
+            public String getExpressionSymbol() {
+                return "NOT";
+            }
+        };
+    }
+
+    public static BooleanExpression createXPath(final String xpath) {
+        return new XPathExpression(xpath);
+    }
+
+    public static BooleanExpression createXQuery(final String xpath) {
+        return new XQueryExpression(xpath);
+    }
+
+    public static BooleanExpression createBooleanCast(Expression left) {
+        return new BooleanUnaryExpression(left) {
+            public Object evaluate(Filterable message) throws FilterException {
+                Object rvalue = right.evaluate(message);
+                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;
+    }
+
+    /**
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        return "(" + getExpressionSymbol() + " " + right.toString() + ")";
+    }
+
+    /**
+     * TODO: more efficient hashCode()
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        return toString().hashCode();
+    }
+
+    /**
+     * TODO: more efficient hashCode()
+     * 
+     * @see java.lang.Object#equals(java.lang.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/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/XPathExpression.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/XPathExpression.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/XPathExpression.java
new file mode 100755
index 0000000..20c7a6d
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/XPathExpression.java
@@ -0,0 +1,75 @@
+/**
+ * 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.jms.selector.filter;
+
+/**
+ * Used to evaluate an XPath Expression in a JMS selector.
+ */
+public final class XPathExpression implements BooleanExpression {
+
+    public static XPathEvaluatorFactory XPATH_EVALUATOR_FACTORY = null;
+    static {
+        // Install the xalan xpath evaluator if it available.
+        new XalanXPathEvaluator("//root").evaluate("<root></root>");
+        try {
+            XPATH_EVALUATOR_FACTORY = new XPathExpression.XPathEvaluatorFactory() {
+                public XPathExpression.XPathEvaluator create(String xpath) {
+                    return new XalanXPathEvaluator(xpath);
+                }
+            };
+        } catch(Throwable e) {
+        }
+    }
+
+    private final String xpath;
+    private final XPathEvaluator evaluator;
+
+    public static interface XPathEvaluatorFactory {
+        XPathEvaluator create(String xpath);
+    }
+
+    public static interface XPathEvaluator {
+        boolean evaluate(Filterable message) throws FilterException;
+    }
+
+    XPathExpression(String xpath) {
+        if( XPATH_EVALUATOR_FACTORY == null ) {
+            throw new IllegalArgumentException("XPATH support not enabled.");
+        }
+        this.xpath = xpath;
+        this.evaluator = XPATH_EVALUATOR_FACTORY.create(xpath);
+    }
+
+    public Object evaluate(Filterable message) throws FilterException {
+        return evaluator.evaluate(message) ? Boolean.TRUE : Boolean.FALSE;
+    }
+
+    public String toString() {
+        return "XPATH " + ConstantExpression.encodeString(xpath);
+    }
+
+    /**
+     * @param message
+     * @return true if the expression evaluates to Boolean.TRUE.
+     * @throws FilterException
+     */
+    public boolean matches(Filterable message) throws FilterException {
+        Object object = evaluate(message);
+        return object != null && object == Boolean.TRUE;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/a5fe3018/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/XQueryExpression.java
----------------------------------------------------------------------
diff --git a/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/XQueryExpression.java b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/XQueryExpression.java
new file mode 100755
index 0000000..114ada2
--- /dev/null
+++ b/qpid-jms-selector/src/main/java/org/apache/qpid/jms/selector/filter/XQueryExpression.java
@@ -0,0 +1,49 @@
+/**
+ * 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.jms.selector.filter;
+
+
+/**
+ * Used to evaluate an XQuery Expression in a JMS selector.
+ */
+public final class XQueryExpression implements BooleanExpression {
+    private final String xpath;
+
+    XQueryExpression(String xpath) {
+        super();
+        this.xpath = xpath;
+    }
+
+    public Object evaluate(Filterable message) throws FilterException {
+        return Boolean.FALSE;
+    }
+
+    public String toString() {
+        return "XQUERY " + ConstantExpression.encodeString(xpath);
+    }
+
+    /**
+     * @param message
+     * @return true if the expression evaluates to Boolean.TRUE.
+     * @throws FilterException
+     */
+    public boolean matches(Filterable message) throws FilterException {
+        Object object = evaluate(message);
+        return object != null && object == Boolean.TRUE;
+    }
+
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org