String expression.
Inheritance: LeafExpression
        public void ToStringInvalidOperatorTest()
        {
            ElementAttributeExpression attributeExpression = new ElementAttributeExpression(ElementAttributeType.Name);
            StringExpression stringExpression = new StringExpression("Test");
            BinaryOperatorExpression operatorExpression = new BinaryOperatorExpression(
                (BinaryExpressionOperator) int.MinValue,
                attributeExpression,
                stringExpression);

            Assert.AreEqual(string.Format("($(Element.Name) {0} 'Test')", int.MinValue), operatorExpression.ToString());
        }
        public void ToStringTest()
        {
            ElementAttributeExpression attributeExpression = new ElementAttributeExpression(
                ElementAttributeType.Name);
            StringExpression stringExpression = new StringExpression("Test");
            BinaryOperatorExpression operatorExpression = new BinaryOperatorExpression(
                BinaryExpressionOperator.Equal,
                attributeExpression,
                stringExpression);

            Assert.AreEqual("($(Element.Name) == 'Test')", operatorExpression.ToString());
        }
        /// <summary>
        /// Parses and expression to an expression tree.
        /// </summary>
        /// <param name="expression">Condition expression text.</param>
        /// <returns>A condition expression instance.</returns>
        public IConditionExpression Parse(string expression)
        {
            const int DefaultExpressionLength = 128;
            IConditionExpression conditionExpression = null;

            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }
            else if (expression.Trim().Length == 0)
            {
                throw new ArgumentException("expression");
            }

            List<IConditionExpression> nodes = new List<IConditionExpression>();

            StringReader reader = new StringReader(expression);

            StringBuilder expressionBuilder = new StringBuilder(DefaultExpressionLength);

            bool inString = false;
            bool inAttribute = false;
            int depth = 0;

            int data = reader.Read();
            while (data > 0)
            {
                char ch = (char)data;
                char nextCh = (char)reader.Peek();

                if (inString && ch != '\'')
                {
                    expressionBuilder.Append(ch);
                }
                else
                {
                    switch (ch)
                    {
                        case ' ':
                        case '\t':
                        case '\r':
                        case '\n':
                            // Eat whitespace
                            break;

                        case ExpressionPrefix:
                            CheckForInvalidOperator(expression, expressionBuilder);
                            if (nextCh == ExpressionStart)
                            {
                                inAttribute = true;
                                reader.Read();
                            }
                            break;

                        case '=':
                            if (nextCh == '=')
                            {
                                nodes.Add(new OperatorExpressionPlaceholder(BinaryExpressionOperator.Equal));
                                reader.Read();
                            }
                            else if (nextCh == '~')
                            {
                                nodes.Add(new OperatorExpressionPlaceholder(BinaryExpressionOperator.Matches));
                                reader.Read();
                            }
                            break;

                        case '!':
                            if (nextCh == '=')
                            {
                                nodes.Add(new OperatorExpressionPlaceholder(BinaryExpressionOperator.NotEqual));
                                reader.Read();
                            }
                            else
                            {
                                expressionBuilder.Append(ch);
                            }
                            break;

                        case ':':
                            nodes.Add(new OperatorExpressionPlaceholder(BinaryExpressionOperator.Contains));
                            reader.Read();
                            break;

                        case 'O':
                            if (nextCh == 'r' && !inAttribute && !inString)
                            {
                                nodes.Add(new OperatorExpressionPlaceholder(BinaryExpressionOperator.Or));
                                reader.Read();
                            }
                            else
                            {
                                expressionBuilder.Append(ch);
                            }
                            break;

                        case 'A':
                            if (nextCh == 'n' && !inAttribute && !inString)
                            {
                                reader.Read();
                                nextCh = (char)reader.Peek();
                                if (nextCh == 'd')
                                {
                                    nodes.Add(new OperatorExpressionPlaceholder(BinaryExpressionOperator.And));
                                    reader.Read();
                                }
                            }
                            else
                            {
                                expressionBuilder.Append(ch);
                            }
                            break;

                        case ExpressionEnd:
                            if (inAttribute)
                            {
                                string attribute = expressionBuilder.ToString();
                                expressionBuilder = new StringBuilder(DefaultExpressionLength);
                                ElementAttributeScope elementScope = ElementAttributeScope.Element;
                                bool isFileExpression = false;

                                int separatorIndex = attribute.LastIndexOf(ScopeSeparator);
                                if (separatorIndex > 0)
                                {
                                    try
                                    {
                                        string attributeScope = attribute.Substring(0, separatorIndex);
                                        attribute = attribute.Substring(separatorIndex + 1);

                                        if (attributeScope == FileAttributeScope)
                                        {
                                            isFileExpression = true;
                                        }
                                        else
                                        {
                                            elementScope = (ElementAttributeScope)
                                                Enum.Parse(typeof(ElementAttributeScope), attributeScope);
                                        }
                                    }
                                    catch (ArgumentException ex)
                                    {
                                        OnInvalidExpression(expression, "Unknown attribute scope: {0}", ex.Message);
                                    }
                                }

                                if (isFileExpression)
                                {
                                    FileAttributeType fileAttribute = FileAttributeType.None;

                                    try
                                    {
                                        fileAttribute = (FileAttributeType)
                                            Enum.Parse(typeof(FileAttributeType), attribute);
                                    }
                                    catch (ArgumentException ex)
                                    {
                                        OnInvalidExpression(expression, "Unknown attribute: {0}", ex.Message);
                                    }

                                    FileAttributeExpression attributeExpresion = new FileAttributeExpression(
                                        fileAttribute);
                                    nodes.Add(attributeExpresion);
                                }
                                else
                                {
                                    ElementAttributeType elementAttribute = ElementAttributeType.None;

                                    try
                                    {
                                        elementAttribute = (ElementAttributeType)
                                            Enum.Parse(typeof(ElementAttributeType), attribute);
                                    }
                                    catch (ArgumentException ex)
                                    {
                                        OnInvalidExpression(expression, "Unknown attribute: {0}", ex.Message);
                                    }

                                    ElementAttributeExpression attributeExpresion = new ElementAttributeExpression(
                                        elementAttribute, elementScope);
                                    nodes.Add(attributeExpresion);
                                }

                                inAttribute = false;
                            }
                            else if (expressionBuilder.Length > 0 && nodes.Count > 0)
                            {
                                IConditionExpression innerExpression = nodes[nodes.Count - 1];
                                nodes.RemoveAt(nodes.Count - 1);

                                string unaryOperatorString = expressionBuilder.ToString().Trim();
                                expressionBuilder = new StringBuilder(DefaultExpressionLength);

                                UnaryExpressionOperator? unaryOperator = null;

                                if (unaryOperatorString == "!")
                                {
                                    unaryOperator = UnaryExpressionOperator.Negate;
                                }
                                else
                                {
                                    OnInvalidExpression(expression,
                                        "Invalid operator {0}", unaryOperatorString);
                                }

                                UnaryOperatorExpression unaryOperatorExpression = new UnaryOperatorExpression(
                                    unaryOperator.Value, innerExpression);

                                nodes.Add(unaryOperatorExpression);
                                depth--;
                            }
                            else
                            {
                                depth--;
                            }
                            break;

                        case ExpressionStart:
                            IConditionExpression nestedExpression = null;
                            StringBuilder childExpressionBuilder = new StringBuilder(DefaultExpressionLength);
                            data = reader.Read();
                            ch = (char)data;
                            nextCh = (char)reader.Peek();
                            depth++;
                            while (data > 0)
                            {
                                if (ch == ExpressionPrefix && nextCh == ExpressionStart)
                                {
                                    inAttribute = true;
                                    childExpressionBuilder.Append(ExpressionPrefix);
                                    data = reader.Read();
                                    childExpressionBuilder.Append(ExpressionStart);
                                }
                                else if (ch == ExpressionStart && !inAttribute)
                                {
                                    depth++;
                                    childExpressionBuilder.Append(ExpressionStart);
                                }
                                else if (nextCh == ExpressionEnd)
                                {
                                    childExpressionBuilder.Append(ch);

                                    if (inAttribute || depth > 1)
                                    {
                                        if (inAttribute)
                                        {
                                            inAttribute = false;
                                        }
                                        else if (depth > 1)
                                        {
                                            depth--;
                                        }
                                    }
                                    else
                                    {
                                        break;
                                    }
                                }
                                else
                                {
                                    childExpressionBuilder.Append(ch);
                                }

                                data = reader.Read();
                                ch = (char)data;
                                nextCh = (char)reader.Peek();
                            }

                            try
                            {
                                nestedExpression = Parse(childExpressionBuilder.ToString());
                            }
                            catch (ArgumentException)
                            {
                                OnInvalidExpression(expression);
                            }
                            nodes.Add(nestedExpression);
                            break;

                        case '\'':
                            if (inString)
                            {
                                if (nextCh == '\'')
                                {
                                    expressionBuilder.Append(ch);
                                    reader.Read();
                                }
                                else
                                {
                                    string str = expressionBuilder.ToString();
                                    expressionBuilder = new StringBuilder(DefaultExpressionLength);
                                    StringExpression stringExpression = new StringExpression(str);
                                    nodes.Add(stringExpression);
                                    inString = false;
                                }
                            }
                            else
                            {
                                CheckForInvalidOperator(expression, expressionBuilder);
                                inString = true;
                            }

                            break;

                        default:
                            expressionBuilder.Append(ch);
                            break;
                    }
                }

                data = reader.Read();
            }

            if (inString)
            {
                OnInvalidExpression(expression, "Expected '");
            }
            else if (inAttribute || depth > 0)
            {
                OnInvalidExpression(expression, "Expected )");
            }
            else if (depth < 0)
            {
                OnInvalidExpression(expression, "Unmatched )");
            }

            //
            // Assembly the flat list of expressions and expression placeholders into an
            // expression tree.
            //
            conditionExpression = AssembleExpressionTree(nodes.AsReadOnly(), expression);

            return conditionExpression;
        }