You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by df...@apache.org on 2021/04/09 20:12:19 UTC

[activemq-nms-msmq] 03/05: Apply patch for AMQNET-554. Suport for message properties, and selectors. Thanks Stephane Ramet!

This is an automated email from the ASF dual-hosted git repository.

dfoulks pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/activemq-nms-msmq.git

commit 16d8f06dd0b178c5030c0da7345aa16e2d51a761
Author: Jim Gomes <jg...@apache.org>
AuthorDate: Thu Jul 7 20:47:10 2016 +0000

    Apply patch for AMQNET-554. Suport for message properties, and selectors. Thanks Stephane Ramet!
---
 src/main/csharp/BaseMessage.cs                     |  154 ++-
 src/main/csharp/DefaultMessageConverter.cs         |  500 +++++++--
 src/main/csharp/IMessageConverter.cs               |   28 +-
 src/main/csharp/IMessageConverterEx.cs             |   44 +
 src/main/csharp/MessageConsumer.cs                 |   93 +-
 src/main/csharp/QueueBrowser.cs                    |   31 +-
 src/main/csharp/Readers/AbstractMessageReader.cs   |  126 +++
 .../csharp/Readers/ByCorrelationIdMessageReader.cs |  139 +++
 src/main/csharp/Readers/ByIdMessageReader.cs       |  136 +++
 src/main/csharp/Readers/ByLookupIdMessageReader.cs |  145 +++
 src/main/csharp/Readers/BySelectorMessageReader.cs |  290 +++++
 src/main/csharp/Readers/IMessageReader.cs          |   93 ++
 src/main/csharp/Readers/MessageReaderUtil.cs       |   91 ++
 .../csharp/Readers/NonFilteringMessageReader.cs    |  128 +++
 src/main/csharp/Selector/ANDExpression.cs          |   47 +
 src/main/csharp/Selector/AlignedNumericValues.cs   |  175 +++
 src/main/csharp/Selector/ArithmeticExpression.cs   |   57 +
 src/main/csharp/Selector/BinaryExpression.cs       |   59 +
 src/main/csharp/Selector/BooleanCastExpression.cs  |   45 +
 .../BooleanConstantExpression.cs}                  |   72 +-
 .../BooleanUnaryExpression.cs}                     |   73 +-
 src/main/csharp/Selector/ComparisonExpression.cs   |  162 +++
 src/main/csharp/Selector/ConstantExpression.cs     |  157 +++
 src/main/csharp/Selector/DivideExpression.cs       |   67 ++
 src/main/csharp/Selector/EqualExpression.cs        |   47 +
 .../GreaterExpression.cs}                          |   76 +-
 .../GreaterOrEqualExpression.cs}                   |   77 +-
 .../IBooleanExpression.cs}                         |   69 +-
 .../IExpression.cs}                                |   69 +-
 src/main/csharp/Selector/InExpression.cs           |   98 ++
 src/main/csharp/Selector/IsNullExpression.cs       |   59 +
 .../LesserExpression.cs}                           |   76 +-
 .../LesserOrEqualExpression.cs}                    |   77 +-
 src/main/csharp/Selector/LikeExpression.cs         |  124 +++
 src/main/csharp/Selector/LogicExpression.cs        |   48 +
 .../csharp/Selector/MessageEvaluationContext.cs    |   78 ++
 src/main/csharp/Selector/MinusExpression.cs        |   67 ++
 src/main/csharp/Selector/ModExpression.cs          |   67 ++
 src/main/csharp/Selector/MultiplyExpression.cs     |   67 ++
 src/main/csharp/Selector/NOTExpression.cs          |   45 +
 src/main/csharp/Selector/NegateExpression.cs       |   51 +
 src/main/csharp/Selector/ORExpression.cs           |   46 +
 src/main/csharp/Selector/ParseException.cs         |  197 ++++
 src/main/csharp/Selector/PlusExpression.cs         |   68 ++
 src/main/csharp/Selector/PropertyExpression.cs     |   53 +
 src/main/csharp/Selector/SelectorParser.cs         | 1172 ++++++++++++++++++++
 src/main/csharp/Selector/SelectorParser.csc        |  589 ++++++++++
 .../csharp/Selector/SelectorParserConstants.cs     |   75 ++
 .../csharp/Selector/SelectorParserTokenManager.cs  | 1042 +++++++++++++++++
 src/main/csharp/Selector/SimpleCharStream.cs       |  366 ++++++
 src/main/csharp/Selector/Token.cs                  |   78 ++
 src/main/csharp/Selector/TokenMgrError.cs          |  130 +++
 src/main/csharp/Selector/UnaryExpression.cs        |   66 ++
 src/main/csharp/Session.cs                         |   12 +-
 vs2008-msmq.csproj                                 |   47 +
 55 files changed, 7585 insertions(+), 463 deletions(-)

diff --git a/src/main/csharp/BaseMessage.cs b/src/main/csharp/BaseMessage.cs
index db73f4c..8ea7e31 100644
--- a/src/main/csharp/BaseMessage.cs
+++ b/src/main/csharp/BaseMessage.cs
@@ -1,4 +1,4 @@
-/*
+ /*
  * 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.
@@ -24,28 +24,9 @@ namespace Apache.NMS.MSMQ
 
 	public class BaseMessage : IMessage
 	{
-		private PrimitiveMap propertiesMap = new PrimitiveMap();
-		private IDestination destination;
-		private string correlationId;
-		private TimeSpan timeToLive;
-		private string messageId;
-		private MsgDeliveryMode deliveryMode;
-		private MsgPriority priority;
-		private Destination replyTo;
-		private byte[] content;
-		private string type;
-		private event AcknowledgeHandler Acknowledger;
-		private DateTime timestamp = new DateTime();
-		private bool readOnlyMsgBody = false;
-
-		public bool ReadOnlyBody
-		{
-			get { return readOnlyMsgBody; }
-			set { readOnlyMsgBody = value; }
-		}
-
-		// IMessage interface
+		#region Acknowledgement
 
+		private event AcknowledgeHandler Acknowledger;
 		public void Acknowledge()
 		{
 			if(null != Acknowledger)
@@ -54,6 +35,27 @@ namespace Apache.NMS.MSMQ
 			}
 		}
 
+		#endregion
+
+		#region Message body
+
+		private byte[] content;
+		public byte[] Content
+		{
+			get { return content; }
+			set { this.content = value; }
+		}
+
+		private bool readOnlyMsgBody = false;
+		/// <summary>
+		/// Whether the message body is read-only.
+		/// </summary>
+		public bool ReadOnlyBody
+		{
+			get { return readOnlyMsgBody; }
+			set { readOnlyMsgBody = value; }
+		}
+
 		/// <summary>
 		/// Clears out the message body. Clearing a message's body does not clear its header
 		/// values or property entries.
@@ -67,26 +69,82 @@ namespace Apache.NMS.MSMQ
 			this.readOnlyMsgBody = false;
 		}
 
+		#endregion
+
+		#region Message properties
+
+		private PrimitiveMap propertiesMap = new PrimitiveMap();
+		private MessagePropertyIntercepter propertyHelper;
+		/// <summary>
+		/// Provides access to the message properties (headers)
+		/// </summary>
+		public Apache.NMS.IPrimitiveMap Properties
+		{
+			get
+			{
+                if(propertyHelper == null)
+                {
+				    propertyHelper = new Apache.NMS.Util.MessagePropertyIntercepter(
+					    this, propertiesMap, this.ReadOnlyProperties);
+				}
+
+                return propertyHelper;
+			}
+		}
+
+		private bool readOnlyMsgProperties = false;
+		/// <summary>
+		/// Whether the message properties is read-only.
+		/// </summary>
+		public virtual bool ReadOnlyProperties
+		{
+			get { return this.readOnlyMsgProperties; }
+
+			set
+			{
+				if(this.propertyHelper != null)
+				{
+					this.propertyHelper.ReadOnly = value;
+				}
+				this.readOnlyMsgProperties = value;
+			}
+		}
+
 		/// <summary>
 		/// Clears a message's properties.
-		///
 		/// The message's header fields and body are not cleared.
 		/// </summary>
-		public virtual void ClearProperties()
+		public void ClearProperties()
 		{
-			propertiesMap.Clear();
+            this.ReadOnlyProperties = false;
+            this.propertiesMap.Clear();
 		}
 
-		// Properties
+		public object GetObjectProperty(string name)
+		{
+			return Properties[name];
+		}
 
-		public IPrimitiveMap Properties
+		public void SetObjectProperty(string name, object value)
 		{
-			get { return propertiesMap; }
+            Properties[name] = value;
 		}
 
+		#endregion
 
-		// NMS headers
+		#region Message header fields
 
+		private string messageId;
+		/// <summary>
+		/// The message ID which is set by the provider
+		/// </summary>
+		public string NMSMessageId
+		{
+			get { return messageId; }
+			set { messageId = value; }
+		}
+
+		private string correlationId;
 		/// <summary>
 		/// The correlation ID used to correlate messages with conversations or long running business processes
 		/// </summary>
@@ -96,6 +154,7 @@ namespace Apache.NMS.MSMQ
 			set { correlationId = value; }
 		}
 
+		private IDestination destination;
 		/// <summary>
 		/// The destination of the message
 		/// </summary>
@@ -105,6 +164,7 @@ namespace Apache.NMS.MSMQ
 			set { destination = value; }
 		}
 
+		private TimeSpan timeToLive;
 		/// <summary>
 		/// The time in milliseconds that this message should expire in
 		/// </summary>
@@ -114,15 +174,7 @@ namespace Apache.NMS.MSMQ
 			set { timeToLive = value; }
 		}
 
-		/// <summary>
-		/// The message ID which is set by the provider
-		/// </summary>
-		public string NMSMessageId
-		{
-			get { return messageId; }
-			set { messageId = value; }
-		}
-
+		private MsgDeliveryMode deliveryMode;
 		/// <summary>
 		/// Whether or not this message is persistent
 		/// </summary>
@@ -132,6 +184,7 @@ namespace Apache.NMS.MSMQ
 			set { deliveryMode = value; }
 		}
 
+		private MsgPriority priority;
 		/// <summary>
 		/// The Priority on this message
 		/// </summary>
@@ -150,7 +203,7 @@ namespace Apache.NMS.MSMQ
             set { }
 		}
 
-
+		private Destination replyTo;
 		/// <summary>
 		/// The destination that the consumer of this message should send replies to
 		/// </summary>
@@ -160,7 +213,7 @@ namespace Apache.NMS.MSMQ
 			set { replyTo = (Destination) value; }
 		}
 
-
+		private DateTime timestamp = new DateTime();
 		/// <summary>
 		/// The timestamp the broker added to the message
 		/// </summary>
@@ -170,12 +223,7 @@ namespace Apache.NMS.MSMQ
 			set { timestamp = value; }
 		}
 
-		public byte[] Content
-		{
-			get { return content; }
-			set { this.content = value; }
-		}
-
+		private string type;
 		/// <summary>
 		/// The type name of this message
 		/// </summary>
@@ -185,15 +233,9 @@ namespace Apache.NMS.MSMQ
 			set { type = value; }
 		}
 
+        #endregion
 
-		public object GetObjectProperty(string name)
-		{
-			return null;
-		}
-
-		public void SetObjectProperty(string name, object value)
-		{
-		}
+        #region Check access mode
 
 		protected void FailIfReadOnlyBody()
 		{
@@ -205,11 +247,13 @@ namespace Apache.NMS.MSMQ
 
 		protected void FailIfWriteOnlyBody()
 		{
-			if( ReadOnlyBody == false )
+			if(ReadOnlyBody == false)
 			{
 				throw new MessageNotReadableException("Message is in Write-Only mode.");
 			}
 		}
+
+        #endregion
 	}
 }
 
diff --git a/src/main/csharp/DefaultMessageConverter.cs b/src/main/csharp/DefaultMessageConverter.cs
index 2aa3438..83097fc 100644
--- a/src/main/csharp/DefaultMessageConverter.cs
+++ b/src/main/csharp/DefaultMessageConverter.cs
@@ -32,12 +32,61 @@ namespace Apache.NMS.MSMQ
 		StreamMessage
 	}
 
-	public class DefaultMessageConverter : IMessageConverter
+    /// <summary>
+    /// This class provides default rules for converting MSMQ to and from
+    /// NMS messages, when the peer system expects or produces compatible
+    /// mappings, typically when the peer system is also implemented on
+    /// Apache.NMS.
+    /// Default mappings are as follows :
+    /// <ul>
+    /// <li>
+    ///   the MSMQ Message.AppSetting field is used for specifying the NMS
+    ///   message type, as specified by the <c>NMSMessageType</c> enumeration.
+    /// </li>
+    /// <li>
+    ///   the MSMQ Message.Extension field is populated with a map
+    ///   (a marshalled <c>PrimitiveMap</c>) of message properties.
+    /// </li>
+    /// <li>
+    ///   in earlier versions of Apache.NMS.MSMQ, the MSMQ Message.Label
+    ///   field was populated with the value of the NMSType field. Setting
+    ///   <c>SetLabelAsNMSType</c> to true (the default value) applies that
+    ///   same rule, which makes it compatible with existing NMS peers. If
+    ///   set to false, the Message.Label field is populated with the value
+    ///   of a "Label" property, if it exists, thus making it readable by
+    ///   standard management or monitoring tools. The NMSType value is then
+    ///   transmitted as a field in the Message.Extension map.
+    /// </li>
+    /// </ul>
+    /// Please note that in earlier versions of Apache.NMS, only one property
+    /// was set in the Message.Extension field : the NMSCorrelationID.
+    /// The native Message.CorrelationId field is not settable, except for
+    /// reply messages explicitely created as such through the MSMQ API.
+    /// Transmission of the correlation id. through a mapped property called
+    /// NMSCorrelationID is therefore maintained.
+    /// When exchanging messages with a non compatible peer, a specific
+    /// message converter must be provided, which should at least be able to
+    /// map message types and define the encoding used for text messages.
+    /// </summary>
+	public class DefaultMessageConverter : IMessageConverterEx
 	{
+        private bool setLabelAsNMSType = true;
+        public bool SetLabelAsNMSType
+        {
+            get { return setLabelAsNMSType; }
+            set { setLabelAsNMSType = value; }
+        }
+
+        #region Messages
+        /// <summary>
+        /// Converts the specified NMS message to an equivalent MSMQ message.
+        /// </summary>
+        /// <param name="message">NMS message to be converted.</param>
+        /// <result>Converted MSMQ message.</result>
 		public virtual Message ToMsmqMessage(IMessage message)
 		{
 			Message msmqMessage = new Message();
-			PrimitiveMap metaData = new PrimitiveMap();
+			PrimitiveMap propertyData = new PrimitiveMap();
 
 			ConvertMessageBodyToMSMQ(message, msmqMessage);
 
@@ -48,32 +97,72 @@ namespace Apache.NMS.MSMQ
 
 			if(message.NMSCorrelationID != null)
 			{
-				metaData.SetString("NMSCorrelationID", message.NMSCorrelationID);
+				propertyData.SetString("NMSCorrelationID", message.NMSCorrelationID);
 			}
 
 			msmqMessage.Recoverable = (message.NMSDeliveryMode == MsgDeliveryMode.Persistent);
-			msmqMessage.Priority = ToMessagePriority(message.NMSPriority);
+			msmqMessage.Priority = ToMsmqMessagePriority(message.NMSPriority);
 			msmqMessage.ResponseQueue = ToMsmqDestination(message.NMSReplyTo);
 			if(message.NMSType != null)
 			{
-				msmqMessage.Label = message.NMSType;
+                if(SetLabelAsNMSType)
+                {
+				    propertyData.SetString("NMSType", message.NMSType);
+                }
+                else
+                {
+                    msmqMessage.Label = message.NMSType;
+                }
 			}
 
-			// Store the NMS meta data in the extension area
-			msmqMessage.Extension = metaData.Marshal();
+            // Populate property data
+            foreach(object keyObject in message.Properties.Keys)
+            {
+              string key = (keyObject as string);
+              object val = message.Properties.GetString(key);
+              if(!SetLabelAsNMSType && string.Compare(key, "Label", true) == 0 && val != null)
+              {
+				msmqMessage.Label = val.ToString();
+              }
+              else
+              {
+				propertyData[key] = val;
+              }
+            }
+
+			// Store the NMS property data in the extension area
+			msmqMessage.Extension = propertyData.Marshal();
 			return msmqMessage;
 		}
 
+        /// <summary>
+        /// Converts the specified MSMQ message to an equivalent NMS message
+        /// (including its message body).
+        /// </summary>
+        /// <param name="message">MSMQ message to be converted.</param>
+        /// <result>Converted NMS message.</result>
 		public virtual IMessage ToNmsMessage(Message message)
 		{
-			BaseMessage answer = CreateNmsMessage(message);
-			// Get the NMS meta data from the extension area
-			PrimitiveMap metaData = PrimitiveMap.Unmarshal(message.Extension);
+            return ToNmsMessage(message, true);
+        }
+
+        /// <summary>
+        /// Converts the specified MSMQ message to an equivalent NMS message.
+        /// </summary>
+        /// <param name="message">MSMQ message to be converted.</param>
+        /// <param name="convertBody">true if message body should be converted.</param>
+        /// <result>Converted NMS message.</result>
+		public virtual IMessage ToNmsMessage(Message message, bool convertBody)
+		{
+			BaseMessage answer = CreateNmsMessage(message, convertBody);
+
+			// Get the NMS property data from the extension area
+			PrimitiveMap propertyData = PrimitiveMap.Unmarshal(message.Extension);
 
 			try
 			{
 				answer.NMSMessageId = message.Id;
-				answer.NMSCorrelationID = metaData.GetString("NMSCorrelationID");
+				answer.NMSCorrelationID = propertyData.GetString("NMSCorrelationID");
 				answer.NMSDeliveryMode = (message.Recoverable ? MsgDeliveryMode.Persistent : MsgDeliveryMode.NonPersistent);
 				answer.NMSDestination = ToNmsDestination(message.DestinationQueue);
 			}
@@ -83,18 +172,85 @@ namespace Apache.NMS.MSMQ
 
 			try
 			{
-				answer.NMSType = message.Label;
 				answer.NMSReplyTo = ToNmsDestination(message.ResponseQueue);
 				answer.NMSTimeToLive = message.TimeToBeReceived;
+			    answer.NMSPriority = ToNmsMsgPriority(message.Priority);
+			}
+			catch(InvalidOperationException)
+			{
+			}
+
+			try
+			{
+                if(message.Label != null)
+                {
+                    if(SetLabelAsNMSType)
+                    {
+                        answer.NMSType = message.Label;
+                    }
+                    else
+                    {
+                        answer.Properties["Label"] = message.Label;
+                    }
+                }
+                answer.Properties["LookupId"] = message.LookupId;
 			}
 			catch(InvalidOperationException)
 			{
 			}
 
+            foreach(object keyObject in propertyData.Keys)
+            {
+			    try
+			    {
+                    string key = (keyObject as string);
+                    if(string.Compare(key, "NMSType", true) == 0)
+                    {
+			    	    answer.NMSType = propertyData.GetString(key);
+                    }
+                    else if(string.Compare(key, "NMSCorrelationID", true) == 0)
+                    {
+			    	    answer.NMSCorrelationID = propertyData.GetString("NMSCorrelationID");
+                    }
+                    else
+                    {
+			    	    answer.Properties[key] = propertyData[key];
+                    }
+			    }
+			    catch(InvalidOperationException)
+			    {
+			    }
+            }
 			return answer;
 		}
 
-		private static MessagePriority ToMessagePriority(MsgPriority msgPriority)
+        #endregion
+
+        #region Message priority
+
+        // Message priorities are defined as follows :
+        // | MSMQ               | NMS                |
+        // | MessagePriority	| MsgPriority        |
+        // +--------------------+--------------------+
+        // | Lowest             | Lowest             |
+        // | VeryLow            | VeryLow            |
+        // | Low                | Low                |
+        // |                \-> | AboveLow           |
+        // |                /-> | BelowNormal        |
+        // | Normal             | Normal             |
+        // | AboveNormal        | AboveNormal        |
+        // | High               | High               |
+        // | VeryHigh           | VeryHigh           |
+        // | Highest            | Highest            |
+        // +--------------------+--------------------+
+
+        /// <summary>
+        /// Converts the specified NMS message priority to an equivalent MSMQ
+        /// message priority.
+        /// </summary>
+        /// <param name="msgPriority">NMS message priority to be converted.</param>
+        /// <result>Converted MSMQ message priority.</result>
+		private static MessagePriority ToMsmqMessagePriority(MsgPriority msgPriority)
 		{
 			switch(msgPriority)
 			{
@@ -127,6 +283,153 @@ namespace Apache.NMS.MSMQ
 			}
 		}
 
+        /// <summary>
+        /// Converts the specified MSMQ message priority to an equivalent NMS
+        /// message priority.
+        /// </summary>
+        /// <param name="messagePriority">MSMQ message priority to be converted.</param>
+        /// <result>Converted NMS message priority.</result>
+		private static MsgPriority ToNmsMsgPriority(MessagePriority messagePriority)
+		{
+			switch(messagePriority)
+			{
+			case MessagePriority.Lowest:
+				return MsgPriority.Lowest;
+
+			case MessagePriority.VeryLow:
+				return MsgPriority.VeryLow;
+
+			case MessagePriority.Low:
+				return MsgPriority.Low;
+
+			default:
+			case MessagePriority.Normal:
+				return MsgPriority.Normal;
+
+			case MessagePriority.AboveNormal:
+				return MsgPriority.AboveNormal;
+
+			case MessagePriority.High:
+				return MsgPriority.High;
+
+			case MessagePriority.VeryHigh:
+				return MsgPriority.VeryHigh;
+
+			case MessagePriority.Highest:
+				return MsgPriority.Highest;
+			}
+		}
+
+        #endregion
+
+        #region Message creation
+
+        // Conversion of the message body has been separated from the creation
+        // of the NMS message object for performance reasons when using
+        // selectors (selectors handle only message attributes, not message
+        // bodies).
+        // CreateNmsMessage(Message) is maintained for compatibility reasons
+        // with existing clients that may have implemented derived classes,
+        // instead of completely removing the body conversion part from the
+        // method.
+
+        /// <summary>
+        /// Creates an NMS message of appropriate type for the specified MSMQ
+        /// message, and convert the message body.
+        /// </summary>
+        /// <param name="message">MSMQ message.</param>
+        /// <result>NMS message created for retrieving the MSMQ message.</result>
+		protected virtual BaseMessage CreateNmsMessage(Message message)
+		{
+            return CreateNmsMessage(message, true);
+        }
+
+        /// <summary>
+        /// Creates an NMS message of appropriate type for the specified MSMQ
+        /// message, and convert the message body if specified.
+        /// </summary>
+        /// <param name="message">MSMQ message.</param>
+        /// <param name="convertBody">true if the message body must be
+        /// converted.</param>
+        /// <result>NMS message created for retrieving the MSMQ message.</result>
+		protected virtual BaseMessage CreateNmsMessage(Message message,
+            bool convertBody)
+		{
+			BaseMessage result = null;
+
+			if((int) NMSMessageType.TextMessage == message.AppSpecific)
+			{
+				TextMessage textMessage = new TextMessage();
+
+                if(convertBody)
+                {
+                    ConvertTextMessageBodyToNMS(message, textMessage);
+                }
+
+				result = textMessage;
+			}
+			else if((int) NMSMessageType.BytesMessage == message.AppSpecific)
+			{
+				BytesMessage bytesMessage = new BytesMessage();
+
+                if(convertBody)
+                {
+                    ConvertBytesMessageBodyToNMS(message, bytesMessage);
+                }
+
+				result = bytesMessage;
+			}
+			else if((int) NMSMessageType.ObjectMessage == message.AppSpecific)
+			{
+				ObjectMessage objectMessage = new ObjectMessage();
+
+                if(convertBody)
+                {
+                    ConvertObjectMessageBodyToNMS(message, objectMessage);
+                }
+
+				result = objectMessage;
+			}
+			else if((int) NMSMessageType.MapMessage == message.AppSpecific)
+			{
+				MapMessage mapMessage = new MapMessage();
+
+                if(convertBody)
+                {
+                    ConvertMapMessageBodyToNMS(message, mapMessage);
+                }
+
+				result = mapMessage;
+			}
+			else if((int) NMSMessageType.StreamMessage == message.AppSpecific)
+			{
+				StreamMessage streamMessage = new StreamMessage();
+
+                if(convertBody)
+                {
+                    ConvertStreamMessageBodyToNMS(message, streamMessage);
+                }
+
+				result = streamMessage;
+			}
+			else
+			{
+				BaseMessage baseMessage = new BaseMessage();
+				result = baseMessage;
+			}
+
+			return result;
+		}
+
+        #endregion
+
+        #region Message body
+
+        /// <summary>
+        /// Converts an NMS message body to the equivalent MSMQ message body.
+        /// </summary>
+        /// <param name="message">Source NMS message.</param>
+        /// <param name="answer">Target MSMQ message.</param>
 		protected virtual void ConvertMessageBodyToMSMQ(IMessage message, Message answer)
 		{
 			if(message is TextMessage)
@@ -172,78 +475,133 @@ namespace Apache.NMS.MSMQ
 			}
 		}
 
-		protected virtual BaseMessage CreateNmsMessage(Message message)
+        /// <summary>
+        /// Converts an MSMQ message body to the equivalent NMS message body.
+        /// </summary>
+        /// <param name="message">Source MSMQ message.</param>
+        /// <param name="answer">Target NMS message.</param>
+		public virtual void ConvertMessageBodyToNMS(Message message, IMessage answer)
 		{
-			BaseMessage result = null;
-
-			if((int) NMSMessageType.TextMessage == message.AppSpecific)
+			if(answer is TextMessage)
 			{
-				TextMessage textMessage = new TextMessage();
-				string content = String.Empty;
-
-				if(message.BodyStream != null && message.BodyStream.Length > 0)
-				{
-					byte[] buf = null;
-					buf = new byte[message.BodyStream.Length];
-					message.BodyStream.Read(buf, 0, buf.Length);
-					content = Encoding.UTF32.GetString(buf);
-				}
-
-				textMessage.Text = content;
-				result = textMessage;
+				ConvertTextMessageBodyToNMS(message, (TextMessage)answer);
 			}
-			else if((int) NMSMessageType.BytesMessage == message.AppSpecific)
+			else if(answer is BytesMessage)
 			{
-				byte[] buf = null;
-
-				if(message.BodyStream != null && message.BodyStream.Length > 0)
-				{
-					buf = new byte[message.BodyStream.Length];
-					message.BodyStream.Read(buf, 0, buf.Length);
-				}
-
-				BytesMessage bytesMessage = new BytesMessage();
-				bytesMessage.Content = buf;
-				result = bytesMessage;
+				ConvertBytesMessageBodyToNMS(message, (BytesMessage)answer);
 			}
-			else if((int) NMSMessageType.ObjectMessage == message.AppSpecific)
+			else if(answer is ObjectMessage)
 			{
-				ObjectMessage objectMessage = new ObjectMessage();
-
-				objectMessage.Body = message.Body;
-				result = objectMessage;
+				ConvertObjectMessageBodyToNMS(message, (ObjectMessage)answer);
 			}
-			else if((int) NMSMessageType.MapMessage == message.AppSpecific)
+			else if(answer is MapMessage)
 			{
-				byte[] buf = null;
-
-				if(message.BodyStream != null && message.BodyStream.Length > 0)
-				{
-					buf = new byte[message.BodyStream.Length];
-					message.BodyStream.Read(buf, 0, buf.Length);
-				}
-
-				MapMessage mapMessage = new MapMessage();
-				mapMessage.Body = PrimitiveMap.Unmarshal(buf);
-				result = mapMessage;
+				ConvertMapMessageBodyToNMS(message, (MapMessage)answer);
 			}
-			else if((int) NMSMessageType.StreamMessage == message.AppSpecific)
+			else if(answer is StreamMessage)
 			{
-				StreamMessage streamMessage = new StreamMessage();
+				ConvertStreamMessageBodyToNMS(message, (StreamMessage)answer);
+			}
 
-				// TODO: Implement
-				result = streamMessage;
+			return;
+		}
+
+        /// <summary>
+        /// Converts an MSMQ message body to the equivalent NMS text message
+        /// body.
+        /// </summary>
+        /// <param name="message">Source MSMQ message.</param>
+        /// <param name="answer">Target NMS text message.</param>
+		public virtual void ConvertTextMessageBodyToNMS(Message message,
+            TextMessage answer)
+		{
+			string content = String.Empty;
+
+			if(message.BodyStream != null && message.BodyStream.Length > 0)
+			{
+				byte[] buf = new byte[message.BodyStream.Length];
+				message.BodyStream.Read(buf, 0, buf.Length);
+				content = Encoding.UTF32.GetString(buf);
 			}
-			else
+
+			answer.Text = content;
+		}
+
+        /// <summary>
+        /// Converts an MSMQ message body to the equivalent NMS bytes message
+        /// body.
+        /// </summary>
+        /// <param name="message">Source MSMQ message.</param>
+        /// <param name="answer">Target NMS bytes message.</param>
+		public virtual void ConvertBytesMessageBodyToNMS(Message message,
+            BytesMessage answer)
+		{
+			byte[] buf = null;
+
+			if(message.BodyStream != null && message.BodyStream.Length > 0)
 			{
-				BaseMessage baseMessage = new BaseMessage();
+				buf = new byte[message.BodyStream.Length];
+				message.BodyStream.Read(buf, 0, buf.Length);
+			}
 
-				result = baseMessage;
+			answer.Content = buf;
+		}
+
+        /// <summary>
+        /// Converts an MSMQ message body to the equivalent NMS object message
+        /// body.
+        /// </summary>
+        /// <param name="message">Source MSMQ message.</param>
+        /// <param name="answer">Target NMS object message.</param>
+		public virtual void ConvertObjectMessageBodyToNMS(Message message,
+            ObjectMessage answer)
+		{
+			answer.Body = message.Body;
+		}
+
+        /// <summary>
+        /// Converts an MSMQ message body to the equivalent NMS map message
+        /// body.
+        /// </summary>
+        /// <param name="message">Source MSMQ message.</param>
+        /// <param name="answer">Target NMS map message.</param>
+		public virtual void ConvertMapMessageBodyToNMS(Message message,
+            MapMessage answer)
+		{
+			byte[] buf = null;
+
+			if(message.BodyStream != null && message.BodyStream.Length > 0)
+			{
+				buf = new byte[message.BodyStream.Length];
+				message.BodyStream.Read(buf, 0, buf.Length);
 			}
 
-			return result;
+			answer.Body = PrimitiveMap.Unmarshal(buf);
+		}
+
+        /// <summary>
+        /// Converts an MSMQ message body to the equivalent NMS stream message
+        /// body.
+        /// </summary>
+        /// <param name="message">Source MSMQ message.</param>
+        /// <param name="answer">Target NMS stream message.</param>
+		public virtual void ConvertStreamMessageBodyToNMS(Message message,
+            StreamMessage answer)
+		{
+			// TODO: Implement
+            throw new NotImplementedException();
 		}
 
+        #endregion
+
+        #region Destination
+
+        /// <summary>
+        /// Converts an NMS destination to the equivalent MSMQ destination
+        /// (ie. queue).
+        /// </summary>
+        /// <param name="destination">NMS destination.</param>
+        /// <result>MSMQ queue.</result>
 		public MessageQueue ToMsmqDestination(IDestination destination)
 		{
 			if(null == destination)
@@ -254,6 +612,12 @@ namespace Apache.NMS.MSMQ
 			return new MessageQueue((destination as Destination).Path);
 		}
 
+        /// <summary>
+        /// Converts an MSMQ destination (ie. queue) to the equivalent NMS
+        /// destination.
+        /// </summary>
+        /// <param name="destinationQueue">MSMQ destination queue.</param>
+        /// <result>NMS destination.</result>
 		protected virtual IDestination ToNmsDestination(MessageQueue destinationQueue)
 		{
 			if(null == destinationQueue)
@@ -263,5 +627,7 @@ namespace Apache.NMS.MSMQ
 
 			return new Queue(destinationQueue.Path);
 		}
+
+        #endregion
 	}
 }
diff --git a/src/main/csharp/IMessageConverter.cs b/src/main/csharp/IMessageConverter.cs
index 152377b..14c6669 100644
--- a/src/main/csharp/IMessageConverter.cs
+++ b/src/main/csharp/IMessageConverter.cs
@@ -20,15 +20,27 @@ namespace Apache.NMS.MSMQ
 {
 	public interface IMessageConverter
 	{
-
-		/// <summary>
-		/// Method ToMSMQMessageQueue
-		/// </summary>
-		/// <param name="destination">An IDestination</param>
-		/// <returns>A  MessageQueue</returns>
-		MessageQueue ToMsmqDestination(IDestination destination);
-
+        /// <summary>
+        /// Converts the specified NMS message to an equivalent MSMQ message.
+        /// </summary>
+        /// <param name="message">NMS message to be converted.</param>
+        /// <result>Converted MSMQ message.</result>
 		Message ToMsmqMessage(IMessage message);
+
+        /// <summary>
+        /// Converts the specified MSMQ message to an equivalent NMS message
+        /// (including its message body).
+        /// </summary>
+        /// <param name="message">MSMQ message to be converted.</param>
+        /// <result>Converted NMS message.</result>
 		IMessage ToNmsMessage(Message message);
+
+        /// <summary>
+        /// Converts an NMS destination to the equivalent MSMQ destination
+        /// (ie. queue).
+        /// </summary>
+        /// <param name="destination">NMS destination.</param>
+        /// <result>MSMQ queue.</result>
+		MessageQueue ToMsmqDestination(IDestination destination);
 	}
 }
diff --git a/src/main/csharp/IMessageConverterEx.cs b/src/main/csharp/IMessageConverterEx.cs
new file mode 100644
index 0000000..92be928
--- /dev/null
+++ b/src/main/csharp/IMessageConverterEx.cs
@@ -0,0 +1,44 @@
+using System.Messaging;
+/*
+ * 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.
+ */
+
+namespace Apache.NMS.MSMQ
+{
+    /// <summary>
+    /// Extended IMessageConverter interface supporting new methods for
+    /// optimizing message selection through "selectors".
+    /// The original IMessageConverter is maintained for compatibility
+    /// reasons with existing clients implementing it.
+    /// </summary>
+	public interface IMessageConverterEx : IMessageConverter
+	{
+        /// <summary>
+        /// Converts the specified MSMQ message to an equivalent NMS message.
+        /// </summary>
+        /// <param name="message">MSMQ message to be converted.</param>
+        /// <param name="convertBody">true if message body should be converted.</param>
+        /// <result>Converted NMS message.</result>
+		IMessage ToNmsMessage(Message message, bool convertBody);
+
+        /// <summary>
+        /// Converts an MSMQ message body to the equivalent NMS message body.
+        /// </summary>
+        /// <param name="message">Source MSMQ message.</param>
+        /// <param name="answer">Target NMS message.</param>
+		void ConvertMessageBodyToNMS(Message message, IMessage answer);
+	}
+}
diff --git a/src/main/csharp/MessageConsumer.cs b/src/main/csharp/MessageConsumer.cs
index 6961298..eaaed5c 100644
--- a/src/main/csharp/MessageConsumer.cs
+++ b/src/main/csharp/MessageConsumer.cs
@@ -1,6 +1,8 @@
 using System;
 using System.Messaging;
 using System.Threading;
