/// <summary>
        /// This constructor takes a Boolean expression in infix format.
        /// Some examples
        /// are shown below. Assume <b>T</b> is an instance of the
        /// <see cref="Tags"/>
        /// class.
        /// <list type="table">
        /// <item>
        /// T[CommonPropertyNames.title] contains "foo"
        /// <term>
        /// </term>
        /// <description>
        /// <see cref="MediaComparer.IsMatch"/> will return
        /// true on media objects that have <i>foo</i> in the title.
        /// </description>
        /// </item>
        ///
        /// <item>
        /// T[CommonPropertyNames.creator] doesNotContain "\"HappyBand\""
        /// <term>
        /// </term>
        /// <description>
        /// <see cref="MediaComparer.IsMatch"/> will return
        /// true on media objects that do not have <i>"HappyBand"</i> in the title.
        /// </description>
        /// </item>
        ///
        /// <item>
        /// (T[CommonPropertyNames.Class] = "object.item.audioItem.musicTrack" and Tags.PropertyAttributes.res_size > "40") or (T[CommonPropertyNames.author] exists true)
        /// <term>
        /// </term>
        /// <description>
        /// <see cref="MediaComparer.IsMatch"/> will return
        /// true on media objects that are music tracks with at least one resource greater than 40 bytes
        /// OR on media objects that have a value set for the author metadata.
        /// </description>
        /// </item>
        /// </list>
        /// </summary>
        /// <param name="infix">The boolean infix expression conforming to the syntax and semantics of ContentDirectory's boolean query language.</param>
        /// <exception cref="OpenSource.UPnP.AV.CdsMetadata.Error_MalformedSearchCriteria">
        /// Thrown if the infix expression has a syntax error.
        /// </exception>
        public MediaComparer(string infix)
        {
            string allstring = infix.Trim();

            if ((allstring == "") || (allstring == "*"))
            {
                this.m_AlwaysMatch = true;
                return;
            }

            //Initialize an empty stack and empty result string variable.
            //
            Stack stack = new Stack();

            m_Postfix = new Queue();

            RelExpStates state = RelExpStates.unknown;
            TokenResult  token;

            while (infix.Length > 0)
            {
                infix = infix.Trim();
                token = GetToken(ref infix, state);

                switch (state)
                {
                case RelExpStates.unknown:
                    if (token.TokenType == Tokens.PropertyName)
                    {
                        state = RelExpStates.expectOp;
                    }
                    break;

                case RelExpStates.expectOp:
                    if (token.TokenType != Tokens.Operator)
                    {
                        throw new UPnPCustomException(402, "Invalid Args: Invalid operator " + token.Data);
                    }
                    state = RelExpStates.expectValue;
                    break;

                case RelExpStates.expectValue:
                    if (token.TokenType != Tokens.PropertyValue)
                    {
                        throw new UPnPCustomException(402, "Invalid Args: Unexpected value " + token.Data);
                    }
                    state = RelExpStates.unknown;
                    break;
                }

                switch (token.TokenType)
                {
                case Tokens.Operator:
                    if (token.OpToken == OperatorTokens.LParen)
                    {
                        //left paren
                        //
                        stack.Push(token);
                    }
                    else if (token.OpToken == OperatorTokens.RParen)
                    {
                        //right paren
                        //
                        TokenResult tr = new TokenResult(false);
                        do
                        {
                            if (stack.Count > 0)
                            {
                                tr = (TokenResult)stack.Pop();
                                if (tr.OpToken != OperatorTokens.LParen)
                                {
                                    m_Postfix.Enqueue(tr);
                                }
                            }
                            else
                            {
                                throw new UPnPCustomException(402, "Invalid Args: Missing Left Parenthesis.");
                            }
                        }while (tr.OpToken != OperatorTokens.LParen);
                    }
                    else
                    {
                        //standard operator
                        //
                        if (token.OpToken == OperatorTokens.Invalid)
                        {
                            throw new Exception("bad code");
                        }

                        while
                        (
                            (stack.Count > 0) &&
                            (((TokenResult)stack.Peek()).Precedence >= token.Precedence) &&
                            (((TokenResult)stack.Peek()).OpToken != OperatorTokens.LParen)
                        )
                        {
                            // While stack is not empty &&
                            // top operator has higher or equal precedence...
                            // pop operator and stuff into queue
                            m_Postfix.Enqueue(stack.Pop());
                        }
                        stack.Push(token);
                    }
                    break;

                case Tokens.PropertyName:
                    m_Postfix.Enqueue(token);
                    TagExtractor te = new TagExtractor(token.Data);
                    this.m_PE[token.Data] = te;
                    break;

                case Tokens.PropertyValue:
                    m_Postfix.Enqueue(token);
                    break;
                }
            }

            // pop remaining items in stack and stuff into queue
            //

            while (stack.Count > 0)
            {
                TokenResult tr = (TokenResult)stack.Pop();
                if (tr.OpToken != OperatorTokens.LParen)
                {
                    m_Postfix.Enqueue(tr);
                }
            }
        }
        /// <summary>
        /// Obtains the next token, given the current state and the expression.
        /// </summary>
        /// <param name="exp">the remaining portion of the infix expression to translate into a postfix expression</param>
        /// <param name="state">the state determines what types of tokens can be accepted.</param>
        /// <returns></returns>
        private TokenResult GetToken(ref string exp, RelExpStates state)
        {
            TokenResult token = new TokenResult(false);

            bool searchOps1     = false;
            bool searchOps2     = false;
            bool searchOps3     = false;
            bool parsePropName  = false;
            bool parsePropValue = false;

            switch (state)
            {
            case RelExpStates.unknown:
                //look for everything
                searchOps1    = true;
                searchOps3    = true;
                parsePropName = true;
                break;

            case RelExpStates.expectOp:
                //look for relop, stringop, derived-op
                searchOps2 = true;
                break;

            case RelExpStates.expectValue:
                parsePropValue = true;
                break;
            }

            //remove any preceding whitespace chars
            exp = exp.Trim();

            if (exp != "")
            {
                if ((searchOps1) && (token.TokenType == Tokens.Invalid))
                {
                    token = this.SeachOperators(ref exp, Operators1);
                }

                if ((searchOps2) && (token.TokenType == Tokens.Invalid))
                {
                    token = this.SeachOperators(ref exp, Operators2);
                }

                if ((searchOps3) && (token.TokenType == Tokens.Invalid))
                {
                    token = this.SeachOperators(ref exp, Operators3);
                }


                if (token.TokenType == Tokens.Invalid)
                {
                    if (parsePropValue)
                    {
                        //it must be an operand... so parse the next whitespace delimited string or
                        //extract the next string enclosed by quotes

                        if ((exp.StartsWith("true")) || (exp.StartsWith("false")))
                        {
                            // This is a value for an existence-op operation
                            token = new TokenResult(exp, false);
                            exp   = exp.Substring(exp.Length);
                        }
                        else if (exp.StartsWith("\""))
                        {
                            // This is a value operand that is delimited
                            // by another double-quote
                            int  endQuote      = 1;
                            int  escape        = 0;
                            bool endQuoteFound = false;

                            // find the next end-quote that is not
                            // an escaped end-quote.

                            while (!endQuoteFound)
                            {
                                endQuote = exp.IndexOf("\"", endQuote);
                                escape   = exp.IndexOf("\\\"", escape);

                                if (
                                    (escape < 0) ||
                                    (escape == endQuote - 1) ||
                                    (escape > endQuote)
                                    )
                                {
                                    endQuoteFound = true;
                                }
                            }

                            if (endQuote <= 0)
                            {
                                StringBuilder msg = new StringBuilder(exp.Length);
                                msg.AppendFormat("Invalid Args: Unterminated end-quote in SearchCriteria, near \"{0}\"", exp);
                                throw new UPnPCustomException(402, msg.ToString());
                            }

                            string unescaped = exp.Substring(1, endQuote - 1);
                            string escaped   = unescaped.Replace("\\\\", "\\").Replace("\\\"", "\"");
                            token = new TokenResult(escaped, false);

                            exp = exp.Substring(endQuote + 1);
                        }
                        else
                        {
                            // Assume the CP provided a white-space delimited value without quotes

                            //int endPos = exp.IndexOf(" ");
                            int    endPos = this.FindNextIndexOf(exp, WHITESPACESANDENDPAREN);
                            string str    = exp;
                            if (endPos > 0)
                            {
                                str = exp.Substring(0, endPos);
                            }
                            token = new TokenResult(str, false);

                            exp = exp.Substring(str.Length);
                        }
                    }
                    else
                    {
                        // This is a property name, that is delimited by
                        // a whitespace.

                        //int endPos = exp.IndexOf(" ");
                        int endPos = this.FindNextIndexOf(exp, WHITESPACES);
                        if (endPos > 0)
                        {
                            string prop = exp.Substring(0, endPos);

                            token = new TokenResult(prop, true);
                            exp   = exp.Substring(prop.Length);
                        }
                        else
                        {
                            throw new Error_MalformedSearchCriteria("Property name has not been properly delimited.");
                        }
                    }
                }
            }

            if (
                (token.TokenType == Tokens.Invalid)
                )
            {
                throw new UPnPCustomException(402, "Invalid Args: Invalid SearchCriteria string.");
            }

            return(token);
        }
