- GWT.runAsync(); same boilerplate every time, but in order to work, each call must be behind a method of its own.
The lightweight dependency injection code I'm working on now wraps all this up behind a generated provider method.
It creates a RunAsyncCallback subclass for every marked async, and generates all the boilerplate to inject the implementation behind a code split.
To write by hand, you must, at the very least, implement the RunAsyncCallback class, plus some kind of sanity-check to avoid dupping the provided object.
Using a generator, it's a single line to annotate the target type, and a single method which takes an interface class and a callback to do DI + code split.
Another good example is DTO objects. How many bean objects have you had to make just to pass around a couple strings and ints?
Using an interface + generator (like autobeans), you can just write out the type of data you want, and let the generator supply implementations on client (and server, if you're crafty).
One example I'm working on here is to present the client with raw JSOs to implement the interface, which translate directly to/from enhanced entities on the server which can access my datastore.
When I get my appengine virtualization layer finished, my DTO interfaces will also have .toEntity(), .fromEntity() methods, as well as .save() and .delete().
to/fromEntity() will be used mainly on the server to translate to datastore types, but also on the client to translate a statically typed object into a hash-bag object for use in CellWidgets.
.save() and .delete() will, on the client, call up (or queue up) requests to delete the entities on the server, and on the server, the objects will perform the actual deletions.
This way, I can help erase the client/server boundary.
Finally, and maybe most importantly, generators are great at spitting out boilerplate code.
If you have a specific procedure you must do (like un/marshalling data, or validating data), you will often find yourself writing, essentially the same code using different accessors for different objects.
These processes cannot be handled by a concrete class, unless you can implement accessor interfaces for every object (which you must still write by hand).
Instead, if you use a generator, you can just dream up any annotation to tell your code "check this, throw X if !Y", and let the generator write the dirty details itself.
Not only does the generator remove messy, error-prone boilerplate, it also updates and adapts as your app changes;
if you change a field's requirements, you just have to update that field's annotation, rather than lookup every place that field is validated and update by hand.
Basically, anywhere you need to repeat a process, but cannot hide it behind a simple concrete abstraction layer, a generator will probably help.
Instead, I specify the -gen directory to point to a project where I have symlinked in all relevant code.
The generated folder is the only concrete source folder, and all other source is just linked in.
This way, any compile errors or warnings or mistakes in the generated code can be picked up immediately,
and I can trace through the generated code easily.
Obviously, if the generator fails and produces no output, you are going to have some headache.
This is where using a subclassed SourceWriter which can debug to console while working can help you pick out errors;
turn a debug flag on, and your console will spew out the generated classes as it writes them, so you can see where the errors are.
If you have the symlinked gen project, you can just copy+paste broken generated code into it, and see where the compile errors are immediately.
Were I a little more ambitious, I would have the subclasses SourceWriter print the generated code to console, to the generator, and to the gen-view project,
so a simply F5 in the gen project will immediately show all compile errors...
Though, I haven't hit any bugs bad enough that I couldn't fix just as easily with debugging output, and maybe java debugger for NPEs here and there. =}