+using Apache.NMS.Util;
+using Apache.NMS.MSMQ.Readers;
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -17,12 +19,11 @@ using System.Threading;
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-using Apache.NMS.Util;
 
 namespace Apache.NMS.MSMQ
 {
     /// <summary>
-    /// An object capable of receiving messages from some destination
+    /// An object capable of receiving messages from some destination.
     /// </summary>
     public class MessageConsumer : IMessageConsumer
     {
@@ -31,8 +32,6 @@ namespace Apache.NMS.MSMQ
         private readonly Session session;
         private readonly AcknowledgementMode acknowledgementMode;
         private MessageQueue messageQueue;
-        private event MessageListener listener;
-        private int listenerCount = 0;
         private Thread asyncDeliveryThread = null;
         private AutoResetEvent pause = new AutoResetEvent(false);
         private Atomic<bool> asyncDelivery = new Atomic<bool>(false);
@@ -44,17 +43,46 @@ namespace Apache.NMS.MSMQ
             set { this.consumerTransformer = value; }
         }
 
-        public MessageConsumer(Session session, AcknowledgementMode acknowledgementMode, MessageQueue messageQueue)
+        private IMessageReader reader;
+
+        /// <summary>
+        /// Constructs a message consumer on the specified queue.
+        /// </summary>
+        /// <param name="session">The messaging session.</param>
+        /// <param name="acknowledgementMode">The message acknowledgement mode.</param>
+        /// <param name="messageQueue">The message queue to consume messages from.</param>
+        public MessageConsumer(Session session,
+            AcknowledgementMode acknowledgementMode, MessageQueue messageQueue)
+            : this(session, acknowledgementMode, messageQueue, null)
+        {
+        }
+
+        /// <summary>
+        /// Constructs a message consumer on the specified queue, using a
+        /// selector for filtering incoming messages.
+        /// </summary>
+        /// <param name="session">The messaging session.</param>
+        /// <param name="acknowledgementMode">The message acknowledgement mode.</param>
+        /// <param name="messageQueue">The message queue to consume messages from.</param>
+        /// <param name="selector">The selection criteria.</param>
+        public MessageConsumer(Session session,
+            AcknowledgementMode acknowledgementMode, MessageQueue messageQueue,
+            string selector)
         {
             this.session = session;
             this.acknowledgementMode = acknowledgementMode;
             this.messageQueue = messageQueue;
-            if(null != this.messageQueue)
+            if(this.messageQueue != null)
             {
                 this.messageQueue.MessageReadPropertyFilter.SetAll();
             }
+
+            reader = MessageReaderUtil.CreateMessageReader(
+                messageQueue, session.MessageConverter, selector);
         }
 
+        private int listenerCount = 0;
+        private event MessageListener listener;
         public event MessageListener Listener
         {
             add
@@ -85,32 +113,8 @@ namespace Apache.NMS.MSMQ
 
             if(messageQueue != null)
             {
-                Message message;
-
-                try
-                {
-                    message = messageQueue.Receive(zeroTimeout);
-                }
-                catch
-                {
-                    message = null;
-                }
-
-                if(null == message)
-                {
-                    ReceiveCompletedEventHandler receiveMsg =
-                            delegate(Object source, ReceiveCompletedEventArgs asyncResult) {
-                                message = messageQueue.EndReceive(asyncResult.AsyncResult);
-                                pause.Set();
-                            };
-
-                    messageQueue.ReceiveCompleted += receiveMsg;
-                    messageQueue.BeginReceive();
-                    pause.WaitOne();
-                    messageQueue.ReceiveCompleted -= receiveMsg;
-                }
-
-                nmsMessage = ToNmsMessage(message);
+                nmsMessage = reader.Receive();
+                nmsMessage = TransformMessage(nmsMessage);
             }
 
             return nmsMessage;
@@ -122,8 +126,8 @@ namespace Apache.NMS.MSMQ
 
             if(messageQueue != null)
             {
-                Message message = messageQueue.Receive(timeout);
-                nmsMessage = ToNmsMessage(message);
+                nmsMessage = reader.Receive(timeout);
+                nmsMessage = TransformMessage(nmsMessage);
             }
 
             return nmsMessage;
@@ -135,8 +139,8 @@ namespace Apache.NMS.MSMQ
 
             if(messageQueue != null)
             {
-                Message message = messageQueue.Receive(zeroTimeout);
-                nmsMessage = ToNmsMessage(message);
+                nmsMessage = reader.Receive(zeroTimeout);
+                nmsMessage = TransformMessage(nmsMessage);
             }
 
             return nmsMessage;
@@ -226,25 +230,20 @@ namespace Apache.NMS.MSMQ
             session.Connection.HandleException(e);
         }
 
-        protected virtual IMessage ToNmsMessage(Message message)
+        protected virtual IMessage TransformMessage(IMessage message)
         {
-            if(message == null)
-            {
-                return null;
-            }
-
-            IMessage converted = session.MessageConverter.ToNmsMessage(message);
+            IMessage transformed = message;
 
-            if(this.ConsumerTransformer != null)
+            if(message != null && this.ConsumerTransformer != null)
             {
-                IMessage newMessage = ConsumerTransformer(this.session, this, converted);
+                IMessage newMessage = ConsumerTransformer(this.session, this, message);
                 if(newMessage != null)
                 {
-                    converted = newMessage;
+                    transformed = newMessage;
                 }
             }
 
-            return converted;
+            return transformed;
         }
     }
 }
diff --git a/src/main/csharp/QueueBrowser.cs b/src/main/csharp/QueueBrowser.cs
index 32752c5..3ff795d 100644
--- a/src/main/csharp/QueueBrowser.cs
+++ b/src/main/csharp/QueueBrowser.cs
@@ -19,6 +19,7 @@ using System.Collections;
 using System.Messaging;
 using Apache.NMS;
 using Apache.NMS.Util;
+using Apache.NMS.MSMQ.Readers;
 
 namespace Apache.NMS.MSMQ
 {
@@ -30,7 +31,17 @@ namespace Apache.NMS.MSMQ
         private readonly Session session;
         private MessageQueue messageQueue;
 
+        private string selector;
+
+        private IMessageReader reader;
+        
 		public QueueBrowser(Session session, MessageQueue messageQueue)
+            : this(session, messageQueue, null)
+		{
+		}
+
+		public QueueBrowser(Session session, MessageQueue messageQueue,
+            string selector)
 		{
             this.session = session;
             this.messageQueue = messageQueue;
@@ -39,6 +50,8 @@ namespace Apache.NMS.MSMQ
                 this.messageQueue.MessageReadPropertyFilter.SetAll();
             }
 
+            reader = MessageReaderUtil.CreateMessageReader(
+                messageQueue, session.MessageConverter, selector);
 		}
 
 		~QueueBrowser()
@@ -95,7 +108,7 @@ namespace Apache.NMS.MSMQ
 
 		public string MessageSelector
 		{
-			get { throw new NotSupportedException(); }
+			get { return selector; }
 		}
 
 		public IQueue Queue
@@ -107,11 +120,14 @@ namespace Apache.NMS.MSMQ
 		{
 			private readonly Session session;
 			private readonly MessageEnumerator innerEnumerator;
+            private readonly IMessageReader reader;
 
-			public Enumerator(Session session, MessageQueue messageQueue)
+			public Enumerator(Session session, MessageQueue messageQueue,
+                IMessageReader reader)
 			{
 				this.session = session;
 				this.innerEnumerator = messageQueue.GetMessageEnumerator2();
+                this.reader = reader;
 			}
 
 			public object Current
@@ -124,7 +140,14 @@ namespace Apache.NMS.MSMQ
 
 			public bool MoveNext()
 			{
-				return this.innerEnumerator.MoveNext();
+                while(this.innerEnumerator.MoveNext())
+                {
+				    if(reader.Matches(this.innerEnumerator.Current))
+                    {
+                        return true;
+                    }
+                }
+                return false;
 			}
 
 			public void Reset()
@@ -135,7 +158,7 @@ namespace Apache.NMS.MSMQ
 
 		public IEnumerator GetEnumerator()
 		{
-			return new Enumerator(this.session, this.messageQueue);
+			return new Enumerator(this.session, this.messageQueue, this.reader);
 		}
 	}
 }
