这个是为了Micolog.Net的“主题预编译”制作的模版工具。主要负责简单的模版替换,以及在模版文件中调用C#方法。
Micolog.Net 的主题机制采用的是XML的数据+XSLT转码生成HTML。在一个博客系统中,有很多设置,比如博客名称等是不经常更换的,与其在每次生成html时都从XML中转换,不如直接在选择主题时直接对这些设置进行替换。考虑到主题应有的扩展性,这个模版机制采用“注入方法”的方式实现。
首先看一个最简单的例子:
var template = new Micolog.Common.Template.Simple(); template.LoadString("Hi,I'm {$Username}"); template.Parameters.Add("Username", "Soar、毅"); template.Process(); Console.WriteLine(template.ToString()); //将输出 Hi,I'm Soar、毅
其次,有时候,我们会需要对传入的参数格式化,比如输出按照一定格式的日期:
var template = new Micolog.Common.Template.Simple(); template.LoadString("Hi,I'm {$Username} And Now Is {$Now:yyyy年MM月dd日}"); template.Parameters.Add("Username", "Soar、毅"); template.Parameters.Add("Now", DateTime.Now); template.Process(); Console.WriteLine(template.ToString()); //将输出 Hi,I'm Soar、毅 And Now Is 2012年08月24日
在做到这一步的时候,这个简单的模版引擎算是差不多了。可是还缺少点什么:扩展。因为一个博客模版在安装完成后,可能需要进行一些自定义的设置。这些设置就要通过另一个方法来完成了。这个就是,动态的调用方法,通过传入不同的参数实现扩展,并且可以定义默认值:
var template = new Micolog.Common.Template.Simple(); template.LoadString("Hi,I'm {$Username} And Now Is {$Now:yyyy年MM月dd日}\n1 + 1 = {$Add(1,1)}\n{$NotDifine(1,1),默认值}"); template.Parameters.Add("Username", "Soar、毅"); template.Parameters.Add("Now", DateTime.Now); template.Methods.Add("Add", (arg) => { if (arg == null || arg.Length != 2) return "参数不正确!"; return (Convert.ToInt32(arg[0]) + Convert.ToInt32(arg[1])).ToString(); }); template.Process(); Console.WriteLine(template.ToString()); //将输出 //Hi,I'm Soar、毅 And Now Is 2012年08月24日 //1 + 1 = 2 //默认值
简单的引擎就是简单的引擎,不带有IF判断,For循环等。但是功能够用了。至少在Micolog.Net中够用了。模版采取的方式是正则表达式,除了,正则部分有点复杂之外,其他的还好。代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.IO; 5 using System.Text.RegularExpressions; 6 using NewLife.Reflection; 7 8 namespace Micolog.Common.Template 9 { 10 /// <summary> 11 /// 简易模板,实现简单的模板替换 12 /// </summary> 13 /// <remarks> 14 /// 简单变量替换:{$Date} 15 /// 格式变量替换:{$Date:yyyy-MM-dd} 16 /// 方法调用替换:{$GetDate(1,2,3),100}{$GetDate(1,2)} 17 /// 方法调用格式替换:{$GetDate(1,2,3):0.00,100}{$GetDate(1,2):0.00} 18 /// </remarks> 19 public class Simple 20 { 21 private static readonly Regex SimpleTagReg = new Regex(@"\{\$(\w+)\}"); 22 private static readonly Regex FormatTagReg = new Regex(@"\{\$(\w+):([\w|\.]+)\}"); 23 private static readonly Regex MethodTagReg = new Regex(@"\{\$(\w+)\(([\w|,]+)\),([\w|\.]+)}"); 24 private static readonly Regex MethodFormatTagReg = new Regex(@"\{\$(\w+)\(([\w|,]+)\):([\w|\.]+),([\w|\.]+)}"); 25 private static readonly Regex MethodNotDefaultTagReg = new Regex(@"\{\$(\w+)\(([\w|,]+)\)}"); 26 private static readonly Regex MethodFormatNotDefaultTagReg = new Regex(@"\{\$(\w+)\(([\w|,]+)\):([\w|\.]+)}"); 27 private StringBuilder template; 28 private String _Body; 29 /// <summary> 30 /// 模板内容 31 /// </summary> 32 public String Body 33 { 34 get 35 { 36 return _Body; 37 } 38 set 39 { 40 _Body = value; 41 this.template = new StringBuilder(value); 42 } 43 } 44 ///// <summary> 45 ///// 输出模板内容 46 ///// </summary> 47 ///// <returns></returns> 48 //public override string ToString() 49 //{ 50 // return this._Body; 51 //} 52 private Dictionary<String,Object> _Parameters; 53 /// <summary> 54 /// 编译参数 55 /// </summary> 56 public Dictionary<String,Object> Parameters 57 { 58 get 59 { 60 if (_Parameters == null) 61 { 62 lock (typeof(Simple)) 63 { 64 if (_Parameters == null) 65 { 66 _Parameters = new Dictionary<String, Object>(); 67 } 68 } 69 } 70 return _Parameters; 71 } 72 } 73 74 private Dictionary<String, Func<String[], Object>> _Methods; 75 /// <summary> 76 /// 编译方法 77 /// </summary> 78 public Dictionary<String, Func<String[], Object>> Methods 79 { 80 get 81 { 82 if (_Methods == null) 83 { 84 lock (typeof(Simple)) 85 { 86 _Methods = new Dictionary<String, Func<String[], Object>>(); 87 } 88 } 89 return _Methods; 90 } 91 } 92 93 /// <summary> 94 /// 载入模板文件 95 /// </summary> 96 /// <param name="path"></param> 97 public void Load(String path) 98 { 99 this.Load(path, Encoding.UTF8); 100 } 101 /// <summary> 102 /// 载入模板文件,并制定编码方式 103 /// </summary> 104 /// <param name="path"></param> 105 /// <param name="encoding"></param> 106 public void Load(String path, Encoding encoding) 107 { 108 using (var sr = new StreamReader(path, encoding)) 109 { 110 this._Body = sr.ReadToEnd(); 111 this.template = new StringBuilder(this._Body); 112 } 113 } 114 /// <summary> 115 /// 载入模板字符串 116 /// </summary> 117 /// <param name="body"></param> 118 public void LoadString(String body) 119 { 120 this._Body = body; 121 this.template = new StringBuilder(body); 122 } 123 124 /// <summary> 125 /// 编译模板 126 /// </summary> 127 public void Process() 128 { 129 //Regex 130 if (this._Parameters == null) return; 131 var map = new Dictionary<String,String>(); 132 //处理简单标签 133 foreach (Match match in SimpleTagReg.Matches(this._Body)) 134 { 135 var tag = match.Groups[0].Value; 136 var name = match.Groups[1].Value; 137 if (this._Parameters.ContainsKey(name) && !map.ContainsKey(tag)) 138 { 139 //如果参数中包含这个标签,就将这个标签加入缓存 140 map.Add(tag, this._Parameters[name].ToString()); 141 } 142 } 143 //处理格式化标签 144 foreach (Match match in FormatTagReg.Matches(this._Body)) 145 { 146 var tag = match.Groups[0].Value; 147 var name = match.Groups[1].Value; 148 var format = match.Groups[2].Value; 149 if (this._Parameters.ContainsKey(name) && !map.ContainsKey(tag)) 150 { 151 map.Add(tag, String.Format("{0:" + format + "}", this._Parameters[name])); 152 } 153 } 154 //处理方法标签 155 foreach (Match match in MethodTagReg.Matches(this._Body)) 156 { 157 var tag = match.Groups[0].Value; 158 var method = match.Groups[1].Value; 159 var parameters = match.Groups[2].Value; 160 var defaultValue = match.Groups[3].Value; 161 if (!map.ContainsKey(tag)) 162 { 163 if (this._Methods.ContainsKey(method)) 164 { 165 var value = this._Methods[method](parameters.Split(',')).ToString(); 166 map.Add(tag, value); 167 } 168 else 169 { 170 map.Add(tag, defaultValue); 171 } 172 } 173 } 174 foreach (Match match in MethodFormatTagReg.Matches(this._Body)) 175 { 176 var tag = match.Groups[0].Value; 177 var method = match.Groups[1].Value; 178 var parameters = match.Groups[2].Value; 179 var format = match.Groups[3].Value; 180 var defaultValue = match.Groups[4].Value; 181 if (!map.ContainsKey(tag)) 182 { 183 if (this._Methods.ContainsKey(method)) 184 { 185 var value = this._Methods[method](parameters.Split(',')); 186 map.Add(tag, String.Format("{0:" + format + "}", value)); 187 } 188 else 189 { 190 map.Add(tag, defaultValue); 191 } 192 } 193 } 194 foreach (Match match in MethodNotDefaultTagReg.Matches(this._Body)) 195 { 196 var tag = match.Groups[0].Value; 197 var method = match.Groups[1].Value; 198 var parameters = match.Groups[2].Value; 199 if (!map.ContainsKey(tag)) 200 { 201 if (this._Methods.ContainsKey(method)) 202 { 203 var value = this._Methods[method](parameters.Split(',')).ToString(); 204 map.Add(tag, value); 205 } 206 else 207 { 208 map.Add(tag, String.Empty); 209 } 210 } 211 } 212 foreach (Match match in MethodFormatNotDefaultTagReg.Matches(this._Body)) // .Matches(this._Body)) 213 { 214 var tag = match.Groups[0].Value; 215 var method = match.Groups[1].Value; 216 var parameters = match.Groups[2].Value; 217 var format = match.Groups[3].Value; 218 var defaultValue = match.Groups[4].Value; 219 if (!map.ContainsKey(tag)) 220 { 221 if (this._Methods.ContainsKey(method)) 222 { 223 var value = this._Methods[method](parameters.Split(',')); 224 map.Add(tag, String.Format("{0:" + format + "}", value)); 225 } 226 else 227 { 228 map.Add(tag, defaultValue); 229 } 230 } 231 } 232 foreach (var i in map) 233 { 234 this.template.Replace(i.Key, i.Value);//替换模板内容 235 } 236 } 237 /// <summary> 238 /// 保存处理结果 239 /// </summary> 240 /// <param name="path"></param> 241 public void SaveAs(String path) 242 { 243 this.SaveAs(path, Encoding.UTF8); 244 } 245 /// <summary> 246 /// 保存处理结果并制定编码方式 247 /// </summary> 248 /// <param name="path"></param> 249 /// <param name="encoding"></param> 250 public void SaveAs(String path, Encoding encoding) 251 { 252 if (this.template == null) throw new Exception("还未载入模板!"); 253 using (var sw = new StreamWriter(path, false, encoding)) 254 { 255 sw.Write(this.template.ToString()); 256 } 257 } 258 public override string ToString() 259 { 260 return this.template == null ? this._Body : this.template.ToString(); 261 } 262 } 263 }