[stringtemplate-interest] Renderer "modes"
Terence Parr
parrt at cs.usfca.edu
Tue Jun 20 10:45:50 PDT 2006
On Jun 19, 2006, at 8:06 PM, Sean St. Quentin wrote:
> On 20/06/06, Kunle Odutola <Kunle_Odutola at hotmail.com> wrote:
> Hi Sean,
>
> > Without knowing of an easy way to fix this (by easy I mean not
> having two
> > arrays of preformatted information being sent to the view, which
> seems
> kinda
> > wrong), I was thinking about having rendering modes for an
> > AttributeRenderer.
>
> Formatter ojects are currently the prescribed way. See the types in
> the
> "Antlr.StringTemplate.Utils" namespace. You wrap each entry in your
> array.
>
> Hey Kunle,
> Yeah, I considered that option, but it seems to me a little awkward.
It is a pain to manually wrap each object before you inject. Using
types is a crude instrument for rendering as you might have two Lists
of strings that each need to be dealt with differently.
Hmm....interesting...
> For instance, formatter/renderer combination makes this easy:
>
> $people:{
> <h2>$it.Name$</h2>
>
> $it.Relations:{<a onclick="registerRelation('$it.Value;
> format="js"$');">$it.Value$</a> }$
>
> }$
>
> Without using formatters on renderers, it would be possible, but
> the controller would be overly complex with several wrapper classes.
Well, you'd need one for javascript but what others would you need?
> Perhaps. I wonder if formalizing the concept of Formatters would be
> a better
> approach. Formatters expose arbitrary properties that ST templates can
> already access directly. All that is needed is a way to compose a
> renderer
> and a formatter before registering with ST. This would remove the
> formatter-per-attribute association as well.
So you'd only have a singleton renderer as before but that accepted
an Object *and* a formatter String:
class StringRenderer implements AttributeRenderer {
toString(Object data) {...}
toString(Object data, String format) {...}
}
Is that what you mean? If you registered a StringRenderer to deal
with all Strings then $name$ would call the first method toString
(name). If you did $name; format="js"$ then it would call toString
(name, "js"). Interesting...
> Simply combining the concepts ( i.e. renderers exposing formatter-like
> properties in addition to ToString) seems a little brittle. Can't
> articulate
> why it feels brittle yet.
Well, properties are already there like $name.js$ except you'd need
to have wrapped name in an appropriate wrapper object. This can be
done automatically but is very expensive: new object for every string
going in! :(
> Combining the concepts seems like a logical step to me. Renderers
> convert information into an easily readable format and output a
> string, they're already formatters that are hardcoded to a single
> format.
>
> Using formatters to format information before its sent to the view
> implies that the controller knows exactly what the view is going to
> do with the data... does that blur the lines in MVCR?
Well, in general that is proper. For example, only the controller
knows the client's language and encoding...the view cannot know this
so it can't say "use language Swahili". Dealing with URL encoding
and uppercasing and all that rot is possible using renderers in some
cases and resorting to wrappers (formatter objects) in the worst
case. This keeps it all nice and separated. However, sometimes you
really want to just do some quick formatting on a string like padding
it to 80char or uppercasing it: $name.toUpper$ etc...
I'm uncomfortable with the ability to pass a string back to the
controller. Clearly you can then call f(x):
$sqldb; format="SELECT * FROM USER"$
yikes! Boy that is a slippery slope, eh? This is why I am so
ruthless with this infernal separation ;)
Instead, let's follow what I think you originally posted: registering
formatter objects as renderers but we expose some properties. The
key efficiency thing here is that the wrapper/formatter can be a
singleton now--you don't have to wrap every string in an formatter
object. $name.toUpper$ would be converted to
r = renderer.get("String");
r.toUpper(name);
So, in the controller you register String->SeansHTMLRendererThingie
and then call $name.seansMethodForManipulatingStringsInACoolWay$.
This prevents the user from passing arbitrary data; the format string
must be a valid method name, preventing methods called "SELECT * FROM
USER", though for a few cases, you could actually do this. This hole
already exists though.
In summary, the syntax is identical but we'd have to modify
ASTExpr:
protected Object rawGetObjectProperty(StringTemplate self, Object o,
String propertyName) {...}
so that it checked for a renderer for o's type before doing anything
else. A renderer registered for o's type would take precedence over
anything else even if o is a Map or StringTemplate. This way we can
automatically add properties essentially to any type of object.
How does this sound? I like it.
Ter
More information about the stringtemplate-interest
mailing list