Apply patch for AMQNET-554. Suport for message properties, and selectors. Thanks Stephane Ramet!
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 @@
 
 	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;
+		#region Acknowledgement
+
 		private event AcknowledgeHandler Acknowledger;
-		private DateTime timestamp = new DateTime();
-		private bool readOnlyMsgBody = false;
-
-		public bool ReadOnlyBody
-		{
-			get { return readOnlyMsgBody; }
-			set { readOnlyMsgBody = value; }
-		}
-
-		// IMessage interface
-
 		public void Acknowledge()
 		{
 			if(null != Acknowledger)
@@ -54,6 +35,27 @@
 			}
 		}
 
+		#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,53 +69,72 @@
 			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 IPrimitiveMap Properties
+		public object GetObjectProperty(string name)
 		{
-			get { return propertiesMap; }
+			return Properties[name];
 		}
 
-
-		// NMS headers
-
-		/// <summary>
-		/// The correlation ID used to correlate messages with conversations or long running business processes
-		/// </summary>
-		public string NMSCorrelationID
+		public void SetObjectProperty(string name, object value)
 		{
-			get { return correlationId; }
-			set { correlationId = value; }
+            Properties[name] = value;
 		}
 
-		/// <summary>
-		/// The destination of the message
-		/// </summary>
-		public IDestination NMSDestination
-		{
-			get { return destination; }
-			set { destination = value; }
-		}
+		#endregion
 
-		/// <summary>
-		/// The time in milliseconds that this message should expire in
-		/// </summary>
-		public TimeSpan NMSTimeToLive
-		{
-			get { return timeToLive; }
-			set { timeToLive = value; }
-		}
+		#region Message header fields
 
+		private string messageId;
 		/// <summary>
 		/// The message ID which is set by the provider
 		/// </summary>
@@ -123,6 +144,37 @@
 			set { messageId = value; }
 		}
 
+		private string correlationId;
+		/// <summary>
+		/// The correlation ID used to correlate messages with conversations or long running business processes
+		/// </summary>
+		public string NMSCorrelationID
+		{
+			get { return correlationId; }
+			set { correlationId = value; }
+		}
+
+		private IDestination destination;
+		/// <summary>
+		/// The destination of the message
+		/// </summary>
+		public IDestination NMSDestination
+		{
+			get { return destination; }
+			set { destination = value; }
+		}
+
+		private TimeSpan timeToLive;
+		/// <summary>
+		/// The time in milliseconds that this message should expire in
+		/// </summary>
+		public TimeSpan NMSTimeToLive
+		{
+			get { return timeToLive; }
+			set { timeToLive = value; }
+		}
+
+		private MsgDeliveryMode deliveryMode;
 		/// <summary>
 		/// Whether or not this message is persistent
 		/// </summary>
@@ -132,6 +184,7 @@
 			set { deliveryMode = value; }
 		}
 
+		private MsgPriority priority;
 		/// <summary>
 		/// The Priority on this message
 		/// </summary>
@@ -150,7 +203,7 @@
             set { }
 		}
 
-
+		private Destination replyTo;
 		/// <summary>
 		/// The destination that the consumer of this message should send replies to
 		/// </summary>
@@ -160,7 +213,7 @@
 			set { replyTo = (Destination) value; }
 		}
 
-
+		private DateTime timestamp = new DateTime();
 		/// <summary>
 		/// The timestamp the broker added to the message
 		/// </summary>
@@ -170,12 +223,7 @@
 			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 @@
 			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 @@
 
 		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 @@
 		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 @@
 
 			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 @@
 
 			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 @@
 			}
 		}
 
+        /// <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 @@
 			}
 		}
 
-		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();
-
-				// TODO: Implement
-				result = streamMessage;
-			}
-			else
-			{
-				BaseMessage baseMessage = new BaseMessage();
-
-				result = baseMessage;
+				ConvertStreamMessageBodyToNMS(message, (StreamMessage)answer);
 			}
 
