这个是为了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 }