using System; using System.Collections.Generic; using System.Text; namespace ImaginationOverflow.UniversalDeepLinking.Editor.Xcode.PBX { class PropertyCommentChecker { private int m_Level; private bool m_All; private List> m_Props; /* The argument is an array of matcher strings each of which determine whether a property with a certain path needs to be decorated with a comment. A path is a number of path elements concatenated by '/'. The last path element is either: the key if we're referring to a dict key the value if we're referring to a value in an array the value if we're referring to a value in a dict All other path elements are either: the key if the container is dict '*' if the container is array Path matcher has the same structure as a path, except that any of the elements may be '*'. Matcher matches a path if both have the same number of elements and for each pair matcher element is the same as path element or is '*'. a/b/c matches a/b/c but not a/b nor a/b/c/d a/* /c matches a/d/c but not a/b nor a/b/c/d * /* /* matches any path from three elements */ protected PropertyCommentChecker(int level, List> props) { m_Level = level; m_All = false; m_Props = props; } public PropertyCommentChecker() { m_Level = 0; m_All = false; m_Props = new List>(); } public PropertyCommentChecker(IEnumerable props) { m_Level = 0; m_All = false; m_Props = new List>(); foreach (var prop in props) { m_Props.Add(new List(prop.Split('/'))); } } bool CheckContained(string prop) { if (m_All) return true; foreach (var list in m_Props) { if (list.Count == m_Level+1) { if (list[m_Level] == prop) return true; if (list[m_Level] == "*") { m_All = true; // short-circuit all at this level return true; } } } return false; } public bool CheckStringValueInArray(string value) { return CheckContained(value); } public bool CheckKeyInDict(string key) { return CheckContained(key); } public bool CheckStringValueInDict(string key, string value) { foreach (var list in m_Props) { if (list.Count == m_Level + 2) { if ((list[m_Level] == "*" || list[m_Level] == key) && list[m_Level+1] == "*" || list[m_Level+1] == value) return true; } } return false; } public PropertyCommentChecker NextLevel(string prop) { var newList = new List>(); foreach (var list in m_Props) { if (list.Count <= m_Level+1) continue; if (list[m_Level] == "*" || list[m_Level] == prop) newList.Add(list); } return new PropertyCommentChecker(m_Level + 1, newList); } } class Serializer { public static PBXElementDict ParseTreeAST(TreeAST ast, TokenList tokens, string text) { var el = new PBXElementDict(); foreach (var kv in ast.values) { PBXElementString key = ParseIdentifierAST(kv.key, tokens, text); PBXElement value = ParseValueAST(kv.value, tokens, text); el[key.value] = value; } return el; } public static PBXElementArray ParseArrayAST(ArrayAST ast, TokenList tokens, string text) { var el = new PBXElementArray(); foreach (var v in ast.values) { el.values.Add(ParseValueAST(v, tokens, text)); } return el; } public static PBXElement ParseValueAST(ValueAST ast, TokenList tokens, string text) { if (ast is TreeAST) return ParseTreeAST((TreeAST)ast, tokens, text); if (ast is ArrayAST) return ParseArrayAST((ArrayAST)ast, tokens, text); if (ast is IdentifierAST) return ParseIdentifierAST((IdentifierAST)ast, tokens, text); return null; } public static PBXElementString ParseIdentifierAST(IdentifierAST ast, TokenList tokens, string text) { Token tok = tokens[ast.value]; string value; switch (tok.type) { case TokenType.String: value = text.Substring(tok.begin, tok.end - tok.begin); return new PBXElementString(value); case TokenType.QuotedString: value = text.Substring(tok.begin, tok.end - tok.begin); value = PBXStream.UnquoteString(value); return new PBXElementString(value); default: throw new Exception("Internal parser error"); } } static string k_Indent = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; static string GetIndent(int indent) { return k_Indent.Substring(0, indent); } static void WriteStringImpl(StringBuilder sb, string s, bool comment, GUIDToCommentMap comments) { if (comment) comments.WriteStringBuilder(sb, s); else sb.Append(PBXStream.QuoteStringIfNeeded(s)); } public static void WriteDictKeyValue(StringBuilder sb, string key, PBXElement value, int indent, bool compact, PropertyCommentChecker checker, GUIDToCommentMap comments) { if (!compact) { sb.Append("\n"); sb.Append(GetIndent(indent)); } WriteStringImpl(sb, key, checker.CheckKeyInDict(key), comments); sb.Append(" = "); if (value is PBXElementString) WriteStringImpl(sb, value.AsString(), checker.CheckStringValueInDict(key, value.AsString()), comments); else if (value is PBXElementDict) WriteDict(sb, value.AsDict(), indent, compact, checker.NextLevel(key), comments); else if (value is PBXElementArray) WriteArray(sb, value.AsArray(), indent, compact, checker.NextLevel(key), comments); sb.Append(";"); if (compact) sb.Append(" "); } public static void WriteDict(StringBuilder sb, PBXElementDict el, int indent, bool compact, PropertyCommentChecker checker, GUIDToCommentMap comments) { sb.Append("{"); if (el.Contains("isa")) WriteDictKeyValue(sb, "isa", el["isa"], indent+1, compact, checker, comments); var keys = new List(el.values.Keys); keys.Sort(StringComparer.Ordinal); foreach (var key in keys) { if (key != "isa") WriteDictKeyValue(sb, key, el[key], indent+1, compact, checker, comments); } if (!compact) { sb.Append("\n"); sb.Append(GetIndent(indent)); } sb.Append("}"); } public static void WriteArray(StringBuilder sb, PBXElementArray el, int indent, bool compact, PropertyCommentChecker checker, GUIDToCommentMap comments) { sb.Append("("); foreach (var value in el.values) { if (!compact) { sb.Append("\n"); sb.Append(GetIndent(indent+1)); } if (value is PBXElementString) WriteStringImpl(sb, value.AsString(), checker.CheckStringValueInArray(value.AsString()), comments); else if (value is PBXElementDict) WriteDict(sb, value.AsDict(), indent+1, compact, checker.NextLevel("*"), comments); else if (value is PBXElementArray) WriteArray(sb, value.AsArray(), indent+1, compact, checker.NextLevel("*"), comments); sb.Append(","); if (compact) sb.Append(" "); } if (!compact) { sb.Append("\n"); sb.Append(GetIndent(indent)); } sb.Append(")"); } } } // namespace UnityModule.iOS.Xcode