Guest User

Untitled

a guest
Jul 23rd, 2018
79
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.54 KB | None | 0 0
  1. namespace TinyTemplates.ViewEngine
  2. {
  3. using System;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Dynamic;
  7. using System.Linq;
  8. using System.Reflection;
  9. using System.Text.RegularExpressions;
  10.  
  11. /// <summary>
  12. /// A super-simple view engine
  13. /// </summary>
  14. public class SuperSimpleViewEngine
  15. {
  16. /// <summary>
  17. /// Compiled Regex for single substitutions
  18. /// </summary>
  19. private Regex singleSubstitutionsRegEx = new Regex(@"@Model\.(?<ParameterName>[a-zA-Z0-9-_]*)", RegexOptions.Compiled);
  20.  
  21. /// <summary>
  22. /// Compiled Regex for each blocks
  23. /// </summary>
  24. private Regex eachSubstitutionRegEx = new Regex(@"@Each\.(?<ParameterName>[a-zA-Z0-9-_]*)(?<Contents>.*?)@EndEach", RegexOptions.Compiled | RegexOptions.Singleline);
  25.  
  26. /// <summary>
  27. /// Compiled Regex for each block current substitutions
  28. /// </summary>
  29. private Regex eachItemSubstitutionRegEx = new Regex("@Current", RegexOptions.Compiled);
  30.  
  31. /// <summary>
  32. /// Compiled Regex for if blocks
  33. /// </summary>
  34. private Regex ifSubstitutionRegEx = new Regex(@"@If\.(?<ParameterName>[a-zA-Z0-9-_]*)(?<Contents>.*?)@EndIf", RegexOptions.Compiled | RegexOptions.Singleline);
  35.  
  36. /// <summary>
  37. /// Compiled Regex for ifnot blocks
  38. /// </summary>
  39. private Regex ifNotSubstitutionRegEx = new Regex(@"@IfNot\.(?<ParameterName>[a-zA-Z0-9-_]*)(?<Contents>.*?)@EndIf", RegexOptions.Compiled | RegexOptions.Singleline);
  40.  
  41. /// <summary>
  42. /// View engine transform processors
  43. /// </summary>
  44. private List<Func<string, object, Func<object, string, object>, string>> processors;
  45.  
  46. /// <summary>
  47. /// Initializes a new instance of the <see cref="SuperSimpleViewEngine"/> class.
  48. /// </summary>
  49. public SuperSimpleViewEngine()
  50. {
  51. this.processors = new List<Func<string, object, Func<object, string, object>, string>>()
  52. {
  53. this.PerformSingleSubstitutions,
  54. this.PerformEachSubstitutions,
  55. this.PerformConditionalSubstitutions,
  56. };
  57. }
  58.  
  59. /// <summary>
  60. /// Renders a template
  61. /// </summary>
  62. /// <param name="template">
  63. /// The template to render.
  64. /// </param>
  65. /// <param name="model">
  66. /// The model to user for rendering.
  67. /// </param>
  68. /// <returns>
  69. /// A string containing the expanded template.
  70. /// </returns>
  71. public string Render(string template, dynamic model)
  72. {
  73. var propertyExtractor = this.GetPropertyExtractor(model);
  74.  
  75. return this.processors.Aggregate(template, (current, processor) => processor(current, model, propertyExtractor));
  76. }
  77.  
  78. /// <summary>
  79. /// <para>
  80. /// Gets the correct property extractor for the given model.
  81. /// </para>
  82. /// <para>
  83. /// Anonymous types, standard types and ExpandoObject are supported.
  84. /// Arbitrary dynamics (implementing IDynamicMetaObjectProvicer) are not, unless
  85. /// they also implmennt IDictionary string, object for accessing properties.
  86. /// </para>
  87. /// </summary>
  88. /// <param name="model">
  89. /// The model.
  90. /// </param>
  91. /// <returns>
  92. /// Delegate for getting properties - delegate returns a value or null if not there.
  93. /// </returns>
  94. /// <exception cref="ArgumentException">
  95. /// Model type is not supported.
  96. /// </exception>
  97. private Func<object, string, object> GetPropertyExtractor(object model)
  98. {
  99. if (!typeof(IDynamicMetaObjectProvider).IsAssignableFrom(model.GetType()))
  100. {
  101. return this.GetStandardTypePropertyExtractor(model);
  102. }
  103.  
  104. if (typeof(IDictionary<string, object>).IsAssignableFrom(model.GetType()))
  105. {
  106. return this.DynamicDictionaryPropertyExtractor;
  107. }
  108.  
  109. throw new ArgumentException("model must be a standard type or implement IDictionary<string, object>", "model");
  110. }
  111.  
  112. /// <summary>
  113. /// <para>
  114. /// Returns the standard property extractor.
  115. /// </para>
  116. /// <para>
  117. /// Model properties are enumerated once and a closure is returned that captures them.
  118. /// </para>
  119. /// </summary>
  120. /// <param name="model">
  121. /// The model.
  122. /// </param>
  123. /// <returns>Delegate for getting properties - delegate returns a value or null if not there.</returns>
  124. private Func<object, string, object> GetStandardTypePropertyExtractor(object model)
  125. {
  126. var properties = model.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
  127.  
  128. return (mdl, propName) =>
  129. {
  130. var property =
  131. properties.Where(p => String.Equals(p.Name, propName, StringComparison.InvariantCulture)).
  132. FirstOrDefault();
  133.  
  134. return property == null ? null : property.GetValue(mdl, null);
  135. };
  136. }
  137.  
  138. /// <summary>
  139. /// A property extractor designed for ExpandoObject, but also for any
  140. /// type that implements IDictionary string object for accessing its
  141. /// properties.
  142. /// </summary>
  143. /// <param name="model">
  144. /// The model.
  145. /// </param>
  146. /// <param name="propertyName">
  147. /// The property name.
  148. /// </param>
  149. /// <returns>
  150. /// Object if property is found, null if not.
  151. /// </returns>
  152. private object DynamicDictionaryPropertyExtractor(object model, string propertyName)
  153. {
  154. var dictionaryModel = (IDictionary<string, object>)model;
  155.  
  156. object output;
  157. dictionaryModel.TryGetValue(propertyName, out output);
  158.  
  159. return output;
  160. }
  161.  
  162. /// <summary>
  163. /// Performs single @Model.PropertyName substitutions.
  164. /// </summary>
  165. /// <param name="template">
  166. /// The template.
  167. /// </param>
  168. /// <param name="model">
  169. /// The model.
  170. /// </param>
  171. /// <param name="propertyExtractor">
  172. /// The property extractor.
  173. /// </param>
  174. /// <returns>
  175. /// Template with @Model.PropertyName blocks expanded
  176. /// </returns>
  177. private string PerformSingleSubstitutions(string template, object model, Func<object, string, object> propertyExtractor)
  178. {
  179. return this.singleSubstitutionsRegEx.Replace(
  180. template,
  181. (m) =>
  182. {
  183. var substitution = propertyExtractor(model, m.Groups["ParameterName"].Value);
  184.  
  185. return substitution == null ? "[ERR!]" : substitution.ToString();
  186. });
  187. }
  188.  
  189. /// <summary>
  190. /// Performs @Each.PropertyName substitutions
  191. /// </summary>
  192. /// <param name="template">
  193. /// The template.
  194. /// </param>
  195. /// <param name="model">
  196. /// The model.
  197. /// </param>
  198. /// <param name="propertyExtractor">
  199. /// The property extractor.
  200. /// </param>
  201. /// <returns>
  202. /// Template with @Each.PropertyName blocks expanded
  203. /// </returns>
  204. private string PerformEachSubstitutions(string template, object model, Func<object, string, object> propertyExtractor)
  205. {
  206. return this.eachSubstitutionRegEx.Replace(
  207. template,
  208. (m) =>
  209. {
  210. var substitutionObject = propertyExtractor(model, m.Groups["ParameterName"].Value);
  211.  
  212. if (substitutionObject == null)
  213. {
  214. return "[ERR!]";
  215. }
  216.  
  217. var substitutionEnumerable = substitutionObject as IEnumerable;
  218. if (substitutionEnumerable == null)
  219. {
  220. return "[ERR!]";
  221. }
  222.  
  223. var result = string.Empty;
  224. foreach (var item in substitutionEnumerable)
  225. {
  226. result += eachItemSubstitutionRegEx.Replace(m.Groups["Contents"].Value, item.ToString());
  227. }
  228.  
  229. return result;
  230. });
  231. }
  232.  
  233. /// <summary>
  234. /// Performs @If.PropertyName and @IfNot.PropertyName substitutions
  235. /// </summary>
  236. /// <param name="template">
  237. /// The template.
  238. /// </param>
  239. /// <param name="model">
  240. /// The model.
  241. /// </param>
  242. /// <param name="propertyExtractor">
  243. /// The property extractor.
  244. /// </param>
  245. /// <returns>
  246. /// Template with @If.PropertyName @IfNot.PropertyName blocks removed/expanded
  247. /// </returns>
  248. private string PerformConditionalSubstitutions(string template, object model, Func<object, string, object> propertyExtractor)
  249. {
  250. var result = template;
  251.  
  252. result = this.ifSubstitutionRegEx.Replace(
  253. result,
  254. (m) =>
  255. {
  256. var predicateResult = false;
  257. var substitutionObject = propertyExtractor(model, m.Groups["ParameterName"].Value);
  258.  
  259. if (substitutionObject != null)
  260. {
  261. var substitutionBool = substitutionObject as bool?;
  262. if (substitutionBool != null)
  263. {
  264. predicateResult = substitutionBool.Value;
  265. }
  266. }
  267.  
  268. return !predicateResult ? String.Empty : m.Groups["Contents"].Value;
  269. });
  270.  
  271. result = this.ifNotSubstitutionRegEx.Replace(
  272. result,
  273. (m) =>
  274. {
  275. var predicateResult = true;
  276. var substitutionObject = propertyExtractor(model, m.Groups["ParameterName"].Value);
  277.  
  278. if (substitutionObject != null)
  279. {
  280. var substitutionBool = substitutionObject as bool?;
  281. if (substitutionBool != null)
  282. {
  283. predicateResult = !substitutionBool.Value;
  284. }
  285. }
  286.  
  287. return !predicateResult ? String.Empty : m.Groups["Contents"].Value;
  288. });
  289.  
  290. return result;
  291. }
  292. }
  293. }
Add Comment
Please, Sign In to add comment