StringTemplate.Net 学习笔记(9):深入了解模板组文件

上文展示了简单的模板组文件语法,本文来更详细的了解模板组文件的语法。

本文假设已经在应用程序启动时设置了默认的模板组loader(详细看上一篇):

	void Application_Start(object sender, EventArgs e)
	{
		StringTemplateGroup.RegisterGroupLoader(new LWMEGroupLoader(AppDomain.CurrentDomain.BaseDirectory, null));
	}

1、注释

这里的注释是指模板组文件的注释,它不同于模板文件的注释,仅仅用于在模板组文件内(模板之外),它们是:

  1. //单行注释
  2. /**/多行注释

示例:

	group test;
	//单行模板组文件注释
	/*多行
	模板组文件
	注释*/
	t1() ::= <<
		<!模板之内注释语法不变!>
	>>
	//单行模板组文件注释
	/*多行
	模板组文件
	注释*/
	t2() ::= <<
		<!模板之内注释语法不变!>
	>>

2、模板的内容换行及缩进

与模板文件一样,模板组内的模板同样会清除内容两边的空格。如:

	foo() ::= <<
	rodent
	>>

	foo() ::= "rodent"

是一样的。但是在多行模板中换行,它会保持换行符,如:

	foo() ::= <<
	
	2nd line is not blank, but first is
	>>

	foo() ::= <<<\n>
	same as before; newline then this line
	>>

再如:

	foo() ::= <<
	rodent
	
	>>

	foo() ::= <<
	rodent<\n>
	>>

此方法同样适用于模板文件,也可以通过实现IStringTemplateWriter接口或继承AutoIndentWriter类来定制模板内容的缩进。

3、模板参数

在模板定义中,每一个参数都必须明确的定义在参数列表中,否则在调用setAttribute的时候会出错,例如定义以下模板:

	group test;
	t1(): <<
		<arg>
	>>

当调用SetAttribute("arg", "test")时,会报错,不调用SetAttribute("arg", "test")则不会,感觉这个限制应该再严格一点。

参数对默认值的支持,这个与sql server的存储过程参数类似,定义如下模板组(bin\debug\templates\test.stg),当未传递arg2时使用arg2的默认值:

	group test;

	t1(arg1,arg2="参数2") ::= <<
		<arg1> <arg2>
	>>

当调用的代码为:

	StringTemplateGroup g = StringTemplateGroup.LoadGroup("Templates/test");
	StringTemplate st = g.GetInstanceOf("t1");
	st.SetAttribute("arg1", "参数1");
	st.SetAttribute("arg2", "参数2");
	Console.WriteLine(st.ToString());
	输出:参数1 参数2

当注释掉st.SetAttribute("arg2", "参数2"); 时:

	输出:参数1 参数2默认值

它同样支持从匿名模板获取默认值,把模板组定义改成:

	group test;

	t1(arg1, arg2={arg2默认值}) ::= <<
		<arg1> <arg2>
	>>

同样适用,但若仅仅只是这样的话倒是没太多意义,或许是我还没找到使用的方法。

4、模板作为参数

ST支持模板作为参数传递,定义模板组:

	group test;

	t1(arg1) ::= <<
		<arg1>
	>>
	
	t2(arg1) ::= <<
		<arg1>
	>>

调用代码:

	StringTemplateGroup g = StringTemplateGroup.LoadGroup("Templates/test");
	StringTemplate st1 = g.GetInstanceOf("t1");
	StringTemplate st2 = g.GetInstanceOf("t2");
	st1.SetAttribute("arg1", st2);
	st2.SetAttribute("arg1", "test");
	Console.WriteLine(st1.ToString());
	输出:	test

但假如把st2.SetAttribute("arg1", "test")改成st2.SetAttribute("arg1", st1),而且开启了LintMode时(StringTemplate.LintMode = true,在调试的时候使用的)将会引起递归调用,参考http://www.antlr.org/wiki/display/ST/Template+and+attribute+lookup+rules

5、Maps

模板组文件支持一种字典(key/value)类型,稍微有点不同的是它还定义一个默认值,先看例子(调用代码省略):

	group test;
	
	dict ::= [
		"int" : "0",
		"long" : "00",
		"float" : "0.0",
		"bool" : "false",
		"first" : "1",
		default : "null"
	]

	t1() ::= <<
		<dict.int> <dict.float> <dict.none> <dict.("first")>
	>>
	输出:0 0.0 null 1

从上面例子可以看出,当访问Maps不存在的key时,会返回它的默认值定义;对于保留字,它同样需要使用dict.("reserveword")来访问。

Maps也可以通过代码访问,它返回的是一个HashTable实例:

	StringTemplateGroup g = StringTemplateGroup.LoadGroup("Templates/test");			
	IDictionary map = g.GetMap("dict");
	foreach(DictionaryEntry entry in map) {
		Console.WriteLine("{0} {1}", entry.Key, entry.Value);
	}

输出:

	float 0.0
	first 1
	_default_ null
	bool false
	long 00
	int 0

既然是集合类型,那么同样能够在模板组文件里遍历:

	group test;
	
	mapIterator() ::= <<
	    <dict.keys:{key| 键:<key> 值:<dict.(key)>};separator=",">
	    <\n>
	    <dict.values:{value | <value>};separator=",">
	>>

输出:

    键:float 值:0.0,键:first 值:1,键:_default_ 值:null,键:bool 值:fa
lse,键:long 值:00,键:int 值:0

    0.0,1,null,false,00,0

不过,default也输出来了...

Maps是一个很好的特性,使用它可以轻松的实现多语言化,如:

中文界面

	dict ::= [
		"file" : "打开文件",
		"close" : "关闭文件",
		"exit" : "退出",
		default : "未定义"
	]

英文界面

	dict ::= [
		"file" : "open file",
		"close" : "close file",
		"exit" : "exit",
		default : "undefined"
	]

通过不同的区域设置来加载不同的语言文件。当然,还可以用于其他类似场景。

本篇就到此为止吧,继承及接口放到下篇。

本文地址:http://www.cnblogs.com/lwme/archive/2010/05/01/1725784.html

参考:http://www.antlr.org/wiki/display/ST/Group+Files

原文地址:https://www.cnblogs.com/lwme/p/1725784.html