-			return result;
+			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);
+			}
+
+			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)
+			{
+				buf = new byte[message.BodyStream.Length];
+				message.BodyStream.Read(buf, 0, buf.Length);
+			}
+
+			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);
+			}
+
+			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 @@
 			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 @@
 
 			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 @@
 {
 	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 @@
  * 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 @@
         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 @@
             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 @@
 
             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 @@
 
             if(messageQueue != null)
             {
-                Message message = messageQueue.Receive(timeout);
-                nmsMessage = ToNmsMessage(message);
+                nmsMessage = reader.Receive(timeout);
+                nmsMessage = TransformMessage(nmsMessage);
             }
 
             return nmsMessage;
@@ -135,8 +139,8 @@
 
             if(messageQueue != null)
             {
-                Message message = messageQueue.Receive(zeroTimeout);
-                nmsMessage = ToNmsMessage(message);
+                nmsMessage = reader.Receive(zeroTimeout);
+                nmsMessage = TransformMessage(nmsMessage);
             }
 
             return nmsMessage;
@@ -226,25 +230,20 @@
             session.Connection.HandleException(e);
         }
 
-        protected virtual IMessage ToNmsMessage(Message message)
+        protected virtual IMessage TransformMessage(IMessage message)
         {
-            if(message == null)
-            {
-                return null;
-            }
+            IMessage transformed = message;
 
-            IMessage converted = session.MessageConverter.ToNmsMessage(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.Messaging;

 using Apache.NMS;

 using Apache.NMS.Util;

+using Apache.NMS.MSMQ.Readers;

 

 namespace Apache.NMS.MSMQ

 {

@@ -30,7 +31,17 @@
         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 @@
                 this.messageQueue.MessageReadPropertyFilter.SetAll();

             }

 

+            reader = MessageReaderUtil.CreateMessageReader(

+                messageQueue, session.MessageConverter, selector);

 		}

 

 		~QueueBrowser()

@@ -95,7 +108,7 @@
 

 		public string MessageSelector

 		{

-			get { throw new NotSupportedException(); }

+			get { return selector; }

 		}

 

 		public IQueue Queue

@@ -107,11 +120,14 @@
 		{

 			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 @@
 

 			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 @@
 

 		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/Selector/BooleanConstantExpression.cs b/src/main/csharp/Selector/BooleanConstantExpression.cs
new file mode 100644
index 0000000..eb6447a
--- /dev/null
+++ b/src/main/csharp/Selector/BooleanConstantExpression.cs
@@ -0,0 +1,38 @@
+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/Selector/BooleanUnaryExpression.cs b/src/main/csharp/Selector/BooleanUnaryExpression.cs
new file mode 100644
index 0000000..3873050
--- /dev/null
+++ b/src/main/csharp/Selector/BooleanUnaryExpression.cs
@@ -0,0 +1,39 @@
+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/Selector/GreaterExpression.cs b/src/main/csharp/Selector/GreaterExpression.cs
new file mode 100644
index 0000000..eb264e5
--- /dev/null
+++ b/src/main/csharp/Selector/GreaterExpression.cs
@@ -0,0 +1,42 @@
+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/Selector/GreaterOrEqualExpression.cs b/src/main/csharp/Selector/GreaterOrEqualExpression.cs
new file mode 100644
index 0000000..7a456f8
--- /dev/null
+++ b/src/main/csharp/Selector/GreaterOrEqualExpression.cs
@@ -0,0 +1,43 @@
+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/Selector/IBooleanExpression.cs b/src/main/csharp/Selector/IBooleanExpression.cs
new file mode 100644
index 0000000..af6c0f5
--- /dev/null
+++ b/src/main/csharp/Selector/IBooleanExpression.cs
@@ -0,0 +1,35 @@
+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/Selector/IExpression.cs b/src/main/csharp/Selector/IExpression.cs
new file mode 100644
index 0000000..30fb893
--- /dev/null
+++ b/src/main/csharp/Selector/IExpression.cs
@@ -0,0 +1,35 @@
+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/Selector/LesserExpression.cs b/src/main/csharp/Selector/LesserExpression.cs
new file mode 100644
index 0000000..4be2c9d
--- /dev/null
+++ b/src/main/csharp/Selector/LesserExpression.cs
@@ -0,0 +1,42 @@
+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/Selector/LesserOrEqualExpression.cs b/src/main/csharp/Selector/LesserOrEqualExpression.cs
new file mode 100644
index 0000000..abdc7e5
--- /dev/null
+++ b/src/main/csharp/Selector/LesserOrEqualExpression.cs
@@ -0,0 +1,43 @@
+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 @@
 
         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 @@
 
         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" />