Friday, May 23, 2008

toString() with templates? Hmm...

As my blog has just joined Planet Eclipse, I'd like to say 'hi' to all the Eclipse fans :) For the next few months you'll probably be hearing from me about automatic toString() generation feature for Eclipse as this is my project for this year's Google Summer of Code.



I'd like to start by sharing some dilemmas I have regarding the form of templates support in the generator. At first I wanted to use templates to set the format of the generated string, for example this template would create toString() returning a class name, followed by a comma separated list of values in one line:

${class_name}: ${field_value}, ${other_fields}

If every field should be placed in a separate line and have its name printed, the template would look like this:
${class_name} (
${field_name} = ${field_value}
${other_fields}
[Super: ${super_string}]
)

The ${super_string} element puts in the call to super.toString(). There could be other elements to call other common methods. The method generated with this template would look like this:
public String toString() {
return "FooClass (\n"
+ " field1 = " + field1
+ " field2 = " + field2
+ " [Super: " + super.toString() + "]\n"
+ ")";



I thought this was fine until Mike Haller suggested that not only the format of generated string is important, but also the style of the implementation itself. Some might want to use string concatenation, others StringBuilder or ToStringCreator from the Spring Framework and so on. I'm still not completely convinced about this - string concatenation is IMO the easiest to read and probably the fastest implementation (java compiler turns '+' into StringBuilder.append()) - then why would someone need something else? But let's assume that multiple style support is necessary. One solution is to hardcode the most common implementations and let the user choose one in the generator's dialog box. But then there will always be unhappy users who need a style that was not implemented.



The other solution is to use templates to determine the toString() method body itself. This approach is used by JUtils ToString Generator, but it's not very flexible. You cannot change the method behaviour for specific categories of fields, for example to skip null values (comparing primitives to null is an error). Considering this I realized that if there were some additional mechanisms making it more flexible, it would be more than just a toString() generator. It could easily replace almost all source generators currently available in Eclipse - a general purpose code generator useful always when you need to perform similar operations on multiple fields (or methods) of a class!



My idea to accomplish this is to use two level template-tree. Every node would have attributes determining when it should be used. To show what I mean, I will use an example of hashCode() method. Here, the top level template (marked to use only with fields) would look like this:

public int hashCode() {
final int prime = 31;
int result = 1;
${hashcode.sub1}
${hashcode.sub2}
return result;
}

The subtemplates should be called twice in order to determine if there should be any separator between them (here the separator is just "\n\t"). There will be several children:

  • result = prime * result + ((${field_name} == null) ? 0 : ${field_name}.hashCode()); (Marked for use with objects)

  • result = prime * result + ${field_name}; (Marked for use with ints)

  • result = prime * result + Float.floattoIntBits(${field_name}); (Marked for use with floats)

  • result = prime * result + (${field_name} ? 1231 : 1237); (Marked for use with booleans)

  • And so on...


Can you see the potential of this mechanism? :) One more extension is template grouping, which allows to iterate over class fields more than once. Again, let me use an example - generate constructor using fields. Here's the top level template:
public ${class_name}(${arg.sub1}, ${arg.sub2}) {
${body.sub1}
${body.sub2}
}

We have two groups here: the arg group for method arguments with this template:
${field_type} ${field_name}

and the body group with this template:
this.${field_name} = ${field_name};

This is just a general idea and definitely needs more design, but it looks very promising to me. As this idea is a bit different from what I presented in my GSOC application, I'm not sure if I should implement it. So I have a question to GSOC mentors who voted on my project: would you still vote for me if I presented the idea above? Another question is to people who know more about JDT and generation of the code from templates: is this going to work? I mean - maybe there are some major problems I'm not aware of... Of course, the question to everyone is how do you like the idea and how would you extend it? :)



If the project turns out to be too hard for a beginning developer like me or mentors won't agree to these changes, I would just implement the modest toString() generator and go for the general generator later. Now I'd like to hear as many opinions as possible :) I'm not sure if comments to this post is a good place for such discussion, but let's start with it. If it should moved somewhere else, please let me know.

9 comments:

Anonymous said...

I prefer

public String toString() {
return String.format("<Item: id=%d, seq=%d, location=%s>", id, seq, location);
}

instead of return "<Item: id=" + id + ", seq=" + seq + ", location=" + location + ">"

I like having template string as it gives me quick overview of result.

Chris Aniszczyk (zx) said...

Can you setup your blog so it posts the full feed on the Planet? Currently you only get a snippet on PlanetEclipse and that makes me sad :(

Mati said...

Thanks Chris, it should work now.

Scott Stanchfield said...

You should look at how java.awt.Component deals with toString().

It uses the Template Method pattern. For example, in Component:

public String toString() {
return getClass().getName() +
'[' + paramString() + ']';
}

protected String paramString() {
return "a=" + a +
",b" + b +...;
}

This allows subclasses to override paramString and add their fields. Much cleaner. IMHO.

-- Scott

Markus said...

Can't the two level template approach simple be another style (extension) which can be hooked into toString (extension point)? That way both solutions can be turned into one without excluding each other.

And for code generation I'd suggest having a look at JET [1]. Might safe us (you) some time.

Btw. do you want to have a meeting (e.g. telephone conference) to discuss things over in greater detail?

[1]http://www.eclipse.org/modeling/m2t/?project=jet#jet

mhaller said...

i like the template tree idea and how the separator for subtemplates is configured by calling them twice.

makes the template easy to understand. sub1/sub2 is a bit unclear, e.g. if this could be just a convention that subtemplates need to have numberings.

Anonymous said...

how about just using eclise template ? you would need to add support for foreach and fields, but i think in this way everybody will be happy and it wont be too compilcated.

Jakub Jurkiewicz said...

It's good to see you here Mateusz :) Good luck with the project!

Benno Baumgartner said...

Hi Mateusz

Mmm, shouldn't the hashCode template look something like:

public int hashCode() {
final int prime = 31;
int result = 1;
${forAll(${f:class.fields})}
${hashcode.sub1(f)}
return result;
}

I recommend, that you use and extend the existing template mechanism, you can see here: http://dev.eclipse.org/blogs/jdtui/2007/12/04/text-templates-2/ how to write your own template variables. If you need to extend the template language then have a look at org.eclipse.jface.text.templates.TemplateTranslator

I also recommend to you, that you write a "simple" toString generation wizard without the template stuff and then explore further.

Good luck with your project.