/// <summary>
        /// Evaluate an argument list within the context of the enclosing
        /// template but store the values in the context of self, the
        /// new embedded template.  For example, bold(item=item) means
        /// that bold.item should get the value of enclosing.item.
        ///	</summary>
        protected internal virtual void evaluateArguments(StringTemplate self)
        {
            StringTemplateAST argumentsAST = self.getArgumentsAST();
            if (argumentsAST == null || argumentsAST.getFirstChild() == null)
            {
                // return immediately if missing tree or no actual args
                return ;
            }
            // Evaluate args in the context of the enclosing template, but we
            // need the predefined args like 'it', 'attr', and 'i' to be
            // available as well so we put a dummy ST between the enclosing
            // context and the embedded context.  The dummy has the predefined
            // context as does the embedded.
            StringTemplate enclosing = self.getEnclosingInstance();
            StringTemplate argContextST = new StringTemplate(self.getGroup(), "");
            argContextST.setName("<invoke "+self.getName()+" arg context>");
            argContextST.setEnclosingInstance(enclosing);
            argContextST.setArgumentContext(self.getArgumentContext());

            ActionEvaluator eval = new ActionEvaluator(argContextST, this, null);
            try
            {
                // using any initial argument context (such as when obj is set),
                // evaluate the arg list like bold(item=obj).  Since we pass
                // in any existing arg context, that context gets filled with
                // new values.  With bold(item=obj), context becomes:
                // {[obj=...],[item=...]}.
                IDictionary ac = eval.argList(argumentsAST, self, self.getArgumentContext());
                self.setArgumentContext(ac);
            }
            catch (RecognitionException re)
            {
                self.error("can't evaluate tree: " + argumentsAST.ToStringList(), re);
            }
        }
 protected internal virtual void evaluateArguments(StringTemplate self)
 {
     StringTemplateAST argumentsAST = self.getArgumentsAST();
     if (argumentsAST == null || argumentsAST.getFirstChild() == null)
     {
         // return immediately if missing tree or no actual args
         return ;
     }
     ActionEvaluator eval = new ActionEvaluator(self, this, null);
     try
     {
         // using any initial argument context (such as when obj is set),
         // evaluate the arg list like bold(item=obj).  Since we pass
         // in any existing arg context, that context gets filled with
         // new values.  With bold(item=obj), context becomes:
         // {[obj=...],[item=...]}.
         IDictionary ac = eval.argList(argumentsAST, self.getArgumentContext());
         self.setArgumentContext(ac);
     }
     catch (RecognitionException re)
     {
         self.error("can't evaluate tree: " + argumentsAST.ToStringList(), re);
     }
 }
        /// <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;
        }