You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by as...@apache.org on 2013/03/04 22:08:46 UTC

svn commit: r1452525 - in /qpid/trunk/qpid/cpp/src: ./ qpid/broker/ tests/

Author: astitcher
Date: Mon Mar  4 21:08:45 2013
New Revision: 1452525

URL: http://svn.apache.org/r1452525
Log:
QPID-4558: Selectors for C++ broker
- Added numeric and boolean values
* To literals and identifier values
* To the code that extracts values from message properties
- Added the full set of comparison operators
- Implemented full "unknown" semantics for all
  operators.
- Implemented extended "is null" and "is not null" operators
  that allow expressions as well as just identifiers.

Added:
    qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.cpp
    qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.h
Modified:
    qpid/trunk/qpid/cpp/src/CMakeLists.txt
    qpid/trunk/qpid/cpp/src/Makefile.am
    qpid/trunk/qpid/cpp/src/qpid/broker/Selector.cpp
    qpid/trunk/qpid/cpp/src/qpid/broker/Selector.h
    qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.cpp
    qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.h
    qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.cpp
    qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.h
    qpid/trunk/qpid/cpp/src/tests/Selector.cpp

Modified: qpid/trunk/qpid/cpp/src/CMakeLists.txt
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/CMakeLists.txt?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/CMakeLists.txt (original)
+++ qpid/trunk/qpid/cpp/src/CMakeLists.txt Mon Mar  4 21:08:45 2013
@@ -1211,6 +1211,8 @@ set (qpidbroker_SOURCES
      qpid/broker/SelectorExpression.cpp
      qpid/broker/SelectorToken.h
      qpid/broker/SelectorToken.cpp
+     qpid/broker/SelectorValue.h
+     qpid/broker/SelectorValue.cpp
      qpid/broker/SemanticState.h
      qpid/broker/SemanticState.cpp
      qpid/broker/SessionAdapter.cpp

Modified: qpid/trunk/qpid/cpp/src/Makefile.am
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/Makefile.am?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/Makefile.am (original)
+++ qpid/trunk/qpid/cpp/src/Makefile.am Mon Mar  4 21:08:45 2013
@@ -719,6 +719,8 @@ libqpidbroker_la_SOURCES = \
   qpid/broker/SelectorExpression.h \
   qpid/broker/SelectorToken.cpp \
   qpid/broker/SelectorToken.h \
+  qpid/broker/SelectorValue.cpp \
+  qpid/broker/SelectorValue.h \
   qpid/broker/SemanticState.cpp \
   qpid/broker/SemanticState.h \
   qpid/broker/SessionAdapter.cpp \

Modified: qpid/trunk/qpid/cpp/src/qpid/broker/Selector.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/Selector.cpp?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/Selector.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/Selector.cpp Mon Mar  4 21:08:45 2013
@@ -23,56 +23,23 @@
 
 #include "qpid/broker/Message.h"
 #include "qpid/broker/SelectorExpression.h"
+#include "qpid/broker/SelectorValue.h"
 #include "qpid/log/Statement.h"
+#include "qpid/types/Variant.h"
 
 #include <string>
 #include <sstream>
+#include "qpid/sys/unordered_map.h"
 
 #include <boost/make_shared.hpp>
 #include <boost/lexical_cast.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
 
 namespace qpid {
 namespace broker {
 
 using std::string;
-
-Selector::Selector(const string& e) :
-    parse(parseTopBoolExpression(e)),
-    expression(e)
-{
-    bool debugOut;
-    QPID_LOG_TEST(debug, debugOut);
-    if (debugOut) {
-        std::stringstream ss;
-        parse->repr(ss);
-        QPID_LOG(debug, "Selector parsed[" << e << "] into: " << ss.str());
-    }
-}
-
-Selector::~Selector()
-{
-}
-
-bool Selector::eval(const SelectorEnv& env)
-{
-    return parse->eval(env);
-}
-
-bool Selector::filter(const Message& msg)
-{
-    return eval(MessageSelectorEnv(msg));
-}
-
-MessageSelectorEnv::MessageSelectorEnv(const Message& m) :
-    msg(m)
-{
-}
-
-bool MessageSelectorEnv::present(const string& identifier) const
-{
-    // If the value we get is void then most likely the property wasn't present
-    return !msg.getProperty(identifier).isVoid();
-}
+using qpid::sys::unordered_map;
 
 /**
  * Identifier  (amqp.)  | JMS...       | amqp 1.0 equivalent
@@ -89,48 +56,124 @@ bool MessageSelectorEnv::present(const s
  * creation_time        | Timestamp    | creation-time        properties section
  * jms_type             | Type         | jms-type             message-annotations section
  */
+const string EMPTY;
+const string PERSISTENT("PERSISTENT");
+const string NON_PERSISTENT("NON_PERSISTENT");
 
-string specialValue(const Message& msg, const string& id)
+const Value specialValue(const Message& msg, const string& id)
 {
     // TODO: Just use a simple if chain for now - improve this later
     if ( id=="delivery_mode" ) {
-        return msg.getEncoding().isPersistent() ? "PERSISTENT" : "NON_PERSISTENT";
+        return msg.getEncoding().isPersistent() ? PERSISTENT : NON_PERSISTENT;
     } else if ( id=="redelivered" ) {
-        return msg.getDeliveryCount()>0 ? "TRUE" : "FALSE";
+        return msg.getDeliveryCount()>0 ? true : false;
     } else if ( id=="priority" ) {
-        return boost::lexical_cast<string>(static_cast<uint32_t>(msg.getEncoding().getPriority()));
+        return int64_t(msg.getPriority());
     } else if ( id=="correlation_id" ) {
-        return ""; // Needs an indirection in getEncoding().
+        return EMPTY; // Needs an indirection in getEncoding().
     } else if ( id=="message_id" ) {
-        return ""; // Needs an indirection in getEncoding().
+        return EMPTY; // Needs an indirection in getEncoding().
     } else if ( id=="to" ) {
-        return msg.getRoutingKey(); // This is good for 0-10, not sure about 1.0
+        return EMPTY; // This is good for 0-10, not sure about 1.0
     } else if ( id=="reply_to" ) {
-        return ""; // Needs an indirection in getEncoding().
+        return EMPTY; // Needs an indirection in getEncoding().
     } else if ( id=="absolute_expiry_time" ) {
-        return ""; // Needs an indirection in getEncoding().
+        return EMPTY; // Needs an indirection in getEncoding().
     } else if ( id=="creation_time" ) {
-        return ""; // Needs an indirection in getEncoding().
+        return EMPTY; // Needs an indirection in getEncoding().
     } else if ( id=="jms_type" ) {
-        return msg.getAnnotation("jms-type");
-    } else return "";
+        return EMPTY;
+    } else return Value();
 }
 
-string MessageSelectorEnv::value(const string& identifier) const
+class MessageSelectorEnv : public SelectorEnv {
+    const Message& msg;
+    mutable boost::ptr_vector<string> returnedStrings;
+    mutable unordered_map<string, Value> returnedValues;
+
+    const Value& value(const string&) const;
+
+public:
+    MessageSelectorEnv(const Message&);
+};
+
+MessageSelectorEnv::MessageSelectorEnv(const Message& m) :
+msg(m)
 {
-    string v;
+}
+
+const Value& MessageSelectorEnv::value(const string& identifier) const
+{
+    if (returnedValues.find(identifier)==returnedValues.end()) {
+        Value v;
+
+        // Check for amqp prefix and strip it if present
+        if (identifier.substr(0, 5) == "amqp.") {
+            v = specialValue(msg, identifier.substr(5));
+        } else {
+            // Just return property as string
+            //v = &msg.getPropertyAsString(identifier);
+            qpid::types::Variant var = msg.getProperty(identifier);
+            switch (var.getType()) {
+            case types::VAR_VOID:
+                v = Value(); break;
+            case types::VAR_STRING: {
+                string& s = var.getString();
+                returnedStrings.push_back(new string(s));
+                v = returnedStrings[returnedStrings.size()-1];
+                break;
+            }
+            case types::VAR_UINT64:
+                // TODO: Need to take care of values too high to be int64_t
+            case types::VAR_UINT32:
+            case types::VAR_UINT16:
+            case types::VAR_UINT8:
+            case types::VAR_INT64:
+            case types::VAR_INT32:
+            case types::VAR_INT16:
+            case types::VAR_INT8:
+                v = var.asInt64(); break;
+            case types::VAR_FLOAT:
+            case types::VAR_DOUBLE:
+                v = var.asDouble(); break;
+            case types::VAR_BOOL:
+                v = var.asBool(); break;
+            default:
+                v = Value(); break;
+            }
+        }
+        QPID_LOG(debug, "Selector identifier: " << identifier << "->" << v);
+        returnedValues[identifier] = v;
+    }
+    return returnedValues[identifier];
+}
 
-    // Check for amqp prefix and strip it if present
-    if (identifier.substr(0, 5) == "amqp.") {
-        v = specialValue(msg, identifier.substr(5));
-    } else {
-        // Just return property as string
-        v = msg.getPropertyAsString(identifier);
+Selector::Selector(const string& e) :
+    parse(TopExpression::parse(e)),
+    expression(e)
+{
+    bool debugOut;
+    QPID_LOG_TEST(debug, debugOut);
+    if (debugOut) {
+        std::stringstream ss;
+        parse->repr(ss);
+        QPID_LOG(debug, "Selector parsed[" << e << "] into: " << ss.str());
     }
-    QPID_LOG(debug, "Selector identifier: " << identifier << "->" << v);
-    return v;
 }
 
+Selector::~Selector()
+{
+}
+
+bool Selector::eval(const SelectorEnv& env)
+{
+    return parse->eval(env);
+}
+
+bool Selector::filter(const Message& msg)
+{
+    return eval(MessageSelectorEnv(msg));
+}
 
 namespace {
 const boost::shared_ptr<Selector> NULL_SELECTOR = boost::shared_ptr<Selector>();

Modified: qpid/trunk/qpid/cpp/src/qpid/broker/Selector.h
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/Selector.h?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/Selector.h (original)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/Selector.h Mon Mar  4 21:08:45 2013
@@ -31,7 +31,8 @@ namespace qpid {
 namespace broker {
 
 class Message;
-class BoolExpression;
+class Value;
+class TopExpression;
 
 /**
  * Interface to provide values to a Selector evaluation
@@ -42,22 +43,11 @@ class SelectorEnv {
 public:
     virtual ~SelectorEnv() {};
 
-    virtual bool present(const std::string&) const = 0;
-    virtual std::string value(const std::string&) const = 0;
-};
-
-class MessageSelectorEnv : public SelectorEnv {
-    const Message& msg;
-
-    bool present(const std::string&) const;
-    std::string value(const std::string&) const;
-
-public:
-    MessageSelectorEnv(const Message&);
+    virtual const Value& value(const std::string&) const = 0;
 };
 
 class Selector {
-    boost::scoped_ptr<BoolExpression> parse;
+    boost::scoped_ptr<TopExpression> parse;
     const std::string expression;
 
 public:

Modified: qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.cpp?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.cpp Mon Mar  4 21:08:45 2013
@@ -23,12 +23,15 @@
 
 #include "qpid/broker/Selector.h"
 #include "qpid/broker/SelectorToken.h"
+#include "qpid/broker/SelectorValue.h"
+#include "qpid/sys/IntegerTypes.h"
 
 #include <string>
 #include <memory>
 #include <ostream>
 
 #include <boost/scoped_ptr.hpp>
+#include <boost/lexical_cast.hpp>
 
 /*
  * Syntax for JMS style selector expressions (informal):
@@ -42,17 +45,15 @@
  *
  * LiteralString ::= ("'" ~[']* "'")+ // Repeats to cope with embedded single quote
  *
- * // Currently no numerics at all
- * //LiteralExactNumeric ::= Digit+
- * //LiteralApproxNumeric ::= ( Digit "." Digit* [ "E" LiteralExactNumeric ] ) |
- * //                         ( "." Digit+ [ "E" LiteralExactNumeric ] ) |
- * //                         ( Digit+ "E" LiteralExactNumeric )
- * //LiteralBool ::= "TRUE" | "FALSE"
- * //
+ * LiteralExactNumeric ::= Digit+
+ * Exponent ::= ['+'|'-'] LiteralExactNumeric
+ * LiteralApproxNumeric ::= ( Digit "." Digit* [ "E" Exponent ] ) |
+ *                          ( "." Digit+ [ "E" Exponent ] ) |
+ *                          ( Digit+ "E" Exponent )
+ * LiteralBool ::= "TRUE" | "FALSE"
  *
  * Literal ::= LiteralBool | LiteralString | LiteralApproxNumeric | LiteralExactNumeric
  *
- * // Currently only simple string comparison expressions + IS NULL or IS NOT NULL
  * EqOps ::= "=" | "<>"
  *
  * ComparisonOps ::= EqOps | ">" | ">=" | "<" | "<="
@@ -63,38 +64,54 @@
  *
  * AndExpression :: = EqualityExpression ( "AND" EqualityExpression  )*
  *
- * EqualityExpression ::= Identifier "IS" "NULL" |
- *                        Identifier "IS "NOT" "NULL" |
- *                        PrimaryExpression EqOps PrimaryExpression |
- *                        "NOT" EqualityExpression |
+ * ComparisonExpression ::= PrimaryExpression "IS" "NULL" |
+ *                        PrimaryExpression "IS" "NOT" "NULL" |
+ *                        PrimaryExpression ComparisonOpsOps PrimaryExpression |
+ *                        "NOT" ComparisonExpression |
  *                        "(" OrExpression ")"
  *
  * PrimaryExpression :: = Identifier |
- *                        LiteralString
+ *                        Literal
  *
  */
 
 namespace qpid {
 namespace broker {
 
-class Expression;
-
 using std::string;
 using std::ostream;
 
+class Expression {
+public:
+    virtual ~Expression() {}
+    virtual void repr(std::ostream&) const = 0;
+    virtual Value eval(const SelectorEnv&) const = 0;
+};
+
+class BoolExpression : public Expression {
+public:
+    virtual ~BoolExpression() {}
+    virtual void repr(std::ostream&) const = 0;
+    virtual BoolOrNone eval_bool(const SelectorEnv&) const = 0;
+
+    Value eval(const SelectorEnv& env) const {
+        return eval_bool(env);
+    }
+};
+
 // Operators
 
-class EqualityOperator {
+class ComparisonOperator {
 public:
     virtual void repr(ostream&) const = 0;
-    virtual bool eval(Expression&, Expression&, const SelectorEnv&) const = 0;
+    virtual BoolOrNone eval(Expression&, Expression&, const SelectorEnv&) const = 0;
 };
 
 template <typename T>
 class UnaryBooleanOperator {
 public:
     virtual void repr(ostream&) const = 0;
-    virtual bool eval(T&, const SelectorEnv&) const = 0;
+    virtual BoolOrNone eval(T&, const SelectorEnv&) const = 0;
 };
 
 ////////////////////////////////////////////////////
@@ -107,13 +124,7 @@ ostream& operator<<(ostream& os, const E
     return os;
 }
 
-ostream& operator<<(ostream& os, const BoolExpression& e)
-{
-    e.repr(os);
-    return os;
-}
-
-ostream& operator<<(ostream& os, const EqualityOperator& e)
+ostream& operator<<(ostream& os, const ComparisonOperator& e)
 {
     e.repr(os);
     return os;
@@ -128,13 +139,13 @@ ostream& operator<<(ostream& os, const U
 
 // Boolean Expression types...
 
-class EqualityExpression : public BoolExpression {
-    EqualityOperator* op;
+class ComparisonExpression : public BoolExpression {
+    ComparisonOperator* op;
     boost::scoped_ptr<Expression> e1;
     boost::scoped_ptr<Expression> e2;
 
 public:
-    EqualityExpression(EqualityOperator* o, Expression* e, Expression* e_):
+    ComparisonExpression(ComparisonOperator* o, Expression* e, Expression* e_):
         op(o),
         e1(e),
         e2(e_)
@@ -144,7 +155,7 @@ public:
         os << "(" << *e1 << *op << *e2 << ")";
     }
 
-    bool eval(const SelectorEnv& env) const {
+    BoolOrNone eval_bool(const SelectorEnv& env) const {
         return op->eval(*e1, *e2, env);
     }
 };
@@ -163,9 +174,19 @@ public:
         os << "(" << *e1 << " OR " << *e2 << ")";
     }
 
-    // We can use the regular C++ short-circuiting operator||
-    bool eval(const SelectorEnv& env) const {
-        return e1->eval(env) || e2->eval(env);
+    BoolOrNone eval_bool(const SelectorEnv& env) const {
+        BoolOrNone bn1(e1->eval_bool(env));
+        if (bn1==BN_TRUE) {
+            return BN_TRUE;
+        } else {
+            BoolOrNone bn2(e2->eval_bool(env));
+            if (bn2==BN_TRUE) {
+                return BN_TRUE;
+            } else {
+                if (bn1==BN_FALSE && bn2==BN_FALSE) return BN_FALSE;
+                else return BN_UNKNOWN;
+            }
+        }
     }
 };
 
@@ -183,9 +204,19 @@ public:
         os << "(" << *e1 << " AND " << *e2 << ")";
     }
 
-    // We can use the regular C++ short-circuiting operator&&
-    bool eval(const SelectorEnv& env) const {
-        return e1->eval(env) && e2->eval(env);
+    BoolOrNone eval_bool(const SelectorEnv& env) const {
+        BoolOrNone bn1(e1->eval_bool(env));
+        if (bn1==BN_FALSE) {
+            return BN_FALSE;
+        } else {
+            BoolOrNone bn2(e2->eval_bool(env));
+            if (bn2==BN_FALSE) {
+                return BN_FALSE;
+            } else {
+                if (bn1==BN_TRUE && bn2==BN_TRUE) return BN_TRUE;
+                else return BN_UNKNOWN;
+            }
+        }
     }
 };
 
@@ -204,7 +235,7 @@ public:
         os << *op << "(" << *e1 << ")";
     }
 
-    virtual bool eval(const SelectorEnv& env) const {
+    virtual BoolOrNone eval_bool(const SelectorEnv& env) const {
         return op->eval(*e1, env);
     }
 };
@@ -212,10 +243,28 @@ public:
 // Expression types...
 
 class Literal : public Expression {
-    string value;
+    const Value value;
+
+public:
+    template <typename T>
+    Literal(const T& v) :
+        value(v)
+    {}
+
+    void repr(ostream& os) const {
+        os << value;
+    }
+
+    Value eval(const SelectorEnv&) const {
+        return value;
+    }
+};
+
+class StringLiteral : public Expression {
+    const string value;
 
 public:
-    Literal(const string& v) :
+    StringLiteral(const string& v) :
         value(v)
     {}
 
@@ -223,7 +272,7 @@ public:
         os << "'" << value << "'";
     }
 
-    string eval(const SelectorEnv&) const {
+    Value eval(const SelectorEnv&) const {
         return value;
     }
 };
@@ -240,60 +289,113 @@ public:
         os << "I:" << identifier;
     }
 
-    string eval(const SelectorEnv& env) const {
+    Value eval(const SelectorEnv& env) const {
         return env.value(identifier);
     }
-
-    bool present(const SelectorEnv& env) const {
-        return env.present(identifier);
-    }
 };
 
 ////////////////////////////////////////////////////
 
 // Some operators...
 
+typedef bool BoolOp(const Value&, const Value&);
+
+BoolOrNone booleval(BoolOp* op, Expression& e1, Expression& e2, const SelectorEnv& env) {
+    const Value v1(e1.eval(env));
+    if (!unknown(v1)) {
+        const Value v2(e2.eval(env));
+        if (!unknown(v2)) {
+            return BoolOrNone(op(v1, v2));
+        }
+    }
+    return BN_UNKNOWN;
+}
+
 // "="
-class Eq : public EqualityOperator {
+class Eq : public ComparisonOperator {
     void repr(ostream& os) const {
         os << "=";
     }
 
-    bool eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
-        return e1.eval(env) == e2.eval(env);
+    BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
+        return booleval(&operator==, e1, e2, env);
     }
 };
 
 // "<>"
-class Neq : public EqualityOperator {
+class Neq : public ComparisonOperator {
     void repr(ostream& os) const {
         os << "<>";
     }
 
-    bool eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
-        return e1.eval(env) != e2.eval(env);
+    BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
+        return booleval(&operator!=, e1, e2, env);
+    }
+};
+
+// "<"
+class Ls : public ComparisonOperator {
+    void repr(ostream& os) const {
+        os << "<";
+    }
+
+    BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
+        return booleval(&operator<, e1, e2, env);
+    }
+};
+
+// ">"
+class Gr : public ComparisonOperator {
+    void repr(ostream& os) const {
+        os << ">";
+    }
+
+    BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
+        return booleval(&operator>, e1, e2, env);
+    }
+};
+
+// "<="
+class Lseq : public ComparisonOperator {
+    void repr(ostream& os) const {
+        os << "<=";
+    }
+
+    BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
+        return booleval(&operator<=, e1, e2, env);
+    }
+};
+
+// ">="
+class Greq : public ComparisonOperator {
+    void repr(ostream& os) const {
+        os << ">=";
+    }
+
+    BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
+        return booleval(&operator>=, e1, e2, env);
     }
 };
 
 // "IS NULL"
-class IsNull : public UnaryBooleanOperator<Identifier> {
+class IsNull : public UnaryBooleanOperator<Expression> {
     void repr(ostream& os) const {
         os << "IsNull";
     }
 
-    bool eval(Identifier& i, const SelectorEnv& env) const {
-        return !i.present(env);
+    BoolOrNone eval(Expression& e, const SelectorEnv& env) const {
+        return BoolOrNone(unknown(e.eval(env)));
     }
 };
 
 // "IS NOT NULL"
-class IsNonNull : public UnaryBooleanOperator<Identifier> {
+class IsNonNull : public UnaryBooleanOperator<Expression> {
     void repr(ostream& os) const {
         os << "IsNonNull";
     }
 
-    bool eval(Identifier& i, const SelectorEnv& env) const {
-        return i.present(env);
+    BoolOrNone eval(Expression& e, const SelectorEnv& env) const {
+        return BoolOrNone(!unknown(e.eval(env)));
     }
 };
 
@@ -303,21 +405,51 @@ class Not : public UnaryBooleanOperator<
         os << "NOT";
     }
 
-    bool eval(BoolExpression& e, const SelectorEnv& env) const {
-        return !e.eval(env);
+    BoolOrNone eval(BoolExpression& e, const SelectorEnv& env) const {
+        BoolOrNone bn = e.eval_bool(env);
+        if (bn==BN_UNKNOWN) return bn;
+        else return BoolOrNone(!bn);
     }
 };
 
 Eq eqOp;
 Neq neqOp;
+Ls lsOp;
+Gr grOp;
+Lseq lseqOp;
+Greq greqOp;
 IsNull isNullOp;
 IsNonNull isNonNullOp;
 Not notOp;
 
 ////////////////////////////////////////////////////
 
+BoolExpression* parseOrExpression(Tokeniser&);
+BoolExpression* parseAndExpression(Tokeniser&);
+BoolExpression* parseComparisonExpression(Tokeniser&);
+Expression* parsePrimaryExpression(Tokeniser&);
+
 // Top level parser
-BoolExpression* parseTopBoolExpression(const string& exp)
+class TopBoolExpression : public TopExpression {
+    boost::scoped_ptr<BoolExpression> expression;
+
+    void repr(ostream& os) const {
+        expression->repr(os);
+    }
+
+    bool eval(const SelectorEnv& env) const {
+        BoolOrNone bn = expression->eval_bool(env);
+        if (bn==BN_TRUE) return true;
+        else return false;
+    }
+
+public:
+    TopBoolExpression(BoolExpression* be) :
+        expression(be)
+    {}
+};
+
+TopExpression* TopExpression::parse(const string& exp)
 {
     string::const_iterator s = exp.begin();
     string::const_iterator e = exp.end();
@@ -325,7 +457,7 @@ BoolExpression* parseTopBoolExpression(c
     std::auto_ptr<BoolExpression> b(parseOrExpression(tokeniser));
     if (!b.get()) throw std::range_error("Illegal selector: couldn't parse");
     if (tokeniser.nextToken().type != T_EOS) throw std::range_error("Illegal selector: too much input");
-    return b.release();
+    return new TopBoolExpression(b.release());
 }
 
 BoolExpression* parseOrExpression(Tokeniser& tokeniser)
@@ -344,11 +476,11 @@ BoolExpression* parseOrExpression(Tokeni
 
 BoolExpression* parseAndExpression(Tokeniser& tokeniser)
 {
-    std::auto_ptr<BoolExpression> e(parseEqualityExpression(tokeniser));
+    std::auto_ptr<BoolExpression> e(parseComparisonExpression(tokeniser));
     if (!e.get()) return 0;
     while ( tokeniser.nextToken().type==T_AND ) {
         std::auto_ptr<BoolExpression> e1(e);
-        std::auto_ptr<BoolExpression> e2(parseEqualityExpression(tokeniser));
+        std::auto_ptr<BoolExpression> e2(parseComparisonExpression(tokeniser));
         if (!e2.get()) return 0;
         e.reset(new AndExpression(e1.release(), e2.release()));
     }
@@ -356,31 +488,16 @@ BoolExpression* parseAndExpression(Token
     return e.release();
 }
 
-BoolExpression* parseEqualityExpression(Tokeniser& tokeniser)
+BoolExpression* parseComparisonExpression(Tokeniser& tokeniser)
 {
     const Token t = tokeniser.nextToken();
-    if ( t.type==T_IDENTIFIER ) {
-        // Check for "IS NULL" and "IS NOT NULL"
-        if ( tokeniser.nextToken().type==T_IS ) {
-            // The rest must be T_NULL or T_NOT, T_NULL
-            switch (tokeniser.nextToken().type) {
-            case T_NULL:
-                return new UnaryBooleanExpression<Identifier>(&isNullOp, new Identifier(t.val));
-            case T_NOT:
-                if ( tokeniser.nextToken().type == T_NULL)
-                    return new UnaryBooleanExpression<Identifier>(&isNonNullOp, new Identifier(t.val));
-            default:
-                return 0;
-            }
-        }
-        tokeniser.returnTokens();
-    } else if ( t.type==T_LPAREN ) {
+    if ( t.type==T_LPAREN ) {
         std::auto_ptr<BoolExpression> e(parseOrExpression(tokeniser));
         if (!e.get()) return 0;
         if ( tokeniser.nextToken().type!=T_RPAREN ) return 0;
         return e.release();
     } else if ( t.type==T_NOT ) {
-        std::auto_ptr<BoolExpression> e(parseEqualityExpression(tokeniser));
+        std::auto_ptr<BoolExpression> e(parseComparisonExpression(tokeniser));
         if (!e.get()) return 0;
         return new UnaryBooleanExpression<BoolExpression>(&notOp, e.release());
     }
@@ -389,6 +506,21 @@ BoolExpression* parseEqualityExpression(
     std::auto_ptr<Expression> e1(parsePrimaryExpression(tokeniser));
     if (!e1.get()) return 0;
 
+    // Check for "IS NULL" and "IS NOT NULL"
+    if ( tokeniser.nextToken().type==T_IS ) {
+        // The rest must be T_NULL or T_NOT, T_NULL
+        switch (tokeniser.nextToken().type) {
+            case T_NULL:
+                return new UnaryBooleanExpression<Expression>(&isNullOp, e1.release());
+            case T_NOT:
+                if ( tokeniser.nextToken().type == T_NULL)
+                    return new UnaryBooleanExpression<Expression>(&isNonNullOp, e1.release());
+            default:
+                return 0;
+        }
+    }
+    tokeniser.returnTokens();
+
     const Token op = tokeniser.nextToken();
     if (op.type != T_OPERATOR) {
         return 0;
@@ -397,8 +529,12 @@ BoolExpression* parseEqualityExpression(
     std::auto_ptr<Expression> e2(parsePrimaryExpression(tokeniser));
     if (!e2.get()) return 0;
 
-    if (op.val == "=") return new EqualityExpression(&eqOp, e1.release(), e2.release());
-    if (op.val == "<>") return new EqualityExpression(&neqOp, e1.release(), e2.release());
+    if (op.val == "=") return new ComparisonExpression(&eqOp, e1.release(), e2.release());
+    if (op.val == "<>") return new ComparisonExpression(&neqOp, e1.release(), e2.release());
+    if (op.val == "<") return new ComparisonExpression(&lsOp, e1.release(), e2.release());
+    if (op.val == ">") return new ComparisonExpression(&grOp, e1.release(), e2.release());
+    if (op.val == "<=") return new ComparisonExpression(&lseqOp, e1.release(), e2.release());
+    if (op.val == ">=") return new ComparisonExpression(&greqOp, e1.release(), e2.release());
 
     return 0;
 }
@@ -410,7 +546,15 @@ Expression* parsePrimaryExpression(Token
         case T_IDENTIFIER:
             return new Identifier(t.val);
         case T_STRING:
-            return new Literal(t.val);
+            return new StringLiteral(t.val);
+        case T_FALSE:
+            return new Literal(false);
+        case T_TRUE:
+            return new Literal(true);
+        case T_NUMERIC_EXACT:
+            return new Literal(boost::lexical_cast<int64_t>(t.val));
+        case T_NUMERIC_APPROX:
+            return new Literal(boost::lexical_cast<double>(t.val));
         default:
             return 0;
     }

Modified: qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.h
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.h?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.h (original)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.h Mon Mar  4 21:08:45 2013
@@ -29,29 +29,15 @@ namespace qpid {
 namespace broker {
 
 class SelectorEnv;
-class Tokeniser;
 
-class Expression {
+class TopExpression {
 public:
-    virtual ~Expression() {}
-    virtual void repr(std::ostream&) const = 0;
-    virtual std::string eval(const SelectorEnv&) const = 0;
-};
-
-class BoolExpression {
-public:
-    virtual ~BoolExpression() {};
+    virtual ~TopExpression() {};
     virtual void repr(std::ostream&) const = 0;
     virtual bool eval(const SelectorEnv&) const = 0;
-};
 
-BoolExpression* parseTopBoolExpression(const std::string& exp);
-BoolExpression* parseBoolExpression(Tokeniser&);
-BoolExpression* parseOrExpression(Tokeniser&);
-BoolExpression* parseAndExpression(Tokeniser&);
-BoolExpression* parseNotExpression(Tokeniser&);
-BoolExpression* parseEqualityExpression(Tokeniser&);
-Expression* parsePrimaryExpression(Tokeniser&);
+    static TopExpression* parse(const std::string& exp);
+};
 
 }}
 

Modified: qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.cpp?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.cpp Mon Mar  4 21:08:45 2013
@@ -221,10 +221,77 @@ bool tokeniseOperator(std::string::const
     return true;
 }
 
-// Can't parse numerics yet
-bool tokeniseNumeric(std::string::const_iterator& /*s*/, std::string::const_iterator& /*e*/, Token& /*tok*/)
+bool tokeniseNumeric(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok)
 {
-    return false;
+    std::string::const_iterator t = s;
+
+    // Hand constructed state machine recogniser
+    enum {
+        START,
+        REJECT,
+        DIGIT,
+        DECIMAL_START,
+        DECIMAL,
+        EXPONENT_SIGN,
+        EXPONENT_START,
+        EXPONENT,
+        ACCEPT_EXACT,
+        ACCEPT_INEXACT
+    } state = START;
+
+    while (true)
+    switch (state) {
+    case START:
+        if (t==e) {state = REJECT;}
+        else if (std::isdigit(*t)) {++t; state = DIGIT;}
+        else if (*t=='.') {++t; state = DECIMAL_START;}
+        else state = REJECT;
+        break;
+    case DECIMAL_START:
+        if (t==e) {state = REJECT;}
+        else if (std::isdigit(*t)) {++t; state = DECIMAL;}
+        else state = REJECT;
+        break;
+    case EXPONENT_SIGN:
+        if (t==e) {state = REJECT;}
+        else if (*t=='-' || *t=='+') {++t; state = EXPONENT_START;}
+        else if (std::isdigit(*t)) {++t; state = EXPONENT;}
+        else state = REJECT;
+        break;
+    case EXPONENT_START:
+        if (t==e) {state = REJECT;}
+        else if (std::isdigit(*t)) {++t; state = EXPONENT;}
+        else state = REJECT;
+        break;
+    case DIGIT:
+        if (t==e) {state = ACCEPT_EXACT;}
+        else if (std::isdigit(*t)) {++t; state = DIGIT;}
+        else if (*t=='.') {++t; state = DECIMAL;}
+        else if (*t=='e' || *t=='E') {++t; state = EXPONENT_SIGN;}
+        else state = ACCEPT_EXACT;
+        break;
+    case DECIMAL:
+        if (t==e) {state = ACCEPT_INEXACT;}
+        else if (std::isdigit(*t)) {++t; state = DECIMAL;}
+        else if (*t=='e' || *t=='E') {++t; state = EXPONENT_SIGN;}
+        else state = ACCEPT_INEXACT;
+        break;
+    case EXPONENT:
+        if (t==e) {state = ACCEPT_INEXACT;}
+        else if (std::isdigit(*t)) {++t; state = EXPONENT;}
+        else state = ACCEPT_INEXACT;
+        break;
+    case ACCEPT_EXACT:
+        tok = Token(T_NUMERIC_EXACT, s, t);
+        s = t;
+        return true;
+    case ACCEPT_INEXACT:
+        tok = Token(T_NUMERIC_APPROX, s, t);
+        s = t;
+        return true;
+    case REJECT:
+        return false;
+    };
 }
 
 Tokeniser::Tokeniser(const std::string::const_iterator& s, const std::string::const_iterator& e) :

Modified: qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.h
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.h?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.h (original)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.h Mon Mar  4 21:08:45 2013
@@ -83,19 +83,12 @@ public:
 };
 
 bool tokeniseEos(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
-
 bool tokeniseIdentifier(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
-
 bool tokeniseReservedWord(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
-
 bool tokeniseIdentifierOrReservedWord(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
-
 bool tokeniseString(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
-
 bool tokeniseParens(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
-
 bool tokeniseOperator(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
-
 bool tokeniseNumeric(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
 
 class Tokeniser {

Added: qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.cpp?rev=1452525&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.cpp (added)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.cpp Mon Mar  4 21:08:45 2013
@@ -0,0 +1,196 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/broker/SelectorValue.h"
+
+#include <ostream>
+#include <boost/scoped_ptr.hpp>
+
+using std::ostream;
+
+namespace qpid {
+namespace broker {
+
+ostream& operator<<(ostream& os, const Value& v)
+{
+    switch (v.type) {
+    case Value::T_UNKNOWN: os << "UNKNOWN"; break;
+    case Value::T_BOOL: os << "BOOL:" << std::boolalpha << v.b; break;
+    case Value::T_EXACT: os << "EXACT:" << v.i; break;
+    case Value::T_INEXACT: os << "APPROX:" << v.x; break;
+    case Value::T_STRING: os << "STRING:'" << *v.s << "'"; break;
+    };
+    return os;
+}
+
+class NumericPairBase {
+public:
+    virtual Value add() = 0;
+    virtual Value sub() = 0;
+    virtual Value mul() = 0;
+    virtual Value div() = 0;
+
+    virtual bool eq() = 0;
+    virtual bool ne() = 0;
+    virtual bool ls() = 0;
+    virtual bool gr() = 0;
+    virtual bool le() = 0;
+    virtual bool ge() = 0;
+};
+
+template <typename T>
+class NumericPair : public NumericPairBase {
+    const T n1;
+    const T n2;
+
+    Value add() { return n1+n2; }
+    Value sub() { return n1-n2; }
+    Value mul() { return n1*n2; }
+    Value div() { return n1/n2; }
+
+    bool eq() { return n1==n2; }
+    bool ne() { return n1!=n2; }
+    bool ls() { return n1<n2; }
+    bool gr() { return n1>n2; }
+    bool le() { return n1<=n2; }
+    bool ge() { return n1>=n2; }
+
+public:
+    NumericPair(T x, T y) :
+        n1(x),
+        n2(y)
+    {}
+};
+
+NumericPairBase* promoteNumeric(const Value& v1, const Value& v2)
+{
+    if (!numeric(v1) || !numeric(v2)) return 0;
+
+    if (v1.type != v2.type) {
+        switch (v1.type) {
+        case Value::T_INEXACT: return new NumericPair<double>(v1.x, v2.i);
+        case Value::T_EXACT: return new NumericPair<double>(v1.i, v2.x);
+        default:
+            assert(false);
+        }
+    } else {
+        switch (v1.type) {
+        case Value::T_INEXACT: return new NumericPair<double>(v1.x, v2.x);
+        case Value::T_EXACT: return new NumericPair<int64_t>(v1.i, v2.i);
+        default:
+            assert(false);
+        }
+    }
+}
+
+bool operator==(const Value& v1, const Value& v2)
+{
+    boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+    if (nbp) return nbp->eq();
+
+    if (v1.type != v2.type) return false;
+    switch (v1.type) {
+    case Value::T_BOOL: return v1.b == v2.b;
+    case Value::T_STRING: return *v1.s == *v2.s;
+    default: // Cannot ever get here
+        return false;
+    }
+}
+
+bool operator!=(const Value& v1, const Value& v2)
+{
+    boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+    if (nbp) return nbp->ne();
+
+    if (v1.type != v2.type) return false;
+    switch (v1.type) {
+    case Value::T_BOOL: return v1.b != v2.b;
+    case Value::T_STRING: return *v1.s != *v2.s;
+    default: // Cannot ever get here
+        return false;
+    }
+}
+
+bool operator<(const Value& v1, const Value& v2)
+{
+    boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+    if (nbp) return nbp->ls();
+
+    return false;
+}
+
+bool operator>(const Value& v1, const Value& v2)
+{
+    boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+    if (nbp) return nbp->gr();
+
+    return false;
+}
+
+bool operator<=(const Value& v1, const Value& v2)
+{
+    boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+    if (nbp) return nbp->le();
+
+    return false;
+}
+
+bool operator>=(const Value& v1, const Value& v2)
+{
+    boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+    if (nbp) return nbp->ge();
+
+    return false;
+}
+
+Value operator+(const Value& v1, const Value& v2)
+{
+    boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+    if (nbp) return nbp->add();
+
+    return Value();
+}
+
+Value operator-(const Value& v1, const Value& v2)
+{
+    boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+    if (nbp) return nbp->sub();
+
+    return Value();
+}
+
+Value operator*(const Value& v1, const Value& v2)
+{
+    boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+    if (nbp) return nbp->mul();
+
+    return Value();
+}
+
+Value operator/(const Value& v1, const Value& v2)
+{
+    boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+    if (nbp) return nbp->div();
+
+    return Value();
+}
+
+}}
\ No newline at end of file

Added: qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.h
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.h?rev=1452525&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.h (added)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.h Mon Mar  4 21:08:45 2013
@@ -0,0 +1,115 @@
+#ifndef QPID_BROKER_SELECTORVALUE_H
+#define QPID_BROKER_SELECTORVALUE_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/sys/IntegerTypes.h"
+
+#include <iosfwd>
+#include <string>
+
+namespace qpid {
+namespace broker {
+
+enum BoolOrNone {
+    BN_FALSE = false,
+    BN_TRUE = true,
+    BN_UNKNOWN
+};
+
+// The user of the Value class for strings must ensure that
+// the string has a lifetime longer than the string used and
+// is responsible for managing its lifetime.
+class Value {
+public:
+    union {
+        bool               b;
+        int64_t            i;
+        double             x;
+        const std::string* s;
+    };
+    enum {
+        T_UNKNOWN,
+        T_BOOL,
+        T_STRING,
+        T_EXACT,
+        T_INEXACT
+    } type;
+
+    // Default copy contructor
+    // Default assignment operator
+    // Default destructor
+    Value() :
+        type(T_UNKNOWN)
+    {}
+
+    Value(const std::string& s0) :
+        s(&s0),
+        type(T_STRING)
+    {}
+
+    Value(const int64_t i0) :
+        i(i0),
+        type(T_EXACT)
+    {}
+
+    Value(const double x0) :
+        x(x0),
+        type(T_INEXACT)
+    {}
+
+    Value(bool b0) :
+        b(b0),
+        type(T_BOOL)
+    {}
+
+    Value(BoolOrNone bn) :
+        b(bn),
+        type(bn==BN_UNKNOWN ? T_UNKNOWN : T_BOOL)
+    {}
+};
+
+inline bool unknown(const Value& v) {
+    return v.type == Value::T_UNKNOWN;
+}
+
+inline bool numeric(const Value& v) {
+    return v.type == Value::T_EXACT || v.type == Value::T_INEXACT;
+}
+
+std::ostream& operator<<(std::ostream& os, const Value& v);
+
+bool operator==(const Value&, const Value&);
+bool operator!=(const Value&, const Value&);
+bool operator<(const Value&, const Value&);
+bool operator>(const Value&, const Value&);
+bool operator<=(const Value&, const Value&);
+bool operator>=(const Value&, const Value&);
+
+Value operator+(const Value&, const Value&);
+Value operator-(const Value&, const Value&);
+Value operator*(const Value&, const Value&);
+Value operator/(const Value&, const Value&);
+
+}}
+
+#endif

Modified: qpid/trunk/qpid/cpp/src/tests/Selector.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/tests/Selector.cpp?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/tests/Selector.cpp (original)
+++ qpid/trunk/qpid/cpp/src/tests/Selector.cpp Mon Mar  4 21:08:45 2013
@@ -21,14 +21,17 @@
 
 #include "qpid/broker/SelectorToken.h"
 #include "qpid/broker/Selector.h"
+#include "qpid/broker/SelectorValue.h"
 
 #include "unit_test.h"
 
 #include <string>
 #include <map>
+#include <boost/ptr_container/ptr_vector.hpp>
 
 using std::string;
 using std::map;
+using std::vector;
 
 namespace qb = qpid::broker;
 
@@ -86,6 +89,15 @@ QPID_AUTO_TEST_CASE(tokeniseSuccess)
     verifyTokeniserSuccess(&tokeniseOperator, "<> Identifier", qb::T_OPERATOR, "<>", " Identifier");
     verifyTokeniserSuccess(&tokeniseParens, "(a and b) not c", qb::T_LPAREN, "(", "a and b) not c");
     verifyTokeniserSuccess(&tokeniseParens, ") not c", qb::T_RPAREN, ")", " not c");
+    verifyTokeniserSuccess(&tokeniseNumeric, "019kill", qb::T_NUMERIC_EXACT, "019", "kill");
+    verifyTokeniserSuccess(&tokeniseNumeric, "0kill", qb::T_NUMERIC_EXACT, "0", "kill");
+    verifyTokeniserSuccess(&tokeniseNumeric, "0.kill", qb::T_NUMERIC_APPROX, "0.", "kill");
+    verifyTokeniserSuccess(&tokeniseNumeric, "3.1415=pi", qb::T_NUMERIC_APPROX, "3.1415", "=pi");
+    verifyTokeniserSuccess(&tokeniseNumeric, ".25.kill", qb::T_NUMERIC_APPROX, ".25", ".kill");
+    verifyTokeniserSuccess(&tokeniseNumeric, "2e5.kill", qb::T_NUMERIC_APPROX, "2e5", ".kill");
+    verifyTokeniserSuccess(&tokeniseNumeric, "3.e50easy to kill", qb::T_NUMERIC_APPROX, "3.e50", "easy to kill");
+    verifyTokeniserSuccess(&tokeniseNumeric, "34.25e+50easy to kill", qb::T_NUMERIC_APPROX, "34.25e+50", "easy to kill");
+    verifyTokeniserSuccess(&tokeniseNumeric, "34.e-50easy to kill", qb::T_NUMERIC_APPROX, "34.e-50", "easy to kill");
 }
 
 QPID_AUTO_TEST_CASE(tokeniseFailure)
@@ -105,6 +117,13 @@ QPID_AUTO_TEST_CASE(tokeniseFailure)
     verifyTokeniserFail(&tokeniseOperator, ")");
     verifyTokeniserFail(&tokeniseParens, "=");
     verifyTokeniserFail(&tokeniseParens, "what ho!");
+    verifyTokeniserFail(&tokeniseNumeric, "kill");
+    verifyTokeniserFail(&tokeniseNumeric, "e3");
+    verifyTokeniserFail(&tokeniseNumeric, "1.e.5");
+    verifyTokeniserFail(&tokeniseNumeric, ".e5");
+    verifyTokeniserFail(&tokeniseNumeric, "34e");
+    verifyTokeniserFail(&tokeniseNumeric, ".3e+");
+    verifyTokeniserFail(&tokeniseNumeric, ".3e-.");
 }
 
 QPID_AUTO_TEST_CASE(tokenString)
@@ -138,11 +157,25 @@ QPID_AUTO_TEST_CASE(tokenString)
     BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_NULL, "null"));
     BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_EOS, ""));
     BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_EOS, ""));
+
+    exp = "(a+6)*7.5/1e6";
+    s = exp.begin();
+    e = exp.end();
+    Tokeniser v(s, e);
+
+    BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_LPAREN, "("));
+    BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_IDENTIFIER, "a"));
+    BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_OPERATOR, "+"));
+    BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_NUMERIC_EXACT, "6"));
+    BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_RPAREN, ")"));
+    BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_OPERATOR, "*"));
+    BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_NUMERIC_APPROX, "7.5"));
+    BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_OPERATOR, "/"));
+    BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_NUMERIC_APPROX, "1e6"));
 }
 
 QPID_AUTO_TEST_CASE(parseStringFail)
 {
-    BOOST_CHECK_THROW(qb::Selector e("'Daft' is not null"), std::range_error);
     BOOST_CHECK_THROW(qb::Selector e("A is null not"), std::range_error);
     BOOST_CHECK_THROW(qb::Selector e("A is null or not"), std::range_error);
     BOOST_CHECK_THROW(qb::Selector e("A is null or and"), std::range_error);
@@ -152,67 +185,132 @@ QPID_AUTO_TEST_CASE(parseStringFail)
 }
 
 class TestSelectorEnv : public qpid::broker::SelectorEnv {
-    map<string, string> values;
+    map<string, qb::Value> values;
+    boost::ptr_vector<string> strings;
+    static const qb::Value EMPTY;
 
-    bool present(const std::string& v) const {
+    bool present(const string& v) const {
         return values.find(v)!=values.end();
     }
 
-    std::string value(const std::string& v) const {
-        return present(v) ? values.at(v) : "";
+    const qb::Value& value(const string& v) const {
+        const qb::Value& r = present(v) ? values.at(v) : EMPTY;
+        return r;
     }
 
 public:
-    void set(const string& id, const string& value) {
-        values[id] = value;
+    void set(const string& id, const char* value) {
+        strings.push_back(new string(value));
+        values[id] = strings[strings.size()-1];
+    }
+
+    void set(const string& id, const qb::Value& value) {
+        if (value.type==qb::Value::T_STRING) {
+            strings.push_back(new string(*value.s));
+            values[id] = strings[strings.size()-1];
+        } else {
+            values[id] = value;
+        }
     }
 };
 
+const qb::Value TestSelectorEnv::EMPTY;
+
 QPID_AUTO_TEST_CASE(parseString)
 {
-    qb::Selector a("A is not null");
-    qb::Selector a1("A is null");
-    qb::Selector a2("A = C");
-    qb::Selector a3("A <> C");
-    qb::Selector c("C is not null");
-    qb::Selector c1("C is null");
-    qb::Selector d("A='hello kitty'");
-    qb::Selector e("A<>'hello kitty'");
-    qb::Selector f("A=B");
-    qb::Selector g("A<>B");
-    qb::Selector h("A='hello kitty' OR B='Bye, bye cruel world'");
-    qb::Selector i("B='hello kitty' OR A='Bye, bye cruel world'");
-    qb::Selector j("B='hello kitty' AnD A='Bye, bye cruel world'");
-    qb::Selector k("B='hello kitty' AnD B='Bye, bye cruel world'");
-    qb::Selector a4("A is null or A='Bye, bye cruel world'");
-    qb::Selector a5("Z is null OR A is not null and A<>'Bye, bye cruel world'");
-    qb::Selector a6("(Z is null OR A is not null) and A<>'Bye, bye cruel world'");
-    qb::Selector t("NOT C is not null OR C is null");
-    qb::Selector n("Not A='' or B=z");
+    BOOST_CHECK_NO_THROW(qb::Selector e("'Daft' is not null"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("42 is null"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("A is not null"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("A is null"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("A = C"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("A <> C"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("A='hello kitty'"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("A<>'hello kitty'"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("A=B"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("A<>B"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("A='hello kitty' OR B='Bye, bye cruel world'"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("B='hello kitty' AnD A='Bye, bye cruel world'"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("A is null or A='Bye, bye cruel world'"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("Z is null OR A is not null and A<>'Bye, bye cruel world'"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("(Z is null OR A is not null) and A<>'Bye, bye cruel world'"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("NOT C is not null OR C is null"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("Not A='' or B=z"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("Not A=17 or B=5.6"));
+    BOOST_CHECK_NO_THROW(qb::Selector e("A<>17 and B=5.6e17"));
+}
 
+QPID_AUTO_TEST_CASE(simpleEval)
+{
     TestSelectorEnv env;
     env.set("A", "Bye, bye cruel world");
     env.set("B", "hello kitty");
 
-    BOOST_CHECK(a.eval(env));
-    BOOST_CHECK(!a1.eval(env));
-    BOOST_CHECK(!a2.eval(env));
-    BOOST_CHECK(a3.eval(env));
-    BOOST_CHECK(!c.eval(env));
-    BOOST_CHECK(c1.eval(env));
-    BOOST_CHECK(!d.eval(env));
-    BOOST_CHECK(e.eval(env));
-    BOOST_CHECK(!f.eval(env));
-    BOOST_CHECK(g.eval(env));
-    BOOST_CHECK(!h.eval(env));
-    BOOST_CHECK(i.eval(env));
-    BOOST_CHECK(j.eval(env));
-    BOOST_CHECK(!k.eval(env));
-    BOOST_CHECK(a4.eval(env));
-    BOOST_CHECK(a5.eval(env));
-    BOOST_CHECK(!a6.eval(env));
-    BOOST_CHECK(t.eval(env));
-    BOOST_CHECK(n.eval(env));
+    BOOST_CHECK(qb::Selector("A is not null").eval(env));
+    BOOST_CHECK(!qb::Selector("A is null").eval(env));
+    BOOST_CHECK(!qb::Selector("A = C").eval(env));
+    BOOST_CHECK(!qb::Selector("A <> C").eval(env));
+    BOOST_CHECK(!qb::Selector("C is not null").eval(env));
+    BOOST_CHECK(qb::Selector("C is null").eval(env));
+    BOOST_CHECK(qb::Selector("A='Bye, bye cruel world'").eval(env));
+    BOOST_CHECK(!qb::Selector("A<>'Bye, bye cruel world'").eval(env));
+    BOOST_CHECK(!qb::Selector("A='hello kitty'").eval(env));
+    BOOST_CHECK(qb::Selector("A<>'hello kitty'").eval(env));
+    BOOST_CHECK(!qb::Selector("A=B").eval(env));
+    BOOST_CHECK(qb::Selector("A<>B").eval(env));
+    BOOST_CHECK(!qb::Selector("A='hello kitty' OR B='Bye, bye cruel world'").eval(env));
+    BOOST_CHECK(qb::Selector("B='hello kitty' OR A='Bye, bye cruel world'").eval(env));
+    BOOST_CHECK(qb::Selector("B='hello kitty' AnD A='Bye, bye cruel world'").eval(env));
+    BOOST_CHECK(!qb::Selector("B='hello kitty' AnD B='Bye, bye cruel world'").eval(env));
+    BOOST_CHECK(qb::Selector("A is null or A='Bye, bye cruel world'").eval(env));
+    BOOST_CHECK(qb::Selector("Z is null OR A is not null and A<>'Bye, bye cruel world'").eval(env));
+    BOOST_CHECK(!qb::Selector("(Z is null OR A is not null) and A<>'Bye, bye cruel world'").eval(env));
+    BOOST_CHECK(qb::Selector("NOT C is not null OR C is null").eval(env));
+    BOOST_CHECK(qb::Selector("Not A='' or B=z").eval(env));
+    BOOST_CHECK(qb::Selector("Not A=17 or B=5.6").eval(env));
+    BOOST_CHECK(!qb::Selector("A<>17 and B=5.6e17").eval(env));
+    BOOST_CHECK(!qb::Selector("C=D").eval(env));
+    BOOST_CHECK(qb::Selector("13 is not null").eval(env));
+    BOOST_CHECK(!qb::Selector("'boo!' is null").eval(env));
+}
+
+QPID_AUTO_TEST_CASE(numericEval)
+{
+    TestSelectorEnv env;
+    env.set("A", 42.0);
+    env.set("B", 39l);
+
+    BOOST_CHECK(qb::Selector("A>B").eval(env));
+    BOOST_CHECK(qb::Selector("A=42").eval(env));
+    BOOST_CHECK(qb::Selector("B=39.0").eval(env));
+    BOOST_CHECK(qb::Selector("Not A=17 or B=5.6").eval(env));
+    BOOST_CHECK(!qb::Selector("A<>17 and B=5.6e17").eval(env));
+}
+
+QPID_AUTO_TEST_CASE(comparisonEval)
+{
+    TestSelectorEnv env;
+
+    BOOST_CHECK(!qb::Selector("17 > 19.0").eval(env));
+    BOOST_CHECK(!qb::Selector("'hello' > 19.0").eval(env));
+    BOOST_CHECK(!qb::Selector("'hello' < 19.0").eval(env));
+    BOOST_CHECK(!qb::Selector("'hello' = 19.0").eval(env));
+    BOOST_CHECK(!qb::Selector("'hello'>42 and 'hello'<42 and 'hello'=42 and 'hello'<>42").eval(env));
+    BOOST_CHECK(qb::Selector("20 >= 19.0 and 20 > 19").eval(env));
+    BOOST_CHECK(qb::Selector("42 <= 42.0 and 37.0 >= 37").eval(env));
+}
+
+QPID_AUTO_TEST_CASE(NullEval)
+{
+    TestSelectorEnv env;
+
+    BOOST_CHECK(qb::Selector("P > 19.0 or (P is null)").eval(env));
+    BOOST_CHECK(qb::Selector("P is null or P=''").eval(env));
+    BOOST_CHECK(!qb::Selector("P=Q").eval(env));
+    BOOST_CHECK(!qb::Selector("not P=Q").eval(env));
+    BOOST_CHECK(!qb::Selector("not P=Q and not P=Q").eval(env));
+    BOOST_CHECK(!qb::Selector("P=Q or not P=Q").eval(env));
+    BOOST_CHECK(!qb::Selector("P > 19.0 or P <= 19.0").eval(env));
+    BOOST_CHECK(qb::Selector("P > 19.0 or 17 <= 19.0").eval(env));
 }
 
 QPID_AUTO_TEST_SUITE_END()



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