private static void Initialize() { root = new IdentificationTreeNode(string.Empty); // root.Category = "1.1"; // Category modality is compsed by a keywords sequence. // Only operator and function name are treated as keyword in schematron. // Single category may have mutiple modalities due to optional parameters. AddCategoryModality("1.1", new string[] { "@" }, -1, -1); AddCategoryModality("1.2", new string[] { "fn:matches", "@" }, -1, -1); AddCategoryModality("1.3", new string[] { ">=", "@" }, -1, -1); AddCategoryModality("1.3", new string[] { "<=", "@" }, -1, -1); AddCategoryModality("1.3", new string[] { ">", "@" }, -1, -1); AddCategoryModality("1.3", new string[] { "<", "@" }, -1, -1); AddCategoryModality("1.3", new string[] { "and", ">=", "@", "<=", "@" }, -1, -1); AddCategoryModality("1.3", new string[] { "and", ">", "@", "<", "@" }, -1, -1); AddCategoryModality("1.3", new string[] { "and", ">=", "@", "<", "@" }, -1, -1); AddCategoryModality("1.3", new string[] { "and", ">", "@", "<=", "@" }, -1, -1); AddCategoryModality("1.4", new string[] { "=", "@" }, -1, -1); AddCategoryModality("1.4", new string[] { "or", "=", "@", "=", "@" }, 0, 2); AddCategoryModality("1.10", new string[] { "!=", "@" }, -1, -1); AddCategoryModality("1.10", new string[] { "and", "!=", "@", "!=", "@" }, 0, 2); AddCategoryModality("1.12", new string[] { "=", "fn:string-length", "@" }, -1, -1); AddCategoryModality("1.12", new string[] { ">=", "fn:string-length", "@" }, -1, -1); AddCategoryModality("1.12", new string[] { "<=", "fn:string-length", "@" }, -1, -1); AddCategoryModality("1.12", new string[] { "and", ">=", "fn:string-length", "@", "<=", "fn:string-length", "@" }, -1, -1); AddCategoryModality("1.14", new string[] { "and", "@", "=", "@" }, -1, -1); AddCategoryModality("1.14", new string[] { "and", "@", "or", "=", "@", "=", "@" }, 2, 4); AddCategoryModality("1.15", new string[] { "and", "@", "!=", "@" }, -1, -1); AddCategoryModality("1.15", new string[] { "and", "@", "and", "!=", "@", "!=", "@" }, 2, 4); AddCategoryModality("1.16", new string[] { "and", "@", "@" }, -1, -1); AddCategoryModality("1.16", new string[] { "or", "and", "@", "@", "and", "@", "@" }, 0, 3); AddCategoryModality("1.17", new string[] { "<", "@", "@" }, -1, -1); AddCategoryModality("1.17", new string[] { "<=", "@", "@" }, -1, -1); AddCategoryModality("1.18", new string[] { "or", "and", "@", "=", "@", "!=", "@" }, -1, -1); AddCategoryModality("1.18", new string[] { "or", "and", "@", "or", "=", "@", "=", "@", "and", "!=", "@", "!=", "@" }, 3, 5, 8, 10); AddCategoryModality("1.19", new string[] { "or", "and", "=", "@", "=", "@", "!=", "@" }, -1, -1); AddCategoryModality("1.19", new string[] { "or", "and", "=", "@", "or", "=", "@", "=", "@", "and", "!=", "@", "!=", "@" }, 4, 6, 9, 11); AddCategoryModality("1.19", new string[] { "or", "and", "or", "=", "@", "=", "@", "or", "=", "@", "=", "@", "and", "!=", "@", "!=", "@" }, 2, 4, 7, 9, 12, 14); AddCategoryModality("1.19", new string[] { "or", "and", "or", "=", "@", "=", "@", "=", "@", "!=", "@" }, 2, 2, 7, 8); AddCategoryModality("2.1", new string[] { "[]", "//", "fn:document", "=", "@", "/@" }, -1, -1); AddCategoryModality("2.2", new string[] { "=", "/@", "[]", "//", "fn:document", "=", "@", "/@" }, -1, -1); AddCategoryModality("2.3", new string[] { "=", "fn:count", "fn:distinct-values", "//", "/@", "fn:count", "//", "/@" }, -1, -1); AddCategoryModality("2.3", new string[] { "=", "fn:count", "fn:distinct-values", "fn:lower-case", "//", "/@", "fn:count", "fn:lower-case", "//", "/@" }, -1, -1); AddCategoryModality("2.3", new string[] { "=", "fn:count", "fn:distinct-values", "//", "ancestor::", "/@", "fn:count", "//", "ancestor::", "/@" }, -1, -1); AddCategoryModality("2.3", new string[] { "=", "fn:count", "fn:distinct-values", "fn:lower-case", "//", "ancestor::", "/@", "fn:count", "fn:lower-case", "//", "ancestor::", "/@" }, -1, -1); AddCategoryModality("3.1", new string[] { "fn:Index-of", "//", "fn:document", "/@", "@" }, -1, -1); AddCategoryModality("3.2", new string[] { "<", "@", "+", "fn:count", "//", "fn:document" }, -1, -1); AddCategoryModality("3.3", new string[] { "=", "fn:count", "fn:distinct-values", "//", "fn:document", "/@", "fn:count", "//", "fn:document", "/@" }, -1, -1); AddCategoryModality("3.3", new string[] { "=", "fn:count", "fn:distinct-values", "fn:lower-case", "//", "fn:document", "/@", "fn:count", "fn:lower-case", "//", "fn:document", "/@" }, -1, -1); // todo: add more category modalities here. }
private static void AddCategoryModality(string category, string[] keywords, params int[] rings) { if (rings.Length % 2 != 0) { throw new ArgumentException(); } var ringStarts = new List <int>(); var ringEnds = new List <int>(); for (var i = 0; i < rings.Length; i++) { if (i % 2 == 0) { ringStarts.Add(rings[i]); } else { ringEnds.Add(rings[i]); } } var cur = root; IdentificationTreeNode ringHead = null; var ringHeadStack = new Stack <IdentificationTreeNode>(); for (var i = 0; i < keywords.Length; i++) { var nodes = cur.Children.Where(c => c.Text == keywords[i]); Debug.Assert(!nodes.Any() || nodes.Count() == 1); if (!nodes.Any()) { var node = new IdentificationTreeNode(keywords[i]); cur.Children.Add(node); cur = node; } else { cur = nodes.First(); } if (ringStarts.Contains(i)) { if (ringHead != null) { ringHeadStack.Push(ringHead); } ringHead = cur; } if (ringEnds.Contains(i)) { Debug.Assert(ringHead != null); cur.Children.Add(ringHead); if (ringHeadStack.Count != 0) { ringHead = ringHeadStack.Pop(); } else { ringHead = null; } } } Debug.Assert(cur.Category == null); // make sure different category has different preorder traverse sequence. cur.Category = category; }