On Fri, Oct 3, 2008 at 3:47 PM, Ray Cromwell <[EMAIL PROTECTED]> wrote: > On Fri, Oct 3, 2008 at 12:30 PM, Ian Petersen <[EMAIL PROTECTED]> wrote: >> public interface PersonForm extends Form<Person> { >> >> Field<Person, String> getFirstName(); >> >> Field<Person, String> getLastName(); >> >> @Calculate("${firstName} + \" \" + ${lastName}") >> Formula<Person, String> getFullName(); >> } > > What is the second type parameter and why is it needed? I get that > the first parameter probably allows the generator to go introspect the > Person type and find an identically named method getFirstName(), is > the second parameter supposed to represent the return type of the > Person.getFirstName() method?
Yes, the second type parameter represents the property type. It's needed to keep the whole system as tightly-typed as possible. The compiled output surprised me with its efficiency. This data binding library has been factored out of a larger project and, on and off, I've been struggling with how to do data binding for a couple of years now. Previous attempts have been slow and ungainly. This attempt seems to be pretty tight after the GWT compiler has its way with it. > Is there any way we can make the actual binding declarations more > succinct? This seems awfully verbose and repetitive. As far as I can tell, the verbosity is this library's biggest downside, and I'm not sure how to improve it. I'm hoping that releasing it publicly will bring in some new eyes and maybe it can be fixed. > This can be tricky. In order to make this work correctly, you're > going to have to implement a master-dependency graph and evaluate > @Calculates according to topological sort order. This is how it's done > in spreadsheets and in XForms. If not, and if @Calculate expressions > reference other formulas, you can end up with inconsistent results. > You can also detect infinite loops as well (A = B + 1, B = A + 1) and > warn people about them. In its current state, @Calculate can only refer to properties of the underlying bean type--not to other fields or formulae in the same form, so cycles are impossible. It would be nice, though, to do as you suggest because it would make certain @Calculate declarations less verbose. (In my project, I have a type PaperDimension with properties cwtPrice, MWeight, and markup. There are two calculated fields: costPerM and chargePerM. costPerM is declared like this: @Calculate("${cwtPrice} * ${MWeight} / 100") and chargePerM is declared like this: @Calculate("${cwtPrice} * ${MWeight} / 100 * (1.0 + ${markup})") It would be nice for chargePerM to be dependent on costPerM so the declaration could be simplified to this: @Calculate("${costPerM} * (1.0 + ${markup})") but it was too complicated for a first pass.) > All in all, I think it looks good, but I'm not totally happy with the > generic type verbosity required. I give you props for avoiding dynamic > runtime introspection, and you proposal should allow for very > efficient binding expressions to be generated by the compiler while > minimally impacting pruning and type tightening. Wow! Props from Ray Cromwell of all people. I should get those framed. Anyway, I agree, the verbosity is high. I'm not sure how to reduce it. Maybe I should post the source immediately and repost it with documentation ASAP. > I'm somewhat confused about how data fields magically turn into > Widgets. How do I say something is a Radio button, or a > CreditCardEntryWidget? The generator makes a bunch of assumptions to keep verbosity down in the Form declaration, but you can override the assumptions with annotations. By default, Boolean values are both displayed and edited in a SimpleCheckBox. All other types are displayed in Labels and edited in TextBoxes. To smooth over the various possible widget interfaces, I defined the following two interfaces: public interface Viewer<T> { /** * Returns [EMAIL PROTECTED] this}. */ Widget asWidget(); void display(T value); } public interface Editor<T> extends Viewer<T> { T readValue(); void addChangeListener(EditorChangeListener<? super T> listener); void removeChangeListener(EditorChangeListener<? super T> listener); } If you want to use a non-default editor or viewer, you can annotate the Field/Formula declaration with @UseEditor or @UseViewer: @Target(METHOD) public @interface UseEditor { Class<? extends Editor> value(); /** * Some arguments to pass to the editor's constructor. Pasted into the generated code as-is. */ String[] args() default {}; } (UseViewer is the same, but value()'s value must extend Viewer.) So, if you want to use a CreditCardEntryWidget, you'd probably define a CreditCardEditor that either extends or wraps CreditCardEntryWidget and implements Editor<String>. The CreditCardEditor might make use of StringConverter<String> to enforce formatting rules: public interface StringConverter<T> { String toString(T value); T fromString(String string) throws ConversionException; } Then, you'd do this: public interface PersonForm extends Form<Person> { // assumes Person.getCreditCard() returns an instance of something // with a String getNumber() and a void setNumber(String) @UseProperty("creditCard.number") @UseEditor(CreditCardEditor.class) Field<Person, String> getCreditCardNumber(); } I'll see about posting the source to my library in the next half hour or so, and I'll post it again when it's documented. I'd really like some outside eyes on the code--I think it's useful but I'm sure it could be improved. Ian --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---