PageRenderTime 55ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/SharpTAL/TemplateParser/ElementParser.cs

https://bitbucket.org/rlacko/sharptal
C# | 316 lines | 249 code | 33 blank | 34 comment | 30 complexity | 24a3f39e85c35ea6bfc958d2cec09adc MD5 | raw file
Possible License(s): Apache-2.0
  1. //
  2. // ElementParser.cs
  3. //
  4. // Ported to C# from project Chameleon.
  5. // Original source code: https://github.com/malthe/chameleon/blob/master/src/chameleon/parser.py
  6. //
  7. // Author:
  8. // Roman Lacko (backup.rlacko@gmail.com)
  9. //
  10. // Copyright (c) 2010 - 2013 Roman Lacko
  11. //
  12. // Permission is hereby granted, free of charge, to any person obtaining
  13. // a copy of this software and associated documentation files (the
  14. // "Software"), to deal in the Software without restriction, including
  15. // without limitation the rights to use, copy, modify, merge, publish,
  16. // distribute, sublicense, and/or sell copies of the Software, and to
  17. // permit persons to whom the Software is furnished to do so, subject to
  18. // the following conditions:
  19. //
  20. // The above copyright notice and this permission notice shall be
  21. // included in all copies or substantial portions of the Software.
  22. //
  23. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  27. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  28. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  29. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  30. //
  31. using System;
  32. using System.Collections.Generic;
  33. using System.Collections.Specialized;
  34. using System.Linq;
  35. using System.Text;
  36. using System.Text.RegularExpressions;
  37. namespace SharpTAL.TemplateParser
  38. {
  39. /// <summary>
  40. /// Parses tokens into elements.
  41. /// </summary>
  42. public class ElementParser
  43. {
  44. static readonly string XML_NS = "http://www.w3.org/XML/1998/namespace";
  45. static readonly Regex match_tag_prefix_and_name = new Regex(
  46. @"^(?<prefix></?)(?<name>([^:\n ]+:)?[^ \r\n\t>/]+)(?<suffix>(?<space>\s*)/?>)?", RegexOptions.Singleline);
  47. static readonly Regex match_single_attribute = new Regex(
  48. @"(?<space>\s+)(?!\d)" +
  49. @"(?<name>[^ =/>\n\t]+)" +
  50. @"((?<eq>\s*=\s*)" +
  51. @"((?<quote>[\'""])(?<value>.*?)\k<quote>|" +
  52. @"(?<alt_value>[^\s\'"">/]+))|" +
  53. @"(?<simple_value>(?![ \\n\\t\\r]*=)))", RegexOptions.Singleline);
  54. static readonly Regex match_comment = new Regex(
  55. @"^<!--(?<text>.*)-->$", RegexOptions.Singleline);
  56. static readonly Regex match_cdata = new Regex(
  57. @"^<!\[CDATA\[(?<text>.*)\]>$", RegexOptions.Singleline);
  58. static readonly Regex match_declaration = new Regex(
  59. @"^<!(?<text>[^>]+)>$", RegexOptions.Singleline);
  60. static readonly Regex match_processing_instruction = new Regex(
  61. //@"^<\?(?<text>.*?)\?>", RegexOptions.Singleline);
  62. @"^<\?(?<name>\w+)(?<text>.*?)\?>", RegexOptions.Singleline);
  63. static readonly Regex match_xml_declaration = new Regex(
  64. @"^<\?xml(?=[ /])", RegexOptions.Singleline);
  65. IEnumerable<Token> stream;
  66. List<Dictionary<string, string>> namespaces;
  67. List<Element> queue;
  68. Stack<KeyValuePair<Token, int>> index;
  69. public ElementParser(IEnumerable<Token> stream, Dictionary<string, string> defaultNamespaces)
  70. {
  71. this.stream = stream;
  72. this.queue = new List<Element>();
  73. this.index = new Stack<KeyValuePair<Token, int>>();
  74. this.namespaces = new List<Dictionary<string, string>> { new Dictionary<string, string>(defaultNamespaces) };
  75. }
  76. public IEnumerable<Element> Parse()
  77. {
  78. foreach (var token in this.stream)
  79. {
  80. var item = this.ParseToken(token);
  81. this.queue.Add(item);
  82. }
  83. return this.queue;
  84. }
  85. Element ParseToken(Token token)
  86. {
  87. TokenKind kind = token.Kind;
  88. if (kind == TokenKind.Comment)
  89. return visit_comment(token);
  90. if (kind == TokenKind.CData)
  91. return visit_cdata(token);
  92. if (kind == TokenKind.ProcessingInstruction)
  93. return visit_processing_instruction(token);
  94. if (kind == TokenKind.EndTag)
  95. return visit_end_tag(token);
  96. if (kind == TokenKind.EmptyTag)
  97. return visit_empty_tag(token);
  98. if (kind == TokenKind.StartTag)
  99. return visit_start_tag(token);
  100. if (kind == TokenKind.Text)
  101. return visit_text(token);
  102. return visit_default(token);
  103. }
  104. Element visit_comment(Token token)
  105. {
  106. return new Element(ElementKind.Comment, token);
  107. }
  108. Element visit_default(Token token)
  109. {
  110. return new Element(ElementKind.Default, token);
  111. }
  112. Element visit_text(Token token)
  113. {
  114. return new Element(ElementKind.Text, token);
  115. }
  116. Element visit_cdata(Token token)
  117. {
  118. return new Element(ElementKind.CData, token);
  119. }
  120. Element visit_processing_instruction(Token token)
  121. {
  122. Match match = match_processing_instruction.Match(token.ToString());
  123. Dictionary<string, object> node = groupdict(match_processing_instruction, match, token);
  124. return new Element(ElementKind.ProcessingInstruction, node);
  125. }
  126. Element visit_start_tag(Token token)
  127. {
  128. var ns = new Dictionary<string, string>(namespaces.Last());
  129. namespaces.Add(ns);
  130. var node = parse_tag(token, ns);
  131. index.Push(new KeyValuePair<Token, int>(node["name"] as Token, queue.Count));
  132. return new Element(ElementKind.StartTag, node);
  133. }
  134. Element visit_end_tag(Token token)
  135. {
  136. Dictionary<string, string> ns;
  137. try
  138. {
  139. ns = namespaces.Last();
  140. namespaces.RemoveAt(namespaces.Count - 1);
  141. }
  142. catch (InvalidOperationException ex)
  143. {
  144. throw new ParseError("Unexpected end tag.", token);
  145. }
  146. Dictionary<string, object> node = parse_tag(token, ns); ;
  147. while (index.Count > 0)
  148. {
  149. KeyValuePair<Token, int> idx = index.Pop();
  150. Token name = idx.Key;
  151. int pos = idx.Value;
  152. if (node["name"].Equals(name))
  153. {
  154. Element el = queue[pos];
  155. queue.RemoveAt(pos);
  156. Dictionary<string, object> start = el.StartTagTokens;
  157. List<Element> children = queue.GetRange(pos, queue.Count - pos);
  158. queue.RemoveRange(pos, queue.Count - pos);
  159. return new Element(ElementKind.Element, start, node, children);
  160. }
  161. }
  162. throw new ParseError("Unexpected end tag.", token);
  163. }
  164. Element visit_empty_tag(Token token)
  165. {
  166. var ns = new Dictionary<string, string>(namespaces.Last());
  167. var node = parse_tag(token, ns);
  168. return new Element(ElementKind.Element, node);
  169. }
  170. static Dictionary<string, object> groupdict(Regex r, Match m, Token token)
  171. {
  172. Dictionary<string, object> d = new Dictionary<string, object>();
  173. foreach (string name in r.GetGroupNames())
  174. {
  175. Group g = m.Groups[name];
  176. if (g != null)
  177. {
  178. int i = g.Index;
  179. int j = g.Length;
  180. d.Add(name, token.Substring(i, j));
  181. }
  182. else
  183. {
  184. d.Add(name, null);
  185. }
  186. }
  187. return d;
  188. }
  189. static Dictionary<string, object> match_tag(Token token)
  190. {
  191. Match match = match_tag_prefix_and_name.Match(token.ToString());
  192. Dictionary<string, object> node = groupdict(match_tag_prefix_and_name, match, token);
  193. int end = match.Index + match.Length;
  194. token = token.Substring(end);
  195. var attrs = new List<Dictionary<string, object>>();
  196. node["attrs"] = attrs;
  197. foreach (Match m in match_single_attribute.Matches(token.ToString()))
  198. {
  199. Dictionary<string, object> attr = groupdict(match_single_attribute, m, token);
  200. Token alt_value = null;
  201. if (attr.Keys.Contains("alt_value"))
  202. {
  203. alt_value = attr["alt_value"] as Token;
  204. attr.Remove("alt_value");
  205. if (!string.IsNullOrEmpty(alt_value.ToString()))
  206. {
  207. attr["value"] = alt_value;
  208. attr["quote"] = "";
  209. }
  210. }
  211. Token simple_value = null;
  212. if (attr.Keys.Contains("simple_value"))
  213. {
  214. simple_value = attr["simple_value"] as Token;
  215. attr.Remove("simple_value");
  216. if (!string.IsNullOrEmpty(simple_value.ToString()))
  217. {
  218. attr["quote"] = "";
  219. attr["value"] = new Token("");
  220. attr["eq"] = "";
  221. }
  222. }
  223. attrs.Add(attr);
  224. int m_end = m.Index + m.Length;
  225. node["suffix"] = token.Substring(m_end);
  226. }
  227. return node;
  228. }
  229. static Dictionary<string, object> parse_tag(Token token, Dictionary<string, string> ns)
  230. {
  231. var node = match_tag(token);
  232. update_namespace(node["attrs"] as List<Dictionary<string, object>>, ns);
  233. string prefix = null;
  234. if ((node["name"] as Token).ToString().Contains(':'))
  235. prefix = (node["name"] as Token).ToString().Split(':')[0];
  236. string defaultNs = prefix != null && ns.ContainsKey(prefix) ? ns[prefix] : XML_NS;
  237. node["namespace"] = defaultNs;
  238. node["ns_attrs"] = unpack_attributes(node["attrs"] as List<Dictionary<string, object>>, ns, defaultNs);
  239. return node;
  240. }
  241. static void update_namespace(List<Dictionary<string, object>> attributes, Dictionary<string, string> ns)
  242. {
  243. foreach (var attribute in attributes)
  244. {
  245. string name = ((Token)attribute["name"]).ToString();
  246. string value = ((Token)attribute["value"]).ToString();
  247. if (name == "xmlns")
  248. ns[""] = value;
  249. else if (name.ToString().StartsWith("xmlns:"))
  250. ns[name.Substring(6)] = value;
  251. }
  252. }
  253. static OrderedDictionary unpack_attributes(List<Dictionary<string, object>> attributes, Dictionary<string, string> ns, string defaultNs)
  254. {
  255. OrderedDictionary namespaced = new OrderedDictionary();
  256. foreach (var attribute in attributes)
  257. {
  258. string name = ((Token)attribute["name"]).ToString();
  259. string value = ((Token)attribute["value"]).ToString();
  260. string n = null;
  261. string prefix = null;
  262. if (name.Contains(':'))
  263. {
  264. prefix = name.Split(':')[0];
  265. name = name.Substring(prefix.Length + 1);
  266. try
  267. {
  268. n = ns[prefix];
  269. }
  270. catch (KeyNotFoundException ex)
  271. {
  272. throw new KeyNotFoundException(
  273. string.Format("Undefined namespace prefix: {0}.", prefix));
  274. }
  275. }
  276. else
  277. n = defaultNs;
  278. namespaced[new KeyValuePair<string, string>(n, name)] = value;
  279. }
  280. return namespaced;
  281. }
  282. }
  283. }