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); } }
protected internal virtual int write(StringTemplate self, Object o, StringTemplateWriter outWriter, Object separator) { if (o == null) { return 0; } int n = 0; try { if (o is StringTemplate) { StringTemplate stToWrite = (StringTemplate) o; stToWrite.setEnclosingInstance(self); // if self is found up the enclosing instance chain, then // infinite recursion if (StringTemplate.inLintMode() && StringTemplate.isRecursiveEnclosingInstance(stToWrite)) { // throw exception since sometimes eval keeps going // even after I ignore this write of o. throw new System.SystemException("infinite recursion to " + stToWrite.getTemplateDeclaratorString() + " referenced in " + stToWrite.getEnclosingInstance().getTemplateDeclaratorString() + "; stack trace:\n" + stToWrite.getEnclosingInstanceStackTrace()); } else { n = stToWrite.write(outWriter); } return n; } // normalize anything iteratable to iterator o = convertAnythingIteratableToIterator(o); if (o is IEnumerator) { IEnumerator iter = (IEnumerator) o; String separatorString = null; if (separator != null) { separatorString = computeSeparator(self, outWriter, separator); } int i = 0; int charWrittenForValue = 0; Object iterValue = null; while (iter.MoveNext()) { if (i > 0) { bool valueIsPureConditional = false; if (iterValue is StringTemplate) { StringTemplate iterValueST = (StringTemplate) iterValue; IList chunks = (IList) iterValueST.getChunks(); Expr firstChunk = (Expr) chunks[0]; valueIsPureConditional = firstChunk is ConditionalExpr && ((ConditionalExpr) firstChunk).getElseSubtemplate() == null; } bool emptyIteratedValue = valueIsPureConditional && charWrittenForValue == 0; if (!emptyIteratedValue && separator != null) { n += outWriter.write(separatorString); } } iterValue = iter.Current; charWrittenForValue = write(self, iterValue, outWriter, separator); n += charWrittenForValue; i++; } } else { n = outWriter.write(o.ToString()); return n; } } catch (System.IO.IOException io) { self.error("problem writing object: " + o, io); } return n; }
/// <summary>To write out the value of an ASTExpr, invoke the evaluator in eval.g /// to walk the tree writing out the values. For efficiency, don't /// compute a bunch of strings and then pack them together. Write out directly. /// </summary> public override int write(StringTemplate self, StringTemplateWriter outWriter) { if (exprTree == null || self == null || outWriter == null) { return 0; } outWriter.pushIndentation(getIndentation()); //System.out.println("evaluating tree: "+exprTree.toStringList()); ActionEvaluator eval = new ActionEvaluator(self, this, outWriter); int n = 0; try { n = eval.action(exprTree); // eval and write out tree } catch (RecognitionException re) { self.error("can't evaluate tree: " + exprTree.ToStringList(), re); } outWriter.popIndentation(); return n; }
/// <summary>A separator is normally just a string literal, but is still an AST that /// we must evaluate. The separator can be any expression such as a template /// include or string cat expression etc... /// </summary> protected internal virtual String computeSeparator(StringTemplate self, StringTemplateWriter outWriter, Object separator) { if (separator == null) { return null; } if (separator is StringTemplateAST) { StringTemplateAST separatorTree = (StringTemplateAST) separator; // must evaluate, writing to a string so we can hand on to it ASTExpr e = new ASTExpr(getEnclosingTemplate(), separatorTree, null); System.IO.StringWriter buf = new System.IO.StringWriter(); // create a new instance of whatever StringTemplateWriter // implementation they are using. Default is AutoIndentWriter. // Defalut behavior is to indent but without // any prior indents surrounding this attribute expression StringTemplateWriter sw = null; System.Type writerClass = outWriter.GetType(); try { System.Reflection.ConstructorInfo ctor = writerClass.GetConstructor(new System.Type[]{typeof(System.IO.TextWriter)}); sw = (StringTemplateWriter) ctor.Invoke(new Object[]{buf}); } catch (System.Exception exc) { // default new AutoIndentWriter(buf) self.error("cannot make implementation of StringTemplateWriter", exc); sw = new AutoIndentWriter(buf); } try { e.write(self, sw); } catch (System.IO.IOException ioe) { self.error("can't evaluate separator expression", ioe); } return buf.ToString(); } else { // just in case we expand in the future and it's something else return separator.ToString(); } }
/// <summary>Call a string template with args and return result. Do not convert /// to a string yet. It may need attributes that will be available after /// this is inserted into another template. /// </summary> public virtual StringTemplate getTemplateInclude(StringTemplate enclosing, String templateName, StringTemplateAST argumentsAST) { StringTemplateGroup group = enclosing.getGroup(); StringTemplate embedded = group.getEmbeddedInstanceOf(enclosing, templateName); if (embedded == null) { enclosing.error("cannot make embedded instance of " + templateName + " in template " + enclosing.getName()); return null; } embedded.setArgumentsAST(argumentsAST); evaluateArguments(embedded); return embedded; }
/// <summary>Return o.getPropertyName() given o and propertyName. If o is /// a stringtemplate then access it's attributes looking for propertyName /// instead (don't check any of the enclosing scopes; look directly into /// that object). Also try isXXX() for booleans. Allow HashMap, /// Hashtable as special case (grab value for key). /// </summary> /// <summary> /// Return o.getPropertyName() given o and propertyName. If o is /// a stringtemplate then access it's attributes looking for propertyName /// instead (don't check any of the enclosing scopes; look directly into /// that object). Also try isXXX() for booleans. Allow HashMap, /// Hashtable as special case (grab value for key). /// </summary> public virtual Object getObjectProperty(StringTemplate self, object o, string propertyName) { if (o == null || propertyName == null) { return null; } Type c = o.GetType(); Object value = null; // Special case: our automatically created Aggregates via // attribute name: "{obj.{prop1,prop2}}" if (c == typeof(StringTemplate.Aggregate)) { value = ((StringTemplate.Aggregate) o).get(propertyName); } // Special case: if it's a template, pull property from // it's attribute table. // TODO: TJP just asked himself why we can't do inherited attr here? else if (c == typeof(StringTemplate)) { IDictionary attributes = ((StringTemplate) o).getAttributes(); if ( attributes!=null ) { value = attributes[propertyName]; } } // Special case: if it's a HashMap, Hashtable then pull using // key not the property method. Do NOT allow general Map interface // as people could pass in their database masquerading as a Map. else if ( isValidMapInstance(c) ) { IDictionary map = (IDictionary)o; value = map[propertyName]; } else { // lookup as property or accessor-method string lookupName = Char.ToUpper(propertyName[0])+ propertyName.Substring(1); //see if it's a property PropertyInfo pi = c.GetProperty(lookupName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase, null, null, new Type[0], null); if (pi != null) { value = pi.GetValue(o, null); } else { //see if it's a method String methodName = "Get" + lookupName; MethodInfo mi = c.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase, null, new Type[0], null); if (mi == null) { mi = c.GetMethod("Is" + lookupName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase, null, new Type[0], null); } if (mi == null) { self.error("Can't get property " +propertyName+ " as C# property '" +lookupName+ "' or as C# methods 'Get" +lookupName+ "' or 'Is" +lookupName+ "' from " +c.FullName+ " instance"); } else { value = mi.Invoke(o, null); } } } return value; }
protected void setSoleFormalArgumentToIthValue(StringTemplate embedded, IDictionary argumentContext, Object ithValue) { IDictionary formalArgs = embedded.getFormalArguments(); if ( formalArgs!=null ) { String soleArgName = null; bool isAnonymous = embedded.getName().Equals(StringTemplate.ANONYMOUS_ST_NAME); if ( formalArgs.Count==1 || (isAnonymous&&formalArgs.Count>0) ) { if ( isAnonymous && formalArgs.Count>1 ) { embedded.error("too many arguments on {...} template: "+formalArgs); } // if exactly 1 arg or anonymous, give that the value of // "it" as a convenience like they said // $list:template(arg=it)$ ICollection argNames = formalArgs.Keys; string[] argNamesArray = new string[argNames.Count]; argNames.CopyTo(argNamesArray,0); soleArgName = argNamesArray[0]; argumentContext[soleArgName] = ithValue; } } }
/// <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); } }
// HELP ROUTINES CALLED BY EVALUATOR TREE WALKER /// <summary> /// For names,phones:{n,p | ...} treat the names, phones as lists /// to be walked in lock step as n=names[i], p=phones[i]. /// </summary> public virtual Object applyTemplateToListOfAttributes(StringTemplate self, IList attributes, StringTemplate templateToApply) { if ( attributes==null || templateToApply==null || attributes.Count==0 ) { return null; // do not apply if missing templates or empty values } IDictionary argumentContext = null; IList results = new ArrayList(); // convert all attributes to iterators even if just one value for (int a = 0; a < attributes.Count; a++) { Object o = attributes[a]; o = convertAnythingToIterator(o); attributes[a] = o; } int numAttributes = attributes.Count; // ensure arguments line up IDictionary formalArguments = templateToApply.getFormalArguments(); if ( formalArguments==null || formalArguments.Count==0 ) { self.error("missing arguments in anonymous"+ " template in context "+self.getEnclosingInstanceStackString()); return null; } ICollection keys = formalArguments.Keys; Object[] formalArgumentNames = new object[keys.Count]; keys.CopyTo(formalArgumentNames,0); if ( formalArgumentNames.Length!=numAttributes ) { self.error("number of arguments "+SupportClass.CollectionToString(formalArguments.Keys)+ " mismatch between attribute list and anonymous"+ " template in context "+self.getEnclosingInstanceStackString()); // truncate arg list to match smaller size int shorterSize = Math.Min(formalArgumentNames.Length, numAttributes); numAttributes = shorterSize; Object[] newFormalArgumentNames = new Object[shorterSize]; System.Array.Copy(formalArgumentNames, 0, newFormalArgumentNames, 0, shorterSize); formalArgumentNames = newFormalArgumentNames; } // keep walking while at least one attribute has values while ( true ) { argumentContext = new Hashtable(); // get a value for each attribute in list; put into arg context // to simulate template invocation of anonymous template int numEmpty = 0; for (int a = 0; a < numAttributes; a++) { IEnumerator it = (IEnumerator)attributes[a]; if ( it.MoveNext() ) { String argName = (String)formalArgumentNames[a]; Object iteratedValue = it.Current; argumentContext[argName] = iteratedValue; } else { numEmpty++; } } if ( numEmpty==numAttributes ) { break; } StringTemplate embedded = templateToApply.getInstanceOf(); embedded.setEnclosingInstance(self); embedded.setArgumentContext(argumentContext); results.Add(embedded); } return results; }