/// <summary> /// Write o relative to self to out /// </summary> /// <remarks> /// John Snyders fixes here for formatString. Basically, any time /// you are about to write a value, check formatting. /// </remarks> protected internal virtual int Write(StringTemplate self, object o, IStringTemplateWriter output) { if (o == null) { if (nullValue == null) { return 0; } // continue with null option if specified o = nullValue; } int n = 0; try { if (o is StringTemplate) { StringTemplate stToWrite = (StringTemplate) o; // failsafe: perhaps enclosing instance not set // Or, it could be set to another context! This occurs // when you store a template instance as an attribute of more // than one template (like both a header file and C file when // generating C code). It must execute within the context of // the enclosing template. stToWrite.EnclosingInstance = self; // if self is found up the enclosing instance chain, then // infinite recursion if (StringTemplate.IsInLintMode && StringTemplate.IsRecursiveEnclosingInstance(stToWrite)) { // throw exception since sometimes eval keeps going // even after I ignore this write of o. throw new SystemException("infinite recursion to " + stToWrite.GetTemplateDeclaratorString() + " referenced in " + stToWrite.EnclosingInstance.GetTemplateDeclaratorString() + "; stack trace:\n" + stToWrite.GetEnclosingInstanceStackTrace()); } else { // if we have a wrap string, then inform writer it // might need to wrap if (wrapString != null) { n = output.WriteWrapSeparator(wrapString); } // check if formatting needs to be applied to the stToWrite if ( formatString != null ) { IAttributeRenderer renderer = self.GetAttributeRenderer(typeof(string)); if ( renderer != null ) { // you pay a penalty for applying format option to a template // because the template must be written to a temp StringWriter so it can // be formatted before being written to the real output. StringWriter buf = new StringWriter(); IStringTemplateWriter sw = self.Group.CreateInstanceOfTemplateWriter(buf); stToWrite.Write(sw); n = output.Write(renderer.ToString(buf.ToString(), formatString)); return n; } } n = stToWrite.Write(output); } return n; } // normalize anything iteratable to iterator o = ConvertAnythingIteratableToIterator(o); if (o is IEnumerator) { IEnumerator iter = (IEnumerator)o; bool seenPrevValue = false; while (iter.MoveNext()) { object iterValue = iter.Current; if (iterValue == null) { iterValue = nullValue; } if (iterValue != null) { if (seenPrevValue /*prevIterValue!=null*/ && (separatorString != null)) { n += output.WriteSeparator(separatorString); } seenPrevValue = true; int nw = Write(self, iterValue, output); n += nw; } } } else { IAttributeRenderer renderer = self.GetAttributeRenderer(o.GetType()); string v = null; if (renderer != null) { if (formatString != null) { v = renderer.ToString(o, formatString); } else { v = renderer.ToString(o); } } else { v = o.ToString(); } if (wrapString != null) { n = output.Write(v, wrapString); } else { n = output.Write(v); } return n; } } catch (IOException io) { self.Error("problem writing object: " + o, io); } return n; }
protected virtual int WriteIterableValue(StringTemplate self, Iterator iter, IStringTemplateWriter @out) { int n = 0; bool seenAValue = false; while (iter.hasNext()) { object iterValue = iter.next() ?? _nullValue; if (iterValue != null) { // if no separator or separator but iterValue isn't a single IF condition template if (_separatorString == null) { // if no separator, don't waste time writing to temp buffer int nw = Write(self, iterValue, @out); if (nw != Missing) n += nw; continue; } /* if value to emit is a template, only buffer its * value if it's nullable (can eval to missing). * Only a sequence of IF can eval to missing. */ StringTemplate st = iterValue as StringTemplate; if (st != null) { int nchunks = st.Chunks != null ? st.Chunks.Count : 0; bool nullable = true; for (int i = 0; i < nchunks; i++) { Expr a = st.Chunks[i]; if (!(a is ConditionalExpr)) nullable = false; } // if not all IF, not nullable, spit out w/o buffering if (!nullable) { if (seenAValue && _separatorString != null) { n += @out.WriteSeparator(_separatorString); } int nw = Write(self, iterValue, @out); n += nw; seenAValue = true; continue; } } else if (!(iterValue is Iterator)) { // if not possible to be missing, don't waste time // writing to temp buffer; might need separator though if (seenAValue && _separatorString != null) { n += @out.WriteSeparator(_separatorString); } int nw = Write(self, iterValue, @out); seenAValue = true; n += nw; continue; } /* if separator exists, write iterated value to a * tmp buffer in case we don't need a separator. * Can't generate separator then test next expr value * as we can't undo separator emit. * Write to dummy buffer to if it is MISSING * but eval/write value again to real out so * we get proper autowrap etc... * Ack: you pay a penalty now for a separator * Later, i can optimze to check if one chunk and * it's a conditional */ StringWriter buf = new StringWriter(); IStringTemplateWriter sw = self.Group.GetStringTemplateWriter(buf); int tmpsize = Write(self, iterValue, sw); if (tmpsize != Missing) { if (seenAValue && _separatorString != null) { n += @out.WriteSeparator(_separatorString); } // do it to real output stream now int nw = Write(self, iterValue, @out); n += nw; seenAValue = true; } } } return n; }