public void CreateTree(TreeNode <T> node)
        {
            //node.Query = queryString;
            string logic = AND;

            node.Query = REGEX_AND.Replace(node.Query, "#", 1, 0);
            var list = node.Query.Split("#");

            if (list.Length == 1)
            {
                //20200303 优先处理括号
                //第一步:去除两端的最外层括号
                //第二步:调用BracketStack.IsValid()
                int endAt = 0;
                if (list[0][0] == '(')
                {
                    node.Query = list[0].Substring(1, list[0].Length - 2);
                    BracketStack.IsValid(node.Query, out endAt);
                }
                // 逻辑运算符 只有 and 和 or;
                // todo: and 优先级比or 高,因此 应先从 OR 拆分条件。补充优先级可要求传入时加括号处理
                // todo: not较特殊,等想通后再处理。补充:!为一元运算符,构建逻辑树时不应处理,
                //       可并入叶子节点一并处理
                logic      = Regex.Match(node.Query.Substring(endAt, node.Query.Length - 1 - endAt), AND_OR).Value;
                node.Query = REGEX_AND_OR.Replace(node.Query, "#", 1, endAt);
                list       = node.Query.Split("#");
                if (list.Length == 1)
                {
                    // 思路:在此处生成叶子节点的Data,在添加完叶子后反向生成父节点Data
                    var data = REGEX_OPERATOR.Split(node.Query);
                    var cond = new UserCondition {
                        Key = data[0], Value = data[2], Operator = data[1]
                    };
                    node.Data = GetCriterion(cond);
                    return;
                }
            }
            //生成逻辑节点
            node.LogicalOperator = logic;
            //node.Data = new AndCriterion<T>();
            //从左向右 构建一个 二叉树结构
            var left = new TreeNode <T>()
            {
                Query = list[0]
            };

            node.Left = left;
            CreateTree(node.Left);
            var right = new TreeNode <T>()
            {
                Query = list[1]
            };

            node.Right = right;
            CreateTree(node.Right);
            //思路:依据logical类型生成Criterion
            if (node.LogicalOperator == "AND")
            {
                node.Data = new AndCriterion <T>(left.Data, right.Data);
            }
            else
            {
                node.Data = new OrCriterion <T>(left.Data, right.Data);
            }
        }
        static void Main(string[] args)
        {
            // Add a using directive for System.Linq.Expressions.
            string[] companies = { "Consolidated Messenger", "Alpine Ski House",     "Southridge Video",         "City Power & Light",
                                   "Coho Winery",            "Wide World Importers", "Graphic Design Institute", "Adventure Works",
                                   "Humongous Insurance",    "Woodgrove Bank",       "Margie's Travel",          "Northwind Traders",
                                   "Blue Yonder Airlines",   "Trey Research",        "The Phone Company",
                                   "Wingtip Toys",           "Lucerne Publishing",   "Fourth Coffee" };

            // The IQueryable data to query.
            IQueryable <String> queryableData = companies.AsQueryable <string>();

            //test criterion
            List <Company> companyList = new List <Company>();

            foreach (var company in companies)
            {
                companyList.Add(new Company {
                    Name = company, Age = 5
                });
            }
            IQueryable <Company> data = companyList.AsQueryable <Company>();
            //Console.WriteLine(data.Where(p=>p.Name.Contains("A")).Expression.Type.ToString());

            // Compose the expression tree that represents the parameter to the predicate.
            ParameterExpression p = Expression.Parameter(typeof(Company), "company");

            var cond1 = new UserCondition {
                Key = "Name", Value = "A"
            };
            var cond2 = new UserCondition {
                Key = "Age", Value = "5"
            };

            var c1     = new NameCriterion(cond1);
            var c2     = new AgeCriterion(cond2);
            var a      = new AndCriterion <Company>(c1, c2);
            var exp    = a.HandleExpression(p);
            var lambda = Expression.Lambda <Func <Company, bool> >(exp, p);
            var r      = data.Where(lambda);

            foreach (var company in r)
            {
                Console.WriteLine(company.Name);
            }

            //var res = a.HandleQueryable(data);
            //foreach (var company in res)
            //    Console.WriteLine(company.Name);
            Console.WriteLine("================================");
            string queryString =
                "((Age>=5 OR Age<=30) AND (((Age>60) AND (Age<10)) OR (Age =1))) " +
                "& (((Age!=5) AND (Age<>30)) OR (Age!>60)) & (Age !<1)";


            //test here
            queryString = "(((Name LIKE A) OR (Name LIKE i)) AND (Name LIKE i))&(Age =5)";
            //remove space
            queryString = queryString.Replace(" ", "");
            System.Console.WriteLine(queryString);

            //test logic tree
            TreeNode <Company> head = new TreeNode <Company>();

            head.Query = queryString;

            new CompanyTree <Company>().CreateTree(head);
            var res = head.Data.HandleQueryable(data);

            foreach (var company in res)
            {
                Console.WriteLine(company.Name);
            }

            Console.WriteLine("=================================");

            var bx = 1;
            var by = 10;
            var ba = bx == 1 && bx < 0 || by > 0;
            var bb = bx == 1 && (bx < 0 || by > 0);

            Console.WriteLine(ba.ToString());
            Console.WriteLine(bb.ToString());
            Console.WriteLine("====================================");
            string bracketString =
                "(Age>=5 OR Age<=30) AND (((Age>60) OR (Age<10)) OR (Age =1))";

            bracketString = bracketString.Replace(" ", "");
            int  endAt;
            bool isValid = BracketStack.IsValid(bracketString, out endAt);

            Console.WriteLine($"{bracketString} is {isValid}, and End at {endAt}");
            Console.WriteLine("=======================================");

            string fullString = "(((Name LIKE A) OR (Name LIKE i)) AND (Name LIKE i))&(Age =5)" +
                                "&(MaxNumRecordReturn=200) &( PageRecordNum =25) &( RecordStartNo =1) " +
                                "& (Sort = Person.AgeUpLimit)";

            fullString = fullString.Replace(" ", "");
            Regex REGEX_COMMON_FIELDS = new Regex("((?:MaxNumRecordReturn|PageRecordNum|RecordStartNo|Sort)=)");
            //var arr = REGEX_COMMON_FIELDS.Split(fullString,2);
            //foreach (var item in arr)
            //{
            //    Console.WriteLine(item);
            //}
            //var m = REGEX_COMMON_FIELDS.Match(fullString)?.Value;
            var    list    = fullString.Split("&");
            string qString = "";
            string maxNumRecordReturn;
            string pageRecordNum;
            string recordStartNo;
            string sort;

            foreach (var item in list)
            {
                var s = item.Substring(1, item.Length - 2);
                var m = REGEX_COMMON_FIELDS.Match(s);
                var l = REGEX_COMMON_FIELDS.Split(s);
                switch (m.Value)
                {
                case "MaxNumRecordReturn=":
                    maxNumRecordReturn = l[2];
                    break;

                case "PageRecordNum=":
                    pageRecordNum = l[2];
                    break;

                case "RecordStartNo=":
                    recordStartNo = l[2];
                    break;

                case "Sort=":
                    sort = l[2];
                    break;

                default:
                    qString += (string.IsNullOrEmpty(qString) ? "" : "&") + item;
                    break;
                }
            }
        }