1、简介
由于需要的呈现方式越来越多,模板文件(.st)也会随着增多,当数量达到一定程度时,对它们的管理将成为一种麻烦,而且频繁的从硬盘加载也不利于性能。
ST2.0引入了模板组文件(.stg),它有2个主要特点:
有一点需要注意:模板组文件必须包含至少一个成员(模板组文件只有2种成员:模板、Maps),否则会报错。
2、定义模板
下面来看一下模板在模板组文件(存放在bin\debug\templates\test.stg,在后面使用)的简单语法:
group test; t1(title) ::= "<title>" t2(title) ::= << <title> >>
在第一行指定模板的名称(必须的),后面的t1、t2分别为此模板组文件里的2个模板。
从上面示例可以看出,在模板组中定义模板文件语法为 templateName(args) ::= "templateContent",假如没有参数,那么args可以为空。
模板组文件提供了2种模板组表达式分隔符:
模板组文件表达式分隔符"…"和<<…>>由Antlr3.ST.Language.GroupLexer定义(分别是mSTRING()、mBIGSTRING()方法),如果不想使用这个分隔符,则可以通过继承Antlr.Runtime.Lexer来实现自定义的分隔符。
3、加载
与模板文件相同,模板组文件同样通过2种方式加载:
话说模板文件的加载由StringTemplateGroup直接实现,而模板组文件通过实现IStringTemplateGroupLoader接口的类型来加载,实在是比较奇怪的设计。
StringTemplateGroup提供了一个RegisterGroupLoader方法,为模板组文件设置默认加载器,先看一下它的实现:
private static IStringTemplateGroupLoader _groupLoader; public static void RegisterGroupLoader(IStringTemplateGroupLoader loader) { _groupLoader = loader; }
非常的简单,直接把loader实例赋给_groupLoader这个静态变量,将在全局共享这个loader。因此,在实际应用中只需要在应用程序启动如Application_Start方法中设置一个loader,以后就可以直接使用StringTemplateGroup.LoadGroup这个静态方法来加载模板组文件,但若是使用了继承、接口等东西会有一些问题,将在后续章节中讲到。
例子:
void Application_Start(object sender, EventArgs e) { StringTemplateGroup.RegisterGroupLoader(new PathGroupLoader(AppDomain.CurrentDomain.BaseDirectory, null)); }
StringTemplateGroup group = StringTemplateGroup.LoadGroup("Templates/test"); StringTemplate st1 = group.GetInstanceOf("t1"); StringTemplate st2 = group.GetInstanceOf("t2"); st1.SetAttribute("title", "模板t1被调用"); st2.SetAttribute("title", "模板t2被调用"); Console.WriteLine(st1.ToString()); Console.WriteLine(st2.ToString());
当然也可以另外创建loader实例来加载:
PathGroupLoader p = new PathGroupLoader(AppDomain.CurrentDomain.BaseDirectory + "Templates", null); StringTemplateGroup group = p.LoadGroup("test"); StringTemplate st1 = group.GetInstanceOf("t1"); StringTemplate st2 = group.GetInstanceOf("t2"); st1.SetAttribute("title", "模板t1被调用"); st2.SetAttribute("title", "模板t2被调用"); Console.WriteLine(st1.ToString()); Console.WriteLine(st2.ToString());
输出:模板t1被调用 模板t2被调用
看了一下PathGroupLoader的实现,发现如果使用PathGroupLoader(IStringTemplateErrorListener errors)这个构造函数创建PathGroupLoader实例,那么在调用LoadGroup方法的时候将会出错,_dirs未初始化,这个问题也影响到CommonGroupLoader:
public PathGroupLoader(IStringTemplateErrorListener errors) { this._fileCharEncoding = Encoding.Default; this._errors = errors; } public PathGroupLoader(string dirStr, IStringTemplateErrorListener errors) { this._fileCharEncoding = Encoding.Default; this._errors = errors; this._dirs = dirStr.Split(new char[] { ':' }); } protected virtual TextReader Locate(string name) { for (int i = 0; i < this._dirs.Length; i++) { string dir = this._dirs[i]; string fileName = dir + "/" + name; if (File.Exists(fileName)) { FileStream fis = File.OpenRead(fileName); return this.GetInputStreamReader(new BufferedStream(fis)); } } return null; }
那么重写这些方法吧:
/// <summary> /// 模板组文件加载器 /// </summary> class LWMEGroupLoader : PathGroupLoader { public LWMEGroupLoader(): base(null) {} public LWMEGroupLoader(IStringTemplateErrorListener errors): base(errors) {} public LWMEGroupLoader(string rootDir) : this(rootDir, null) {} public LWMEGroupLoader(string rootDir, IStringTemplateErrorListener errors): base(errors) { if (!string.IsNullOrEmpty(rootDir)) this._dirs = new string[]{ rootDir }; } public LWMEGroupLoader(string[] rootDirs) : this(rootDirs, null) {} public LWMEGroupLoader(string[] rootDirs, IStringTemplateErrorListener errors) : base(errors) { if (rootDirs != null && rootDirs.Length > 0) this._dirs = rootDirs; } /// <summary> /// 从文件加载内容 /// </summary> /// <param name="name">文件路径</param> private TextReader LoadFromFile(string name) { if (File.Exists(name)) { FileStream fs = File.OpenRead(name); if (fs != null) return this.GetInputStreamReader(new BufferedStream(fs)); } return null; } protected override TextReader Locate(string name) { TextReader txtReader = LoadFromFile(name); if (txtReader == null && this._dirs != null && !Path.IsPathRooted(name)) { for (int i = 0; i < this._dirs.Length; i++) { string dir = this._dirs[i]; string fileName = dir + "/" + name; txtReader = LoadFromFile(fileName); if (txtReader != null) break; } } return txtReader; } }
那么现在它看起来稍微好一点了:
void Application_Start(object sender, EventArgs e) { StringTemplateGroup.RegisterGroupLoader(new LWMEGroupLoader(AppDomain.CurrentDomain.BaseDirectory, null)); }
StringTemplateGroup g1 = StringTemplateGroup.LoadGroup("Templates/test"); StringTemplateGroup g2 = StringTemplateGroup.LoadGroup(AppDomain.CurrentDomain.BaseDirectory + "Templates/test"); Console.WriteLine(g1 == null); Console.WriteLine(g2 == null);
输出:False False
最后,建议模板组名称与模板组文件名称一致,否则可能引起怪异的问题,后续文章将会讲到。
本文地址:http://www.cnblogs.com/lwme/archive/2010/05/01/1725723.html