diff --git a/src/main/csharp/Readers/AbstractMessageReader.cs b/src/main/csharp/Readers/AbstractMessageReader.cs
new file mode 100644
index 0000000..7874696
--- /dev/null
+++ b/src/main/csharp/Readers/AbstractMessageReader.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Messaging;
+using Apache.NMS.MSMQ;
+/*
+ * 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.
+ */
+
+namespace Apache.NMS.MSMQ.Readers
+{
+    /// <summary>
+    /// Abstract MSMQ message reader. Derived classes support various
+    /// message filtering methods.
+    /// </summary>
+	public abstract class AbstractMessageReader : IMessageReader
+	{
+        protected MessageQueue messageQueue;
+        protected IMessageConverter messageConverter;
+        protected IMessageConverterEx messageConverterEx;
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        /// <param name="messageQueue">The MSMQ message queue from which
+        /// messages will be read.</param>
+        /// <param name="messageConverter">A message converter for mapping
+        /// MSMQ messages to NMS messages.</param>
+        public AbstractMessageReader(MessageQueue messageQueue,
+            IMessageConverter messageConverter)
+        {
+            this.messageQueue = messageQueue;
+
+            this.messageConverter = messageConverter;
+            this.messageConverterEx = (messageConverter as IMessageConverterEx);
+        }
+
+        /// <summary>
+        /// Returns without removing (peeks) the first message in the queue
+        /// referenced by this MessageQueue matching the selection criteria.
+        /// The Peek method is synchronous, so it blocks the current thread
+        /// until a message becomes available.
+        /// </summary>
+        /// <returns>Peeked message.</returns>
+        public abstract IMessage Peek();
+
+        /// <summary>
+        /// Returns without removing (peeks) the first message in the queue
+        /// referenced by this MessageQueue matching the selection criteria.
+        /// The Peek method is synchronous, so it blocks the current thread
+        /// until a message becomes available or the specified time-out occurs.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <returns>Peeked message.</returns>
+        public abstract IMessage Peek(TimeSpan timeSpan);
+
+        /// <summary>
+        /// Receives the first message available in the queue referenced by
+        /// the MessageQueue matching the selection criteria.
+        /// This call is synchronous, and blocks the current thread of execution
+        /// until a message is available.
+        /// </summary>
+        /// <returns>Received message.</returns>
+        public abstract IMessage Receive();
+
+        /// <summary>
+        /// Receives the first message available in the queue referenced by the
+        /// MessageQueue matching the selection criteria, and waits until either
+        /// a message is available in the queue, or the time-out expires.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <returns>Received message.</returns>
+        public abstract IMessage Receive(TimeSpan timeSpan);
+
+        /// <summary>
+        /// Receives the first message available in the transactional queue
+        /// referenced by the MessageQueue matching the selection criteria.
+        /// This call is synchronous, and blocks the current thread of execution
+        /// until a message is available.
+        /// </summary>
+        /// <param name="transaction">Transaction.</param>
+        /// <returns>Received message.</returns>
+        public abstract IMessage Receive(MessageQueueTransaction transaction);
+
+        /// <summary>
+        /// Receives the first message available in the transactional queue
+        /// referenced by the MessageQueue matching the selection criteria,
+        /// and waits until either a message is available in the queue, or the
+        /// time-out expires.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <param name="transaction">Transaction.</param>
+        /// <returns>Received message.</returns>
+        public abstract IMessage Receive(TimeSpan timeSpan,
+            MessageQueueTransaction transaction);
+
+        /// <summary>
+        /// Checks if an MSMQ message matches the selection criteria.
+        /// </summary>
+        /// <param name="message">MSMQ message.</param>
+        /// <return>true if the message matches the selection criteria.</return>
+        public abstract bool Matches(Message message);
+
+        /// <summary>
+        /// Converts an MSMQ message to an NMS message, using the converter
+        /// specified at construction time.
+        /// </summary>
+        /// <param name="message">MSMQ message.</param>
+        /// <return>NMS message.</return>
+        protected IMessage Convert(Message message)
+        {
+            return message == null ? null : messageConverter.ToNmsMessage(message);
+        }
+	}
+}
diff --git a/src/main/csharp/Readers/ByCorrelationIdMessageReader.cs b/src/main/csharp/Readers/ByCorrelationIdMessageReader.cs
new file mode 100644
index 0000000..fad3d1a
--- /dev/null
+++ b/src/main/csharp/Readers/ByCorrelationIdMessageReader.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Messaging;
+using Apache.NMS.MSMQ;
+/*
+ * 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.
+ */
+
+namespace Apache.NMS.MSMQ.Readers
+{
+    /// <summary>
+    /// MSMQ message reader, returning messages matching the specified
+    /// message identifier.
+    /// </summary>
+	public class ByCorrelationIdMessageReader : AbstractMessageReader
+	{
+        private string correlationId;
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        /// <param name="messageQueue">The MSMQ message queue from which
+        /// messages will be read.</param>
+        /// <param name="messageConverter">A message converter for mapping
+        /// MSMQ messages to NMS messages.</param>
+        /// <param name="correlationId">The correlation identifier of messages
+        /// to be read.</param>
+        public ByCorrelationIdMessageReader(MessageQueue messageQueue,
+            IMessageConverter messageConverter, string correlationId)
+            : base(messageQueue, messageConverter)
+        {
+            this.correlationId = correlationId;
+        }
+
+        /// <summary>
+        /// Returns without removing (peeks) the first message in the queue
+        /// referenced by this MessageQueue matching the selection criteria.
+        /// The Peek method is synchronous, so it blocks the current thread
+        /// until a message becomes available.
+        /// </summary>
+        /// <returns>Peeked message.</returns>
+        public override IMessage Peek()
+        {
+            return Convert(messageQueue.PeekByCorrelationId(correlationId));
+        }
+
+        /// <summary>
+        /// Returns without removing (peeks) the first message in the queue
+        /// referenced by this MessageQueue matching the selection criteria.
+        /// The Peek method is synchronous, so it blocks the current thread
+        /// until a message becomes available or the specified time-out occurs.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <returns>Peeked message.</returns>
+        public override IMessage Peek(TimeSpan timeSpan)
+        {
+            return Convert(messageQueue.PeekByCorrelationId(correlationId,
+                timeSpan));
+        }
+
+        /// <summary>
+        /// Receives the first message available in the queue referenced by
+        /// the MessageQueue matching the selection criteria.
+        /// This call is synchronous, and blocks the current thread of execution
+        /// until a message is available.
+        /// </summary>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive()
+        {
+            return Convert(messageQueue.ReceiveByCorrelationId(correlationId));
+        }
+
+        /// <summary>
+        /// Receives the first message available in the queue referenced by the
+        /// MessageQueue matching the selection criteria, and waits until either
+        /// a message is available in the queue, or the time-out expires.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive(TimeSpan timeSpan)
+        {
+            return Convert(messageQueue.ReceiveByCorrelationId(correlationId,
+                timeSpan));
+        }
+
+        /// <summary>
+        /// Receives the first message available in the transactional queue
+        /// referenced by the MessageQueue matching the selection criteria.
+        /// This call is synchronous, and blocks the current thread of execution
+        /// until a message is available.
+        /// </summary>
+        /// <param name="transaction">Transaction.</param>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive(MessageQueueTransaction transaction)
+        {
+            return Convert(messageQueue.ReceiveByCorrelationId(correlationId,
+                transaction));
+        }
+
+        /// <summary>
+        /// Receives the first message available in the transactional queue
+        /// referenced by the MessageQueue matching the selection criteria,
+        /// and waits until either a message is available in the queue, or the
+        /// time-out expires.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <param name="transaction">Transaction.</param>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive(TimeSpan timeSpan,
+            MessageQueueTransaction transaction)
+        {
+            return Convert(messageQueue.ReceiveByCorrelationId(correlationId,
+                timeSpan, transaction));
+        }
+
+        /// <summary>
+        /// Checks if an MSMQ message matches the selection criteria.
+        /// </summary>
+        /// <param name="message">MSMQ message.</param>
+        /// <return>true if the message matches the selection criteria.</return>
+        public override bool Matches(Message message)
+        {
+            // NB: case-sensitive match
+            return message.CorrelationId == correlationId;
+        }
+	}
+}
diff --git a/src/main/csharp/Readers/ByIdMessageReader.cs b/src/main/csharp/Readers/ByIdMessageReader.cs
new file mode 100644
index 0000000..f981ca8
--- /dev/null
+++ b/src/main/csharp/Readers/ByIdMessageReader.cs
@@ -0,0 +1,136 @@
+using System;
+using System.Messaging;
+using Apache.NMS.MSMQ;
+/*
+ * 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.
+ */
+
+namespace Apache.NMS.MSMQ.Readers
+{
+    /// <summary>
+    /// MSMQ message reader, returning messages matching the specified
+    /// message identifier.
+    /// </summary>
+	public class ByIdMessageReader : AbstractMessageReader
+	{
+        private string messageId;
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        /// <param name="messageQueue">The MSMQ message queue from which
+        /// messages will be read.</param>
+        /// <param name="messageConverter">A message converter for mapping
+        /// MSMQ messages to NMS messages.</param>
+        /// <param name="messageId">The message identifier of messages to
+        /// be read.</param>
+        public ByIdMessageReader(MessageQueue messageQueue,
+            IMessageConverter messageConverter, string messageId)
+            : base(messageQueue, messageConverter)
+        {
+            this.messageId = messageId;
+        }
+
+        /// <summary>
+        /// Returns without removing (peeks) the first message in the queue
+        /// referenced by this MessageQueue matching the selection criteria.
+        /// The Peek method is synchronous, so it blocks the current thread
+        /// until a message becomes available.
+        /// </summary>
+        /// <returns>Peeked message.</returns>
+        public override IMessage Peek()
+        {
+            return Convert(messageQueue.PeekById(messageId));
+        }
+
+        /// <summary>
+        /// Returns without removing (peeks) the first message in the queue
+        /// referenced by this MessageQueue matching the selection criteria.
+        /// The Peek method is synchronous, so it blocks the current thread
+        /// until a message becomes available or the specified time-out occurs.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <returns>Peeked message.</returns>
+        public override IMessage Peek(TimeSpan timeSpan)
+        {
+            return Convert(messageQueue.PeekById(messageId, timeSpan));
+        }
+
+        /// <summary>
+        /// Receives the first message available in the queue referenced by
+        /// the MessageQueue matching the selection criteria.
+        /// This call is synchronous, and blocks the current thread of execution
+        /// until a message is available.
+        /// </summary>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive()
+        {
+            return Convert(messageQueue.ReceiveById(messageId));
+        }
+
+        /// <summary>
+        /// Receives the first message available in the queue referenced by the
+        /// MessageQueue matching the selection criteria, and waits until either
+        /// a message is available in the queue, or the time-out expires.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive(TimeSpan timeSpan)
+        {
+            return Convert(messageQueue.ReceiveById(messageId, timeSpan));
+        }
+
+        /// <summary>
+        /// Receives the first message available in the transactional queue
+        /// referenced by the MessageQueue matching the selection criteria.
+        /// This call is synchronous, and blocks the current thread of execution
+        /// until a message is available.
+        /// </summary>
+        /// <param name="transaction">Transaction.</param>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive(MessageQueueTransaction transaction)
+        {
+            return Convert(messageQueue.ReceiveById(messageId, transaction));
+        }
+
+        /// <summary>
+        /// Receives the first message available in the transactional queue
+        /// referenced by the MessageQueue matching the selection criteria,
+        /// and waits until either a message is available in the queue, or the
+        /// time-out expires.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <param name="transaction">Transaction.</param>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive(TimeSpan timeSpan,
+            MessageQueueTransaction transaction)
+        {
+            return Convert(messageQueue.ReceiveById(messageId, timeSpan,
+                transaction));
+        }
+
+        /// <summary>
+        /// Checks if an MSMQ message matches the selection criteria.
+        /// </summary>
+        /// <param name="message">MSMQ message.</param>
+        /// <return>true if the message matches the selection criteria.</return>
+        public override bool Matches(Message message)
+        {
+            // NB: case-sensitive match
+            return message.Id == messageId;
+        }
+	}
+}
diff --git a/src/main/csharp/Readers/ByLookupIdMessageReader.cs b/src/main/csharp/Readers/ByLookupIdMessageReader.cs
new file mode 100644
index 0000000..421c52b
--- /dev/null
+++ b/src/main/csharp/Readers/ByLookupIdMessageReader.cs
@@ -0,0 +1,145 @@
+using System;
+using System.Messaging;
+using Apache.NMS.MSMQ;
+/*
+ * 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.
+ */
+
+namespace Apache.NMS.MSMQ.Readers
+{
+    /// <summary>
+    /// MSMQ message reader, returning messages matching the specified
+    /// lookup identifier.
+    /// </summary>
+	public class ByLookupIdMessageReader : AbstractMessageReader
+	{
+        private Int64 lookupId;
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        /// <param name="messageQueue">The MSMQ message queue from which
+        /// messages will be read.</param>
+        /// <param name="messageConverter">A message converter for mapping
+        /// MSMQ messages to NMS messages.</param>
+        /// <param name="lookupId">The lookup identifier of the message
+        /// to be read.</param>
+        public ByLookupIdMessageReader(MessageQueue messageQueue,
+            IMessageConverter messageConverter, Int64 lookupId)
+            : base(messageQueue, messageConverter)
+        {
+            this.lookupId = lookupId;
+        }
+
+        /// <summary>
+        /// Returns without removing (peeks) the first message in the queue
+        /// referenced by this MessageQueue matching the selection criteria.
+        /// The Peek method is synchronous, so it blocks the current thread
+        /// until a message becomes available.
+        /// </summary>
+        /// <returns>Peeked message.</returns>
+        public override IMessage Peek()
+        {
+            return Convert(messageQueue.PeekByLookupId(lookupId));
+        }
+
+        /// <summary>
+        /// Returns without removing (peeks) the first message in the queue
+        /// referenced by this MessageQueue matching the selection criteria.
+        /// The Peek method is synchronous, so it blocks the current thread
+        /// until a message becomes available or the specified time-out occurs.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <returns>Peeked message.</returns>
+        public override IMessage Peek(TimeSpan timeSpan)
+        {
+            // No time-out option for receiving messages by lookup identifiers:
+            // either the message is present in the queue, or the method throws
+            // an exception immediately if the message is not in the queue. 
+            return Convert(messageQueue.PeekByLookupId(lookupId));
+        }
+
+        /// <summary>
+        /// Receives the first message available in the queue referenced by
+        /// the MessageQueue matching the selection criteria.
+        /// This call is synchronous, and blocks the current thread of execution
+        /// until a message is available.
+        /// </summary>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive()
+        {
+            return Convert(messageQueue.ReceiveByLookupId(lookupId));
+        }
+
+        /// <summary>
+        /// Receives the first message available in the queue referenced by the
+        /// MessageQueue matching the selection criteria, and waits until either
+        /// a message is available in the queue, or the time-out expires.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive(TimeSpan timeSpan)
+        {
+            // No time-out option for receiving messages by lookup identifiers:
+            // either the message is present in the queue, or the method throws
+            // an exception immediately if the message is not in the queue. 
+            return Convert(messageQueue.ReceiveByLookupId(lookupId));
+        }
+
+        /// <summary>
+        /// Receives the first message available in the transactional queue
+        /// referenced by the MessageQueue matching the selection criteria.
+        /// This call is synchronous, and blocks the current thread of execution
+        /// until a message is available.
+        /// </summary>
+        /// <param name="transaction">Transaction.</param>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive(MessageQueueTransaction transaction)
+        {
+            return Convert(messageQueue.ReceiveByLookupId(
+                MessageLookupAction.Current, lookupId, transaction));
+        }
+
+        /// <summary>
+        /// Receives the first message available in the transactional queue
+        /// referenced by the MessageQueue matching the selection criteria,
+        /// and waits until either a message is available in the queue, or the
+        /// time-out expires.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <param name="transaction">Transaction.</param>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive(TimeSpan timeSpan,
+            MessageQueueTransaction transaction)
+        {
+            // No time-out option for receiving messages by lookup identifiers:
+            // either the message is present in the queue, or the method throws
+            // an exception immediately if the message is not in the queue. 
+            return Convert(messageQueue.ReceiveByLookupId(
+                MessageLookupAction.Current, lookupId, transaction));
+        }
+
+        /// <summary>
+        /// Checks if an MSMQ message matches the selection criteria.
+        /// </summary>
+        /// <param name="message">MSMQ message.</param>
+        /// <return>true if the message matches the selection criteria.</return>
+        public override bool Matches(Message message)
+        {
+            return message.LookupId == lookupId;
+        }
+	}
+}
diff --git a/src/main/csharp/Readers/BySelectorMessageReader.cs b/src/main/csharp/Readers/BySelectorMessageReader.cs
new file mode 100644
index 0000000..e7cd5c3
--- /dev/null
+++ b/src/main/csharp/Readers/BySelectorMessageReader.cs
@@ -0,0 +1,290 @@
+using System;
+using System.Messaging;
+using Apache.NMS.MSMQ;
+using Apache.NMS;
+using Apache.NMS.Selector;
+/*
+ * 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.
+ */
+
+namespace Apache.NMS.MSMQ.Readers
+{
+    /// <summary>
+    /// MSMQ message reader, returning messages matching the specified
+    /// selector.
+    /// </summary>
+	public class BySelectorMessageReader : AbstractMessageReader
+	{
+        private string selector;
+        private MessageEvaluationContext evaluationContext;
+        private IBooleanExpression selectionExpression;
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        /// <param name="messageQueue">The MSMQ message queue from which
+        /// messages will be read.</param>
+        /// <param name="messageConverter">A message converter for mapping
+        /// MSMQ messages to NMS messages.</param>
+        /// <param name="selector">The selector string.</param>
+        public BySelectorMessageReader(MessageQueue messageQueue,
+            IMessageConverter messageConverter, string selector)
+            : base(messageQueue, messageConverter)
+        {
+            this.selector = selector;
+
+            SelectorParser selectorParser = new SelectorParser();
+            selectionExpression = selectorParser.Parse(selector);
+
+            evaluationContext = new MessageEvaluationContext(null);
+        }
+
+        /// <summary>
+        /// Returns without removing (peeks) the first message in the queue
+        /// referenced by this MessageQueue matching the selection criteria.
+        /// The Peek method is synchronous, so it blocks the current thread
+        /// until a message becomes available.
+        /// </summary>
+        /// <returns>Peeked message.</returns>
+        public override IMessage Peek()
+        {
+            return InternalPeek(DateTime.MaxValue, true);
+        }
+
+        /// <summary>
+        /// Returns without removing (peeks) the first message in the queue
+        /// referenced by this MessageQueue matching the selection criteria.
+        /// The Peek method is synchronous, so it blocks the current thread
+        /// until a message becomes available or the specified time-out occurs.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <returns>Peeked message.</returns>
+        public override IMessage Peek(TimeSpan timeSpan)
+        {
+            DateTime maxTime = DateTime.Now + timeSpan;
+            return InternalPeek(maxTime, true);
+        }
+
+        /// <summary>
+        /// Receives the first message available in the queue referenced by
+        /// the MessageQueue matching the selection criteria.
+        /// This call is synchronous, and blocks the current thread of execution
+        /// until a message is available.
+        /// </summary>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive()
+        {
+            return InternalReceive(DateTime.MaxValue, null);
+        }
+
+        /// <summary>
+        /// Receives the first message available in the queue referenced by the
+        /// MessageQueue matching the selection criteria, and waits until either
+        /// a message is available in the queue, or the time-out expires.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive(TimeSpan timeSpan)
+        {
+            return InternalReceive(DateTime.Now + timeSpan, null);
+        }
+
+        /// <summary>
+        /// Receives the first message available in the transactional queue
+        /// referenced by the MessageQueue matching the selection criteria.
+        /// This call is synchronous, and blocks the current thread of execution
+        /// until a message is available.
+        /// </summary>
+        /// <param name="transaction">Transaction.</param>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive(MessageQueueTransaction transaction)
+        {
+            return InternalReceive(DateTime.MaxValue, transaction);
+        }
+
+        /// <summary>
+        /// Receives the first message available in the transactional queue
+        /// referenced by the MessageQueue matching the selection criteria,
+        /// and waits until either a message is available in the queue, or the
+        /// time-out expires.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <param name="transaction">Transaction.</param>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive(TimeSpan timeSpan,
+            MessageQueueTransaction transaction)
+        {
+            return InternalReceive(DateTime.Now + timeSpan, transaction);
+        }
+
+        /// <summary>
+        /// Receives the first message available in the transactional queue
+        /// referenced by the MessageQueue matching the selection criteria,
+        /// and waits until either a message is available in the queue, or the
+        /// time-out expires.
+        /// </summary>
+        /// <param name="maxTime">Reception time-out.</param>
+        /// <param name="transaction">Transaction.</param>
+        /// <returns>Received message.</returns>
+        public IMessage InternalReceive(DateTime maxTime,
+            MessageQueueTransaction transaction)
+        {
+            // In a shared connection / multi-consumer context, the message may
+            // have been consumed by another client, after it was peeked but
+            // before it was peeked by this client. Hence the loop.
+            // (not sure it can be shared AND transactional, though).
+            while(true)
+            {
+                IMessage peekedMessage = InternalPeek(maxTime, false);
+
+                if(peekedMessage == null)
+                {
+                    return null;
+                }
+
+                try
+                {
+                    long lookupId = peekedMessage.Properties.GetLong("LookupId");
+
+                    Message message = (transaction == null ?
+                        messageQueue.ReceiveByLookupId(lookupId) :
+                        messageQueue.ReceiveByLookupId(
+                            MessageLookupAction.Current, lookupId, transaction));
+
+                    return Convert(message);
+                }
+                catch(InvalidOperationException exc)
+                {
+                    // TODO: filter exceptions, catch only exceptions due to                    
+                    // unknown lookup id.
+                }
+            }
+        }
+
+        /// <summary>
+        /// Returns without removing (peeks) the first message in the queue
+        /// referenced by this MessageQueue, matching the selection criteria.
+        /// </summary>
+        /// <param name="maxTime">Reception time-out.</param>
+        /// <param name="convertBody">true if message body should be converted.</param>
+        /// <returns>Peeked message.</returns>
+        private IMessage InternalPeek(DateTime maxTime, bool convertBody)
+        {
+            TimeSpan timeSpan = maxTime - DateTime.Now;
+            if(timeSpan <= TimeSpan.Zero)
+            {
+                timeSpan = TimeSpan.Zero;
+            }
+
+            Cursor cursor = messageQueue.CreateCursor();
+
+            PeekAction action = PeekAction.Current;
+
+            while(true)
+            {
+                Message msmqMessage = null;
+
+                try
+                {
+                    msmqMessage = messageQueue.Peek(timeSpan, cursor, action);
+                }
+                catch(MessageQueueException exc)
+                {
+                    if(exc.MessageQueueErrorCode != MessageQueueErrorCode.IOTimeout)
+                    {
+                        throw exc;
+                    }
+                }
+
+                if(msmqMessage == null)
+                {
+                    return null;
+                }
+
+                IMessage nmsMessage = InternalMatch(msmqMessage, convertBody);
+
+                if(nmsMessage != null)
+                {
+                    return nmsMessage;
+                }
+
+                action = PeekAction.Next;
+            }
+        }
+
+        /// <summary>
+        /// Checks if an MSMQ message matches the selection criteria. If matched
+        /// the method returns the converted NMS message. Else it returns null.
+        /// </summary>
+        /// <param name="message">The MSMQ message to check.</param>
+        /// <param name="convertBody">true if the message body should be
+        /// converted.</param>
+        /// <returns>The matching message converted to NMS, or null.</returns>
+        private IMessage InternalMatch(Message message, bool convertBody)
+        {
+            if(messageConverterEx == null)
+            {
+                IMessage nmsMessage = messageConverter.ToNmsMessage(message);
+
+                evaluationContext.Message = nmsMessage;
+
+                if(selectionExpression.Matches(evaluationContext))
+                {
+                    return nmsMessage;
+                }
+            }
+            else
+            {
+                // This version converts the message body only for those
+                // messages matching the selection criteria.
+                // Relies on MessageConverterEx for partial conversions.
+                IMessage nmsMessage = messageConverterEx.ToNmsMessage(
+                    message, false);
+
+                evaluationContext.Message = nmsMessage;
+
+                if(selectionExpression.Matches(evaluationContext))
+                {
+                    if(convertBody)
+                    {
+                        messageConverterEx.ConvertMessageBodyToNMS(
+                            message, nmsMessage);
+                    }
+
+                    return nmsMessage;
+                }
+            }
+
+            return null;
+        }
+
+        /// <summary>
+        /// Checks if an MSMQ message matches the selection criteria.
+        /// </summary>
+        /// <param name="message">MSMQ message.</param>
+        /// <return>true if the message matches the selection criteria.</return>
+        public override bool Matches(Message message)
+        {
+            IMessage nmsMessage = messageConverterEx == null ?
+                messageConverter.ToNmsMessage(message) :
+                messageConverterEx.ToNmsMessage(message, false);
+
+            evaluationContext.Message = nmsMessage;
+
+            return selectionExpression.Matches(evaluationContext);
+        }
+	}
+}
diff --git a/src/main/csharp/Readers/IMessageReader.cs b/src/main/csharp/Readers/IMessageReader.cs
new file mode 100644
index 0000000..8168664
--- /dev/null
+++ b/src/main/csharp/Readers/IMessageReader.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Messaging;
+using Apache.NMS.MSMQ;
+/*
+ * 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.
+ */
+
+namespace Apache.NMS.MSMQ.Readers
+{
+    /// <summary>
+    /// MSMQ message reader.
+    /// </summary>
+	public interface IMessageReader
+	{
+        /// <summary>
+        /// Returns without removing (peeks) the first message in the queue
+        /// referenced by this MessageQueue matching the selection criteria.
+        /// The Peek method is synchronous, so it blocks the current thread
+        /// until a message becomes available.
+        /// </summary>
+        /// <returns>Peeked message.</returns>
+        IMessage Peek();
+
+        /// <summary>
+        /// Returns without removing (peeks) the first message in the queue
+        /// referenced by this MessageQueue matching the selection criteria.
+        /// The Peek method is synchronous, so it blocks the current thread
+        /// until a message becomes available or the specified time-out occurs.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <returns>Peeked message.</returns>
+        IMessage Peek(TimeSpan timeSpan);
+
+        /// <summary>
+        /// Receives the first message available in the queue referenced by
+        /// the MessageQueue matching the selection criteria.
+        /// This call is synchronous, and blocks the current thread of execution
+        /// until a message is available.
+        /// </summary>
+        /// <returns>Received message.</returns>
+        IMessage Receive();
+
+        /// <summary>
+        /// Receives the first message available in the queue referenced by the
+        /// MessageQueue matching the selection criteria, and waits until either
+        /// a message is available in the queue, or the time-out expires.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <returns>Received message.</returns>
+        IMessage Receive(TimeSpan timeSpan);
+
+        /// <summary>
+        /// Receives the first message available in the transactional queue
+        /// referenced by the MessageQueue matching the selection criteria.
+        /// This call is synchronous, and blocks the current thread of execution
+        /// until a message is available.
+        /// </summary>
+        /// <param name="transaction">Transaction.</param>
+        /// <returns>Received message.</returns>
+        IMessage Receive(MessageQueueTransaction transaction);
+
+        /// <summary>
+        /// Receives the first message available in the transactional queue
+        /// referenced by the MessageQueue matching the selection criteria,
+        /// and waits until either a message is available in the queue, or the
+        /// time-out expires.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <param name="transaction">Transaction.</param>
+        /// <returns>Received message.</returns>
+        IMessage Receive(TimeSpan timeSpan, MessageQueueTransaction transaction);
+
+        /// <summary>
+        /// Checks if an MSMQ message matches the selection criteria.
+        /// </summary>
+        /// <param name="message">MSMQ message.</param>
+        /// <return>true if the message matches the selection criteria.</return>
+        bool Matches(Message message);
+	}
+}
diff --git a/src/main/csharp/Readers/MessageReaderUtil.cs b/src/main/csharp/Readers/MessageReaderUtil.cs
new file mode 100644
index 0000000..c303965
--- /dev/null
+++ b/src/main/csharp/Readers/MessageReaderUtil.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Messaging;
+using System.Globalization;
+using System.Text.RegularExpressions;
+/*
+ * 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.
+ */
+
+namespace Apache.NMS.MSMQ.Readers
+{
+    /// <summary>
+    /// Utility routines for creating MSMQ message readers.
+    /// </summary>
+	public static class MessageReaderUtil
+	{
+        private static Regex basicSelectorRegex =
+            new Regex(@"^\s*" +
+                      @"(NMSMessageId)\s*=\s*'([^']*)'|" +
+                      @"(NMSCorrelationId)\s*=\s*'([^']*)'|" +
+                      @"(LookupId)\s*=\s*([-+]{0,1}\d+)" +
+                      @"\s*$",
+                RegexOptions.IgnoreCase | RegexOptions.Compiled);
+
+        /// <summary>
+        /// Creates a message reader for the specified message selector.
+        /// </summary>
+        /// <param name="messageQueue">The MSMQ message queue from which
+        /// messages will be read.</param>
+        /// <param name="messageConverter">A message converter for mapping
+        /// MSMQ messages to NMS messages.</param>
+        /// <param name="selector">The message selector.</param>
+        /// <return>A reader for the specified selector.</return>
+        public static IMessageReader CreateMessageReader(
+            MessageQueue messageQueue, IMessageConverter messageConverter,
+            string selector)
+        {
+            IMessageReader reader;
+
+            if(string.IsNullOrEmpty(selector))
+            {
+                reader = new NonFilteringMessageReader(messageQueue,
+                    messageConverter);
+            }
+            else
+            {
+                Match match = basicSelectorRegex.Match(selector);
+                if(match.Success)
+                {
+                    if(!string.IsNullOrEmpty(match.Groups[1].Value))
+                    {
+                        reader = new ByIdMessageReader(messageQueue,
+                            messageConverter, match.Groups[2].Value);
+                    }
+                    else if(!string.IsNullOrEmpty(match.Groups[3].Value))
+                    {
+                        reader = new ByCorrelationIdMessageReader(messageQueue,
+                            messageConverter, match.Groups[4].Value);
+                    }
+                    else
+                    {
+                        Int64 lookupId = Int64.Parse(match.Groups[6].Value,
+                            CultureInfo.InvariantCulture);
+
+                        reader = new ByLookupIdMessageReader(messageQueue,
+                            messageConverter, lookupId);
+                    }
+                }
+                else
+                {
+                    reader = new BySelectorMessageReader(messageQueue,
+                        messageConverter, selector);
+                }
+            }
+
+            return reader;
+        }
+	}
+}
diff --git a/src/main/csharp/Readers/NonFilteringMessageReader.cs b/src/main/csharp/Readers/NonFilteringMessageReader.cs
new file mode 100644
index 0000000..ea98baf
--- /dev/null
+++ b/src/main/csharp/Readers/NonFilteringMessageReader.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Messaging;
+using Apache.NMS.MSMQ;
+/*
+ * 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.
+ */
+
+namespace Apache.NMS.MSMQ.Readers
+{
+    /// <summary>
+    /// MSMQ message reader, returning all messages, without filtering.
+    /// </summary>
+	public class NonFilteringMessageReader : AbstractMessageReader
+	{
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        /// <param name="messageQueue">The MSMQ message queue from which
+        /// messages will be read.</param>
+        /// <param name="messageConverter">A message converter for mapping
+        /// MSMQ messages to NMS messages.</param>
+        public NonFilteringMessageReader(MessageQueue messageQueue,
+            IMessageConverter messageConverter)
+            : base(messageQueue, messageConverter)
+        {
+        }
+
+        /// <summary>
+        /// Returns without removing (peeks) the first message in the queue
+        /// referenced by this MessageQueue matching the selection criteria.
+        /// The Peek method is synchronous, so it blocks the current thread
+        /// until a message becomes available.
+        /// </summary>
+        /// <returns>Peeked message.</returns>
+        public override IMessage Peek()
+        {
+            return Convert(messageQueue.Peek());
+        }
+
+        /// <summary>
+        /// Returns without removing (peeks) the first message in the queue
+        /// referenced by this MessageQueue matching the selection criteria.
+        /// The Peek method is synchronous, so it blocks the current thread
+        /// until a message becomes available or the specified time-out occurs.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <returns>Peeked message.</returns>
+        public override IMessage Peek(TimeSpan timeSpan)
+        {
+            return Convert(messageQueue.Peek(timeSpan));
+        }
+
+        /// <summary>
+        /// Receives the first message available in the queue referenced by
+        /// the MessageQueue matching the selection criteria.
+        /// This call is synchronous, and blocks the current thread of execution
+        /// until a message is available.
+        /// </summary>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive()
+        {
+            return Convert(messageQueue.Receive());
+        }
+
+        /// <summary>
+        /// Receives the first message available in the queue referenced by the
+        /// MessageQueue matching the selection criteria, and waits until either
+        /// a message is available in the queue, or the time-out expires.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive(TimeSpan timeSpan)
+        {
+            return Convert(messageQueue.Receive(timeSpan));
+        }
+
+        /// <summary>
+        /// Receives the first message available in the transactional queue
+        /// referenced by the MessageQueue matching the selection criteria.
+        /// This call is synchronous, and blocks the current thread of execution
+        /// until a message is available.
+        /// </summary>
+        /// <param name="transaction">Transaction.</param>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive(MessageQueueTransaction transaction)
+        {
+            return Convert(messageQueue.Receive(transaction));
+        }
+
+        /// <summary>
+        /// Receives the first message available in the transactional queue
+        /// referenced by the MessageQueue matching the selection criteria,
+        /// and waits until either a message is available in the queue, or the
+        /// time-out expires.
+        /// </summary>
+        /// <param name="timeSpan">Reception time-out.</param>
+        /// <param name="transaction">Transaction.</param>
+        /// <returns>Received message.</returns>
+        public override IMessage Receive(TimeSpan timeSpan,
+            MessageQueueTransaction transaction)
+        {
+            return Convert(messageQueue.Receive(timeSpan, transaction));
+        }
+
+        /// <summary>
+        /// Checks if an MSMQ message matches the selection criteria.
+        /// </summary>
+        /// <param name="message">MSMQ message.</param>
+        /// <return>true if the message matches the selection criteria.</return>
+        public override bool Matches(Message message)
+        {
+            return true;
+        }
+	}
+}
diff --git a/src/main/csharp/Selector/ANDExpression.cs b/src/main/csharp/Selector/ANDExpression.cs
new file mode 100644
index 0000000..285efe3
--- /dev/null
+++ b/src/main/csharp/Selector/ANDExpression.cs
@@ -0,0 +1,47 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A filter performing a logical AND combination of two expressions.
+    /// </summary>
+    public class ANDExpression : LogicExpression
+    {
+        protected override string ExpressionSymbol
+        {
+            get { return "AND"; }
+        }
+
+        public ANDExpression(IBooleanExpression left, IBooleanExpression right)
+            : base(left, right)
+        {
+        }
+
+        public override object Evaluate(MessageEvaluationContext message)
+        {
+            object lvalue = Left.Evaluate(message);
+            if(lvalue == null) return null;
+            if(!(bool)lvalue) return false;
+
+            object rvalue = Right.Evaluate(message);
+            return rvalue == null ? null : rvalue;
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/AlignedNumericValues.cs b/src/main/csharp/Selector/AlignedNumericValues.cs
new file mode 100644
index 0000000..96e2eeb
--- /dev/null
+++ b/src/main/csharp/Selector/AlignedNumericValues.cs
@@ -0,0 +1,175 @@
+using System;
+using System.Globalization;
+using System.Collections.Generic;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A couple of numeric values converted to the type of the largest type.
+    /// </summary>
+    public class AlignedNumericValues
+    {
+        private object left;
+        public object Left
+        {
+            get { return left; }
+        }
+
+        private object right;
+        public object Right
+        {
+            get { return right; }
+        }
+
+        private T type;
+        public T TypeEnum
+        {
+            get { return type; }
+        }
+
+        public Type Type
+        {
+            get { return GetType(type); }
+        }
+
+        public AlignedNumericValues(object lvalue, object rvalue)
+        {
+            if(lvalue == null || rvalue == null)
+            {
+                return;
+            }
+
+            T ltypeEnum = GetTypeEnum(lvalue);
+            T rtypeEnum = GetTypeEnum(rvalue);
+
+            type = targetType[(int)ltypeEnum][(int)rtypeEnum];
+
+            left  = (ltypeEnum == type ? lvalue : ConvertValue(lvalue, type));
+            right = (rtypeEnum == type ? rvalue : ConvertValue(rvalue, type));
+        }
+
+        public enum T
+        {
+            SByteType  =  0, // Signed 8-bit integer (-128 to 127)
+            ByteType   =  1, // Unsigned 8-bit integer (0 to 255)
+            CharType   =  2, // Unicode 16-bit character (U+0000 to U+ffff)
+            ShortType  =  3, // Signed 16-bit integer (-32 768 to 32 767)
+            UShortType =  4, // Unsigned 16-bit integer (0 to 65 535)
+            IntType    =  5, // Signed 32-bit integer (-2 147 483 648 to 2 147 483 647)
+            UIntType   =  6, // Unsigned 32-bit integer (0 to 4 294 967 295)
+            LongType   =  7, // Signed 64-bit integer (-9 223 372 036 854 775 808 to 9 223 372 036 854 775 807)
+            ULongType  =  8, // Unsigned 64-bit integer (0 to 18 446 744 073 709 551 615)
+            FloatType  =  9, // 7 digits (±1.5e−45 to ±3.4e38)
+            DoubleType = 10  // 15-16 digits (±5.0e−324 to ±1.7e308)
+        }
+
+        private static Dictionary<Type, T> typeEnums
+            = new Dictionary<Type, T>
+                {
+                    { typeof(sbyte ), T.SByteType  },
+                    { typeof(byte  ), T.ByteType   },
+                    { typeof(char  ), T.CharType   },
+                    { typeof(short ), T.ShortType  },
+                    { typeof(ushort), T.UShortType },
+                    { typeof(int   ), T.IntType    },
+                    { typeof(uint  ), T.UIntType   },
+                    { typeof(long  ), T.LongType   },
+                    { typeof(ulong ), T.ULongType  },
+                    { typeof(float ), T.FloatType  },
+                    { typeof(double), T.DoubleType }
+                };
+
+        private static T[][] targetType = new T[][]
+        {
+            //                        SByteType ,   ByteType  ,   CharType  ,   ShortType ,   UShortType,   IntType   ,   UIntType  ,   LongType  ,   ULongType ,   FloatType ,   DoubleType
+            /*SByteType */new T[] { T.SByteType , T.ShortType , T.IntType   , T.ShortType , T.IntType   , T.IntType   , T.LongType  , T.LongType  , T.LongType  , T.FloatType , T.DoubleType },
+            /*ByteType  */new T[] { T.ShortType , T.ByteType  , T.UShortType, T.ShortType , T.UShortType, T.IntType   , T.UIntType  , T.LongType  , T.ULongType , T.FloatType , T.DoubleType },
+            /*CharType  */new T[] { T.IntType   , T.UShortType, T.CharType  , T.IntType   , T.UShortType, T.IntType   , T.LongType  , T.LongType  , T.ULongType , T.FloatType , T.DoubleType },
+            /*ShortType */new T[] { T.ShortType , T.ShortType , T.IntType   , T.ShortType , T.IntType   , T.IntType   , T.LongType  , T.LongType  , T.LongType  , T.FloatType , T.DoubleType },
+            /*UShortType*/new T[] { T.IntType   , T.UShortType, T.UShortType, T.IntType   , T.UShortType, T.IntType   , T.UIntType  , T.LongType  , T.ULongType , T.FloatType , T.DoubleType },
+            /*IntType   */new T[] { T.IntType   , T.IntType   , T.IntType   , T.IntType   , T.IntType   , T.IntType   , T.LongType  , T.LongType  , T.LongType  , T.FloatType , T.DoubleType },
+            /*UIntType  */new T[] { T.LongType  , T.UIntType  , T.LongType  , T.LongType  , T.UIntType  , T.LongType  , T.UIntType  , T.LongType  , T.ULongType , T.FloatType , T.DoubleType },
+            /*LongType  */new T[] { T.LongType  , T.LongType  , T.LongType  , T.LongType  , T.LongType  , T.LongType  , T.LongType  , T.LongType  , T.LongType  , T.FloatType , T.DoubleType },
+            /*ULongType */new T[] { T.LongType  , T.ULongType , T.ULongType , T.LongType  , T.ULongType , T.LongType  , T.ULongType , T.LongType  , T.ULongType , T.FloatType , T.DoubleType },
+            /*FloatType */new T[] { T.FloatType , T.FloatType , T.FloatType , T.FloatType , T.FloatType , T.FloatType , T.FloatType , T.FloatType , T.FloatType , T.FloatType , T.DoubleType },
+            /*DoubleType*/new T[] { T.DoubleType, T.DoubleType, T.DoubleType, T.DoubleType, T.DoubleType, T.DoubleType, T.DoubleType, T.DoubleType, T.DoubleType, T.DoubleType, T.DoubleType }
+        };
+
+        private T GetTypeEnum(object value)
+        {
+            return GetTypeEnum(value.GetType());
+        }
+
+        private T GetTypeEnum(Type type)
+        {
+            try
+            {
+                return typeEnums[type];
+            }
+            catch
+            {
+                throw new NotSupportedException(
+                    string.Format("Unsupported data type {0}.", type));
+            }
+        }
+
+        private Type GetType(T typeEnum)
+        {
+            switch(typeEnum)
+            {
+                case T.SByteType : return typeof(sbyte );
+                case T.ByteType  : return typeof(byte  );
+                case T.CharType  : return typeof(char  );
+                case T.ShortType : return typeof(short );
+                case T.UShortType: return typeof(ushort);
+                case T.IntType   : return typeof(int   );
+                case T.UIntType  : return typeof(uint  );
+                case T.LongType  : return typeof(long  );
+                case T.ULongType : return typeof(ulong );
+                case T.FloatType : return typeof(float );
+                case T.DoubleType: return typeof(double);
+                default:
+                    throw new NotSupportedException(
+                        string.Format("Unsupported data type {0}.", typeEnum));
+            }
+        }
+
+        private object ConvertValue(object value, T targetTypeEnum)
+        {
+            switch(targetTypeEnum)
+            {
+                case T.SByteType : return Convert.ToSByte (value);
+                case T.ByteType  : return Convert.ToByte  (value);
+                case T.CharType  : return Convert.ToChar  (value);
+                case T.ShortType : return Convert.ToInt16 (value);
+                case T.UShortType: return Convert.ToUInt16(value);
+                case T.IntType   : return Convert.ToInt32 (value);
+                case T.UIntType  : return Convert.ToUInt32(value);
+                case T.LongType  : return Convert.ToInt64 (value);
+                case T.ULongType : return Convert.ToUInt64(value);
+                case T.FloatType : return Convert.ToSingle(value);
+                case T.DoubleType: return Convert.ToDouble(value);
+                default:
+                    throw new NotSupportedException(
+                        string.Format("Unsupported data type {0}.", targetTypeEnum));
+            }
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/ArithmeticExpression.cs b/src/main/csharp/Selector/ArithmeticExpression.cs
new file mode 100644
index 0000000..a0524ee
--- /dev/null
+++ b/src/main/csharp/Selector/ArithmeticExpression.cs
@@ -0,0 +1,57 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// An expression which performs an operation on two expression values.
+    /// </summary>
+    public abstract class ArithmeticExpression : BinaryExpression
+    {
+        public ArithmeticExpression(IExpression left, IExpression right)
+            : base(left, right)
+        {
+        }
+
+        public static IExpression CreatePlus(IExpression left, IExpression right)
+        {
+            return new PlusExpression(left, right);
+        }
+
+        public static IExpression CreateMinus(IExpression left, IExpression right)
+        {
+            return new MinusExpression(left, right);
+        }
+
+        public static IExpression CreateMultiply(IExpression left, IExpression right)
+        {
+            return new MultiplyExpression(left, right);
+        }
+
+        public static IExpression CreateDivide(IExpression left, IExpression right)
+        {
+            return new DivideExpression(left, right);
+        }
+
+        public static IExpression CreateMod(IExpression left, IExpression right)
+        {
+            return new ModExpression(left, right);
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/BinaryExpression.cs b/src/main/csharp/Selector/BinaryExpression.cs
new file mode 100644
index 0000000..9206f8c
--- /dev/null
+++ b/src/main/csharp/Selector/BinaryExpression.cs
@@ -0,0 +1,59 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// An expression which performs an operation on two expression values.
+    /// </summary>
+    public abstract class BinaryExpression : IExpression
+    {
+        protected IExpression leftExpression;
+        public IExpression Left
+        {
+            get { return leftExpression; }
+            set { leftExpression = value; }
+        }
+
+        protected IExpression rightExpression;
+        public IExpression Right
+        {
+            get { return rightExpression; }
+            set { rightExpression = value; }
+        }
+
+        protected abstract string ExpressionSymbol
+        {
+            get;
+        }
+
+        public BinaryExpression(IExpression left, IExpression right)
+        {
+            leftExpression = left;
+            rightExpression = right;
+        }
+
+        public abstract object Evaluate(MessageEvaluationContext message);
+
+        public override string ToString()
+        {
+            return "(" + leftExpression.ToString() + " " + ExpressionSymbol + " " + rightExpression.ToString() + ")";
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/BooleanCastExpression.cs b/src/main/csharp/Selector/BooleanCastExpression.cs
new file mode 100644
index 0000000..26a6e9e
--- /dev/null
+++ b/src/main/csharp/Selector/BooleanCastExpression.cs
@@ -0,0 +1,45 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// An expression which casts an expression value to a boolean.
+    /// </summary>
+    public class BooleanCastExpression : BooleanUnaryExpression
+    {
+        protected override string ExpressionSymbol
+        {
+            get { return ""; }
+        }
+
+        public BooleanCastExpression(IExpression left)
+            : base(left)
+        {
+        }
+
+        public override object Evaluate(MessageEvaluationContext message)
+        {
+            object rvalue = Right.Evaluate(message);
+            if(rvalue == null   ) return null;
+            if(rvalue is bool   ) return (bool)rvalue;
+            return false;
+        }
+    }
+}
diff --git a/src/main/csharp/IMessageConverter.cs b/src/main/csharp/Selector/BooleanConstantExpression.cs
similarity index 55%
copy from src/main/csharp/IMessageConverter.cs
copy to src/main/csharp/Selector/BooleanConstantExpression.cs
index 152377b..eb6447a 100644
--- a/src/main/csharp/IMessageConverter.cs
+++ b/src/main/csharp/Selector/BooleanConstantExpression.cs
@@ -1,34 +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.
- */
-using System.Messaging;
-
-namespace Apache.NMS.MSMQ
-{
-	public interface IMessageConverter
-	{
-
-		/// <summary>
-		/// Method ToMSMQMessageQueue
-		/// </summary>
-		/// <param name="destination">An IDestination</param>
-		/// <returns>A  MessageQueue</returns>
-		MessageQueue ToMsmqDestination(IDestination destination);
-
-		Message ToMsmqMessage(IMessage message);
-		IMessage ToNmsMessage(Message message);
-	}
-}
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// Represents a boolean constant expression.
+    /// </summary>
+    public class BooleanConstantExpression : ConstantExpression, IBooleanExpression
+    {
+        public BooleanConstantExpression(object value)
+            : base(value)
+        {
+        }
+
+        public bool Matches(MessageEvaluationContext message)
+        {
+            object value = Evaluate(message);
+            return value != null && (bool)value;            
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/csharp/IMessageConverter.cs b/src/main/csharp/Selector/BooleanUnaryExpression.cs
similarity index 52%
copy from src/main/csharp/IMessageConverter.cs
copy to src/main/csharp/Selector/BooleanUnaryExpression.cs
index 152377b..3873050 100644
--- a/src/main/csharp/IMessageConverter.cs
+++ b/src/main/csharp/Selector/BooleanUnaryExpression.cs
@@ -1,34 +1,39 @@
-/*
- * 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.
- */
-using System.Messaging;
-
-namespace Apache.NMS.MSMQ
-{
-	public interface IMessageConverter
-	{
-
-		/// <summary>
-		/// Method ToMSMQMessageQueue
-		/// </summary>
-		/// <param name="destination">An IDestination</param>
-		/// <returns>A  MessageQueue</returns>
-		MessageQueue ToMsmqDestination(IDestination destination);
-
-		Message ToMsmqMessage(IMessage message);
-		IMessage ToNmsMessage(Message message);
-	}
-}
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// An expression which performs an operation on one expression value
+    /// and returns a boolean value.
+    /// </summary>
+    public abstract class BooleanUnaryExpression : UnaryExpression, IBooleanExpression
+    {
+        public BooleanUnaryExpression(IExpression left)
+            : base(left)
+        {        	
+        }
+
+        public bool Matches(MessageEvaluationContext message)
+        {
+            object value = Evaluate(message);
+            return value != null && (bool)value;            
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/ComparisonExpression.cs b/src/main/csharp/Selector/ComparisonExpression.cs
new file mode 100644
index 0000000..4024271
--- /dev/null
+++ b/src/main/csharp/Selector/ComparisonExpression.cs
@@ -0,0 +1,162 @@
+using System;
+using System.Collections;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A filter performing a comparison of two or more expressions or objects.
+    /// </summary>
+    public abstract class ComparisonExpression : BinaryExpression, IBooleanExpression
+    {
+        public ComparisonExpression(IExpression left, IExpression right)
+            : base(left, right)
+        {
+        }
+
+        public override object Evaluate(MessageEvaluationContext message)
+        {
+            object lvalue = Left.Evaluate(message);
+            object rvalue = Right.Evaluate(message);
+
+            int? compared = null;
+
+            if(lvalue == null || rvalue == null)
+            {
+                if(lvalue == null && rvalue == null)
+                {
+                    compared = 0;
+                }
+            }
+            else
+            {
+                if(lvalue == rvalue)
+                {
+                    compared = 0;
+                }
+                else if(lvalue is string && rvalue is string)
+                {
+                    compared = ((string)lvalue).CompareTo(rvalue);
+                }
+                else
+                {
+                    AlignedNumericValues values = new AlignedNumericValues(lvalue, rvalue);
+
+                    switch(values.TypeEnum)
+                    {
+                        case AlignedNumericValues.T.SByteType : compared = ((sbyte )values.Left).CompareTo((sbyte )values.Right); break;
+                        case AlignedNumericValues.T.ByteType  : compared = ((byte  )values.Left).CompareTo((byte  )values.Right); break;
+                        case AlignedNumericValues.T.CharType  : compared = ((char  )values.Left).CompareTo((char  )values.Right); break;
+                        case AlignedNumericValues.T.ShortType : compared = ((short )values.Left).CompareTo((short )values.Right); break;
+                        case AlignedNumericValues.T.UShortType: compared = ((ushort)values.Left).CompareTo((ushort)values.Right); break;
+                        case AlignedNumericValues.T.IntType   : compared = ((int   )values.Left).CompareTo((int   )values.Right); break;
+                        case AlignedNumericValues.T.UIntType  : compared = ((uint  )values.Left).CompareTo((uint  )values.Right); break;
+                        case AlignedNumericValues.T.LongType  : compared = ((long  )values.Left).CompareTo((long  )values.Right); break;
+                        case AlignedNumericValues.T.ULongType : compared = ((ulong )values.Left).CompareTo((ulong )values.Right); break;
+                        case AlignedNumericValues.T.FloatType : compared = ((float )values.Left).CompareTo((float )values.Right); break;
+                        case AlignedNumericValues.T.DoubleType: compared = ((double)values.Left).CompareTo((double)values.Right); break;
+                    }
+                }
+            }
+
+            return AsBoolean(compared);
+        }
+    
+        public abstract bool AsBoolean(int? compared);
+
+        public bool Matches(MessageEvaluationContext message)
+        {
+            object value = Evaluate(message);
+            return value != null && (bool)value;            
+        }
+
+        // Equality expressions
+        public static IBooleanExpression CreateEqual(IExpression left, IExpression right)
+        {
+    	    return new EqualExpression(left, right, true);
+        }
+
+        public static IBooleanExpression CreateNotEqual(IExpression left, IExpression right)
+        {
+            return new EqualExpression(left, right, false);
+        }
+
+        public static IBooleanExpression CreateIsNull(IExpression left)
+        {
+            return new IsNullExpression(left, true);
+        }
+
+        public static IBooleanExpression CreateIsNotNull(IExpression left)
+        {
+            return new IsNullExpression(left, false);
+        }
+
+        // Binary comparison expressions
+        public static IBooleanExpression CreateGreaterThan(IExpression left, IExpression right)
+        {
+    	    return new GreaterExpression(left, right);
+        }
+
+        public static IBooleanExpression CreateGreaterThanOrEqual(IExpression left, IExpression right)
+        {
+    	    return new GreaterOrEqualExpression(left, right);
+        }
+
+        public static IBooleanExpression CreateLesserThan(IExpression left, IExpression right)
+        {
+    	    return new LesserExpression(left, right);
+        }
+
+	    public static IBooleanExpression CreateLesserThanOrEqual(IExpression left, IExpression right)
+        {
+    	    return new LesserOrEqualExpression(left, right);
+        }
+
+        // Other comparison expressions
+        public static IBooleanExpression CreateLike(IExpression left, string right, string escape)
+        {
+            return new LikeExpression(left, right, escape, true);
+        }
+
+        public static IBooleanExpression CreateNotLike(IExpression left, string right, string escape)
+        {
+            return new LikeExpression(left, right, escape, false);
+        }
+
+        public static IBooleanExpression CreateBetween(IExpression value, IExpression left, IExpression right)
+        {
+            return LogicExpression.CreateAND(CreateGreaterThanOrEqual(value, left), CreateLesserThanOrEqual(value, right));
+        }
+
+        public static IBooleanExpression CreateNotBetween(IExpression value, IExpression left, IExpression right)
+        {
+            return LogicExpression.CreateOR(CreateLesserThan(value, left), CreateGreaterThan(value, right));
+        }
+
+        public static IBooleanExpression CreateIn(IExpression left, ArrayList elements)
+        {
+            return new InExpression(left, elements, true);
+        }
+
+        public static IBooleanExpression CreateNotIn(IExpression left, ArrayList elements)
+        {
+            return new InExpression(left, elements, false);
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/ConstantExpression.cs b/src/main/csharp/Selector/ConstantExpression.cs
new file mode 100644
index 0000000..90dfd69
--- /dev/null
+++ b/src/main/csharp/Selector/ConstantExpression.cs
@@ -0,0 +1,157 @@
+using System;
+using System.Text;
+using System.Globalization;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// Represents a constant expression.
+    /// </summary>
+    public class ConstantExpression : IExpression
+    {
+        private object value;
+        public object Value
+        {
+            get { return value; }
+        }    
+
+        public ConstantExpression(object value)
+        {
+            this.value = value;
+        }
+
+        public static ConstantExpression CreateFromDecimal(string text)
+        {
+    	    // Long integer specified ?
+    	    object value;
+            if(text.EndsWith("l") || text.EndsWith("L"))
+            {
+    		    text = text.Substring(0, text.Length - 1);
+                value = Int64.Parse(text, CultureInfo.InvariantCulture);
+            }
+            else
+            {
+                long lvalue = Int64.Parse(text, CultureInfo.InvariantCulture);
+                if(lvalue >= Int32.MinValue && lvalue <= Int32.MaxValue)
+                {
+                    value = (int)lvalue;
+                }
+                else
+                {
+                    value = lvalue;
+                }
+            }
+            return new ConstantExpression(value);
+        }
+
+        public static ConstantExpression CreateFromHex(string text)
+        {
+            long lvalue = Convert.ToInt64(text.Substring(2), 16);
+
+    	    object value;
+            if(lvalue >= Int32.MinValue && lvalue <= Int32.MaxValue)
+            {
+                value = (int)lvalue;
+            }
+            else
+            {
+                value = lvalue;
+            }
+            return new ConstantExpression(value);
+        }
+
+
+        public static ConstantExpression CreateFromOctal(string text)
+        {
+            long lvalue = Convert.ToInt64(text, 8);
+
+    	    object value;
+            if(lvalue >= Int32.MinValue && lvalue <= Int32.MaxValue)
+            {
+                value = (int)lvalue;
+            }
+            else
+            {
+                value = lvalue;
+            }
+            return new ConstantExpression(value);
+        }
+
+        public static ConstantExpression CreateFloat(string text)
+        {
+            double value = Double.Parse(text, CultureInfo.InvariantCulture);
+            return new ConstantExpression(value);
+        }
+
+        public object Evaluate(MessageEvaluationContext message)
+        {
+            return value;
+        }
+
+        public override string ToString()
+        {
+            if(value == null)
+            {
+                return "NULL";
+            }
+            if(value is bool)
+            {
+                return (bool)value ? "TRUE" : "FALSE";
+            }
+            if(value is string)
+            {
+                return EncodeString((string)value);
+            }
+            return value.ToString();
+        }
+
+        public override int GetHashCode()
+        {
+            return (value == null ? 0 : value.GetHashCode());
+        }
+
+        /// <summary>
+        /// Encodes the value of string so that it looks like it would look like
+        /// when it was provided in a selector.
+        /// </summary>
+        /// <param name="s">String to be encoded.</param>
+        /// <return>Encoded string.</return>
+        public static string EncodeString(string s)
+        {
+            StringBuilder b = new StringBuilder();
+            b.Append('\'');
+            for(int c = 0; c < s.Length; c++)
+            {
+                char ch = s[c];
+                if(ch == '\'')
+                {
+                    b.Append(ch);
+                }
+                b.Append(ch);
+            }
+            b.Append('\'');
+            return b.ToString();
+        }
+
+        public static readonly BooleanConstantExpression NULL  = new BooleanConstantExpression(null);
+        public static readonly BooleanConstantExpression TRUE  = new BooleanConstantExpression(true);
+        public static readonly BooleanConstantExpression FALSE = new BooleanConstantExpression(false);
+    }
+}
\ No newline at end of file
diff --git a/src/main/csharp/Selector/DivideExpression.cs b/src/main/csharp/Selector/DivideExpression.cs
new file mode 100644
index 0000000..e280ec8
--- /dev/null
+++ b/src/main/csharp/Selector/DivideExpression.cs
@@ -0,0 +1,67 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A filter performing a division of two expressions.
+    /// </summary>
+    public class DivideExpression : ArithmeticExpression
+    {
+        protected override string ExpressionSymbol
+        {
+            get { return "/"; }
+        }
+
+        public DivideExpression(IExpression left, IExpression right)
+            : base(left, right)
+        {
+        }
+
+        public override object Evaluate(MessageEvaluationContext message)
+        {
+            object lvalue = Left.Evaluate(message);
+            if(lvalue == null) return null;
+
+            object rvalue = Right.Evaluate(message);
+            if(rvalue == null) return null;
+
+            AlignedNumericValues values = new AlignedNumericValues(lvalue, rvalue);
+
+            object result = null;
+
+            switch(values.TypeEnum)
+            {
+                case AlignedNumericValues.T.SByteType : result = (sbyte )values.Left / (sbyte )values.Right; break;
+                case AlignedNumericValues.T.ByteType  : result = (byte  )values.Left / (byte  )values.Right; break;
+                case AlignedNumericValues.T.CharType  : result = (char  )values.Left / (char  )values.Right; break;
+                case AlignedNumericValues.T.ShortType : result = (short )values.Left / (short )values.Right; break;
+                case AlignedNumericValues.T.UShortType: result = (ushort)values.Left / (ushort)values.Right; break;
+                case AlignedNumericValues.T.IntType   : result = (int   )values.Left / (int   )values.Right; break;
+                case AlignedNumericValues.T.UIntType  : result = (uint  )values.Left / (uint  )values.Right; break;
+                case AlignedNumericValues.T.LongType  : result = (long  )values.Left / (long  )values.Right; break;
+                case AlignedNumericValues.T.ULongType : result = (ulong )values.Left / (ulong )values.Right; break;
+                case AlignedNumericValues.T.FloatType : result = (float )values.Left / (float )values.Right; break;
+                case AlignedNumericValues.T.DoubleType: result = (double)values.Left / (double)values.Right; break;
+            }
+
+            return result;
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/EqualExpression.cs b/src/main/csharp/Selector/EqualExpression.cs
new file mode 100644
index 0000000..0e9a792
--- /dev/null
+++ b/src/main/csharp/Selector/EqualExpression.cs
@@ -0,0 +1,47 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A filter performing an equality or inequality comparison
+    /// of two expressions.
+    /// </summary>
+    public class EqualExpression : ComparisonExpression
+    {
+        private bool notNot;
+
+        protected override string ExpressionSymbol
+        {
+            get { return notNot ? "=" : "<>"; }
+        }
+
+        public EqualExpression(IExpression left, IExpression right, bool notNot)
+            : base(left, right)
+        {
+            this.notNot = notNot;
+        }
+
+        public override bool AsBoolean(int? compared)
+        {
+            bool answer = (compared.HasValue ? compared.Value == 0 : false);
+            return notNot ? answer : !answer;
+        }
+    }
+}
diff --git a/src/main/csharp/IMessageConverter.cs b/src/main/csharp/Selector/GreaterExpression.cs
similarity index 52%
copy from src/main/csharp/IMessageConverter.cs
copy to src/main/csharp/Selector/GreaterExpression.cs
index 152377b..eb264e5 100644
--- a/src/main/csharp/IMessageConverter.cs
+++ b/src/main/csharp/Selector/GreaterExpression.cs
@@ -1,34 +1,42 @@
-/*
- * 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.
- */
-using System.Messaging;
-
-namespace Apache.NMS.MSMQ
-{
-	public interface IMessageConverter
-	{
-
-		/// <summary>
-		/// Method ToMSMQMessageQueue
-		/// </summary>
-		/// <param name="destination">An IDestination</param>
-		/// <returns>A  MessageQueue</returns>
-		MessageQueue ToMsmqDestination(IDestination destination);
-
-		Message ToMsmqMessage(IMessage message);
-		IMessage ToNmsMessage(Message message);
-	}
-}
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A filter performing a greater than comparison of two expressions.
+    /// </summary>
+    public class GreaterExpression : ComparisonExpression
+    {
+        protected override string ExpressionSymbol
+        {
+            get { return ">"; }
+        }
+
+        public GreaterExpression(IExpression left, IExpression right)
+            : base(left, right)
+        {
+        }
+
+        public override bool AsBoolean(int? compared)
+        {
+            return compared.HasValue ? compared.Value > 0 : false;
+        }
+    }
+}
diff --git a/src/main/csharp/IMessageConverter.cs b/src/main/csharp/Selector/GreaterOrEqualExpression.cs
similarity index 51%
copy from src/main/csharp/IMessageConverter.cs
copy to src/main/csharp/Selector/GreaterOrEqualExpression.cs
index 152377b..7a456f8 100644
--- a/src/main/csharp/IMessageConverter.cs
+++ b/src/main/csharp/Selector/GreaterOrEqualExpression.cs
@@ -1,34 +1,43 @@
-/*
- * 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.
- */
-using System.Messaging;
-
-namespace Apache.NMS.MSMQ
-{
-	public interface IMessageConverter
-	{
-
-		/// <summary>
-		/// Method ToMSMQMessageQueue
-		/// </summary>
-		/// <param name="destination">An IDestination</param>
-		/// <returns>A  MessageQueue</returns>
-		MessageQueue ToMsmqDestination(IDestination destination);
-
-		Message ToMsmqMessage(IMessage message);
-		IMessage ToNmsMessage(Message message);
-	}
-}
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A filter performing a greater than or equal comparison
+    /// of two expressions.
+    /// </summary>
+    public class GreaterOrEqualExpression : ComparisonExpression
+    {
+        protected override string ExpressionSymbol
+        {
+            get { return ">="; }
+        }
+
+        public GreaterOrEqualExpression(IExpression left, IExpression right)
+            : base(left, right)
+        {
+        }
+
+        public override bool AsBoolean(int? compared)
+        {
+            return compared.HasValue ? compared.Value >= 0 : false;
+        }
+    }
+}
diff --git a/src/main/csharp/IMessageConverter.cs b/src/main/csharp/Selector/IBooleanExpression.cs
similarity index 54%
copy from src/main/csharp/IMessageConverter.cs
copy to src/main/csharp/Selector/IBooleanExpression.cs
index 152377b..af6c0f5 100644
--- a/src/main/csharp/IMessageConverter.cs
+++ b/src/main/csharp/Selector/IBooleanExpression.cs
@@ -1,34 +1,35 @@
-/*
- * 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.
- */
-using System.Messaging;
-
-namespace Apache.NMS.MSMQ
-{
-	public interface IMessageConverter
-	{
-
-		/// <summary>
-		/// Method ToMSMQMessageQueue
-		/// </summary>
-		/// <param name="destination">An IDestination</param>
-		/// <returns>A  MessageQueue</returns>
-		MessageQueue ToMsmqDestination(IDestination destination);
-
-		Message ToMsmqMessage(IMessage message);
-		IMessage ToNmsMessage(Message message);
-	}
-}
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// An IBooleanExpression is an expression that always
+    /// produces a boolean result.
+    /// </summary>
+    public interface IBooleanExpression : IExpression
+    {
+        /// <summary>
+        /// Checks if expression evaluates to <c>true</c>.
+        /// </summary>
+        /// <param name="message">Evaluation context.</param>
+        /// <return><c>true</c> if the expression evaluates to <c>true</c>.</return>
+        bool Matches(MessageEvaluationContext message);
+    }
+}
diff --git a/src/main/csharp/IMessageConverter.cs b/src/main/csharp/Selector/IExpression.cs
similarity index 59%
copy from src/main/csharp/IMessageConverter.cs
copy to src/main/csharp/Selector/IExpression.cs
index 152377b..30fb893 100644
--- a/src/main/csharp/IMessageConverter.cs
+++ b/src/main/csharp/Selector/IExpression.cs
@@ -1,34 +1,35 @@
-/*
- * 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.
- */
-using System.Messaging;
-
-namespace Apache.NMS.MSMQ
-{
-	public interface IMessageConverter
-	{
-
-		/// <summary>
-		/// Method ToMSMQMessageQueue
-		/// </summary>
-		/// <param name="destination">An IDestination</param>
-		/// <returns>A  MessageQueue</returns>
-		MessageQueue ToMsmqDestination(IDestination destination);
-
-		Message ToMsmqMessage(IMessage message);
-		IMessage ToNmsMessage(Message message);
-	}
-}
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// Represents an expression
+    /// </summary>
+    public interface IExpression
+    {
+        /// <summary>
+        /// Evaluates the expression.
+        /// </summary>
+        /// <param name="message">Evaluation context.</param>
+        /// <return>The result of the evaluation.</return>
+        object Evaluate(MessageEvaluationContext message);
+    }
+}
+    
\ No newline at end of file
diff --git a/src/main/csharp/Selector/InExpression.cs b/src/main/csharp/Selector/InExpression.cs
new file mode 100644
index 0000000..cd2d7ea
--- /dev/null
+++ b/src/main/csharp/Selector/InExpression.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Text;
+using System.Collections;
+using System.Collections.Generic;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A boolean expression which checks if an expression value is
+    /// contained in a list of defined values.
+    /// </summary>
+    public class InExpression : BooleanUnaryExpression
+    {
+        private bool notNot;
+        private ArrayList elements;
+        private HashSet<string> hashset;
+
+        protected override string ExpressionSymbol
+        {
+            get { return notNot ? "IN" : "NOT IN"; }
+        }
+
+        public InExpression(IExpression right, ArrayList elements, bool notNot)
+            : base(right)
+        {
+            this.notNot = notNot;
+
+            this.elements = elements;
+            this.hashset = new HashSet<string>();
+
+            foreach(object element in elements)
+            {
+                hashset.Add((string)element);
+            }
+        }
+
+        public override object Evaluate(MessageEvaluationContext message)
+        {
+            object rvalue = Right.Evaluate(message);
+
+            bool answer = false;
+            if(rvalue != null && (rvalue is string))
+            {
+                answer = hashset.Contains((string)rvalue);
+            }
+
+            return notNot ? answer : !answer;
+        }
+
+        public override string ToString()
+        {
+            StringBuilder answer = new StringBuilder();
+            answer.Append(Right);
+            answer.Append(" ");
+            answer.Append(ExpressionSymbol);
+            answer.Append(" (");
+
+            for(int i = 0; i < elements.Count; i++)
+            {
+                if(i > 0) answer.Append(", ");
+
+                string s = (string)elements[i];
+
+                answer.Append('\'');
+                for(int c = 0; c < s.Length; c++)
+                {
+                    char ch = s[c];
+                    if(ch == '\'')
+                    {
+                        answer.Append(ch);
+                    }
+                    answer.Append(ch);
+                }
+                answer.Append('\'');
+            }
+
+            answer.Append(")");
+            return answer.ToString();
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/IsNullExpression.cs b/src/main/csharp/Selector/IsNullExpression.cs
new file mode 100644
index 0000000..28d89a2
--- /dev/null
+++ b/src/main/csharp/Selector/IsNullExpression.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Text;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A boolean expression which checks if an expression value is null.
+    /// </summary>
+    public class IsNullExpression : BooleanUnaryExpression
+    {
+        private bool notNot;
+
+        protected override string ExpressionSymbol
+        {
+            get { return notNot ? "IS NULL" : "IS NOT NULL"; }
+        }
+
+        public IsNullExpression(IExpression right, bool notNot)
+            : base(right)
+        {
+            this.notNot = notNot;
+        }
+
+        public override object Evaluate(MessageEvaluationContext message)
+        {
+            object rvalue = Right.Evaluate(message);
+
+            bool answer = (rvalue == null || rvalue == ConstantExpression.NULL);
+
+            return notNot ? answer : !answer;
+        }
+
+        public override string ToString()
+        {
+            StringBuilder answer = new StringBuilder();
+            answer.Append(Right);
+            answer.Append(" ");
+            answer.Append(ExpressionSymbol);
+            return answer.ToString();
+        }
+    }
+}
diff --git a/src/main/csharp/IMessageConverter.cs b/src/main/csharp/Selector/LesserExpression.cs
similarity index 52%
copy from src/main/csharp/IMessageConverter.cs
copy to src/main/csharp/Selector/LesserExpression.cs
index 152377b..4be2c9d 100644
--- a/src/main/csharp/IMessageConverter.cs
+++ b/src/main/csharp/Selector/LesserExpression.cs
@@ -1,34 +1,42 @@
-/*
- * 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.
- */
-using System.Messaging;
-
-namespace Apache.NMS.MSMQ
-{
-	public interface IMessageConverter
-	{
-
-		/// <summary>
-		/// Method ToMSMQMessageQueue
-		/// </summary>
-		/// <param name="destination">An IDestination</param>
-		/// <returns>A  MessageQueue</returns>
-		MessageQueue ToMsmqDestination(IDestination destination);
-
-		Message ToMsmqMessage(IMessage message);
-		IMessage ToNmsMessage(Message message);
-	}
-}
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A filter performing a lesser than comparison of two expressions.
+    /// </summary>
+    public class LesserExpression : ComparisonExpression
+    {
+        protected override string ExpressionSymbol
+        {
+            get { return "<"; }
+        }
+
+        public LesserExpression(IExpression left, IExpression right)
+            : base(left, right)
+        {
+        }
+
+        public override bool AsBoolean(int? compared)
+        {
+            return compared.HasValue ? compared.Value < 0 : false;
+        }
+    }
+}
diff --git a/src/main/csharp/IMessageConverter.cs b/src/main/csharp/Selector/LesserOrEqualExpression.cs
similarity index 51%
copy from src/main/csharp/IMessageConverter.cs
copy to src/main/csharp/Selector/LesserOrEqualExpression.cs
index 152377b..abdc7e5 100644
--- a/src/main/csharp/IMessageConverter.cs
+++ b/src/main/csharp/Selector/LesserOrEqualExpression.cs
@@ -1,34 +1,43 @@
-/*
- * 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.
- */
-using System.Messaging;
-
-namespace Apache.NMS.MSMQ
-{
-	public interface IMessageConverter
-	{
-
-		/// <summary>
-		/// Method ToMSMQMessageQueue
-		/// </summary>
-		/// <param name="destination">An IDestination</param>
-		/// <returns>A  MessageQueue</returns>
-		MessageQueue ToMsmqDestination(IDestination destination);
-
-		Message ToMsmqMessage(IMessage message);
-		IMessage ToNmsMessage(Message message);
-	}
-}
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A filter performing a lesser than or equal comparison
+    /// of two expressions.
+    /// </summary>
+    public class LesserOrEqualExpression : ComparisonExpression
+    {
+        protected override string ExpressionSymbol
+        {
+            get { return "<="; }
+        }
+
+        public LesserOrEqualExpression(IExpression left, IExpression right)
+            : base(left, right)
+        {
+        }
+
+        public override bool AsBoolean(int? compared)
+        {
+            return compared.HasValue ? compared.Value <= 0 : false;
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/LikeExpression.cs b/src/main/csharp/Selector/LikeExpression.cs
new file mode 100644
index 0000000..8317bd6
--- /dev/null
+++ b/src/main/csharp/Selector/LikeExpression.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Globalization;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A filter performing a string matching comparison.
+    /// </summary>
+    public class LikeExpression : BooleanUnaryExpression
+    {
+        private bool notNot;
+        private Regex pattern;
+
+        protected override string ExpressionSymbol
+        {
+            get { return notNot ? "LIKE" : "NOT LIKE"; }
+        }
+
+        public LikeExpression(IExpression left, string like, string escape, bool notNot)
+            : base(left)
+        {
+            this.notNot = notNot;
+
+            bool doEscape = false;
+            char escapeChar = '%';
+
+            if(escape != null)
+            {
+                if(escape.Length != 1)
+                {
+                    throw new ApplicationException("The ESCAPE string litteral is invalid.  It can only be one character.  Litteral used: " + escape);
+                }
+                doEscape = true;
+                escapeChar = escape[0];
+            }
+
+            StringBuilder temp = new StringBuilder();
+            StringBuilder regexp = new StringBuilder(like.Length * 2);
+            regexp.Append("^"); // The beginning of the input
+            for(int c = 0; c < like.Length; c++)
+            {
+                char ch = like[c];
+                if(doEscape && (ch == escapeChar))
+                {
+                    c++;
+                    if(c >= like.Length)
+                    {
+                        // nothing left to escape...
+                        break;
+                    }
+                    temp.Append(like[c]);
+                }
+                else if(ch == '%')
+                {
+                    if(temp.Length > 0)
+                    {
+                        regexp.Append(Regex.Escape(temp.ToString()));
+                        temp.Length = 0;
+                    }
+                    regexp.Append(".*?"); // Do a non-greedy match 
+                }
+                else if(c == '_')
+                {
+                    if(temp.Length > 0)
+                    {
+                        regexp.Append(Regex.Escape(temp.ToString()));
+                        temp.Length = 0;
+                    }
+                    regexp.Append("."); // match one 
+                }
+                else
+                {
+                    temp.Append(ch);
+                }
+            }
+            if(temp.Length > 0)
+            {
+                regexp.Append(Regex.Escape(temp.ToString()));
+            }
+            regexp.Append("$"); // The end of the input
+
+            pattern = new Regex(regexp.ToString(), RegexOptions.Singleline | RegexOptions.Compiled);
+        }
+
+        public override object Evaluate(MessageEvaluationContext message)
+        {
+            object rvalue = this.Right.Evaluate(message);
+
+            bool answer = false;
+            if(rvalue != null)
+            {
+                if(rvalue is string)
+                {
+                    answer = pattern.IsMatch((string)rvalue);
+                }
+                else
+                {
+                    //throw new ApplicationException("LIKE can only operate on string identifiers. LIKE attemped on " + rvalue.GetType().ToString());
+                }
+            }
+
+            return notNot ? answer : !answer;
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/LogicExpression.cs b/src/main/csharp/Selector/LogicExpression.cs
new file mode 100644
index 0000000..61617a4
--- /dev/null
+++ b/src/main/csharp/Selector/LogicExpression.cs
@@ -0,0 +1,48 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A filter performing a logical combination of two objects.
+    /// </summary>
+    public abstract class LogicExpression : BinaryExpression, IBooleanExpression
+    {
+        public LogicExpression(IBooleanExpression left, IBooleanExpression right)
+            : base(left, right)
+        {
+        }
+
+        public bool Matches(MessageEvaluationContext message)
+        {
+            object value = Evaluate(message);
+            return value != null && (bool)value;            
+        }
+
+        public static IBooleanExpression CreateOR(IBooleanExpression left, IBooleanExpression right)
+        {
+            return new ORExpression(left, right);
+        }
+
+        public static IBooleanExpression CreateAND(IBooleanExpression left, IBooleanExpression right)
+        {
+            return new ANDExpression(left, right);
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/MessageEvaluationContext.cs b/src/main/csharp/Selector/MessageEvaluationContext.cs
new file mode 100644
index 0000000..054d911
--- /dev/null
+++ b/src/main/csharp/Selector/MessageEvaluationContext.cs
@@ -0,0 +1,78 @@
+using System;
+using Apache.NMS;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// MessageEvaluationContext is used to cache selection results.
+    /// 
+    /// A message usually has multiple selectors applied against it. Some selector
+    /// have a high cost of evaluating against the message. Those selectors may whish
+    /// to cache evaluation results associated with the message in the
+    /// MessageEvaluationContext.
+    /// </summary>
+    public class MessageEvaluationContext
+    {
+        private IMessage nmsMessage;
+        public IMessage Message
+        {
+            get { return nmsMessage; }
+            set { nmsMessage = value; }
+        }
+
+        public MessageEvaluationContext(IMessage message)
+        {
+            nmsMessage = message;
+        }
+
+        public object GetProperty(string name)
+        {
+            if(name.Length > 3 && 
+               string.Compare(name.Substring(0, 3), "JMS", true) == 0)
+            {
+                if(string.Compare(name, "JMSCorrelationID", true) == 0)
+                {
+                    return nmsMessage.NMSCorrelationID;
+                }
+                if(string.Compare(name, "JMSMessageID", true) == 0)
+                {
+                    return nmsMessage.NMSMessageId;
+                }
+                if(string.Compare(name, "JMSPriority", true) == 0)
+                {
+                    return nmsMessage.NMSPriority;
+                }
+                if(string.Compare(name, "JMSTimestamp", true) == 0)
+                {
+                    return nmsMessage.NMSTimestamp;
+                }
+                if(string.Compare(name, "JMSType", true) == 0)
+                {
+                    return nmsMessage.NMSType;
+                }
+                if(string.Compare(name, "JMSDeliveryMode", true) == 0)
+                {
+                    return nmsMessage.NMSDeliveryMode;
+                }
+            }
+            return nmsMessage.Properties[name];
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/csharp/Selector/MinusExpression.cs b/src/main/csharp/Selector/MinusExpression.cs
new file mode 100644
index 0000000..260e5e8
--- /dev/null
+++ b/src/main/csharp/Selector/MinusExpression.cs
@@ -0,0 +1,67 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A filter performing a substraction of two expressions.
+    /// </summary>
+    public class MinusExpression : ArithmeticExpression
+    {
+        protected override string ExpressionSymbol
+        {
+            get { return "-"; }
+        }
+
+        public MinusExpression(IExpression left, IExpression right)
+            : base(left, right)
+        {
+        }
+
+        public override object Evaluate(MessageEvaluationContext message)
+        {
+            object lvalue = Left.Evaluate(message);
+            if(lvalue == null) return null;
+
+            object rvalue = Right.Evaluate(message);
+            if(rvalue == null) return null;
+
+            AlignedNumericValues values = new AlignedNumericValues(lvalue, rvalue);
+
+            object result = null;
+
+            switch(values.TypeEnum)
+            {
+                case AlignedNumericValues.T.SByteType : result = (sbyte )values.Left - (sbyte )values.Right; break;
+                case AlignedNumericValues.T.ByteType  : result = (byte  )values.Left - (byte  )values.Right; break;
+                case AlignedNumericValues.T.CharType  : result = (char  )values.Left - (char  )values.Right; break;
+                case AlignedNumericValues.T.ShortType : result = (short )values.Left - (short )values.Right; break;
+                case AlignedNumericValues.T.UShortType: result = (ushort)values.Left - (ushort)values.Right; break;
+                case AlignedNumericValues.T.IntType   : result = (int   )values.Left - (int   )values.Right; break;
+                case AlignedNumericValues.T.UIntType  : result = (uint  )values.Left - (uint  )values.Right; break;
+                case AlignedNumericValues.T.LongType  : result = (long  )values.Left - (long  )values.Right; break;
+                case AlignedNumericValues.T.ULongType : result = (ulong )values.Left - (ulong )values.Right; break;
+                case AlignedNumericValues.T.FloatType : result = (float )values.Left - (float )values.Right; break;
+                case AlignedNumericValues.T.DoubleType: result = (double)values.Left - (double)values.Right; break;
+            }
+
+            return result;
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/ModExpression.cs b/src/main/csharp/Selector/ModExpression.cs
new file mode 100644
index 0000000..386c08d
--- /dev/null
+++ b/src/main/csharp/Selector/ModExpression.cs
@@ -0,0 +1,67 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A filter performing a modulo of two expressions.
+    /// </summary>
+    public class ModExpression : ArithmeticExpression
+    {
+        protected override string ExpressionSymbol
+        {
+            get { return "%"; }
+        }
+
+        public ModExpression(IExpression left, IExpression right)
+            : base(left, right)
+        {
+        }
+
+        public override object Evaluate(MessageEvaluationContext message)
+        {
+            object lvalue = Left.Evaluate(message);
+            if(lvalue == null) return null;
+
+            object rvalue = Right.Evaluate(message);
+            if(rvalue == null) return null;
+
+            AlignedNumericValues values = new AlignedNumericValues(lvalue, rvalue);
+
+            object result = null;
+
+            switch(values.TypeEnum)
+            {
+                case AlignedNumericValues.T.SByteType : result = (sbyte )values.Left % (sbyte )values.Right; break;
+                case AlignedNumericValues.T.ByteType  : result = (byte  )values.Left % (byte  )values.Right; break;
+                case AlignedNumericValues.T.CharType  : result = (char  )values.Left % (char  )values.Right; break;
+                case AlignedNumericValues.T.ShortType : result = (short )values.Left % (short )values.Right; break;
+                case AlignedNumericValues.T.UShortType: result = (ushort)values.Left % (ushort)values.Right; break;
+                case AlignedNumericValues.T.IntType   : result = (int   )values.Left % (int   )values.Right; break;
+                case AlignedNumericValues.T.UIntType  : result = (uint  )values.Left % (uint  )values.Right; break;
+                case AlignedNumericValues.T.LongType  : result = (long  )values.Left % (long  )values.Right; break;
+                case AlignedNumericValues.T.ULongType : result = (ulong )values.Left % (ulong )values.Right; break;
+                case AlignedNumericValues.T.FloatType : result = (float )values.Left % (float )values.Right; break;
+                case AlignedNumericValues.T.DoubleType: result = (double)values.Left % (double)values.Right; break;
+            }
+
+            return result;
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/MultiplyExpression.cs b/src/main/csharp/Selector/MultiplyExpression.cs
new file mode 100644
index 0000000..0009092
--- /dev/null
+++ b/src/main/csharp/Selector/MultiplyExpression.cs
@@ -0,0 +1,67 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A filter performing a multiplication of two expressions.
+    /// </summary>
+    public class MultiplyExpression : ArithmeticExpression
+    {
+        protected override string ExpressionSymbol
+        {
+            get { return "*"; }
+        }
+
+        public MultiplyExpression(IExpression left, IExpression right)
+            : base(left, right)
+        {
+        }
+
+        public override object Evaluate(MessageEvaluationContext message)
+        {
+            object lvalue = Left.Evaluate(message);
+            if(lvalue == null) return null;
+
+            object rvalue = Right.Evaluate(message);
+            if(rvalue == null) return null;
+
+            AlignedNumericValues values = new AlignedNumericValues(lvalue, rvalue);
+
+            object result = null;
+
+            switch(values.TypeEnum)
+            {
+                case AlignedNumericValues.T.SByteType : result = (sbyte )values.Left * (sbyte )values.Right; break;
+                case AlignedNumericValues.T.ByteType  : result = (byte  )values.Left * (byte  )values.Right; break;
+                case AlignedNumericValues.T.CharType  : result = (char  )values.Left * (char  )values.Right; break;
+                case AlignedNumericValues.T.ShortType : result = (short )values.Left * (short )values.Right; break;
+                case AlignedNumericValues.T.UShortType: result = (ushort)values.Left * (ushort)values.Right; break;
+                case AlignedNumericValues.T.IntType   : result = (int   )values.Left * (int   )values.Right; break;
+                case AlignedNumericValues.T.UIntType  : result = (uint  )values.Left * (uint  )values.Right; break;
+                case AlignedNumericValues.T.LongType  : result = (long  )values.Left * (long  )values.Right; break;
+                case AlignedNumericValues.T.ULongType : result = (ulong )values.Left * (ulong )values.Right; break;
+                case AlignedNumericValues.T.FloatType : result = (float )values.Left * (float )values.Right; break;
+                case AlignedNumericValues.T.DoubleType: result = (double)values.Left * (double)values.Right; break;
+            }
+
+            return result;
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/NOTExpression.cs b/src/main/csharp/Selector/NOTExpression.cs
new file mode 100644
index 0000000..6d6ef55
--- /dev/null
+++ b/src/main/csharp/Selector/NOTExpression.cs
@@ -0,0 +1,45 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// An expression which negates a boolean expression value.
+    /// </summary>
+    public class NOTExpression : BooleanUnaryExpression
+    {
+        protected override string ExpressionSymbol
+        {
+            get { return "NOT"; }
+        }
+
+        public NOTExpression(IExpression left)
+            : base(left)
+        {
+        }
+
+        public override object Evaluate(MessageEvaluationContext message)
+        {
+            object rvalue = Right.Evaluate(message);
+            if(rvalue == null   ) return null;
+            if(rvalue is bool   ) return !(bool)rvalue;
+            return null;
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/NegateExpression.cs b/src/main/csharp/Selector/NegateExpression.cs
new file mode 100644
index 0000000..0496b6f
--- /dev/null
+++ b/src/main/csharp/Selector/NegateExpression.cs
@@ -0,0 +1,51 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// An expression which negates a numeric expression value.
+    /// </summary>
+    public class NegateExpression : UnaryExpression
+    {
+        protected override string ExpressionSymbol
+        {
+            get { return "-"; }
+        }
+
+        public NegateExpression(IExpression left)
+            : base(left)
+        {
+        }
+
+        public override object Evaluate(MessageEvaluationContext message)
+        {
+            object rvalue = Right.Evaluate(message);
+            if(rvalue == null   ) return null;
+            if(rvalue is int    ) return -(int    )rvalue;
+            if(rvalue is long   ) return -(long   )rvalue;
+            if(rvalue is double ) return -(double )rvalue;
+            if(rvalue is float  ) return -(float  )rvalue;
+            if(rvalue is decimal) return -(decimal)rvalue;
+            if(rvalue is short  ) return -(short  )rvalue;
+            if(rvalue is byte   ) return -(byte   )rvalue;
+            return null;
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/ORExpression.cs b/src/main/csharp/Selector/ORExpression.cs
new file mode 100644
index 0000000..86648bc
--- /dev/null
+++ b/src/main/csharp/Selector/ORExpression.cs
@@ -0,0 +1,46 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A filter performing a logical OR combination of two expressions.
+    /// </summary>
+    public class ORExpression : LogicExpression
+    {
+        protected override string ExpressionSymbol
+        {
+            get { return "OR"; }
+        }
+
+        public ORExpression(IBooleanExpression left, IBooleanExpression right)
+            : base(left, right)
+        {
+        }
+
+        public override object Evaluate(MessageEvaluationContext message)
+        {
+            object lvalue = Left.Evaluate(message);
+            if(lvalue != null && (bool)lvalue) return true;
+
+            object rvalue = Right.Evaluate(message);
+            return rvalue == null ? null : rvalue;
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/ParseException.cs b/src/main/csharp/Selector/ParseException.cs
new file mode 100644
index 0000000..f1b6d60
--- /dev/null
+++ b/src/main/csharp/Selector/ParseException.cs
@@ -0,0 +1,197 @@
+/* Generated By:CSharpCC: Do not edit this line. ParseException.cs Version 3.2 */
+/// <summary>
+/// This exception is thrown when parse errors are encountered.
+/// </summary>
+/// <remarks>
+/// You can explicitly create objects of this exception type by
+/// calling the method GenerateParseException in the generated
+/// parser.
+/// <para>
+/// You can modify this class to customize your error reporting
+/// mechanisms so long as you retain the public fields.
+/// </para>
+/// </remarks>
+public  class ParseException : System.Exception {
+
+  /**
+   * This constructor is used by the method "GenerateParseException"
+   * in the generated parser. Calling this constructor generates
+   * a new object of this type with the fields "currentToken",
+   * "expectedTokenSequences", and "tokenImage" set.  The boolean
+   * flag "specialConstructor" is also set to true to indicate that
+   * this constructor was used to create this object.
+   * This constructor calls its super class with the empty string
+   * to force the "toString" method of parent class "Throwable" to
+   * print the error message in the form:
+   *     ParseException: result of getMessage
+   */
+  public ParseException(Token currentTokenVal,
+                        int[][] expectedTokenSequencesVal,
+                        string[] tokenImageVal
+                       ) : base("") {
+    specialConstructor = true;
+    currentToken = currentTokenVal;
+    expectedTokenSequences = expectedTokenSequencesVal;
+    tokenImage = tokenImageVal;
+  }
+
+  /**
+   * The following constructors are for use by you for whatever
+   * purpose you can think of.  Constructing the exception in this
+   * manner makes the exception behave in the normal way - i.e., as
+   * documented in the class "Exception".  The fields "errorToken",
+   * "expectedTokenSequences", and "tokenImage" do not contain
+   * relevant information.  The CSharpCC generated code does not use
+   * these constructors.
+   */
+
+  public ParseException() :
+    base() {
+    specialConstructor = false;
+  }
+
+  public ParseException(string message) :
+    base(message) {
+    specialConstructor = false;
+  }
+
+  /**
+   * This variable determines which constructor was used to create
+   * this object and thereby affects the semantics of the
+   * "getMessage" method (see below).
+   */
+  protected bool specialConstructor;
+
+  /**
+   * This is the last token that has been consumed successfully.  If
+   * this object has been created due to a parse error, the token
+   * followng this token will (therefore) be the first error token.
+   */
+  public Token currentToken;
+
+  /**
+   * Each entry in this array is an array of integers.  Each array
+   * of integers represents a sequence of tokens (by their ordinal
+   * values) that is expected at this point of the parse.
+   */
+  public int[][] expectedTokenSequences;
+
+  /**
+   * This is a reference to the "tokenImage" array of the generated
+   * parser within which the parse error occurred.  This array is
+   * defined in the generated ...Constants interface.
+   */
+  public string[] tokenImage;
+
+  /**
+   * This method has the standard behavior when this object has been
+   * created using the standard constructors.  Otherwise, it uses
+   * "currentToken" and "expectedTokenSequences" to generate a parse
+   * error message and returns it.  If this object has been created
+   * due to a parse error, and you do not catch it (it gets thrown
+   * from the parser), then this method is called during the printing
+   * of the final stack trace, and hence the correct error message
+   * gets displayed.
+   */
+  public override string Message {
+    get {
+      if (!specialConstructor) {
+        return base.Message;
+      }
+      string expected = "";
+      int maxSize = 0;
+      for (int i = 0; i < expectedTokenSequences.Length; i++) {
+        if (maxSize < expectedTokenSequences[i].Length) {
+          maxSize = expectedTokenSequences[i].Length;
+        }
+        for (int j = 0; j < expectedTokenSequences[i].Length; j++) {
+          expected += tokenImage[expectedTokenSequences[i][j]] + " ";
+        }
+        if (expectedTokenSequences[i][expectedTokenSequences[i].Length - 1] != 0) {
+          expected += "...";
+        }
+        expected += eol + "    ";
+      }
+      string retval = "Encountered \"";
+      Token tok = currentToken.next;
+      for (int i = 0; i < maxSize; i++) {
+        if (i != 0) retval += " ";
+        if (tok.kind == 0) {
+          retval += tokenImage[0];
+          break;
+        }
+        retval += AddEscapes(tok.image);
+        tok = tok.next; 
+      }
+      if (currentToken.next.kind == 0) {
+        retval += "\" after line ";
+      } else {
+        retval += "\" at line ";
+      }
+      retval += currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+      retval += "." + eol;
+      if (expectedTokenSequences.Length == 1) {
+        retval += "Was expecting:" + eol + "    ";
+      } else {
+        retval += "Was expecting one of:" + eol + "    ";
+      }
+      retval += expected;
+      return retval;
+    }
+  }
+
+  /**
+   * The end of line string for this machine.
+   */
+  protected string eol = System.Environment.NewLine;
+ 
+  /**
+   * Used to convert raw characters to their escaped version
+   * when these raw version cannot be used as part of an ASCII
+   * string literal.
+   */
+  protected string AddEscapes(string str) {
+      System.Text.StringBuilder retval = new System.Text.StringBuilder();
+      char ch;
+      for (int i = 0; i < str.Length; i++) {
+        switch (str[i]) {
+           case '\0' :
+              continue;
+           case '\b':
+              retval.Append("\\b");
+              continue;
+           case '\t':
+              retval.Append("\\t");
+              continue;
+           case '\n':
+              retval.Append("\\n");
+              continue;
+           case '\f':
+              retval.Append("\\f");
+              continue;
+           case '\r':
+              retval.Append("\\r");
+              continue;
+           case '\"':
+              retval.Append("\\\"");
+              continue;
+           case '\'':
+              retval.Append("\\\'");
+              continue;
+           case '\\':
+              retval.Append("\\\\");
+              continue;
+           default:
+              if ((ch = str[i]) < 0x20 || ch > 0x7e) {
+                 string s = "0000" + System.Convert.ToString((int)ch, 16);
+                 retval.Append("\\u" + s.Substring(s.Length - 4, s.Length - (s.Length - 4)));
+              } else {
+                 retval.Append(ch);
+              }
+              continue;
+        }
+      }
+      return retval.ToString();
+   }
+
+}
diff --git a/src/main/csharp/Selector/PlusExpression.cs b/src/main/csharp/Selector/PlusExpression.cs
new file mode 100644
index 0000000..450b653
--- /dev/null
+++ b/src/main/csharp/Selector/PlusExpression.cs
@@ -0,0 +1,68 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// A filter performing an addition of two expressions.
+    /// </summary>
+    public class PlusExpression : ArithmeticExpression
+    {
+        protected override string ExpressionSymbol
+        {
+            get { return "+"; }
+        }
+
+        public PlusExpression(IExpression left, IExpression right)
+            : base(left, right)
+        {
+        }
+
+        public override object Evaluate(MessageEvaluationContext message)
+        {
+            object lvalue = Left.Evaluate(message);
+            if(lvalue == null) return null;
+
+            object rvalue = Right.Evaluate(message);
+            if(lvalue is string) return (string)lvalue + rvalue;
+            if(rvalue == null) return null;
+
+            AlignedNumericValues values = new AlignedNumericValues(lvalue, rvalue);
+
+            object result = null;
+
+            switch(values.TypeEnum)
+            {
+                case AlignedNumericValues.T.SByteType : result = (sbyte )values.Left + (sbyte )values.Right; break;
+                case AlignedNumericValues.T.ByteType  : result = (byte  )values.Left + (byte  )values.Right; break;
+                case AlignedNumericValues.T.CharType  : result = (char  )values.Left + (char  )values.Right; break;
+                case AlignedNumericValues.T.ShortType : result = (short )values.Left + (short )values.Right; break;
+                case AlignedNumericValues.T.UShortType: result = (ushort)values.Left + (ushort)values.Right; break;
+                case AlignedNumericValues.T.IntType   : result = (int   )values.Left + (int   )values.Right; break;
+                case AlignedNumericValues.T.UIntType  : result = (uint  )values.Left + (uint  )values.Right; break;
+                case AlignedNumericValues.T.LongType  : result = (long  )values.Left + (long  )values.Right; break;
+                case AlignedNumericValues.T.ULongType : result = (ulong )values.Left + (ulong )values.Right; break;
+                case AlignedNumericValues.T.FloatType : result = (float )values.Left + (float )values.Right; break;
+                case AlignedNumericValues.T.DoubleType: result = (double)values.Left + (double)values.Right; break;
+            }
+
+            return result;
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/PropertyExpression.cs b/src/main/csharp/Selector/PropertyExpression.cs
new file mode 100644
index 0000000..8d00757
--- /dev/null
+++ b/src/main/csharp/Selector/PropertyExpression.cs
@@ -0,0 +1,53 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// Represents a property expression.
+    /// </summary>
+    public class PropertyExpression : IExpression
+    {
+        private string name;
+        public string Name
+        {
+            get { return name; }
+        }
+
+        public PropertyExpression(string name)
+        {
+            this.name = name;
+        }
+
+        public object Evaluate(MessageEvaluationContext message)
+        {
+            return message.GetProperty(name);
+        }
+
+        public override string ToString()
+        {
+            return name;
+        }
+
+        public override int GetHashCode()
+        {
+            return name.GetHashCode();
+        }
+    }
+}
diff --git a/src/main/csharp/Selector/SelectorParser.cs b/src/main/csharp/Selector/SelectorParser.cs
new file mode 100644
index 0000000..92226ac
--- /dev/null
+++ b/src/main/csharp/Selector/SelectorParser.cs
@@ -0,0 +1,1172 @@
+/* Generated By:CSharpCC: Do not edit this line. SelectorParser.cs */
+/**
+ *
+ * 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.
+ */
+using System;
+using System.IO;
+using System.Text;
+using System.Collections;
+
+using Apache.NMS;
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// JMS Selector Parser generated by <a href="https://github.com/deveel/csharpcc">CSharpCC</a>
+    /// 
+    /// Do not edit this .cs file directly - it is autogenerated from SelectorParser.csc
+    /// using <c>csharpcc.exe -UNICODE_INPUT=true SelectorParser.csc</c>.
+    /// 
+    /// SelectorParser.csc is adapted from
+    /// <a href="https://raw.githubusercontent.com/apache/activemq/activemq-4.0/activemq-core/src/main/grammar/SelectorParser.jj">
+    /// ActiveMQ 4.0 SelectorParser.jj</a>
+    /// </summary>
+    public class SelectorParser : SelectorParserConstants {
+
+        public SelectorParser()
+            : this(new StringReader(""))
+        {
+        }
+
+        public IBooleanExpression Parse(string selector)
+        {
+            this.ReInit(new StringReader(selector));
+
+            try
+            {
+                return this.JmsSelector();
+            }
+            catch(Exception e)
+            {
+                    throw new InvalidSelectorException(selector, e);
+            }
+        }
+
+        private IBooleanExpression AsBooleanExpression(IExpression value)
+        {
+            if(value is IBooleanExpression)
+            {
+                return (IBooleanExpression)value;
+            }
+            if(value is PropertyExpression)
+            {
+                return UnaryExpression.CreateBooleanCast(value);
+            }
+            throw new ParseException("IExpression will not result in a boolean value: " + value);
+        }
+
+// ----------------------------------------------------------------------------
+// Grammar
+// ----------------------------------------------------------------------------
+  public IBooleanExpression JmsSelector() {
+    IExpression left = null;
+    left = GetOrExpression();
+        {return AsBooleanExpression(left);}
+    throw new Exception("Missing return statement in function");
+  }
+
+  public IExpression GetOrExpression() {
+    IExpression left;
+    IExpression right;
+    left = GetAndExpression();
+    while (true) {
+      switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+      case OR:
+        ;
+        break;
+      default:
+        goto label_1;
+      }
+      mcc_consume_token(OR);
+      right = GetAndExpression();
+                left = LogicExpression.CreateOR(AsBooleanExpression(left), AsBooleanExpression(right));
+    }label_1: ;
+    
+        {return left;}
+    throw new Exception("Missing return statement in function");
+  }
+
+  public IExpression GetAndExpression() {
+    IExpression left;
+    IExpression right;
+    left = GetEqualityExpression();
+    while (true) {
+      switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+      case AND:
+        ;
+        break;
+      default:
+        goto label_2;
+      }
+      mcc_consume_token(AND);
+      right = GetEqualityExpression();
+                left = LogicExpression.CreateAND(AsBooleanExpression(left), AsBooleanExpression(right));
+    }label_2: ;
+    
+        {return left;}
+    throw new Exception("Missing return statement in function");
+  }
+
+  public IExpression GetEqualityExpression() {
+    IExpression left;
+    IExpression right;
+    left = GetComparisonExpression();
+    while (true) {
+      switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+      case IS:
+      case 28:
+      case 29:
+        ;
+        break;
+      default:
+        goto label_3;
+      }
+      switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+      case 28:
+        mcc_consume_token(28);
+        right = GetComparisonExpression();
+                left = ComparisonExpression.CreateEqual(left, right);
+        break;
+      case 29:
+        mcc_consume_token(29);
+        right = GetComparisonExpression();
+                left = ComparisonExpression.CreateNotEqual(left, right);
+        break;
+      default:
+        if (mcc_2_1(2)) {
+          mcc_consume_token(IS);
+          mcc_consume_token(NULL);
+                left = ComparisonExpression.CreateIsNull(left);
+        } else {
+          switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+          case IS:
+            mcc_consume_token(IS);
+            mcc_consume_token(NOT);
+            mcc_consume_token(NULL);
+                left = ComparisonExpression.CreateIsNotNull(left);
+            break;
+          default:
+            mcc_consume_token(-1);
+            throw new ParseException();
+          }
+        }
+        break;
+      }
+    }label_3: ;
+    
+        {return left;}
+    throw new Exception("Missing return statement in function");
+  }
+
+  public IExpression GetComparisonExpression() {
+    IExpression left;
+    IExpression right;
+    IExpression low;
+    IExpression high;
+    string t;
+    string u;
+        ArrayList list;
+    left = GetAddExpression();
+    while (true) {
+      switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+      case NOT:
+      case BETWEEN:
+      case LIKE:
+      case IN:
+      case 30:
+      case 31:
+      case 32:
+      case 33:
+        ;
+        break;
+      default:
+        goto label_4;
+      }
+      switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+      case 30:
+        mcc_consume_token(30);
+        right = GetAddExpression();
+                    left = ComparisonExpression.CreateGreaterThan(left, right);
+        break;
+      case 31:
+        mcc_consume_token(31);
+        right = GetAddExpression();
+                    left = ComparisonExpression.CreateGreaterThanOrEqual(left, right);
+        break;
+      case 32:
+        mcc_consume_token(32);
+        right = GetAddExpression();
+                    left = ComparisonExpression.CreateLesserThan(left, right);
+        break;
+      case 33:
+        mcc_consume_token(33);
+        right = GetAddExpression();
+                    left = ComparisonExpression.CreateLesserThanOrEqual(left, right);
+        break;
+      case LIKE:
+                                        u = null;
+        mcc_consume_token(LIKE);
+        t = GetStringLitteral();
+        switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+        case ESCAPE:
+          mcc_consume_token(ESCAPE);
+          u = GetStringLitteral();
+          break;
+        default:
+          ;
+          break;
+        }
+                    left = ComparisonExpression.CreateLike(left, t, u);
+        break;
+      default:
+        if (mcc_2_2(2)) {
+                                        u=null;
+          mcc_consume_token(NOT);
+          mcc_consume_token(LIKE);
+          t = GetStringLitteral();
+          switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+          case ESCAPE:
+            mcc_consume_token(ESCAPE);
+            u = GetStringLitteral();
+            break;
+          default:
+            ;
+            break;
+          }
+                    left = ComparisonExpression.CreateNotLike(left, t, u);
+        } else {
+          switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+          case BETWEEN:
+            mcc_consume_token(BETWEEN);
+            low = GetAddExpression();
+            mcc_consume_token(AND);
+            high = GetAddExpression();
+                                        left = ComparisonExpression.CreateBetween(left, low, high);
+            break;
+          default:
+            if (mcc_2_3(2)) {
+              mcc_consume_token(NOT);
+              mcc_consume_token(BETWEEN);
+              low = GetAddExpression();
+              mcc_consume_token(AND);
+              high = GetAddExpression();
+                                        left = ComparisonExpression.CreateNotBetween(left, low, high);
+            } else {
+              switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+              case IN:
+                mcc_consume_token(IN);
+                mcc_consume_token(34);
+                t = GetStringLitteral();
+                                    list = new ArrayList();
+                                    list.Add(t);
+                while (true) {
+                  switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+                  case 35:
+                    ;
+                    break;
+                  default:
+                    goto label_5;
+                  }
+                  mcc_consume_token(35);
+                  t = GetStringLitteral();
+                                            list.Add(t);
+                }label_5: ;
+                
+                mcc_consume_token(36);
+                           left = ComparisonExpression.CreateIn(left, list);
+                break;
+              default:
+                if (mcc_2_4(2)) {
+                  mcc_consume_token(NOT);
+                  mcc_consume_token(IN);
+                  mcc_consume_token(34);
+                  t = GetStringLitteral();
+                                    list = new ArrayList();
+                                    list.Add(t);
+                  while (true) {
+                    switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+                    case 35:
+                      ;
+                      break;
+                    default:
+                      goto label_6;
+                    }
+                    mcc_consume_token(35);
+                    t = GetStringLitteral();
+                                            list.Add(t);
+                  }label_6: ;
+                  
+                  mcc_consume_token(36);
+                           left = ComparisonExpression.CreateNotIn(left, list);
+                } else {
+                  mcc_consume_token(-1);
+                  throw new ParseException();
+                }
+                break;
+              }
+            }
+            break;
+          }
+        }
+        break;
+      }
+    }label_4: ;
+    
+        {return left;}
+    throw new Exception("Missing return statement in function");
+  }
+
+  public IExpression GetAddExpression() {
+    IExpression left;
+    IExpression right;
+    left = GetMultiplyExpression();
+    while (true) {
+      if (mcc_2_5(2147483647)) {
+        ;
+      } else {
+        goto label_7;
+      }
+      switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+      case 37:
+        mcc_consume_token(37);
+        right = GetMultiplyExpression();
+                    left = ArithmeticExpression.CreatePlus(left, right);
+        break;
+      case 38:
+        mcc_consume_token(38);
+        right = GetMultiplyExpression();
+                    left = ArithmeticExpression.CreateMinus(left, right);
+        break;
+      default:
+        mcc_consume_token(-1);
+        throw new ParseException();
+      }
+    }label_7: ;
+    
+        {return left;}
+    throw new Exception("Missing return statement in function");
+  }
+
+  public IExpression GetMultiplyExpression() {
+    IExpression left;
+    IExpression right;
+    left = GetUnaryExpression();
+    while (true) {
+      switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+      case 39:
+      case 40:
+      case 41:
+        ;
+        break;
+      default:
+        goto label_8;
+      }
+      switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+      case 39:
+        mcc_consume_token(39);
+        right = GetUnaryExpression();
+                left = ArithmeticExpression.CreateMultiply(left, right);
+        break;
+      case 40:
+        mcc_consume_token(40);
+        right = GetUnaryExpression();
+                left = ArithmeticExpression.CreateDivide(left, right);
+        break;
+      case 41:
+        mcc_consume_token(41);
+        right = GetUnaryExpression();
+                left = ArithmeticExpression.CreateMod(left, right);
+        break;
+      default:
+        mcc_consume_token(-1);
+        throw new ParseException();
+      }
+    }label_8: ;
+    
+        {return left;}
+    throw new Exception("Missing return statement in function");
+  }
+
+  public IExpression GetUnaryExpression() {
+    IExpression left = null;
+    if (mcc_2_6(2147483647)) {
+      mcc_consume_token(37);
+      left = GetUnaryExpression();
+    } else {
+      switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+      case 38:
+        mcc_consume_token(38);
+        left = GetUnaryExpression();
+                left = UnaryExpression.CreateNegate(left);
+        break;
+      case NOT:
+        mcc_consume_token(NOT);
+        left = GetUnaryExpression();
+                    left = UnaryExpression.CreateNOT(AsBooleanExpression(left));
+        break;
+      case TRUE:
+      case FALSE:
+      case NULL:
+      case DECIMAL_LITERAL:
+      case HEX_LITERAL:
+      case OCTAL_LITERAL:
+      case FLOATING_POINT_LITERAL:
+      case STRING_LITERAL:
+      case ID:
+      case 34:
+        left = GetPrimaryExpression();
+        break;
+      default:
+        mcc_consume_token(-1);
+        throw new ParseException();
+      }
+    }
+        {return left;}
+    throw new Exception("Missing return statement in function");
+  }
+
+  public IExpression GetPrimaryExpression() {
+    IExpression left = null;
+    switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+    case TRUE:
+    case FALSE:
+    case NULL:
+    case DECIMAL_LITERAL:
+    case HEX_LITERAL:
+    case OCTAL_LITERAL:
+    case FLOATING_POINT_LITERAL:
+    case STRING_LITERAL:
+      left = GetLiteral();
+      break;
+    case ID:
+      left = GetVariable();
+      break;
+    case 34:
+      mcc_consume_token(34);
+      left = GetOrExpression();
+      mcc_consume_token(36);
+      break;
+    default:
+      mcc_consume_token(-1);
+      throw new ParseException();
+    }
+        {return left;}
+    throw new Exception("Missing return statement in function");
+  }
+
+  public ConstantExpression GetLiteral() {
+    Token t;
+    string s;
+    ConstantExpression left = null;
+    switch ((mcc_ntk==-1)?mcc_mntk():mcc_ntk) {
+    case STRING_LITERAL:
+      s = GetStringLitteral();
+                left = new ConstantExpression(s);
+      break;
+    case DECIMAL_LITERAL:
+      t = mcc_consume_token(DECIMAL_LITERAL);
+                left = ConstantExpression.CreateFromDecimal(t.image);
+      break;
+    case HEX_LITERAL:
+      t = mcc_consume_token(HEX_LITERAL);
+                left = ConstantExpression.CreateFromHex(t.image);
+      break;
+    case OCTAL_LITERAL:
+      t = mcc_consume_token(OCTAL_LITERAL);
+                left = ConstantExpression.CreateFromOctal(t.image);
+      break;
+    case FLOATING_POINT_LITERAL:
+      t = mcc_consume_token(FLOATING_POINT_LITERAL);
+                left = ConstantExpression.CreateFloat(t.image);
+      break;
+    case TRUE:
+      mcc_consume_token(TRUE);
+                left = ConstantExpression.TRUE;
+      break;
+    case FALSE:
+      mcc_consume_token(FALSE);
+                left = ConstantExpression.FALSE;
+      break;
+    case NULL:
+      mcc_consume_token(NULL);
+                left = ConstantExpression.NULL;
+      break;
+    default:
+      mcc_consume_token(-1);
+      throw new ParseException();
+    }
+        {return left;}
+    throw new Exception("Missing return statement in function");
+  }
+
+  public string GetStringLitteral() {
+    Token t;
+    StringBuilder rc = new StringBuilder();
+    t = mcc_consume_token(STRING_LITERAL);
+        // Decode the sting value.
+        String image = t.image;
+        for(int c = 1; c < image.Length - 1; c++)
+        {
+                char ch = image[c];
+                if(ch == '\'')
+            {
+                        c++;
+            }
+                        rc.Append(ch);
+        }
+            {return rc.ToString();}
+    throw new Exception("Missing return statement in function");
+  }
+
+  public PropertyExpression GetVariable() {
+    Token t;
+    PropertyExpression left = null;
+    t = mcc_consume_token(ID);
+            left = new PropertyExpression(t.image);
+        {return left;}
+    throw new Exception("Missing return statement in function");
+  }
+
+  private bool mcc_2_1(int xla) {
+    mcc_la = xla; mcc_lastpos = mcc_scanpos = token;
+    try { return !mcc_3_1(); }
+    catch(LookaheadSuccess) { return true; }
+  }
+
+  private bool mcc_2_2(int xla) {
+    mcc_la = xla; mcc_lastpos = mcc_scanpos = token;
+    try { return !mcc_3_2(); }
+    catch(LookaheadSuccess) { return true; }
+  }
+
+  private bool mcc_2_3(int xla) {
+    mcc_la = xla; mcc_lastpos = mcc_scanpos = token;
+    try { return !mcc_3_3(); }
+    catch(LookaheadSuccess) { return true; }
+  }
+
+  private bool mcc_2_4(int xla) {
+    mcc_la = xla; mcc_lastpos = mcc_scanpos = token;
+    try { return !mcc_3_4(); }
+    catch(LookaheadSuccess) { return true; }
+  }
+
+  private bool mcc_2_5(int xla) {
+    mcc_la = xla; mcc_lastpos = mcc_scanpos = token;
+    try { return !mcc_3_5(); }
+    catch(LookaheadSuccess) { return true; }
+  }
+
+  private bool mcc_2_6(int xla) {
+    mcc_la = xla; mcc_lastpos = mcc_scanpos = token;
+    try { return !mcc_3_6(); }
+    catch(LookaheadSuccess) { return true; }
+  }
+
+  private bool mcc_3R_57() {
+    if (mcc_scan_token(ESCAPE)) return true;
+    if (mcc_3R_36()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_19() {
+    Token xsp;
+    xsp = mcc_scanpos;
+    if (mcc_3R_20()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_21()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_22()) return true;
+    }
+    }
+    return false;
+  }
+
+  private bool mcc_3R_39() {
+    if (mcc_3R_41()) return true;
+    Token xsp;
+    while (true) {
+      xsp = mcc_scanpos;
+      if (mcc_3R_42()) { mcc_scanpos = xsp; break; }
+    }
+    return false;
+  }
+
+  private bool mcc_3_4() {
+    if (mcc_scan_token(NOT)) return true;
+    if (mcc_scan_token(IN)) return true;
+    if (mcc_scan_token(34)) return true;
+    if (mcc_3R_36()) return true;
+    Token xsp;
+    while (true) {
+      xsp = mcc_scanpos;
+      if (mcc_3R_59()) { mcc_scanpos = xsp; break; }
+    }
+    if (mcc_scan_token(36)) return true;
+    return false;
+  }
+
+  private bool mcc_3_6() {
+    if (mcc_scan_token(37)) return true;
+    if (mcc_3R_10()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_15() {
+    if (mcc_3R_19()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_36() {
+    if (mcc_scan_token(STRING_LITERAL)) return true;
+    return false;
+  }
+
+  private bool mcc_3R_14() {
+    if (mcc_scan_token(NOT)) return true;
+    if (mcc_3R_10()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_12() {
+    if (mcc_scan_token(37)) return true;
+    if (mcc_3R_10()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_53() {
+    if (mcc_scan_token(IN)) return true;
+    if (mcc_scan_token(34)) return true;
+    if (mcc_3R_36()) return true;
+    Token xsp;
+    while (true) {
+      xsp = mcc_scanpos;
+      if (mcc_3R_58()) { mcc_scanpos = xsp; break; }
+    }
+    if (mcc_scan_token(36)) return true;
+    return false;
+  }
+
+  private bool mcc_3R_45() {
+    if (mcc_scan_token(IS)) return true;
+    if (mcc_scan_token(NOT)) return true;
+    if (mcc_scan_token(NULL)) return true;
+    return false;
+  }
+
+  private bool mcc_3R_13() {
+    if (mcc_scan_token(38)) return true;
+    if (mcc_3R_10()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_33() {
+    if (mcc_scan_token(NULL)) return true;
+    return false;
+  }
+
+  private bool mcc_3_1() {
+    if (mcc_scan_token(IS)) return true;
+    if (mcc_scan_token(NULL)) return true;
+    return false;
+  }
+
+  private bool mcc_3R_10() {
+    Token xsp;
+    xsp = mcc_scanpos;
+    if (mcc_3R_12()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_13()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_14()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_15()) return true;
+    }
+    }
+    }
+    return false;
+  }
+
+  private bool mcc_3R_44() {
+    if (mcc_scan_token(29)) return true;
+    if (mcc_3R_39()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_32() {
+    if (mcc_scan_token(FALSE)) return true;
+    return false;
+  }
+
+  private bool mcc_3_3() {
+    if (mcc_scan_token(NOT)) return true;
+    if (mcc_scan_token(BETWEEN)) return true;
+    if (mcc_3R_41()) return true;
+    if (mcc_scan_token(AND)) return true;
+    if (mcc_3R_41()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_43() {
+    if (mcc_scan_token(28)) return true;
+    if (mcc_3R_39()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_40() {
+    Token xsp;
+    xsp = mcc_scanpos;
+    if (mcc_3R_43()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_44()) {
+    mcc_scanpos = xsp;
+    if (mcc_3_1()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_45()) return true;
+    }
+    }
+    }
+    return false;
+  }
+
+  private bool mcc_3R_52() {
+    if (mcc_scan_token(BETWEEN)) return true;
+    if (mcc_3R_41()) return true;
+    if (mcc_scan_token(AND)) return true;
+    if (mcc_3R_41()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_31() {
+    if (mcc_scan_token(TRUE)) return true;
+    return false;
+  }
+
+  private bool mcc_3R_56() {
+    if (mcc_scan_token(ESCAPE)) return true;
+    if (mcc_3R_36()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_18() {
+    if (mcc_scan_token(41)) return true;
+    if (mcc_3R_10()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_30() {
+    if (mcc_scan_token(FLOATING_POINT_LITERAL)) return true;
+    return false;
+  }
+
+  private bool mcc_3R_37() {
+    if (mcc_3R_39()) return true;
+    Token xsp;
+    while (true) {
+      xsp = mcc_scanpos;
+      if (mcc_3R_40()) { mcc_scanpos = xsp; break; }
+    }
+    return false;
+  }
+
+  private bool mcc_3_2() {
+    if (mcc_scan_token(NOT)) return true;
+    if (mcc_scan_token(LIKE)) return true;
+    if (mcc_3R_36()) return true;
+    Token xsp;
+    xsp = mcc_scanpos;
+    if (mcc_3R_57()) mcc_scanpos = xsp;
+    return false;
+  }
+
+  private bool mcc_3R_51() {
+    if (mcc_scan_token(LIKE)) return true;
+    if (mcc_3R_36()) return true;
+    Token xsp;
+    xsp = mcc_scanpos;
+    if (mcc_3R_56()) mcc_scanpos = xsp;
+    return false;
+  }
+
+  private bool mcc_3R_17() {
+    if (mcc_scan_token(40)) return true;
+    if (mcc_3R_10()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_29() {
+    if (mcc_scan_token(OCTAL_LITERAL)) return true;
+    return false;
+  }
+
+  private bool mcc_3R_16() {
+    if (mcc_scan_token(39)) return true;
+    if (mcc_3R_10()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_11() {
+    Token xsp;
+    xsp = mcc_scanpos;
+    if (mcc_3R_16()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_17()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_18()) return true;
+    }
+    }
+    return false;
+  }
+
+  private bool mcc_3R_38() {
+    if (mcc_scan_token(AND)) return true;
+    if (mcc_3R_37()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_28() {
+    if (mcc_scan_token(HEX_LITERAL)) return true;
+    return false;
+  }
+
+  private bool mcc_3R_9() {
+    if (mcc_3R_10()) return true;
+    Token xsp;
+    while (true) {
+      xsp = mcc_scanpos;
+      if (mcc_3R_11()) { mcc_scanpos = xsp; break; }
+    }
+    return false;
+  }
+
+  private bool mcc_3R_27() {
+    if (mcc_scan_token(DECIMAL_LITERAL)) return true;
+    return false;
+  }
+
+  private bool mcc_3R_55() {
+    if (mcc_scan_token(38)) return true;
+    if (mcc_3R_9()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_34() {
+    if (mcc_3R_37()) return true;
+    Token xsp;
+    while (true) {
+      xsp = mcc_scanpos;
+      if (mcc_3R_38()) { mcc_scanpos = xsp; break; }
+    }
+    return false;
+  }
+
+  private bool mcc_3_5() {
+    Token xsp;
+    xsp = mcc_scanpos;
+    if (mcc_scan_token(37)) {
+    mcc_scanpos = xsp;
+    if (mcc_scan_token(38)) return true;
+    }
+    if (mcc_3R_9()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_50() {
+    if (mcc_scan_token(33)) return true;
+    if (mcc_3R_41()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_54() {
+    if (mcc_scan_token(37)) return true;
+    if (mcc_3R_9()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_26() {
+    if (mcc_3R_36()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_49() {
+    if (mcc_scan_token(32)) return true;
+    if (mcc_3R_41()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_59() {
+    if (mcc_scan_token(35)) return true;
+    if (mcc_3R_36()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_46() {
+    Token xsp;
+    xsp = mcc_scanpos;
+    if (mcc_3R_54()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_55()) return true;
+    }
+    return false;
+  }
+
+  private bool mcc_3R_35() {
+    if (mcc_scan_token(OR)) return true;
+    if (mcc_3R_34()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_23() {
+    Token xsp;
+    xsp = mcc_scanpos;
+    if (mcc_3R_26()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_27()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_28()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_29()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_30()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_31()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_32()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_33()) return true;
+    }
+    }
+    }
+    }
+    }
+    }
+    }
+    return false;
+  }
+
+  private bool mcc_3R_48() {
+    if (mcc_scan_token(31)) return true;
+    if (mcc_3R_41()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_24() {
+    if (mcc_scan_token(ID)) return true;
+    return false;
+  }
+
+  private bool mcc_3R_47() {
+    if (mcc_scan_token(30)) return true;
+    if (mcc_3R_41()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_42() {
+    Token xsp;
+    xsp = mcc_scanpos;
+    if (mcc_3R_47()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_48()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_49()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_50()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_51()) {
+    mcc_scanpos = xsp;
+    if (mcc_3_2()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_52()) {
+    mcc_scanpos = xsp;
+    if (mcc_3_3()) {
+    mcc_scanpos = xsp;
+    if (mcc_3R_53()) {
+    mcc_scanpos = xsp;
+    if (mcc_3_4()) return true;
+    }
+    }
+    }
+    }
+    }
+    }
+    }
+    }
+    }
+    return false;
+  }
+
+  private bool mcc_3R_41() {
+    if (mcc_3R_9()) return true;
+    Token xsp;
+    while (true) {
+      xsp = mcc_scanpos;
+      if (mcc_3R_46()) { mcc_scanpos = xsp; break; }
+    }
+    return false;
+  }
+
+  private bool mcc_3R_25() {
+    if (mcc_3R_34()) return true;
+    Token xsp;
+    while (true) {
+      xsp = mcc_scanpos;
+      if (mcc_3R_35()) { mcc_scanpos = xsp; break; }
+    }
+    return false;
+  }
+
+  private bool mcc_3R_22() {
+    if (mcc_scan_token(34)) return true;
+    if (mcc_3R_25()) return true;
+    if (mcc_scan_token(36)) return true;
+    return false;
+  }
+
+  private bool mcc_3R_21() {
+    if (mcc_3R_24()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_20() {
+    if (mcc_3R_23()) return true;
+    return false;
+  }
+
+  private bool mcc_3R_58() {
+    if (mcc_scan_token(35)) return true;
+    if (mcc_3R_36()) return true;
+    return false;
+  }
+
+  public SelectorParserTokenManager token_source;
+  SimpleCharStream mcc_input_stream;
+  public Token token, mcc_nt;
+  private int mcc_ntk;
+  private Token mcc_scanpos, mcc_lastpos;
+  private int mcc_la;
+  public bool lookingAhead = false;
+  //private bool mcc_semLA;
+
+  public SelectorParser(System.IO.Stream stream) {
+    mcc_input_stream = new SimpleCharStream(stream, 1, 1);
+    token_source = new SelectorParserTokenManager(mcc_input_stream);
+    token = new Token();
+    mcc_ntk = -1;
+  }
+
+  public void ReInit(System.IO.Stream stream) {
+    mcc_input_stream.ReInit(stream, 1, 1);
+    token_source.ReInit(mcc_input_stream);
+    token = new Token();
+    mcc_ntk = -1;
+  }
+
+  public SelectorParser(System.IO.TextReader stream) {
+    mcc_input_stream = new SimpleCharStream(stream, 1, 1);
+    token_source = new SelectorParserTokenManager(mcc_input_stream);
+    token = new Token();
+    mcc_ntk = -1;
+  }
+
+  public void ReInit(System.IO.TextReader stream) {
+    mcc_input_stream.ReInit(stream, 1, 1);
+    token_source.ReInit(mcc_input_stream);
+    token = new Token();
+    mcc_ntk = -1;
+  }
+
+  public SelectorParser(SelectorParserTokenManager tm) {
+    token_source = tm;
+    token = new Token();
+    mcc_ntk = -1;
+  }
+
+  public void ReInit(SelectorParserTokenManager tm) {
+    token_source = tm;
+    token = new Token();
+    mcc_ntk = -1;
+  }
+
+   private Token mcc_consume_token(int kind) {
+    Token oldToken = null;
+    if ((oldToken = token).next != null) token = token.next;
+    else token = token.next = token_source.GetNextToken();
+    mcc_ntk = -1;
+    if (token.kind == kind) {
+      return token;
+    }
+    token = oldToken;
+    throw GenerateParseException();
+  }
+
+  private class LookaheadSuccess : System.Exception { }
+  private LookaheadSuccess mcc_ls = new LookaheadSuccess();
+  private bool mcc_scan_token(int kind) {
+    if (mcc_scanpos == mcc_lastpos) {
+      mcc_la--;
+      if (mcc_scanpos.next == null) {
+        mcc_lastpos = mcc_scanpos = mcc_scanpos.next = token_source.GetNextToken();
+      } else {
+        mcc_lastpos = mcc_scanpos = mcc_scanpos.next;
+      }
+    } else {
+      mcc_scanpos = mcc_scanpos.next;
+    }
+    if (mcc_scanpos.kind != kind) return true;
+    if (mcc_la == 0 && mcc_scanpos == mcc_lastpos) throw mcc_ls;
+    return false;
+  }
+
+  public Token GetNextToken() {
+    if (token.next != null) token = token.next;
+    else token = token.next = token_source.GetNextToken();
+    mcc_ntk = -1;
+    return token;
+  }
+
+  public Token GetToken(int index) {
+    Token t = lookingAhead ? mcc_scanpos : token;
+    for (int i = 0; i < index; i++) {
+      if (t.next != null) t = t.next;
+      else t = t.next = token_source.GetNextToken();
+    }
+    return t;
+  }
+
+  private int mcc_mntk() {
+    if ((mcc_nt=token.next) == null)
+      return (mcc_ntk = (token.next=token_source.GetNextToken()).kind);
+    else
+      return (mcc_ntk = mcc_nt.kind);
+  }
+
+  public ParseException GenerateParseException() {
+    Token errortok = token.next;
+    int line = errortok.beginLine, column = errortok.beginColumn;
+    string mess = (errortok.kind == 0) ? tokenImage[0] : errortok.image;
+    return new ParseException("Parse error at line " + line + ", column " + column + ".  Encountered: " + mess);
+  }
+
+  public void enable_tracing() {
+  }
+
+  public void disable_tracing() {
+  }
+
+}
+
+}
\ No newline at end of file
diff --git a/src/main/csharp/Selector/SelectorParser.csc b/src/main/csharp/Selector/SelectorParser.csc
new file mode 100644
index 0000000..be05ab8
--- /dev/null
+++ b/src/main/csharp/Selector/SelectorParser.csc
@@ -0,0 +1,589 @@
+/**
+ *
+ * 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
+  OPTIMIZE_TOKEN_MANAGER = true;
+  ERROR_REPORTING = false;
+}
+
+// ----------------------------------------------------------------------------
+// PARSER
+// ----------------------------------------------------------------------------
+
+PARSER_BEGIN(SelectorParser)
+/**
+ *
+ * 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.
+ */
+using System;
+using System.IO;
+using System.Text;
+using System.Collections;
+
+using Apache.NMS;
+
+//namespace Apache.NMS.Selector
+//{
+    /// <summary>
+    /// JMS Selector Parser generated by <a href="https://github.com/deveel/csharpcc">CSharpCC</a>
+    /// 
+    /// Do not edit this .cs file directly - it is autogenerated from SelectorParser.csc
+    /// using <c>csharpcc.exe -UNICODE_INPUT=true SelectorParser.csc</c>.
+    /// 
+    /// SelectorParser.csc is adapted from
+    /// <a href="https://raw.githubusercontent.com/apache/activemq/activemq-4.0/activemq-core/src/main/grammar/SelectorParser.jj">
+    /// ActiveMQ 4.0 SelectorParser.jj</a>
+    /// </summary>
+    public class SelectorParser
+    {
+
+        public SelectorParser()
+        {
+        }
+
+        public IBooleanExpression Parse(string selector)
+        {
+            this.ReInit(new StringReader(selector));
+
+            try
+            {
+                return this.JmsSelector();
+            } 
+            catch(Exception e)
+            {
+	            throw new InvalidSelectorException(selector, e);
+            }
+        }
+
+        private IBooleanExpression AsBooleanExpression(IExpression value)
+        {
+            if(value is IBooleanExpression)
+            {
+                return (IBooleanExpression)value;
+            }
+            if(value is PropertyExpression)
+            {
+                return UnaryExpression.CreateBooleanCast(value);
+            }
+            throw new ParseException("IExpression will not result in a boolean value: " + value);
+        }
+    }
+
+//}
+
+PARSER_END(SelectorParser)
+
+// ----------------------------------------------------------------------------
+// Tokens
+// ----------------------------------------------------------------------------
+
+/* White Space */
+SPECIAL_TOKEN :
+{
+  " " | "\t" | "\n" | "\r" | "\f"
+}
+
+/* Comments */
+SKIP:
+{
+  <LINE_COMMENT: "--" (~["\n","\r"])* ("\n"|"\r"|"\r\n") >
+}
+
+SKIP:
+{
+  <BLOCK_COMMENT: "/*" (~["*"])* "*" ("*" | (~["*","/"] (~["*"])* "*"))* "/">
+}
+
+/* Reserved Words */
+TOKEN [IGNORE_CASE] :
+{
+    <  NOT     : "NOT">
+  | <  AND     : "AND">
+  | <  OR      : "OR">
+  | <  BETWEEN : "BETWEEN">
+  | <  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","_", "$"])* >
+}
+
+// ----------------------------------------------------------------------------
+// Grammar
+// ----------------------------------------------------------------------------
+IBooleanExpression JmsSelector() :
+{
+    IExpression left = null;
+}
+{
+    (
+        left = GetOrExpression()
+    ) 
+    {
+        return AsBooleanExpression(left);
+    }
+
+}
+
+IExpression GetOrExpression() :
+{
+    IExpression left;
+    IExpression right;
+}
+{
+    (
+        left = GetAndExpression() 
+        ( 
+            <OR> right = GetAndExpression() 
+            {
+                left = LogicExpression.CreateOR(AsBooleanExpression(left), AsBooleanExpression(right));
+            }
+        )*
+    ) 
+    {
+        return left;
+    }
+
+}
+
+
+IExpression GetAndExpression() :
+{
+    IExpression left;
+    IExpression right;
+}
+{
+    (
+        left = GetEqualityExpression() 
+        ( 
+            <AND> right = GetEqualityExpression() 
+            {
+                left = LogicExpression.CreateAND(AsBooleanExpression(left), AsBooleanExpression(right));
+            }
+        )*
+    ) 
+    {
+        return left;
+    }
+}
+
+IExpression GetEqualityExpression() :
+{
+    IExpression left;
+    IExpression right;
+}
+{
+    (
+        left = GetComparisonExpression() 
+        ( 
+            
+            "=" right = GetComparisonExpression() 
+            {
+                left = ComparisonExpression.CreateEqual(left, right);
+            }
+            |            
+            "<>" right = GetComparisonExpression() 
+            {
+                left = ComparisonExpression.CreateNotEqual(left, right);
+            }
+            |            
+            LOOKAHEAD(2)
+            <IS> <NULL>
+            {
+                left = ComparisonExpression.CreateIsNull(left);
+            }
+            |            
+            <IS> <NOT> <NULL>
+            {
+                left = ComparisonExpression.CreateIsNotNull(left);
+            }
+        )*
+    ) 
+    {
+        return left;
+    }
+}
+
+IExpression GetComparisonExpression() :
+{
+    IExpression left;
+    IExpression right;
+    IExpression low;
+    IExpression high;
+    string t;
+    string u;
+	ArrayList list;
+}
+{
+    (
+        left = GetAddExpression() 
+        ( 
+            
+                ">" right = GetAddExpression() 
+                {
+                    left = ComparisonExpression.CreateGreaterThan(left, right);
+                }
+            |            
+                ">=" right = GetAddExpression() 
+                {
+                    left = ComparisonExpression.CreateGreaterThanOrEqual(left, right);
+                }
+            |            
+                "<" right = GetAddExpression() 
+                {
+                    left = ComparisonExpression.CreateLesserThan(left, right);
+                }
+            |            
+                "<=" right = GetAddExpression() 
+                {
+                    left = ComparisonExpression.CreateLesserThanOrEqual(left, right);
+                }
+           |
+				{
+					u = null;
+				}           		
+		        <LIKE> t = GetStringLitteral() 
+		        	[ <ESCAPE> u = GetStringLitteral() ]
+		        {
+                    left = ComparisonExpression.CreateLike(left, t, u);
+		        }
+           |
+	        	LOOKAHEAD(2)
+				{
+					u=null;
+				}           		
+		        <NOT> <LIKE> t = GetStringLitteral() [ <ESCAPE> u = GetStringLitteral() ]
+		        {
+                    left = ComparisonExpression.CreateNotLike(left, t, u);
+		        }
+            |
+		        <BETWEEN> low = GetAddExpression() <AND> high = GetAddExpression()
+		        {
+					left = ComparisonExpression.CreateBetween(left, low, high);
+		        }
+	        |
+	        	LOOKAHEAD(2)
+		        <NOT> <BETWEEN> low = GetAddExpression() <AND> high = GetAddExpression()
+		        {
+					left = ComparisonExpression.CreateNotBetween(left, low, high);
+		        }
+            |
+				<IN> 
+		        "(" 
+		            t = GetStringLitteral()
+		            {
+			            list = new ArrayList();
+			            list.Add(t);
+		            }
+			        ( 
+			        	","
+			            t = GetStringLitteral() 
+			            {
+				            list.Add(t);
+			            }
+			        	
+			        )*
+		        ")"
+		        {
+		           left = ComparisonExpression.CreateIn(left, list);
+		        }
+            |
+	        	LOOKAHEAD(2)
+	            <NOT> <IN> 
+		        "(" 
+		            t = GetStringLitteral()
+		            {
+			            list = new ArrayList();
+			            list.Add(t);
+		            }
+			        ( 
+			        	","
+			            t = GetStringLitteral() 
+			            {
+				            list.Add(t);
+			            }
+			        	
+			        )*
+		        ")"
+		        {
+		           left = ComparisonExpression.CreateNotIn(left, list);
+		        }
+            
+        )*
+    ) 
+    {
+        return left;
+    }
+}
+
+IExpression GetAddExpression() :
+{
+    IExpression left;
+    IExpression right;
+}
+{
+    left = GetMultiplyExpression() 
+    ( 
+	    LOOKAHEAD( ("+"|"-") GetMultiplyExpression())
+	    (
+	        "+" right = GetMultiplyExpression() 
+	        {
+	            left = ArithmeticExpression.CreatePlus(left, right);
+	        }
+	        |            
+	        "-" right = GetMultiplyExpression() 
+	        {
+	            left = ArithmeticExpression.CreateMinus(left, right);
+	        }
+        )
+        
+    )*
+    {
+        return left;
+    }
+}
+
+IExpression GetMultiplyExpression() :
+{
+    IExpression left;
+    IExpression right;
+}
+{
+    left = GetUnaryExpression() 
+    ( 
+        "*" right = GetUnaryExpression() 
+        {
+	        left = ArithmeticExpression.CreateMultiply(left, right);
+        }
+        |            
+        "/" right = GetUnaryExpression() 
+        {
+	        left = ArithmeticExpression.CreateDivide(left, right);
+        }
+        |            
+        "%" right = GetUnaryExpression() 
+        {
+	        left = ArithmeticExpression.CreateMod(left, right);
+        }
+        
+    )*
+    {
+        return left;
+    }
+}
+
+
+IExpression GetUnaryExpression() :
+{
+    IExpression left = null;
+}
+{
+	(
+		LOOKAHEAD( "+" GetUnaryExpression() )
+	    "+" left = GetUnaryExpression()
+	    |
+	    "-" left = GetUnaryExpression()
+	    {
+	        left = UnaryExpression.CreateNegate(left);
+	    }
+	    |
+	    <NOT> left = GetUnaryExpression()
+	    {
+		    left = UnaryExpression.CreateNOT(AsBooleanExpression(left));
+	    }
+	    |
+	    left = GetPrimaryExpression()
+    )
+    {
+        return left;
+    }
+
+}
+
+IExpression GetPrimaryExpression() :
+{
+    IExpression left = null;
+}
+{
+    (
+        left = GetLiteral()
+        |
+        left = GetVariable()
+        |
+        "(" left = GetOrExpression() ")"
+    ) 
+    {
+        return left;
+    }
+}
+
+
+
+ConstantExpression GetLiteral() :
+{
+    Token t;
+    string s;
+    ConstantExpression left = null;
+}
+{
+    (
+        (
+            s = GetStringLitteral()
+            {
+                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 GetStringLitteral() :
+{
+    Token t;
+    StringBuilder rc = new StringBuilder();
+}
+{
+    t = <STRING_LITERAL> 
+    {
+    	// Decode the sting value.
+    	String image = t.image;
+    	for(int c = 1; c < image.Length - 1; c++)
+        {
+    		char ch = image[c];
+    		if(ch == '\'')
+            {
+    			c++;    			
+            }
+   			rc.Append(ch);
+    	}
+	    return rc.ToString();
+    }    
+}
+
+PropertyExpression GetVariable() :
+{
+    Token t;
+    PropertyExpression left = null;
+}
+{
+    ( 
+        t = <ID> 
+        {
+            left = new PropertyExpression(t.image);
+        }    
+    )
+    {
+        return left;
+    }
+}
diff --git a/src/main/csharp/Selector/SelectorParserConstants.cs b/src/main/csharp/Selector/SelectorParserConstants.cs
new file mode 100644
index 0000000..5f75539
--- /dev/null
+++ b/src/main/csharp/Selector/SelectorParserConstants.cs
@@ -0,0 +1,75 @@
+/* Generated By:CSharpCC: Do not edit this line. SelectorParserConstants.cs */
+public  class SelectorParserConstants {
+
+  public const int EOF = 0;
+  public const int LINE_COMMENT = 6;
+  public const int BLOCK_COMMENT = 7;
+  public const int NOT = 8;
+  public const int AND = 9;
+  public const int OR = 10;
+  public const int BETWEEN = 11;
+  public const int LIKE = 12;
+  public const int ESCAPE = 13;
+  public const int IN = 14;
+  public const int IS = 15;
+  public const int TRUE = 16;
+  public const int FALSE = 17;
+  public const int NULL = 18;
+  public const int XPATH = 19;
+  public const int XQUERY = 20;
+  public const int DECIMAL_LITERAL = 21;
+  public const int HEX_LITERAL = 22;
+  public const int OCTAL_LITERAL = 23;
+  public const int FLOATING_POINT_LITERAL = 24;
+  public const int EXPONENT = 25;
+  public const int STRING_LITERAL = 26;
+  public const int ID = 27;
+
+  public const int DEFAULT = 0;
+
+  public readonly string[] tokenImage = {
+    "<EOF>",
+    "\" \"",
+    "\"\\t\"",
+    "\"\\n\"",
+    "\"\\r\"",
+    "\"\\f\"",
+    "<LINE_COMMENT>",
+    "<BLOCK_COMMENT>",
+    "\"NOT\"",
+    "\"AND\"",
+    "\"OR\"",
+    "\"BETWEEN\"",
+    "\"LIKE\"",
+    "\"ESCAPE\"",
+    "\"IN\"",
+    "\"IS\"",
+    "\"TRUE\"",
+    "\"FALSE\"",
+    "\"NULL\"",
+    "\"XPATH\"",
+    "\"XQUERY\"",
+    "<DECIMAL_LITERAL>",
+    "<HEX_LITERAL>",
+    "<OCTAL_LITERAL>",
+    "<FLOATING_POINT_LITERAL>",
+    "<EXPONENT>",
+    "<STRING_LITERAL>",
+    "<ID>",
+    "\"=\"",
+    "\"<>\"",
+    "\">\"",
+    "\">=\"",
+    "\"<\"",
+    "\"<=\"",
+    "\"(\"",
+    "\",\"",
+    "\")\"",
+    "\"+\"",
+    "\"-\"",
+    "\"*\"",
+    "\"/\"",
+    "\"%\"",
+  };
+
+}
diff --git a/src/main/csharp/Selector/SelectorParserTokenManager.cs b/src/main/csharp/Selector/SelectorParserTokenManager.cs
new file mode 100644
index 0000000..e198265
--- /dev/null
+++ b/src/main/csharp/Selector/SelectorParserTokenManager.cs
@@ -0,0 +1,1042 @@
+/* Generated By:CSharpCC: Do not edit this line. SelectorParserTokenManager.cs */
+/**
+ *
+ * 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.
+ */
+using System;
+using System.IO;
+using System.Text;
+using System.Collections;
+using Apache.NMS;
+
+public  class SelectorParserTokenManager : SelectorParserConstants {
+  public  System.IO.TextWriter debugStream = Console.Out;
+  public  void SetDebugStream(System.IO.TextWriter ds) { debugStream = ds; }
+private int mccStopAtPos(int pos, int kind)
+{
+   mccmatchedKind = kind;
+   mccmatchedPos = pos;
+   return pos + 1;
+}
+private int mccMoveStringLiteralDfa0_0()
+{
+   switch((int)curChar) {
+      case 9:
+         mccmatchedKind = 2;
+         return mccMoveNfa_0(5, 0);
+      case 10:
+         mccmatchedKind = 3;
+         return mccMoveNfa_0(5, 0);
+      case 12:
+         mccmatchedKind = 5;
+         return mccMoveNfa_0(5, 0);
+      case 13:
+         mccmatchedKind = 4;
+         return mccMoveNfa_0(5, 0);
+      case 32:
+         mccmatchedKind = 1;
+         return mccMoveNfa_0(5, 0);
+      case 37:
+         mccmatchedKind = 41;
+         return mccMoveNfa_0(5, 0);
+      case 40:
+         mccmatchedKind = 34;
+         return mccMoveNfa_0(5, 0);
+      case 41:
+         mccmatchedKind = 36;
+         return mccMoveNfa_0(5, 0);
+      case 42:
+         mccmatchedKind = 39;
+         return mccMoveNfa_0(5, 0);
+      case 43:
+         mccmatchedKind = 37;
+         return mccMoveNfa_0(5, 0);
+      case 44:
+         mccmatchedKind = 35;
+         return mccMoveNfa_0(5, 0);
+      case 45:
+         mccmatchedKind = 38;
+         return mccMoveNfa_0(5, 0);
+      case 47:
+         mccmatchedKind = 40;
+         return mccMoveNfa_0(5, 0);
+      case 60:
+         mccmatchedKind = 32;
+         return mccMoveStringLiteralDfa1_0(9126805504L);
+      case 61:
+         mccmatchedKind = 28;
+         return mccMoveNfa_0(5, 0);
+      case 62:
+         mccmatchedKind = 30;
+         return mccMoveStringLiteralDfa1_0(2147483648L);
+      case 65:
+         return mccMoveStringLiteralDfa1_0(512L);
+      case 66:
+         return mccMoveStringLiteralDfa1_0(2048L);
+      case 69:
+         return mccMoveStringLiteralDfa1_0(8192L);
+      case 70:
+         return mccMoveStringLiteralDfa1_0(131072L);
+      case 73:
+         return mccMoveStringLiteralDfa1_0(49152L);
+      case 76:
+         return mccMoveStringLiteralDfa1_0(4096L);
+      case 78:
+         return mccMoveStringLiteralDfa1_0(262400L);
+      case 79:
+         return mccMoveStringLiteralDfa1_0(1024L);
+      case 84:
+         return mccMoveStringLiteralDfa1_0(65536L);
+      case 88:
+         return mccMoveStringLiteralDfa1_0(1572864L);
+      case 97:
+         return mccMoveStringLiteralDfa1_0(512L);
+      case 98:
+         return mccMoveStringLiteralDfa1_0(2048L);
+      case 101:
+         return mccMoveStringLiteralDfa1_0(8192L);
+      case 102:
+         return mccMoveStringLiteralDfa1_0(131072L);
+      case 105:
+         return mccMoveStringLiteralDfa1_0(49152L);
+      case 108:
+         return mccMoveStringLiteralDfa1_0(4096L);
+      case 110:
+         return mccMoveStringLiteralDfa1_0(262400L);
+      case 111:
+         return mccMoveStringLiteralDfa1_0(1024L);
+      case 116:
+         return mccMoveStringLiteralDfa1_0(65536L);
+      case 120:
+         return mccMoveStringLiteralDfa1_0(1572864L);
+      default :
+         return mccMoveNfa_0(5, 0);
+   }
+}
+private int mccMoveStringLiteralDfa1_0(long active0)
+{
+   try { curChar = input_stream.ReadChar(); }
+   catch(System.IO.IOException) {
+   return mccMoveNfa_0(5, 0);
+   }
+   switch((int)curChar) {
+      case 61:
+         if ((active0 & 2147483648L) != 0L)
+         {
+            mccmatchedKind = 31;
+            mccmatchedPos = 1;
+         }
+         else if ((active0 & 8589934592L) != 0L)
+         {
+            mccmatchedKind = 33;
+            mccmatchedPos = 1;
+         }
+         break;
+      case 62:
+         if ((active0 & 536870912L) != 0L)
+         {
+            mccmatchedKind = 29;
+            mccmatchedPos = 1;
+         }
+         break;
+      case 65:
+         return mccMoveStringLiteralDfa2_0(active0, 131072L);
+      case 69:
+         return mccMoveStringLiteralDfa2_0(active0, 2048L);
+      case 73:
+         return mccMoveStringLiteralDfa2_0(active0, 4096L);
+      case 78:
+         if ((active0 & 16384L) != 0L)
+         {
+            mccmatchedKind = 14;
+            mccmatchedPos = 1;
+         }
+         return mccMoveStringLiteralDfa2_0(active0, 512L);
+      case 79:
+         return mccMoveStringLiteralDfa2_0(active0, 256L);
+      case 80:
+         return mccMoveStringLiteralDfa2_0(active0, 524288L);
+      case 81:
+         return mccMoveStringLiteralDfa2_0(active0, 1048576L);
+      case 82:
+         if ((active0 & 1024L) != 0L)
+         {
+            mccmatchedKind = 10;
+            mccmatchedPos = 1;
+         }
+         return mccMoveStringLiteralDfa2_0(active0, 65536L);
+      case 83:
+         if ((active0 & 32768L) != 0L)
+         {
+            mccmatchedKind = 15;
+            mccmatchedPos = 1;
+         }
+         return mccMoveStringLiteralDfa2_0(active0, 8192L);
+      case 85:
+         return mccMoveStringLiteralDfa2_0(active0, 262144L);
+      case 97:
+         return mccMoveStringLiteralDfa2_0(active0, 131072L);
+      case 101:
+         return mccMoveStringLiteralDfa2_0(active0, 2048L);
+      case 105:
+         return mccMoveStringLiteralDfa2_0(active0, 4096L);
+      case 110:
+         if ((active0 & 16384L) != 0L)
+         {
+            mccmatchedKind = 14;
+            mccmatchedPos = 1;
+         }
+         return mccMoveStringLiteralDfa2_0(active0, 512L);
+      case 111:
+         return mccMoveStringLiteralDfa2_0(active0, 256L);
+      case 112:
+         return mccMoveStringLiteralDfa2_0(active0, 524288L);
+      case 113:
+         return mccMoveStringLiteralDfa2_0(active0, 1048576L);
+      case 114:
+         if ((active0 & 1024L) != 0L)
+         {
+            mccmatchedKind = 10;
+            mccmatchedPos = 1;
+         }
+         return mccMoveStringLiteralDfa2_0(active0, 65536L);
+      case 115:
+         if ((active0 & 32768L) != 0L)
+         {
+            mccmatchedKind = 15;
+            mccmatchedPos = 1;
+         }
+         return mccMoveStringLiteralDfa2_0(active0, 8192L);
+      case 117:
+         return mccMoveStringLiteralDfa2_0(active0, 262144L);
+      default :
+         break;
+   }
+   return mccMoveNfa_0(5, 1);
+}
+private int mccMoveStringLiteralDfa2_0(long old0, long active0)
+{
+   if (((active0 &= old0)) == 0L)
+      return mccMoveNfa_0(5, 1);
+   try { curChar = input_stream.ReadChar(); }
+   catch(System.IO.IOException) {
+   return mccMoveNfa_0(5, 1);
+   }
+   switch((int)curChar) {
+      case 65:
+         return mccMoveStringLiteralDfa3_0(active0, 524288L);
+      case 67:
+         return mccMoveStringLiteralDfa3_0(active0, 8192L);
+      case 68:
+         if ((active0 & 512L) != 0L)
+         {
+            mccmatchedKind = 9;
+            mccmatchedPos = 2;
+         }
+         break;
+      case 75:
+         return mccMoveStringLiteralDfa3_0(active0, 4096L);
+      case 76:
+         return mccMoveStringLiteralDfa3_0(active0, 393216L);
+      case 84:
+         if ((active0 & 256L) != 0L)
+         {
+            mccmatchedKind = 8;
+            mccmatchedPos = 2;
+         }
+         return mccMoveStringLiteralDfa3_0(active0, 2048L);
+      case 85:
+         return mccMoveStringLiteralDfa3_0(active0, 1114112L);
+      case 97:
+         return mccMoveStringLiteralDfa3_0(active0, 524288L);
+      case 99:
+         return mccMoveStringLiteralDfa3_0(active0, 8192L);
+      case 100:
+         if ((active0 & 512L) != 0L)
+         {
+            mccmatchedKind = 9;
+            mccmatchedPos = 2;
+         }
+         break;
+      case 107:
+         return mccMoveStringLiteralDfa3_0(active0, 4096L);
+      case 108:
+         return mccMoveStringLiteralDfa3_0(active0, 393216L);
+      case 116:
+         if ((active0 & 256L) != 0L)
+         {
+            mccmatchedKind = 8;
+            mccmatchedPos = 2;
+         }
+         return mccMoveStringLiteralDfa3_0(active0, 2048L);
+      case 117:
+         return mccMoveStringLiteralDfa3_0(active0, 1114112L);
+      default :
+         break;
+   }
+   return mccMoveNfa_0(5, 2);
+}
+private int mccMoveStringLiteralDfa3_0(long old0, long active0)
+{
+   if (((active0 &= old0)) == 0L)
+      return mccMoveNfa_0(5, 2);
+   try { curChar = input_stream.ReadChar(); }
+   catch(System.IO.IOException) {
+   return mccMoveNfa_0(5, 2);
+   }
+   switch((int)curChar) {
+      case 65:
+         return mccMoveStringLiteralDfa4_0(active0, 8192L);
+      case 69:
+         if ((active0 & 4096L) != 0L)
+         {
+            mccmatchedKind = 12;
+            mccmatchedPos = 3;
+         }
+         else if ((active0 & 65536L) != 0L)
+         {
+            mccmatchedKind = 16;
+            mccmatchedPos = 3;
+         }
+         return mccMoveStringLiteralDfa4_0(active0, 1048576L);
+      case 76:
+         if ((active0 & 262144L) != 0L)
+         {
+            mccmatchedKind = 18;
+            mccmatchedPos = 3;
+         }
+         break;
+      case 83:
+         return mccMoveStringLiteralDfa4_0(active0, 131072L);
+      case 84:
+         return mccMoveStringLiteralDfa4_0(active0, 524288L);
+      case 87:
+         return mccMoveStringLiteralDfa4_0(active0, 2048L);
+      case 97:
+         return mccMoveStringLiteralDfa4_0(active0, 8192L);
+      case 101:
+         if ((active0 & 4096L) != 0L)
+         {
+            mccmatchedKind = 12;
+            mccmatchedPos = 3;
+         }
+         else if ((active0 & 65536L) != 0L)
+         {
+            mccmatchedKind = 16;
+            mccmatchedPos = 3;
+         }
+         return mccMoveStringLiteralDfa4_0(active0, 1048576L);
+      case 108:
+         if ((active0 & 262144L) != 0L)
+         {
+            mccmatchedKind = 18;
+            mccmatchedPos = 3;
+         }
+         break;
+      case 115:
+         return mccMoveStringLiteralDfa4_0(active0, 131072L);
+      case 116:
+         return mccMoveStringLiteralDfa4_0(active0, 524288L);
+      case 119:
+         return mccMoveStringLiteralDfa4_0(active0, 2048L);
+      default :
+         break;
+   }
+   return mccMoveNfa_0(5, 3);
+}
+private int mccMoveStringLiteralDfa4_0(long old0, long active0)
+{
+   if (((active0 &= old0)) == 0L)
+      return mccMoveNfa_0(5, 3);
+   try { curChar = input_stream.ReadChar(); }
+   catch(System.IO.IOException) {
+   return mccMoveNfa_0(5, 3);
+   }
+   switch((int)curChar) {
+      case 69:
+         if ((active0 & 131072L) != 0L)
+         {
+            mccmatchedKind = 17;
+            mccmatchedPos = 4;
+         }
+         return mccMoveStringLiteralDfa5_0(active0, 2048L);
+      case 72:
+         if ((active0 & 524288L) != 0L)
+         {
+            mccmatchedKind = 19;
+            mccmatchedPos = 4;
+         }
+         break;
+      case 80:
+         return mccMoveStringLiteralDfa5_0(active0, 8192L);
+      case 82:
+         return mccMoveStringLiteralDfa5_0(active0, 1048576L);
+      case 101:
+         if ((active0 & 131072L) != 0L)
+         {
+            mccmatchedKind = 17;
+            mccmatchedPos = 4;
+         }
+         return mccMoveStringLiteralDfa5_0(active0, 2048L);
+      case 104:
+         if ((active0 & 524288L) != 0L)
+         {
+            mccmatchedKind = 19;
+            mccmatchedPos = 4;
+         }
+         break;
+      case 112:
+         return mccMoveStringLiteralDfa5_0(active0, 8192L);
+      case 114:
+         return mccMoveStringLiteralDfa5_0(active0, 1048576L);
+      default :
+         break;
+   }
+   return mccMoveNfa_0(5, 4);
+}
+private int mccMoveStringLiteralDfa5_0(long old0, long active0)
+{
+   if (((active0 &= old0)) == 0L)
+      return mccMoveNfa_0(5, 4);
+   try { curChar = input_stream.ReadChar(); }
+   catch(System.IO.IOException) {
+   return mccMoveNfa_0(5, 4);
+   }
+   switch((int)curChar) {
+      case 69:
+         if ((active0 & 8192L) != 0L)
+         {
+            mccmatchedKind = 13;
+            mccmatchedPos = 5;
+         }
+         return mccMoveStringLiteralDfa6_0(active0, 2048L);
+      case 89:
+         if ((active0 & 1048576L) != 0L)
+         {
+            mccmatchedKind = 20;
+            mccmatchedPos = 5;
+         }
+         break;
+      case 101:
+         if ((active0 & 8192L) != 0L)
+         {
+            mccmatchedKind = 13;
+            mccmatchedPos = 5;
+         }
+         return mccMoveStringLiteralDfa6_0(active0, 2048L);
+      case 121:
+         if ((active0 & 1048576L) != 0L)
+         {
+            mccmatchedKind = 20;
+            mccmatchedPos = 5;
+         }
+         break;
+      default :
+         break;
+   }
+   return mccMoveNfa_0(5, 5);
+}
+private int mccMoveStringLiteralDfa6_0(long old0, long active0)
+{
+   if (((active0 &= old0)) == 0L)
+      return mccMoveNfa_0(5, 5);
+   try { curChar = input_stream.ReadChar(); }
+   catch(System.IO.IOException) {
+   return mccMoveNfa_0(5, 5);
+   }
+   switch((int)curChar) {
+      case 78:
+         if ((active0 & 2048L) != 0L)
+         {
+            mccmatchedKind = 11;
+            mccmatchedPos = 6;
+         }
+         break;
+      case 110:
+         if ((active0 & 2048L) != 0L)
+         {
+            mccmatchedKind = 11;
+            mccmatchedPos = 6;
+         }
+         break;
+      default :
+         break;
+   }
+   return mccMoveNfa_0(5, 6);
+}
+private void mccCheckNAdd(int state)
+{
+   if (mccrounds[state] != mccround)
+   {
+      mccstateSet[mccnewStateCnt++] = state;
+      mccrounds[state] = mccround;
+   }
+}
+private void mccAddStates(int start, int end)
+{
+   do {
+      mccstateSet[mccnewStateCnt++] = mccnextStates[start];
+   } while (start++ != end);
+}
+private void mccCheckNAddTwoStates(int state1, int state2)
+{
+   mccCheckNAdd(state1);
+   mccCheckNAdd(state2);
+}
+private void mccCheckNAddStates(int start, int end)
+{
+   do {
+      mccCheckNAdd(mccnextStates[start]);
+   } while (start++ != end);
+}
+private void mccCheckNAddStates(int start)
+{
+   mccCheckNAdd(mccnextStates[start]);
+   mccCheckNAdd(mccnextStates[start + 1]);
+}
+static readonly long[] mccbitVec0 = {
+   -2, -1L, -1L, -1L
+};
+static readonly long[] mccbitVec1 = {
+   -1L, -1L, -1L, -1L
+};
+static readonly long[] mccbitVec2 = {
+   0L, 0L, -1L, -1L
+};
+private int mccMoveNfa_0(int startState, int curPos)
+{
+   int strKind = mccmatchedKind;
+   int strPos = mccmatchedPos;
+   int seenUpto = curPos + 1;
+   input_stream.Backup(seenUpto);
+   try { curChar = input_stream.ReadChar(); }
+   catch(System.IO.IOException) { throw new Exception("Internal Error"); }
+   curPos = 0;
+   //int[] nextStates;
+   int startsAt = 0;
+   mccnewStateCnt = 43;
+   int i = 1;
+   mccstateSet[0] = startState;
+   int /*j,*/ kind = Int32.MaxValue;
+   for (;;)
+   {
+      if (++mccround == Int32.MaxValue)
+         ReInitRounds();
+      if (curChar < 64)
+      {
+         long l = 1L << curChar;
+         do
+         {
+            switch(mccstateSet[--i])
+            {
+               case 5:
+                  if ((287948901175001088 & l) != 0L)
+                     mccCheckNAddStates(0, 3);
+                  else if (curChar == 36)
+                  {
+                     if (kind > 27)
+                        kind = 27;
+                     mccCheckNAdd(27);
+                  }
+                  else if (curChar == 39)
+                     mccCheckNAddStates(4, 6);
+                  else if (curChar == 46)
+                     mccCheckNAdd(17);
+                  else if (curChar == 47)
+                     mccstateSet[mccnewStateCnt++] = 6;
+                  else if (curChar == 45)
+                     mccstateSet[mccnewStateCnt++] = 0;
+                  if ((287667426198290432 & l) != 0L)
+                  {
+                     if (kind > 21)
+                        kind = 21;
+                     mccCheckNAddTwoStates(14, 15);
+                  }
+                  else if (curChar == 48)
+                  {
+                     if (kind > 23)
+                        kind = 23;
+                     mccCheckNAddTwoStates(40, 42);
+                  }
+                  break;
+               case 0:
+                  if (curChar == 45)
+                     mccCheckNAddStates(7, 9);
+                  break;
+               case 1:
+                  if ((-9217 & l) != 0L)
+                     mccCheckNAddStates(7, 9);
+                  break;
+               case 2:
+                  if ((9216 & l) != 0L && kind > 6)
+                     kind = 6;
+                  break;
+               case 3:
+                  if (curChar == 10 && kind > 6)
+                     kind = 6;
+                  break;
+               case 4:
+                  if (curChar == 13)
+                     mccstateSet[mccnewStateCnt++] = 3;
+                  break;
+               case 6:
+                  if (curChar == 42)
+                     mccCheckNAddTwoStates(7, 8);
+                  break;
+               case 7:
+                  if ((-4398046511105 & l) != 0L)
+                     mccCheckNAddTwoStates(7, 8);
+                  break;
+               case 8:
+                  if (curChar == 42)
+                     mccCheckNAddStates(10, 12);
+                  break;
+               case 9:
+                  if ((-145135534866433 & l) != 0L)
+                     mccCheckNAddTwoStates(10, 8);
+                  break;
+               case 10:
+                  if ((-4398046511105 & l) != 0L)
+                     mccCheckNAddTwoStates(10, 8);
+                  break;
+               case 11:
+                  if (curChar == 47 && kind > 7)
+                     kind = 7;
+                  break;
+               case 12:
+                  if (curChar == 47)
+                     mccstateSet[mccnewStateCnt++] = 6;
+                  break;
+               case 13:
+                  if ((287667426198290432 & l) == 0L)
+                     break;
+                  if (kind > 21)
+                     kind = 21;
+                  mccCheckNAddTwoStates(14, 15);
+                  break;
+               case 14:
+                  if ((287948901175001088 & l) == 0L)
+                     break;
+                  if (kind > 21)
+                     kind = 21;
+                  mccCheckNAddTwoStates(14, 15);
+                  break;
+               case 16:
+                  if (curChar == 46)
+                     mccCheckNAdd(17);
+                  break;
+               case 17:
+                  if ((287948901175001088 & l) == 0L)
+                     break;
+                  if (kind > 24)
+                     kind = 24;
+                  mccCheckNAddTwoStates(17, 18);
+                  break;
+               case 19:
+                  if ((43980465111040 & l) != 0L)
+                     mccCheckNAdd(20);
+                  break;
+               case 20:
+                  if ((287948901175001088 & l) == 0L)
+                     break;
+                  if (kind > 24)
+                     kind = 24;
+                  mccCheckNAdd(20);
+                  break;
+               case 21:
+               case 22:
+                  if (curChar == 39)
+                     mccCheckNAddStates(4, 6);
+                  break;
+               case 23:
+                  if (curChar == 39)
+                     mccstateSet[mccnewStateCnt++] = 22;
+                  break;
+               case 24:
+                  if ((-549755813889 & l) != 0L)
+                     mccCheckNAddStates(4, 6);
+                  break;
+               case 25:
+                  if (curChar == 39 && kind > 26)
+                     kind = 26;
+                  break;
+               case 26:
+                  if (curChar != 36)
+                     break;
+                  if (kind > 27)
+                     kind = 27;
+                  mccCheckNAdd(27);
+                  break;
+               case 27:
+                  if ((287948969894477824 & l) == 0L)
+                     break;
+                  if (kind > 27)
+                     kind = 27;
+                  mccCheckNAdd(27);
+                  break;
+               case 28:
+                  if ((287948901175001088 & l) != 0L)
+                     mccCheckNAddStates(0, 3);
+                  break;
+               case 29:
+                  if ((287948901175001088 & l) != 0L)
+                     mccCheckNAddTwoStates(29, 30);
+                  break;
+               case 30:
+                  if (curChar != 46)
+                     break;
+                  if (kind > 24)
+                     kind = 24;
+                  mccCheckNAddTwoStates(31, 32);
+                  break;
+               case 31:
+                  if ((287948901175001088 & l) == 0L)
+                     break;
+                  if (kind > 24)
+                     kind = 24;
+                  mccCheckNAddTwoStates(31, 32);
+                  break;
+               case 33:
+                  if ((43980465111040 & l) != 0L)
+                     mccCheckNAdd(34);
+                  break;
+               case 34:
+                  if ((287948901175001088 & l) == 0L)
+                     break;
+                  if (kind > 24)
+                     kind = 24;
+                  mccCheckNAdd(34);
+                  break;
+               case 35:
+                  if ((287948901175001088 & l) != 0L)
+                     mccCheckNAddTwoStates(35, 36);
+                  break;
+               case 37:
+                  if ((43980465111040 & l) != 0L)
+                     mccCheckNAdd(38);
+                  break;
+               case 38:
+                  if ((287948901175001088 & l) == 0L)
+                     break;
+                  if (kind > 24)
+                     kind = 24;
+                  mccCheckNAdd(38);
+                  break;
+               case 39:
+                  if (curChar != 48)
+                     break;
+                  if (kind > 23)
+                     kind = 23;
+                  mccCheckNAddTwoStates(40, 42);
+                  break;
+               case 41:
+                  if ((287948901175001088 & l) == 0L)
+                     break;
+                  if (kind > 22)
+                     kind = 22;
+                  mccstateSet[mccnewStateCnt++] = 41;
+                  break;
+               case 42:
+                  if ((71776119061217280 & l) == 0L)
+                     break;
+                  if (kind > 23)
+                     kind = 23;
+                  mccCheckNAdd(42);
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else if (curChar < 128)
+      {
+         long l = 1L << (curChar & 63);
+         do
+         {
+            switch(mccstateSet[--i])
+            {
+               case 5:
+               case 27:
+                  if ((576460745995190270 & l) == 0L)
+                     break;
+                  if (kind > 27)
+                     kind = 27;
+                  mccCheckNAdd(27);
+                  break;
+               case 1:
+                  mccAddStates(7, 9);
+                  break;
+               case 7:
+                  mccCheckNAddTwoStates(7, 8);
+                  break;
+               case 9:
+               case 10:
+                  mccCheckNAddTwoStates(10, 8);
+                  break;
+               case 15:
+                  if ((17592186048512 & l) != 0L && kind > 21)
+                     kind = 21;
+                  break;
+               case 18:
+                  if ((137438953504 & l) != 0L)
+                     mccAddStates(13, 14);
+                  break;
+               case 24:
+                  mccAddStates(4, 6);
+                  break;
+               case 32:
+                  if ((137438953504 & l) != 0L)
+                     mccAddStates(15, 16);
+                  break;
+               case 36:
+                  if ((137438953504 & l) != 0L)
+                     mccAddStates(17, 18);
+                  break;
+               case 40:
+                  if ((72057594054705152 & l) != 0L)
+                     mccCheckNAdd(41);
+                  break;
+               case 41:
+                  if ((541165879422 & l) == 0L)
+                     break;
+                  if (kind > 22)
+                     kind = 22;
+                  mccCheckNAdd(41);
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else
+      {
+         int hiByte = (curChar >> 8);
+         int i1 = hiByte >> 6;
+         long l1 = 1L << (hiByte & 63);
+         int i2 = (curChar & 0xff) >> 6;
+         long l2 = 1L << (curChar & 63);
+         do
+         {
+            switch(mccstateSet[--i])
+            {
+               case 1:
+                  if (mccCanMove_0(hiByte, i1, i2, l1, l2))
+                     mccAddStates(7, 9);
+                  break;
+               case 7:
+                  if (mccCanMove_0(hiByte, i1, i2, l1, l2))
+                     mccCheckNAddTwoStates(7, 8);
+                  break;
+               case 9:
+               case 10:
+                  if (mccCanMove_0(hiByte, i1, i2, l1, l2))
+                     mccCheckNAddTwoStates(10, 8);
+                  break;
+               case 24:
+                  if (mccCanMove_0(hiByte, i1, i2, l1, l2))
+                     mccAddStates(4, 6);
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      if (kind != Int32.MaxValue)
+      {
+         mccmatchedKind = kind;
+         mccmatchedPos = curPos;
+         kind = Int32.MaxValue;
+      }
+      ++curPos;
+      if ((i = mccnewStateCnt) == (startsAt = 43 - (mccnewStateCnt = startsAt)))
+         break;
+      try { curChar = input_stream.ReadChar(); }
+      catch(System.IO.IOException) { break; }
+   }
+   if (mccmatchedPos > strPos)
+      return curPos;
+
+   int toRet = Math.Max(curPos, seenUpto);
+
+   if (curPos < toRet)
+      for (i = toRet - Math.Min(curPos, seenUpto); i-- > 0; )
+         try { curChar = input_stream.ReadChar(); }
+         catch(System.IO.IOException) { throw new Exception("Internal Error : Please send a bug report."); }
+
+   if (mccmatchedPos < strPos)
+   {
+      mccmatchedKind = strKind;
+      mccmatchedPos = strPos;
+   }
+   else if (mccmatchedPos == strPos && mccmatchedKind > strKind)
+      mccmatchedKind = strKind;
+
+   return toRet;
+}
+static readonly int[] mccnextStates = {
+   29, 30, 35, 36, 23, 24, 25, 1, 2, 4, 8, 9, 11, 19, 20, 33, 
+   34, 37, 38, 
+};
+private static bool mccCanMove_0(int hiByte, int i1, int i2, long l1, long l2)
+{
+   switch(hiByte)
+   {
+      case 0:
+         return ((mccbitVec2[i2] & l2) != 0L);
+      default : 
+         if ((mccbitVec0[i1] & l1) != 0L)
+            if ((mccbitVec1[i2] & l2) == 0L)
+               return false;
+            else
+            return true;
+         return false;
+   }
+}
+public static readonly string[] mccstrLiteralImages = {
+"", null, null, null, null, null, null, null, null, null, null, null, null, 
+null, null, null, null, null, null, null, null, null, null, null, null, null, null, 
+null, "=", "<>", ">", ">=", "<", "<=", "(", ",", ")", "+", "-", "*", "/", "%", };
+public static readonly string[] lexStateNames = {
+   "DEFAULT", 
+};
+static readonly long[] mcctoToken = {
+   4398012956417, 
+};
+static readonly long[] mcctoSkip = {
+   254, 
+};
+static readonly long[] mcctoSpecial = {
+   62, 
+};
+protected SimpleCharStream input_stream;
+private readonly int[] mccrounds = new int[43];
+private readonly int[] mccstateSet = new int[86];
+protected char curChar;
+public SelectorParserTokenManager(SimpleCharStream stream) {
+   if (SimpleCharStream.staticFlag)
+      throw new System.SystemException("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+   input_stream = stream;
+}
+public SelectorParserTokenManager(SimpleCharStream stream, int lexState)
+   : this(stream) {
+   SwitchTo(lexState);
+}
+public void ReInit(SimpleCharStream stream) {
+   mccmatchedPos = mccnewStateCnt = 0;
+   curLexState = defaultLexState;
+   input_stream = stream;
+   ReInitRounds();
+}
+private void ReInitRounds()
+{
+   int i;
+   mccround = -2147483647;
+   for (i = 43; i-- > 0;)
+      mccrounds[i] = Int32.MinValue;
+}
+public void ReInit(SimpleCharStream stream, int lexState) {
+   ReInit(stream);
+   SwitchTo(lexState);
+}
+public void SwitchTo(int lexState) {
+   if (lexState >= 1 || lexState < 0)
+      throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.InvalidLexicalState);
+   else
+      curLexState = lexState;
+}
+
+protected Token mccFillToken()
+{
+   Token t = Token.NewToken(mccmatchedKind);
+   t.kind = mccmatchedKind;
+   string im = mccstrLiteralImages[mccmatchedKind];
+   t.image = (im == null) ? input_stream.GetImage() : im;
+   t.beginLine = input_stream.BeginLine;
+   t.beginColumn = input_stream.BeginColumn;
+   t.endLine = input_stream.EndLine;
+   t.endColumn = input_stream.EndColumn;
+   return t;
+}
+
+int curLexState = 0;
+int defaultLexState = 0;
+int mccnewStateCnt;
+int mccround;
+int mccmatchedPos;
+int mccmatchedKind;
+
+public Token GetNextToken() {
+  //int kind;
+  Token specialToken = null;
+  Token matchedToken;
+  int curPos = 0;
+
+for (;;) {
+   try {
+      curChar = input_stream.BeginToken();
+   } catch(System.IO.IOException) {
+      mccmatchedKind = 0;
+      matchedToken = mccFillToken();
+      matchedToken.specialToken = specialToken;
+      return matchedToken;
+   }
+
+   mccmatchedKind = Int32.MaxValue;
+   mccmatchedPos = 0;
+   curPos = mccMoveStringLiteralDfa0_0();
+   if (mccmatchedKind != Int32.MaxValue) {
+      if (mccmatchedPos + 1 < curPos)
+         input_stream.Backup(curPos - mccmatchedPos - 1);
+      if ((mcctoToken[mccmatchedKind >> 6] & (1L << (mccmatchedKind & 63))) != 0L) {
+         matchedToken = mccFillToken();
+         matchedToken.specialToken = specialToken;
+         return matchedToken;
+      }
+      else
+      {
+         if ((mcctoSpecial[mccmatchedKind >> 6] & (1L << (mccmatchedKind & 63))) != 0L) {
+            matchedToken = mccFillToken();
+            if (specialToken == null)
+               specialToken = matchedToken;
+            else {
+               matchedToken.specialToken = specialToken;
+               specialToken = (specialToken.next = matchedToken);
+            }
+         }
+         goto EOFLoop;
+      }
+   }
+   int error_line = input_stream.EndLine;
+   int error_column = input_stream.EndColumn;
+   string error_after = null;
+   bool EOFSeen = false;
+   try { input_stream.ReadChar(); input_stream.Backup(1); }
+   catch (System.IO.IOException) {
+      EOFSeen = true;
+      error_after = curPos <= 1 ? "" : input_stream.GetImage();
+      if (curChar == '\n' || curChar == '\r') {
+         error_line++;
+         error_column = 0;
+      } else
+         error_column++;
+   }
+   if (!EOFSeen) {
+      input_stream.Backup(1);
+      error_after = curPos <= 1 ? "" : input_stream.GetImage();
+   }
+   throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LexicalError);
+EOFLoop: ;
+  }
+}
+
+}
diff --git a/src/main/csharp/Selector/SimpleCharStream.cs b/src/main/csharp/Selector/SimpleCharStream.cs
new file mode 100644
index 0000000..a49a9da
--- /dev/null
+++ b/src/main/csharp/Selector/SimpleCharStream.cs
@@ -0,0 +1,366 @@
+/* Generated By:CSharpCC: Do not edit this line. SimpleCharStream.cs Version 3.3 */
+/// <summary>
+/// An implementation of interface CharStream, where the stream is assumed to
+/// contain only ASCII characters (without unicode processing).
+/// </summary>
+
+public  class SimpleCharStream {
+  public static readonly bool staticFlag = false;
+  int bufsize;
+  int available;
+  int tokenBegin;
+  public int bufpos = -1;
+  protected int[] bufline;
+  protected int[] bufcolumn;
+
+  protected int column = 0;
+  protected int line = 1;
+
+  protected bool prevCharIsCR = false;
+  protected bool prevCharIsLF = false;
+
+  protected System.IO.TextReader inputStream;
+
+  protected char[] buffer;
+  protected int maxNextCharInd = 0;
+  protected int inBuf = 0;
+
+  protected void ExpandBuff(bool wrapAround)
+  {
+     char[] newbuffer = new char[bufsize + 2048];
+     int[] newbufline = new int[bufsize + 2048];
+     int[] newbufcolumn = new int[bufsize + 2048];
+
+     try {
+        if (wrapAround) {
+           System.Array.Copy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+           System.Array.Copy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos);
+           buffer = newbuffer;
+
+           System.Array.Copy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+           System.Array.Copy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos);
+           bufline = newbufline;
+
+           System.Array.Copy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+           System.Array.Copy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos);
+           bufcolumn = newbufcolumn;
+
+           maxNextCharInd = (bufpos += (bufsize - tokenBegin));
+        } else {
+           System.Array.Copy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+           buffer = newbuffer;
+
+           System.Array.Copy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+           bufline = newbufline;
+
+           System.Array.Copy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+           bufcolumn = newbufcolumn;
+
+           maxNextCharInd = (bufpos -= tokenBegin);
+        }
+     } catch (System.Exception e) {
+        throw new System.SystemException(e.Message);
+     }
+
+
+     bufsize += 2048;
+     available = bufsize;
+     tokenBegin = 0;
+  }
+
+  protected void FillBuff() {
+     if (maxNextCharInd == available) {
+        if (available == bufsize) {
+           if (tokenBegin > 2048) {
+              bufpos = maxNextCharInd = 0;
+              available = tokenBegin;
+           } else if (tokenBegin < 0)
+              bufpos = maxNextCharInd = 0;
+           else
+              ExpandBuff(false);
+        } else if (available > tokenBegin)
+           available = bufsize;
+        else if ((tokenBegin - available) < 2048)
+           ExpandBuff(true);
+        else
+           available = tokenBegin;
+     }
+
+     int i;
+     try {
+        if ((i = inputStream.Read(buffer, maxNextCharInd,
+                                    available - maxNextCharInd)) == -1) {
+           inputStream.Close();
+           throw new System.IO.IOException();
+        } else
+           maxNextCharInd += i;
+        return;
+     } catch(System.IO.IOException e) {
+        --bufpos;
+        Backup(0);
+        if (tokenBegin == -1)
+           tokenBegin = bufpos;
+        throw e;
+     }
+  }
+
+  public char BeginToken() {
+     tokenBegin = -1;
+     try {
+         char c = ReadChar();
+         tokenBegin = bufpos;
+         return c;
+     } catch (System.IO.EndOfStreamException e) {
+         if (tokenBegin == -1)
+             tokenBegin = bufpos;
+         throw e;
+     }
+  }
+
+  protected void UpdateLineColumn(char c) {
+     column++;
+
+     if (prevCharIsLF) {
+        prevCharIsLF = false;
+        line += (column = 1);
+     } else if (prevCharIsCR) {
+        prevCharIsCR = false;
+        if (c == '\n') {
+           prevCharIsLF = true;
+        } else
+           line += (column = 1);
+     }
+
+     switch (c) {
+        case '\r' :
+           prevCharIsCR = true;
+           break;
+        case '\n' :
+           prevCharIsLF = true;
+           break;
+        case '\t' :
+           column--;
+           column += (8 - (column & 07));
+           break;
+        default :
+           break;
+     }
+
+     bufline[bufpos] = line;
+     bufcolumn[bufpos] = column;
+  }
+
+  public char ReadChar() {
+     if (inBuf > 0) {
+        --inBuf;
+
+        if (++bufpos == bufsize)
+           bufpos = 0;
+
+        return buffer[bufpos];
+     }
+
+     if (++bufpos >= maxNextCharInd)
+        FillBuff();
+     if (bufpos >= maxNextCharInd) {
+        bufpos--;
+        if (bufpos < 0)
+            bufpos += bufsize;
+        throw new System.IO.EndOfStreamException();
+     }
+
+     char c = buffer[bufpos];
+
+     UpdateLineColumn(c);
+     return (c);
+  }
+
+[System.Obsolete("Deprecated - use EndColumn instead.", false)]
+
+  public int Column {
+  get {
+       return bufcolumn[bufpos];
+    }
+  }
+
+[System.Obsolete("Deprecated - use EndLine instead.", false)]
+
+  public int Line {
+  get {
+       return bufline[bufpos];
+    }
+  }
+
+  public int EndColumn {
+  get {
+       return bufcolumn[bufpos];
+    }
+  }
+
+  public int EndLine {
+  get {
+       return bufline[bufpos];
+    }
+  }
+
+  public int BeginColumn {
+  get {
+       return bufcolumn[tokenBegin];
+    }
+  }
+
+  public int BeginLine {
+  get {
+       return bufline[tokenBegin];
+    }
+  }
+
+  public void Backup(int amount) {
+
+    inBuf += amount;
+    if ((bufpos -= amount) < 0)
+       bufpos += bufsize;
+  }
+
+  public SimpleCharStream(System.IO.TextReader dstream, int startline,
+  int startcolumn, int buffersize) {
+    inputStream = dstream;
+    line = startline;
+    column = startcolumn - 1;
+
+    available = bufsize = buffersize;
+    buffer = new char[buffersize];
+    bufline = new int[buffersize];
+    bufcolumn = new int[buffersize];
+  }
+
+  public SimpleCharStream(System.IO.TextReader dstream, int startline,
+                                                           int startcolumn) : 
+     this(dstream, startline, startcolumn, 4096) {
+  }
+
+  public SimpleCharStream(System.IO.TextReader dstream) :
+     this(dstream, 1, 1, 4096) {
+  }
+  public void ReInit(System.IO.TextReader dstream, int startline,
+  int startcolumn, int buffersize) {
+    inputStream = dstream;
+    line = startline;
+    column = startcolumn - 1;
+
+    if (buffer == null || buffersize != buffer.Length) {
+      available = bufsize = buffersize;
+      buffer = new char[buffersize];
+      bufline = new int[buffersize];
+      bufcolumn = new int[buffersize];
+    }
+    prevCharIsLF = prevCharIsCR = false;
+    tokenBegin = inBuf = maxNextCharInd = 0;
+    bufpos = -1;
+  }
+
+  public void ReInit(System.IO.TextReader dstream, int startline,
+                                                           int startcolumn) {
+     ReInit(dstream, startline, startcolumn, 4096);
+  }
+
+  public void ReInit(System.IO.TextReader dstream) {
+     ReInit(dstream, 1, 1, 4096);
+  }
+  public SimpleCharStream(System.IO.Stream dstream, int startline,
+  int startcolumn, int buffersize) :
+     this(new System.IO.StreamReader(dstream), startline, startcolumn, 4096) {
+  }
+
+  public SimpleCharStream(System.IO.Stream dstream, int startline,
+                                                           int startcolumn) :
+     this(dstream, startline, startcolumn, 4096) {
+  }
+
+  public SimpleCharStream(System.IO.Stream dstream) :
+     this(dstream, 1, 1, 4096) {
+  }
+
+  public void ReInit(System.IO.Stream dstream, int startline,
+                          int startcolumn, int buffersize) {
+     ReInit(new System.IO.StreamReader(dstream), startline, startcolumn, 4096);
+  }
+
+  public void ReInit(System.IO.Stream dstream) {
+     ReInit(dstream, 1, 1, 4096);
+  }
+  public void ReInit(System.IO.Stream dstream, int startline,
+                                                           int startcolumn) {
+     ReInit(dstream, startline, startcolumn, 4096);
+  }
+  public string GetImage() {
+     if (bufpos >= tokenBegin)
+        return new string(buffer, tokenBegin, bufpos - tokenBegin + 1);
+     else
+        return new string(buffer, tokenBegin, bufsize - tokenBegin) +
+                              new string(buffer, 0, bufpos + 1);
+  }
+
+  public char[] GetSuffix(int len) {
+     char[] ret = new char[len];
+
+     if ((bufpos + 1) >= len)
+        System.Array.Copy(buffer, bufpos - len + 1, ret, 0, len);
+     else {
+        System.Array.Copy(buffer, bufsize - (len - bufpos - 1), ret, 0,
+                                                          len - bufpos - 1);
+        System.Array.Copy(buffer, 0, ret, len - bufpos - 1, bufpos + 1);
+     }
+
+     return ret;
+  }
+
+  public void Done()
+  {
+     buffer = null;
+     bufline = null;
+     bufcolumn = null;
+  }
+
+  /// <summary>
+  /// Method to adjust line and column numbers for the start of a token.
+  /// </summary>
+  public void AdjustBeginLineColumn(int newLine, int newCol) {
+     int start = tokenBegin;
+     int len;
+
+     if (bufpos >= tokenBegin) {
+        len = bufpos - tokenBegin + inBuf + 1;
+     } else {
+        len = bufsize - tokenBegin + bufpos + 1 + inBuf;
+     }
+
+     int i = 0, j = 0, k = 0;
+     int nextColDiff = 0, columnDiff = 0;
+
+     while (i < len &&
+            bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) {
+        bufline[j] = newLine;
+        nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j];
+        bufcolumn[j] = newCol + columnDiff;
+        columnDiff = nextColDiff;
+        i++;
+     } 
+
+     if (i < len) {
+        bufline[j] = newLine++;
+        bufcolumn[j] = newCol + columnDiff;
+
+        while (i++ < len) {
+           if (bufline[j = start % bufsize] != bufline[++start % bufsize])
+              bufline[j] = newLine++;
+           else
+              bufline[j] = newLine;
+        }
+     }
+
+     line = bufline[j];
+     column = bufcolumn[j];
+  }
+
+}
diff --git a/src/main/csharp/Selector/Token.cs b/src/main/csharp/Selector/Token.cs
new file mode 100644
index 0000000..bc5b705
--- /dev/null
+++ b/src/main/csharp/Selector/Token.cs
@@ -0,0 +1,78 @@
+/* Generated By:CSharpCC: Do not edit this line. Token.cs Version 3.0 */
+/// <summary>
+/// Describes the input token stream.
+/// </summary>
+
+public  class Token {
+
+  /// <summary>
+  /// Gets an integer that describes the kind of this token.
+  /// </summary>
+  /// <remarks>
+  /// This numbering system is determined by CSharpCCParser, and 
+  /// a table of these numbers is stored in the class <see cref="SelectorParserConstants"/>.
+  /// </remarks>
+  public int kind;
+
+  /**
+   * beginLine and beginColumn describe the position of the first character
+   * of this token; endLine and endColumn describe the position of the
+   * last character of this token.
+   */
+  public int beginLine, beginColumn, endLine, endColumn;
+
+  /**
+   * The string image of the token.
+   */
+  public string image;
+
+  /**
+   * A reference to the next regular (non-special) token from the input
+   * stream.  If this is the last token from the input stream, or if the
+   * token manager has not read tokens beyond this one, this field is
+   * set to null.  This is true only if this token is also a regular
+   * token.  Otherwise, see below for a description of the contents of
+   * this field.
+   */
+  public Token next;
+
+  /**
+   * This field is used to access special tokens that occur prior to this
+   * token, but after the immediately preceding regular (non-special) token.
+   * If there are no such special tokens, this field is set to null.
+   * When there are more than one such special token, this field refers
+   * to the last of these special tokens, which in turn refers to the next
+   * previous special token through its specialToken field, and so on
+   * until the first special token (whose specialToken field is null).
+   * The next fields of special tokens refer to other special tokens that
+   * immediately follow it (without an intervening regular token).  If there
+   * is no such token, this field is null.
+   */
+  public Token specialToken;
+
+  /**
+   * Returns the image.
+   */
+  public override string ToString() {
+     return image;
+  }
+
+  /**
+   * Returns a new Token object, by default. However, if you want, you
+   * can create and return subclass objects based on the value of ofKind.
+   * Simply add the cases to the switch for all those special cases.
+   * For example, if you have a subclass of Token called IDToken that
+   * you want to create if ofKind is ID, simlpy add something like :
+   *
+   *    case MyParserConstants.ID : return new IDToken();
+   *
+   * to the following switch statement. Then you can cast matchedToken
+   * variable to the appropriate type and use it in your lexical actions.
+   */
+  public static Token NewToken(int ofKind) {
+     switch(ofKind) {
+       default : return new Token();
+     }
+  }
+
+}
diff --git a/src/main/csharp/Selector/TokenMgrError.cs b/src/main/csharp/Selector/TokenMgrError.cs
new file mode 100644
index 0000000..b011703
--- /dev/null
+++ b/src/main/csharp/Selector/TokenMgrError.cs
@@ -0,0 +1,130 @@
+/* Generated By:CSharpCC: Do not edit this line. TokenMgrError.cs Version 3.0 */
+public  class TokenMgrError : System.SystemException
+{
+   /*
+    * Ordinals for various reasons why an Exceptions of this type can be thrown.
+    */
+
+   /**
+    * Lexical error occured.
+    */
+   internal static readonly int LexicalError = 0;
+
+   /**
+    * An attempt wass made to create a second instance of a static token manager.
+    */
+   internal static readonly int StaticLexerError = 1;
+
+   /**
+    * Tried to change to an invalid lexical state.
+    */
+   internal static readonly int InvalidLexicalState = 2;
+
+   /**
+    * Detected (and bailed out of) an infinite loop in the token manager.
+    */
+   internal static readonly int LoopDetected = 3;
+
+   /**
+    * Indicates the reason why the exception is thrown. It will have
+    * one of the above 4 values.
+    */
+   int errorCode;
+
+   /**
+    * Replaces unprintable characters by their espaced (or unicode escaped)
+    * equivalents in the given string
+    */
+   protected static string AddEscapes(string str) {
+      System.Text.StringBuilder retval = new System.Text.StringBuilder();
+      char ch;
+      for (int i = 0; i < str.Length; i++) {
+        switch (str[i]) {
+           case '\0' :
+              continue;
+           case '\b':
+              retval.Append("\\b");
+              continue;
+           case '\t':
+              retval.Append("\\t");
+              continue;
+           case '\n':
+              retval.Append("\\n");
+              continue;
+           case '\f':
+              retval.Append("\\f");
+              continue;
+           case '\r':
+              retval.Append("\\r");
+              continue;
+           case '\"':
+              retval.Append("\\\"");
+              continue;
+           case '\'':
+              retval.Append("\\\'");
+              continue;
+           case '\\':
+              retval.Append("\\\\");
+              continue;
+           default:
+              if ((ch = str[i]) < 0x20 || ch > 0x7e) {
+                 string s = "0000" + System.Convert.ToString((int)ch, 16);
+                 retval.Append("\\u" + s.Substring(s.Length - 4, s.Length - (s.Length - 4)));
+              } else {
+                 retval.Append(ch);
+              }
+              continue;
+        }
+      }
+      return retval.ToString();
+   }
+
+   /**
+    * Returns a detailed message for the Exception when it is thrown by the
+    * token manager to indicate a lexical error.
+    * Parameters : 
+    *    EOFSeen     : indicates if EOF caused the lexicl error
+    *    curLexState : lexical state in which this error occured
+    *    errorLine   : line number when the error occured
+    *    errorColumn : column number when the error occured
+    *    errorAfter  : prefix that was seen before this error occured
+    *    curchar     : the offending character
+    * Note: You can customize the lexical error message by modifying this method.
+    */
+   protected static string GetLexicalError(bool EOFSeen, int lexState, int errorLine, int errorColumn, string errorAfter, char curChar) {
+      return("Lexical error at line " +
+           errorLine + ", column " +
+           errorColumn + ".  Encountered: " +
+           (EOFSeen ? "<EOF> " : ("\"" + AddEscapes(curChar.ToString()) + "\"") + " (" + (int)curChar + "), ") +
+           "after : \"" + AddEscapes(errorAfter) + "\"");
+   }
+
+   /**
+    * You can also modify the body of this method to customize your error messages.
+    * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not
+    * of end-users concern, so you can return something like : 
+    *
+    *     "Internal Error : Please file a bug report .... "
+    *
+    * from this method for such cases in the release version of your parser.
+    */
+   public override string Message {
+      get { return base.Message; }
+   }
+
+   /*
+    * Constructors of various flavors follow.
+    */
+
+   public TokenMgrError() {
+   }
+
+   public TokenMgrError(string message, int reason) :
+      base(message) {
+      errorCode = reason;
+   }
+
+   public TokenMgrError(bool EOFSeen, int lexState, int errorLine, int errorColumn, string errorAfter, char curChar, int reason) :
+      this(GetLexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason) {
+   }
+}
diff --git a/src/main/csharp/Selector/UnaryExpression.cs b/src/main/csharp/Selector/UnaryExpression.cs
new file mode 100644
index 0000000..4ccbbc0
--- /dev/null
+++ b/src/main/csharp/Selector/UnaryExpression.cs
@@ -0,0 +1,66 @@
+using System;
+/**
+ *
+ * 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.
+ */
+
+namespace Apache.NMS.Selector
+{
+    /// <summary>
+    /// An expression which performs an operation on one expression value.
+    /// </summary>
+    public abstract class UnaryExpression : IExpression
+    {
+        protected IExpression rightExpression;
+        public IExpression Right
+        {
+            get { return rightExpression; }
+            set { rightExpression = value; }
+        }
+
+        protected abstract string ExpressionSymbol
+        {
+            get;
+        }
+
+        public UnaryExpression(IExpression left)
+        {
+            this.rightExpression = left;
+        }
+
+        public abstract object Evaluate(MessageEvaluationContext message);
+
+        public override string ToString()
+        {
+            return "(" + ExpressionSymbol + " " + rightExpression.ToString() + ")";
+        }
+
+        public static IExpression CreateNegate(IExpression left)
+        {
+            return new NegateExpression(left);
+        }
+
+        public static IBooleanExpression CreateNOT(IBooleanExpression left)
+        {
+            return new NOTExpression(left);
+        }
+
+        public static IBooleanExpression CreateBooleanCast(IExpression left)
+        {
+            return new BooleanCastExpression(left);
+        }
+    }
+}
diff --git a/src/main/csharp/Session.cs b/src/main/csharp/Session.cs
index 5172c08..b64c6ae 100644
--- a/src/main/csharp/Session.cs
+++ b/src/main/csharp/Session.cs
@@ -70,12 +70,8 @@ namespace Apache.NMS.MSMQ
 
         public IMessageConsumer CreateConsumer(IDestination destination, string selector, bool noLocal)
         {
-            if(selector != null)
-            {
-                throw new NotSupportedException("Selectors are not supported by MSMQ");
-            }
             MessageQueue queue = MessageConverter.ToMsmqDestination(destination);
-            return new MessageConsumer(this, acknowledgementMode, queue);
+            return new MessageConsumer(this, acknowledgementMode, queue, selector);
         }
 
         public IMessageConsumer CreateDurableConsumer(ITopic destination, string name, string selector, bool noLocal)
@@ -95,12 +91,8 @@ namespace Apache.NMS.MSMQ
 
         public IQueueBrowser CreateBrowser(IQueue queue, string selector)
         {
-            if(selector != null)
-            {
-                throw new NotSupportedException("Selectors are not supported by MSMQ");
-            }
             MessageQueue msmqQueue = MessageConverter.ToMsmqDestination(queue);
-            return new QueueBrowser(this, msmqQueue);
+            return new QueueBrowser(this, msmqQueue, selector);
         }
 
         public IQueue GetQueue(string name)
diff --git a/vs2008-msmq.csproj b/vs2008-msmq.csproj
index 8a20d12..3023e88 100644
--- a/vs2008-msmq.csproj
+++ b/vs2008-msmq.csproj
@@ -75,6 +75,7 @@
     <Compile Include="src\main\csharp\DefaultMessageConverter.cs" />
     <Compile Include="src\main\csharp\Destination.cs" />
     <Compile Include="src\main\csharp\IMessageConverter.cs" />
+    <Compile Include="src\main\csharp\IMessageConverterEx.cs" />
     <Compile Include="src\main\csharp\MapMessage.cs" />
     <Compile Include="src\main\csharp\MessageConsumer.cs" />
     <Compile Include="src\main\csharp\MessageProducer.cs" />
@@ -84,6 +85,52 @@
     <Compile Include="src\main\csharp\Session.cs" />
     <Compile Include="src\main\csharp\StreamMessage.cs" />
     <Compile Include="src\main\csharp\TextMessage.cs" />
+    <Compile Include="src\main\csharp\Readers\AbstractMessageReader.cs" />
+    <Compile Include="src\main\csharp\Readers\ByCorrelationIdMessageReader.cs" />
+    <Compile Include="src\main\csharp\Readers\ByIdMessageReader.cs" />
+    <Compile Include="src\main\csharp\Readers\ByLookupIdMessageReader.cs" />
+    <Compile Include="src\main\csharp\Readers\BySelectorMessageReader.cs" />
+    <Compile Include="src\main\csharp\Readers\IMessageReader.cs" />
+    <Compile Include="src\main\csharp\Readers\MessageReaderUtil.cs" />
+    <Compile Include="src\main\csharp\Readers\NonFilteringMessageReader.cs" />
+    <Compile Include="src\main\csharp\Selector\AlignedNumericValues.cs" />
+    <Compile Include="src\main\csharp\Selector\ANDExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\ArithmeticExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\BinaryExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\BooleanCastExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\BooleanConstantExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\BooleanUnaryExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\ComparisonExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\ConstantExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\DivideExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\EqualExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\GreaterExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\GreaterOrEqualExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\IBooleanExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\IExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\InExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\IsNullExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\LesserExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\LesserOrEqualExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\LikeExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\LogicExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\MessageEvaluationContext.cs" />
+    <Compile Include="src\main\csharp\Selector\MinusExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\ModExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\MultiplyExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\NegateExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\NOTExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\ORExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\ParseException.cs" />
+    <Compile Include="src\main\csharp\Selector\PlusExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\PropertyExpression.cs" />
+    <Compile Include="src\main\csharp\Selector\SelectorParser.cs" />
+    <Compile Include="src\main\csharp\Selector\SelectorParserConstants.cs" />
+    <Compile Include="src\main\csharp\Selector\SelectorParserTokenManager.cs" />
+    <Compile Include="src\main\csharp\Selector\SimpleCharStream.cs" />
+    <Compile Include="src\main\csharp\Selector\Token.cs" />
+    <Compile Include="src\main\csharp\Selector\TokenMgrError.cs" />
+    <Compile Include="src\main\csharp\Selector\UnaryExpression.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="keyfile\NMSKey.snk" />