/// <summary>Resolve an attribute reference. It can be in three possible places: /// /// 1. the attribute list for the current template /// 2. if self is an embedded template, somebody invoked us possibly /// with arguments--check the argument context /// 3. if self is an embedded template, the attribute list for the enclosing /// instance (recursively up the enclosing instance chain) /// /// Attribute references are checked for validity. If an attribute has /// a value, its validity was checked before template rendering. /// If the attribute has no value, then we must check to ensure it is a /// valid reference. Somebody could reference any random value like $xyz$; /// formal arg checks before rendering cannot detect this--only the ref /// can initiate a validity check. So, if no value, walk up the enclosed /// template tree again, this time checking formal parameters not /// attributes Map. The formal definition must exist even if no value. /// /// To avoid infinite recursion in toString(), we have another condition /// to check regarding attribute values. If your template has a formal /// argument, foo, then foo will hide any value available from "above" /// in order to prevent infinite recursion. /// /// This method is not static so people can overrided functionality. /// </summary> public virtual Object get(StringTemplate self, String attribute) { //System.out.println("get("+self.getName()+", "+attribute+")"); if (self == null) { return null; } if (lintMode) { self.trackAttributeReference(attribute); } // is it here? Object o = null; if (self.attributes != null) { o = self.attributes[attribute]; } // nope, check argument context in case embedded if (o == null) { IDictionary argContext = self.getArgumentContext(); if (argContext != null) { o = argContext[attribute]; } } if (o == null && self.getFormalArgument(attribute) != null) { // if you've defined attribute as formal arg for this // template and it has no value, do not look up the // enclosing dynamic scopes. This avoids potential infinite // recursion. return null; } // not locally defined, check enclosingInstance if embedded if (o == null && self.enclosingInstance != null) { /* System.out.println("looking for "+getName()+"."+attribute+" in super="+ enclosingInstance.getName()); */ Object valueFromEnclosing = get(self.enclosingInstance, attribute); if (valueFromEnclosing == null) { checkNullAttributeAgainstFormalArguments(self, attribute); } o = valueFromEnclosing; } return o; }
/// <summary> /// Argument evaluation such as foo(x=y), x must /// be checked against foo's argument list not this's (which is /// the enclosing context). So far, only eval.g uses arg self as /// something other than "this". /// </summary> public virtual void rawSetArgumentAttribute(StringTemplate embedded, IDictionary attributes, String name, Object value) { if ( embedded.formalArguments!=FormalArgument.UNKNOWN && embedded.getFormalArgument(name)==null ) { throw new ArgumentOutOfRangeException("template "+embedded.getName()+ " has no such attribute: "+name+ " in template context "+ getEnclosingInstanceStackString()); } if ( value == null ) { return; } attributes[name] = value; }