Example #3
0
		/// <summary>
		/// Obtains the next token, given the current state and the expression.
		/// </summary>
		/// <param name="exp">the remaining portion of the infix expression to translate into a postfix expression</param>
		/// <param name="state">the state determines what types of tokens can be accepted.</param>
		/// <returns></returns>
		private TokenResult GetToken(ref string exp, RelExpStates state)
		{
			TokenResult token = new TokenResult(false);

			bool searchOps1 = false;
			bool searchOps2 = false;
			bool searchOps3 = false;
			bool parsePropName = false;
			bool parsePropValue = false;

			switch (state)
			{
				case RelExpStates.unknown:
					//look for everything
					searchOps1 = true;
					searchOps3 = true;
					parsePropName = true;
					break;

				case RelExpStates.expectOp:
					//look for relop, stringop, derived-op 
					searchOps2 = true;
					break;

				case RelExpStates.expectValue:
					parsePropValue = true;
					break;
			}

			//remove any preceding whitespace chars
			exp = exp.Trim();

			if (exp != "")
			{
				if ((searchOps1) && (token.TokenType == Tokens.Invalid))
				{
					token = this.SeachOperators(ref exp, Operators1);
				}

				if ((searchOps2) && (token.TokenType == Tokens.Invalid))
				{
					token = this.SeachOperators(ref exp, Operators2);
				}

				if ((searchOps3) && (token.TokenType == Tokens.Invalid))
				{
					token = this.SeachOperators(ref exp, Operators3);
				}


				if (token.TokenType == Tokens.Invalid)
				{
					if (parsePropValue)
					{
						//it must be an operand... so parse the next whitespace delimited string or 
						//extract the next string enclosed by quotes

						if ((exp.StartsWith("true")) || (exp.StartsWith("false")))
						{
							// This is a value for an existence-op operation
							token = new TokenResult(exp, false);
							exp = exp.Substring(exp.Length);
						}
						else if (exp.StartsWith("\""))
						{
							// This is a value operand that is delimited
							// by another double-quote
							int endQuote = 1;
							int escape = 0;
							bool endQuoteFound = false;

							// find the next end-quote that is not
							// an escaped end-quote.

							while (!endQuoteFound)
							{
								endQuote = exp.IndexOf("\"", endQuote);
								escape = exp.IndexOf("\\\"", escape);

								if (
									(escape < 0) ||
									(escape == endQuote - 1) ||
									(escape > endQuote)
									)
								{
									endQuoteFound = true;
								}
							}

							if (endQuote <= 0)
							{
								StringBuilder msg = new StringBuilder(exp.Length);
								msg.AppendFormat("Invalid Args: Unterminated end-quote in SearchCriteria, near \"{0}\"", exp);
								throw new UPnPCustomException(402, msg.ToString());
							}

							string unescaped = exp.Substring(1, endQuote-1);
							string escaped = unescaped.Replace("\\\\", "\\").Replace("\\\"", "\"");
							token = new TokenResult(escaped, false);
					
							exp = exp.Substring(endQuote+1);
						}
						else
						{
							// Assume the CP provided a white-space delimited value without quotes
						
							//int endPos = exp.IndexOf(" ");
							int endPos = this.FindNextIndexOf(exp, WHITESPACESANDENDPAREN);
							string str = exp;
							if (endPos > 0)
							{
								str = exp.Substring(0, endPos);
							}
							token = new TokenResult(str, false);

							exp = exp.Substring(str.Length);
						}
					}
					else
					{
						// This is a property name, that is delimited by 
						// a whitespace.

						//int endPos = exp.IndexOf(" ");
						int endPos = this.FindNextIndexOf(exp, WHITESPACES);
						if (endPos > 0)
						{
							string prop = exp.Substring(0, endPos);

							token = new TokenResult(prop, true);
							exp = exp.Substring(prop.Length);
						}
						else
						{
							throw new Error_MalformedSearchCriteria("Property name has not been properly delimited.");
						}
					}
				}
			}

			if (
				(token.TokenType == Tokens.Invalid) 
				)
			{
				throw new UPnPCustomException(402, "Invalid Args: Invalid SearchCriteria string.");
			}

			return token;
		}