zoukankan      html  css  js  c++  java
  • StringTemplate属性范围 模板引用

    属性范围
    StringTemplate的文档:
    Reference to attribute a in template t is resolved as follows:
    1. Look in t's attribute table 在自己的属性里面找
    2. Look in t's arguments 在参数里面找
    3. Look recursively up t's enclosing template instance chain 沿着模板嵌套链向上找
    4. Look recursively up t's group / supergroup chain for a map 在group/supergroup链的map里面搜索

    同一个模板,可能会被在多个模板中调用,那这个模板所需要的参数,需要慎重考虑如何设置。
    1. 在模板嵌套链的上一层、上上一层...设置,通过模板调用自然传递下来。
    2. 上一层模板使用参数方式传过来。
    属性查找采用就近原则,即在搜索路径上,找到第一个就停止。所以需要关注属性的传递、覆盖问题。
    StringTemplate里面有个Formal Argument的概念,还不清楚怎样使用,但可以肯定它跟属性的范围、传递相关的。

    一个属性范围相关问题的解决方案

    上面这样一个显示商品的html块,可以提取成一个模板item.st。当页面需要显示多个商品时怎么办?
    $items: {<div>$item()$</div>}$这样的方法看似能够解决问题,但是它只适用于在一排显示4个、5个商品这样的情况,如果我需要在页面的不同位置显示这几个商品呢?
    我的做法是提供一个界面定义item1.st、item2.st...,他们都使用item.st这个模板,并且让用户选择一个要显示的商品。item1.st、item2.st...的内容: $item()$,它们都只是调用一下item.st这个模板,程序根据item1关联的商品,可以把这个商品加载出来,把商品属性设置在item1上。这样,在开发时就可以在界面的任意位置使用类似$item1()$、$item2()$这样的方法,显示不同的商品了。
    这种情况下,item.st类似于一个类,而item1.st、item2.st等类似于类的不同实例了。

    模板引用
    创建一个StringTemplateGroup后,他会集中管理各个StringTemplate对象,StringTemplateGroup内部使用一个Hashtable缓存已经加载、定义的ST对象。
    group.LookupTemplate(string name): 直接加载ST对象,或者从缓存中获取,将ST对象的直接引用返回给调用者。
    group.GetInstanceOf(string name): 加载ST对象,或者从缓存中获取,将ST对象作一份DeepCopy(不包括Attributes、EnclosingInstance)返回给调用者。
    这个区别就很明显了,如果对LookupTemplate返回的对象进行修改,StringTemplateGroup内部缓存的那个也被修改了;而GetInstanceOf则不会。有的时候你需要使用DeepCopy的实例,而有的时候需要直接使用缓存的那个,根据具体情况决定使用哪个方法了。

    看一下这样一个需求:
    a.st: $b()$
    b.st: $c()$ - $attrB$
    c.st: $attrC$
    工作代码类似这样:
    StringTemplateGroup group = new StringTemplateGroup("test"@"c:\test");
    StringTemplate a 
    = group.GetInstanceOf("a");
    StringTemplate b 
    = group.LookupTemplate("b");
    StringTemplate c 
    = group.LookupTemplate("c");
    b.SetAttribute(
    "attrB","test attr B");
    b.SetAttribute(
    "attrC","test attr C");
    Assert.AreEqual(
    "test attr C - test attr B", a.ToString());
    这段代码无法得到预期的结果,因为StringTemplate在a.ToString()方法解析过程中,使用GetInstanceOf方法获取b.st、c.st这两个模板对象,也就是使用了缓存对象的一个DeepCopy版本,没有包含Attributes。
    在这个简单的例子里面,可以在用a.SetAttribute()设置attrB、attrC属性来解决。不过我的情况比较复杂,必须要改变
    StringTemplate对象的这个行为,所以我修改了原代码来实现。
    StringTemplateGroup.cs里面
    public virtual StringTemplate GetEmbeddedInstanceOf(StringTemplate enclosingInstance, string name)
    修改成
    public virtual StringTemplate GetEmbeddedInstanceOf(StringTemplate enclosingInstance, string name)
    {
        StringTemplate st 
    = null;
        
    // TODO: seems like this should go into LookupTemplate
        if (name.StartsWith("super."))
        {
            
    // for super.foo() refs, ensure that we look at the native
            
    // group for the embedded instance not the current evaluation
            
    // group (which is always pulled down to the original group
            
    // from which somebody did group.getInstanceOf("foo");
            
    //modified by RicCC, 08.22.2007
            
    //st = enclosingInstance.NativeGroup.GetInstanceOf(enclosingInstance, name);
            st = enclosingInstance.NativeGroup.LookupTemplate(enclosingInstance, name);
        }
        
    else
        {
            
    //modified by RicCC, 08.22.2007
            
    //st = GetInstanceOf(enclosingInstance, name);
            st = LookupTemplate(enclosingInstance, name);
        }
        
    // make sure all embedded templates have the same group as enclosing
        
    // so that polymorphic refs will start looking at the original group
        st.Group = this;
        st.EnclosingInstance 
    = enclosingInstance;
        
    return st;
    }
    方法里面的TODO注释也说明了作者在考虑应该使用LookupTemplate方法,应当是TODO忘了Review了,否则也不应当再把这个TODO留在这里。

    看源代码发现的另外一个问题
    StringTemplate会监控*.st文件,如果文件有修改,会重新加载相应的ST对象,用它把原来内部缓存的对象给覆盖掉。假如你为内部缓存的对象设置好属性,调用StringTemplate开始解析,这个时候st文件被修改了,那么设置的属性就被丢弃掉了。不过模板被修改,对当前的解析过程肯定有影响;不监控模板文件的变化,也不合适。我根据自己的情况,还是选择在覆盖的时候,把原来的属性copy过来,这样符合我的需求。
    把StringTemplateGroup.cs文件下面的方法改成这样就可以了
    public virtual StringTemplate LookupTemplate(StringTemplate enclosingInstance, string name)
    {
        
    lock (this)
        {
            
    if (name.StartsWith("super."))
            {
                
    if (superGroup != null)
                {
                    
    int dot = name.IndexOf('.');
                    name 
    = name.Substring(dot + 1, (name.Length) - (dot + 1));
                    StringTemplate superScopeST 
    =
                        superGroup.LookupTemplate(enclosingInstance, name);
                    
    return superScopeST;
                }
                
    throw new StringTemplateException(Name + " has no super group; invalid template: " + name);
            }
            StringTemplate st 
    = (StringTemplate)templates[name];
            
    //added by RicCC, 08.20.2007
            IDictionary originalAttributes = null;  //added
            if (st != null)
            {
                
    // Discard cached template?
                if (st.NativeGroup.TemplateHasChanged(name))
                {
                    
    //added by RicCC, 08.20.2007, discard the cached template, but save it's attributes
                    originalAttributes = st.attributes;   //added
                    templates.Remove(name);
                    st 
    = null;
                }
            }
            
    if (st == null)
            {
                
    // not there?  Attempt to load
                if (!templatesDefinedInGroupFile)
                {
                    
    // only check the disk for individual template
                    st = LoadTemplate(name);
                }
                
    if (st == null && superGroup != null)
                {
                    
    // try to resolve in super group
                    
    //st = superGroup.GetInstanceOf(name);
                    st = superGroup.GetInstanceOf(enclosingInstance, name);
                    
    // make sure that when we inherit a template, that it's
                    
    // group is reset; it's nativeGroup will remain where it was
                    if (st != null)
                    {
                        st.Group 
    = this;
                    }
                }
                
    if (st != null)
                {
                    
    // found in superGroup
                    
    // insert into this group; refresh will allow super
                    
    // to change it's def later or this group to add
                    
    // an override.
                    
    //added by RicCC, 08.20.2007
                    if (originalAttributes != null)
                        st.attributes 
    = originalAttributes;   //added, approach this by invoking SetAttribute() method?
                    templates[name] = st;
                }
                
    else
                {
                    
    // not found; remember that this sucker doesn't exist
                    templates[name] = NOT_FOUND_ST;
                    
    string context = "";
                    
    if (enclosingInstance != null)
                    {
                        context 
    = "; context is " + enclosingInstance.GetEnclosingInstanceStackString();
                    }
                    
    throw new TemplateLoadException(this"Can't load template '" + GetLocationFromTemplateName(name) + "'" + context);
                }
            }
            
    else if (st == NOT_FOUND_ST)
            {
                
    return null;
            }
            
    return st;
        }
    }

    其它问题

    从StringTemplate.Net源代码的一些主要处理过程来看,里面是存在不少缺陷的,一些处理方案比较粗糙,考虑的不够成熟完善,可能给系统带来隐患问题,特别是多线程的并发、冲突等方面的控制,基本就看不到一个全局清晰的解决思路。
    所以,原来还以为可以全局共享同一个StringTemplateGroup对象,现在只能改成每个请求需要使用时,就新开一个StringTemplateGroup了,毕竟这样安全。

    这一次在架构之前没有很好的掌握StringTemplate,吃了一点亏(加班、核心架构做了一次重大调整)。StringTemplate本身文档很少,要全面掌握它的使用方法,必须结合源代码,投入的精力比较大。
    不过它的思想还是非常不错,一个开源项目要成熟下来,是需要大量的投入和时间,以及实践反馈的。基于这样一个状况,采用BSD协议也是它最好的一个选择了。现在看来,使用它解决我当前项目的问题,实在是最合适不过了,框架的核心部分简洁而又行云流水,扩展性非常强,StringTemplate本身的一些问题,后续再慢慢完善,也是非常值得的。
  • 相关阅读:
    LeetCode 811. Subdomain Visit Count (子域名访问计数)
    LeetCode 884. Uncommon Words from Two Sentences (两句话中的不常见单词)
    LeetCode 939. Minimum Area Rectangle (最小面积矩形)
    LeetCode 781. Rabbits in Forest (森林中的兔子)
    LeetCode 739. Daily Temperatures (每日温度)
    三种方式实现按钮的点击事件
    239. Sliding Window Maximum
    14.TCP的坚持定时器和保活定时器
    13.TCP的超时与重传
    12.TCP的成块数据流
  • 原文地址:https://www.cnblogs.com/RicCC/p/863249.html
Copyright © 2011-2022 走看看