public static FilterNode Parse(string filterStr)
        {
            var tok = new StringTokenizer(filterStr)
            {
                IgnoreWhiteSpace = true,
                // the following two should be mutually exclusive
                // if the following have a mutually shared char, Word char will take precedence
                // when creating tokens at this time...
                AdditionalWordChars = new char[] { '*', ' ', '.' },
                SymbolChars         = new[] { '(', ')', '^', '|', ':' }
            };

            var nodeid = 0;
            var nodes  = new Dictionary <int, FilterNode>();

            var currentNodeId = ++nodeid;

            nodes.Add(currentNodeId, new FilterNode {
                ParentNodeId = 0, NodeId = currentNodeId, NodeType = FilterNodeType.Root
            });
            var currentNode = nodes[nodeid];

            // parse tokens
            Token token;

            do
            {
                token = tok.Next();

                if (token.Kind == TokenKind.Symbol && token.Value == "(")
                {
                    currentNodeId = ++nodeid;
                    nodes.Add(currentNodeId, new FilterNode {
                        ParentNodeId = currentNode.NodeId, NodeId = currentNodeId
                    });
                    currentNode = nodes[nodeid];
                }
                else if (token.Kind == TokenKind.Symbol && token.Value == ")")
                {
                    currentNode = nodes[currentNode.ParentNodeId];
                }
                else if (token.Kind == TokenKind.Symbol && token.Value == "^")
                {
                    currentNode.NodeType = FilterNodeType.AndExpression;
                }
                else if (token.Kind == TokenKind.Symbol && token.Value == "|")
                {
                    currentNode.NodeType = FilterNodeType.OrExpression;
                }
                else if (token.Kind == TokenKind.Symbol && token.Value == ":")
                {
                }
                else if (token.Kind == TokenKind.Word || token.Kind == TokenKind.Number || token.Kind == TokenKind.QuotedString)
                {
                    currentNode.Elements.Add(token.Value);
                }
            } while (token.Kind != TokenKind.EOF);

            // build the tree
            foreach (var node in nodes)
            {
                node.Value.Nodes.AddRange(nodes.Where(n => n.Value.ParentNodeId == node.Key).Select(n => n.Value));
                node.Value.ParentNode = nodes.SingleOrDefault(n => n.Key == node.Value.ParentNodeId).Value;
            }

            // return the root node
            var filterNode = nodes[1];

            return(filterNode);
        }
        public static FilterNode Parse(string filterStr)
        {
            var tok = new StringTokenizer(filterStr)
                {
                    IgnoreWhiteSpace = true,
                    // the following two should be mutually exclusive
                    // if the following have a mutually shared char, Word char will take precedence
                    // when creating tokens at this time...
                    AdditionalWordChars = new char[] { '*', ' ', '.' },
                    SymbolChars = new[] { '(', ')', '^', '|', ':' }
                };

            var nodeid = 0;
            var nodes = new Dictionary<int, FilterNode>();

            var currentNodeId = ++nodeid;
            nodes.Add(currentNodeId, new FilterNode { ParentNodeId = 0, NodeId = currentNodeId, NodeType = FilterNodeType.Root });
            var currentNode = nodes[nodeid];

            // parse tokens
            Token token;
            do
            {
                token = tok.Next();

                if (token.Kind == TokenKind.Symbol && token.Value == "(")
                {
                    currentNodeId = ++nodeid;
                    nodes.Add(currentNodeId, new FilterNode { ParentNodeId = currentNode.NodeId, NodeId = currentNodeId });
                    currentNode = nodes[nodeid];
                }
                else if (token.Kind == TokenKind.Symbol && token.Value == ")")
                {
                    currentNode = nodes[currentNode.ParentNodeId];
                }
                else if (token.Kind == TokenKind.Symbol && token.Value == "^")
                {
                    currentNode.NodeType = FilterNodeType.AndExpression;
                }
                else if (token.Kind == TokenKind.Symbol && token.Value == "|")
                {
                    currentNode.NodeType = FilterNodeType.OrExpression;
                }
                else if (token.Kind == TokenKind.Symbol && token.Value == ":")
                {
                }
                else if (token.Kind == TokenKind.Word || token.Kind == TokenKind.Number || token.Kind == TokenKind.QuotedString)
                {
                    currentNode.Elements.Add(token.Value);
                }

            } while (token.Kind != TokenKind.EOF);

            // build the tree
            foreach (var node in nodes)
            {
                node.Value.Nodes.AddRange(nodes.Where(n => n.Value.ParentNodeId == node.Key).Select(n => n.Value));
                node.Value.ParentNode = nodes.SingleOrDefault(n => n.Key == node.Value.ParentNodeId).Value;
            }

            // return the root node
            var filterNode = nodes[1];
            return filterNode;
        }