/Core/Text/Template.cs

https://bitbucket.org/jdeselms/magenoco · C# · 155 lines · 119 code · 30 blank · 6 comment · 8 complexity · 42f68d02c92b3155e30e00d1547c428c MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Resources;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7. using System.Reflection;
  8. using System.IO;
  9. using Magenoco.Core.IO;
  10. using Magenoco.Core.Processor;
  11. namespace Magenoco.Core.Text
  12. {
  13. public class Template
  14. {
  15. private readonly string _name;
  16. private readonly string _text;
  17. private readonly string[] _appliesTo;
  18. private readonly Lazy<TextToken[]> _tokens;
  19. private readonly Lazy<ChildToken[]> _childTokens;
  20. private readonly Template _fileNameTemplateOrNull;
  21. private static Regex _tokenRegex;
  22. private static Regex _childTokenRegex;
  23. public Template(string name, string appliesTo, string text, Template fileNameTemplateOrNull=null)
  24. {
  25. _name = name;
  26. _appliesTo = appliesTo == null ? new string[0] : appliesTo.Split(',');
  27. _tokenRegex = new Regex("\\<\\<([a-zA-Z_][a-zA-Z0-9_]*)(\\.[a-zA-Z_][a-zA-Z0-9_]*)?\\>\\>");
  28. _childTokenRegex = new Regex("{{([a-zA-Z_][a-zA-Z0-9_]*)(\\.[a-zA-Z_][a-zA-Z0-9_]*)?}}");
  29. _fileNameTemplateOrNull = fileNameTemplateOrNull;
  30. // Handle escpaing: If you want literal <<, then use \<<.
  31. // But if you really do want "\<<", then use "\\<<".
  32. _text = text.Replace("\\\\<<", "~~NOTESCAPED`_`TOKEN~~");
  33. _text = _text.Replace("\\\\{{", "~~NOTESCAPED`_`CHILD~~");
  34. _text = _text.Replace("\\<<", "~~ESCAPED`_`TOKEN~~");
  35. _text = _text.Replace("\\{{", "~~ESCAPED`_`CHILD~~");
  36. _text = _text.Replace("~~NOTESCAPED`_`TOKEN~~", "\\<<");
  37. _text = _text.Replace("~~NOTESCAPED`_`CHILD~~", "\\{{");
  38. _tokens = new Lazy<TextToken[]>(ParseForTokens);
  39. _childTokens = new Lazy<ChildToken[]>(ParseForChildTokens);
  40. }
  41. public TextToken[] Tokens
  42. {
  43. get { return _tokens.Value; }
  44. }
  45. public ChildToken[] ChildTokens
  46. {
  47. get { return _childTokens.Value; }
  48. }
  49. public string[] AppliesTo { get { return _appliesTo; } }
  50. public string Replace(ITokenSource tokenSource, ITemplateSource templateSource, IFileSystem fileSystem=null)
  51. {
  52. if (fileSystem == null)
  53. {
  54. fileSystem = Defaults.FileSystem;
  55. }
  56. string result = _text;
  57. foreach (var token in Tokens)
  58. {
  59. result = result.Replace("<<" + token.ToString() + ">>", tokenSource.GetAttribute(token));
  60. }
  61. foreach (var childToken in ChildTokens)
  62. {
  63. var template = templateSource.GetTemplate(_name + "." + childToken.TemplateName);
  64. StringBuilder replacement = new StringBuilder();
  65. List<ICodeGenerator> childCodeGenerators = new List<ICodeGenerator>();
  66. foreach (var codeGen in tokenSource.GetCodeGeneratorsForChildren(template._name, template.AppliesTo, templateSource))
  67. {
  68. childCodeGenerators.Add(codeGen);
  69. }
  70. foreach (var childCodeGen in tokenSource.TransformCodeGeneratorForChildToken(childCodeGenerators, childToken))
  71. {
  72. replacement.Append(childCodeGen.GenerateCode(template));
  73. }
  74. result = result.Replace(childToken.ToString(), replacement.ToString());
  75. }
  76. // Replace the tag with "<<" so that the end result will actually
  77. // be a token.
  78. result = result.Replace("~~ESCAPED`_`TOKEN~~", "<<");
  79. result = result.Replace("~~ESCAPED`_`CHILD~~", "{{");
  80. // And if we're supposed to write it out to a file, do that too.
  81. if (_fileNameTemplateOrNull != null)
  82. {
  83. string outputPath = _fileNameTemplateOrNull.Replace(tokenSource, templateSource, fileSystem);
  84. fileSystem.CreateDirectoryIfNotExists(Path.GetDirectoryName(outputPath));
  85. // .cs File? pretty print for C#.
  86. if (outputPath.ToLower().EndsWith(".cs"))
  87. {
  88. CSharpPrettyPrinter prettyPrinter = new CSharpPrettyPrinter();
  89. result = prettyPrinter.Prettify(result);
  90. }
  91. fileSystem.WriteFile(outputPath, result);
  92. }
  93. return result;
  94. }
  95. public string GetOutputFileNameOrNull(ITokenSource tokenSource, ITemplateSource templateSource, IFileSystem fileSystem)
  96. {
  97. if (_fileNameTemplateOrNull != null)
  98. {
  99. return _fileNameTemplateOrNull.Replace(tokenSource, templateSource, fileSystem);
  100. }
  101. else
  102. {
  103. return null;
  104. }
  105. }
  106. private TextToken[] ParseForTokens()
  107. {
  108. HashSet<TextToken> matches = new HashSet<TextToken>();
  109. foreach (Match match in _tokenRegex.Matches(_text))
  110. {
  111. matches.Add(new TextToken(match.Value.Substring(2, match.Value.Length-4)));
  112. }
  113. return matches.ToArray();
  114. }
  115. private ChildToken[] ParseForChildTokens()
  116. {
  117. HashSet<ChildToken> matches = new HashSet<ChildToken>();
  118. foreach (Match match in _childTokenRegex.Matches(_text))
  119. {
  120. matches.Add(new ChildToken(match.Value));
  121. }
  122. return matches.OrderBy(t => t).ToArray();
  123. }
  124. }
  125. }