I have an alternative approach that results in a less chatty (exposed to
developer) API while preserving absolutely optimal compiler output that I'll
be using for GQuery. I use a type-safe enum pattern where the enums
themselves are implemented as a JSO. The overall result is to effectively
subclass java.lang.String with tighter types. I'm calling this the
"type-safe JSO enum" pattern.
Here's how it works: Start with an interface for enumerated values:
interface CssProp<T> {
void set(Style s, T value);
T get(Style s);
}
Create a single method for setting property values, as an example:
public static <S, T extends CssProp<S>> void css(T prop, Style s, S val) {
prop.set(s, val);
}
We could like to call this as so:
css(VERTICAL_ALIGN, element.getStyle(), TOP); // legal
css(VERTICAL_ALIGN, element.getStyle(), LEFT); // edit time/compile time
error
and we want the generated code to be:
element.style['verticalAlign']='top';
with no extra classes, no extra clinits, no nothing.
Let's implement an example for VerticalAlign:
public class VerticalAlign
implements CssProp<VerticalAlign.VAlignValue>,
TakesLength, TakesPercentage {
public static VAlignValue TOP;
public static VAlignValue BOTTOM;
public static VerticalAlign VERTICAL_ALIGN;
public VerticalAlign() {
TOP = VAlignValue.create("top");
BOTTOM = VAlignValue.create("bottom");
}
public static void init() {
VERTICAL_ALIGN = new VerticalAlign();
}
final public void set(Style s, VAlignValue value) {
s.setProperty("verticalAlign", value.value());
}
public VAlignValue get(Style s) {
return s.getProperty("verticalAlign").equals(TOP.value()) ? TOP :
BOTTOM;
}
public VAlignValue get() {
return TOP;
}
public void setLength(Style s, Length l) {
s.setProperty("verticalAlign", l.value());
}
public void setPercentage(Style s, Percentage p) {
s.setProperty("verticalAlign", p.value());
}
final static public class VAlignValue extends JavaScriptObject {
protected VAlignValue() {
}
public static VAlignValue create(String val) {
return GWT.isScript() ? createWeb(val) : createHosted(val);
}
public String value() {
return GWT.isScript() ? valueWeb() : valueHosted();
}
private static native VAlignValue createWeb(String val) /*-{
return val;
}-*/;
private static native VAlignValue createHosted(String val) /*-{
return [val];
}-*/;
private native String valueWeb() /*-{
return this;
}-*/;
private native String valueHosted() /*-{
return this[0];
}-*/;
}
}
There are two versions of the enums, one for Hosted Mode and one for Web
Mode, due to Hosted Mode's inability to return a String from a method
returning a JSO. In any case, I have verified using the trunk that this
results in the desired output, moreover, the optimality is not changed by
polymorphic implementations of CssProp. Calls to css() are boiled down to an
elegant inlined style assignment.
Now, not all CSS properties take a closed set of enum values, some take
multiple enum values, or an open set of values typed by units, like lengths
(e.g. 10px, 100%, 1.2em). Here, you introduce interfaces like TakesLength,
TakesPercentage, etc.
public static void css(TakesLength prop,
Style s, Length val) {
prop.setLength(s, val);
}
public static void css(TakesPercentage prop,
Style s, Percentage val) {
prop.setPercentage(s, val);
}
With the Percentage and Length being JSOs as well with statically importable
type constructors, e.g. web-mode version
public native Percentage pct(int pct) /*-{
return pct+"%";
}-*/;
This let's you write:
css(VERTICAL_ALIGN, elem, pct(25)); // compile succeeds
but you can't write
css(BACKGROUND_COLOR, elem, px(10)); // error, BACKGROUND_COLOR doesn't take
Length types
Again, this results in optimal output, except that the JsStaticEval pass of
the compiler currently won't statically collapse a JS expression like '100'
+ '%' into '100%'.
In my IDE (IntelliJ), this all results in a very pleasant development
experience. The IDE autocompletes all CSS property types, and then
auto-completes only valid enums for that property, or valid constructor
functions.
-Ray
2009/3/29 Freeland Abbott <[email protected]>
> As we'd discussed earlier, here's a cut at giving our Style class explicit
> accessors for the various property attributes.
>
> (Not that it matters, but the only thing I *really* hate about our
> checkstyle alphabetization is that it splits clear/get/setFoo apart. C'est
> la vie.)
>
>
> >
>
--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---