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
-~----------~----~----~----~------~----~------~--~---

Reply via email to