1、简介
由于需要的呈现方式越来越多,模板文件(.st)也会随着增多,当数量达到一定程度时,对它们的管理将成为一种麻烦,而且频繁的从硬盘加载也不利于性能。
ST2.0引入了模板组文件(.stg),它有2个主要特点:
- 可以把多个模板定义在一个模板组文件里(如果把模板文件比作method的话,那么模板组就是class,有点OO的概念)
- 提供了对模板参数的检测(所有参数都必须在模板参数列表中列出,否则会报错),使模板更加容易阅读
有一点需要注意:模板组文件必须包含至少一个成员(模板组文件只有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种方式加载:
- 从绝对路径加载,由Antlr3.ST.PathGroupLoader实现
- 从程序集嵌入资源中加载,由Antlr3.ST.CommonGroupLoader实现,同时CommonGroupLoader支持从绝对路径及相对路径加载
话说模板文件的加载由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