• StringTemplate.Net 学习笔记(8):加载模板组文件


    1、简介

    由于需要的呈现方式越来越多,模板文件(.st)也会随着增多,当数量达到一定程度时,对它们的管理将成为一种麻烦,而且频繁的从硬盘加载也不利于性能。

    ST2.0引入了模板组文件(.stg),它有2个主要特点:

    1. 可以把多个模板定义在一个模板组文件里(如果把模板文件比作method的话,那么模板组就是class,有点OO的概念)
    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种模板组表达式分隔符:

    1. "…",模板内容为单行时
    2. <<…>>,模板内容为多行时

    模板组文件表达式分隔符"…"和<<…>>由Antlr3.ST.Language.GroupLexer定义(分别是mSTRING()、mBIGSTRING()方法),如果不想使用这个分隔符,则可以通过继承Antlr.Runtime.Lexer来实现自定义的分隔符。

    3、加载

    与模板文件相同,模板组文件同样通过2种方式加载:

    1. 从绝对路径加载,由Antlr3.ST.PathGroupLoader实现
    2. 从程序集嵌入资源中加载,由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

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

  • 相关阅读:
    (转载)C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
    网络编程前奏(三)
    Linux学习之第三课时--Linux目录
    Linux学习之第二课时--linux命令格式及命令概述
    Linux学习之第一课时--linux前生今世
    python学习之第十四课时--基本数据练习
    python学习之第十三课时--其他数据类型,其他
    python学习之第十二课时--基本数据类型(set)
    python学习之第十一课时--基本数据类型(dict)
    python学习之第十课时--基本数据类型(tuple)
  • 原文地址:https://www.cnblogs.com/lwme/p/1725723.html
Copyright © 2020-2023  润新知