Revision: 6062
Author: [email protected]
Date: Tue Sep 1 15:14:29 2009
Log: Nukes the deprecated and over complicated
create-the-root-now-create-the-rest-later flow from UiBinder, and in the
process
squashes a number of reported and unreported bugs.
Not as big a change as it looks, most of the listed files are a result of
compulsive method name change refactoring.
The real meat is in moving the parsing of the root UiBinder element out of
UiBinderWriter and into its own parser, and greatly simplified
bootstrapping of
the parsing process.
Because we now start parsing above the UI, at the ui:UiBinder element,
parseElementToField (was parseWidget) gets a crack at the topmost element
of the
UI--it gets treated like every other element, and various inconsistencies
in its
handling (esp. with {computed.attributes} and localizable ones) evaporate.
Reviewed by: jgw
http://code.google.com/p/google-web-toolkit/source/detail?r=6062
Added:
/trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
Deleted:
/trunk/user/src/com/google/gwt/uibinder/client/AbstractUiBinder.java
/trunk/user/src/com/google/gwt/uibinder/client/DomHolder.java
Modified:
/trunk/user/src/com/google/gwt/uibinder/client/UiBinder.java
/trunk/user/src/com/google/gwt/uibinder/parsers/CellPanelParser.java
/trunk/user/src/com/google/gwt/uibinder/parsers/DisclosurePanelParser.java
/trunk/user/src/com/google/gwt/uibinder/parsers/DockPanelParser.java
/trunk/user/src/com/google/gwt/uibinder/parsers/DomElementParser.java
/trunk/user/src/com/google/gwt/uibinder/parsers/HasWidgetsParser.java
/trunk/user/src/com/google/gwt/uibinder/parsers/MenuBarParser.java
/trunk/user/src/com/google/gwt/uibinder/parsers/MenuItemParser.java
/trunk/user/src/com/google/gwt/uibinder/parsers/StackPanelParser.java
/trunk/user/src/com/google/gwt/uibinder/parsers/TabPanelParser.java
/trunk/user/src/com/google/gwt/uibinder/parsers/WidgetInterpreter.java
/trunk/user/src/com/google/gwt/uibinder/parsers/WidgetPlaceholderInterpreter.java
/trunk/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java
/trunk/user/src/com/google/gwt/uibinder/rebind/HandlerEvaluator.java
/trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
/trunk/user/src/com/google/gwt/uibinder/rebind/messages/PlaceholderInterpreter.java
/trunk/user/src/com/google/gwt/uibinder/rebind/model/OwnerFieldClass.java
/trunk/user/src/com/google/gwt/uibinder/sample/client/DomBasedUi.java
/trunk/user/src/com/google/gwt/uibinder/sample/client/DomBasedUi.ui.xml
=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java Tue
Sep 1 15:14:29 2009
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package com.google.gwt.uibinder.rebind;
+
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.uibinder.rebind.messages.MessagesWriter;
+import com.google.gwt.uibinder.rebind.model.OwnerField;
+
+/**
+ * Parses the root UiBinder element, and kicks of the parsing of the rest
of the
+ * document.
+ */
+public class UiBinderParser {
+ // TODO(rjrjr) Make all the ElementParsers receive their dependencies via
+ // constructor like this one does, and make this an ElementParser
+
+ private final UiBinderWriter writer;
+ private final MessagesWriter messagesWriter;
+ private final FieldManager fieldManager;
+
+ public UiBinderParser(UiBinderWriter writer, MessagesWriter
messagesWriter,
+ FieldManager fieldManager) {
+ this.writer = writer;
+ this.messagesWriter = messagesWriter;
+ this.fieldManager = fieldManager;
+ }
+
+ /**
+ * Parses the root UiBinder element, and kicks off the parsing of the
rest of
+ * the document.
+ */
+ public String parse(XMLElement elem)
+ throws UnableToCompleteException {
+ findResources(elem);
+ messagesWriter.findMessagesConfig(elem);
+ XMLElement uiRoot = elem.consumeSingleChildElement();
+ return writer.parseElementToField(uiRoot);
+ }
+
+ /**
+ * Interprets <ui:with> elements.
+ */
+ private void createResource(XMLElement elem)
+ throws UnableToCompleteException {
+ String resourceName = elem.consumeRequiredAttribute("name");
+ String resourceTypeName = elem.consumeRequiredAttribute("type");
+
+ JClassType resourceType =
writer.getOracle().findType(resourceTypeName);
+ if (resourceType == null) {
+ writer.die("In %s, no such type %s", elem, resourceTypeName);
+ }
+
+ if (elem.getAttributeCount() > 0) {
+ writer.die("In %s, should only find attributes \"name\" and
\"type\"");
+ }
+
+ FieldWriter fieldWriter = fieldManager.registerField(resourceTypeName,
+ resourceName);
+ OwnerField ownerField =
writer.getOwnerClass().getUiField(resourceName);
+
+ // Perhaps it is provided via @UiField
+
+ if (ownerField != null) {
+ if (!resourceType.equals(ownerField.getType().getRawType())) {
+ writer.die("In %s, type must match %s", ownerField);
+ }
+
+ if (ownerField.isProvided()) {
+ fieldWriter.setInitializer("owner." + ownerField.getName());
+ return;
+ }
+ }
+
+ // Nope. Maybe a @UiFactory will make it
+
+ JMethod factoryMethod = writer.getOwnerClass().getUiFactoryMethod(
+ resourceType);
+ if (factoryMethod != null) {
+ fieldWriter.setInitializer(String.format("owner.%s()",
+ factoryMethod.getName()));
+ }
+
+ // If neither of the above, the FieldWriter's default GWT.create call
will
+ // do just fine.
+ }
+
+ private void findResources(XMLElement binderElement)
+ throws UnableToCompleteException {
+ binderElement.consumeChildElements(new
XMLElement.Interpreter<Boolean>() {
+ public Boolean interpretElement(XMLElement elem)
+ throws UnableToCompleteException {
+ if (!(writer.isBinderElement(elem)
+ && "with".equals(elem.getLocalName()))) {
+ return false; // Not of interest, do not consume
+ }
+
+ createResource(elem);
+
+ return true; // Yum
+ }
+ });
+ }
+
+}
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/client/AbstractUiBinder.java
Wed Aug 5 20:27:52 2009
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2008 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
- * use this file except in compliance with the License. You may obtain a
copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
under
- * the License.
- */
-package com.google.gwt.uibinder.client;
-
-/**
- * Convenient base class for implementations of {...@link UiBinder},
- * generated or otherwise.
- *
- * @param <U> The type of the UI's root object
- * @param <O> The type of the owner of the UI
- */
-public abstract class AbstractUiBinder<U,O> implements UiBinder<U, O> {
-
- /**
- * An interface used by some generated implementations of UiBinder.
- *
- * @param <U> The type of the UI's root object
- * @param <O> The type of the owner of the UI
- */
- protected interface InstanceBinder<U, O> {
- U makeUi();
- void doBind(O owner);
- }
-
- public U createAndBindUi(O owner) {
- U ui = createUiRoot(owner);
- bindUi(ui, owner);
- return ui;
- }
-}
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/client/DomHolder.java Wed Aug
5 20:27:52 2009
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2008 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
- * use this file except in compliance with the License. You may obtain a
copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
under
- * the License.
- */
-package com.google.gwt.uibinder.client;
-
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.user.client.ui.UIObject;
-
-/**
- * Runtime helper for templates.
- */
-public class DomHolder extends UIObject {
- public DomHolder(Element element) {
- setElement(element);
- }
-}
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/client/UiBinder.java Wed Aug
5
20:27:52 2009
+++ /trunk/user/src/com/google/gwt/uibinder/client/UiBinder.java Tue Sep
1
15:14:29 2009
@@ -1,12 +1,12 @@
/*
* Copyright 2008 Google Inc.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may
not
* use this file except in compliance with the License. You may obtain a
copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -26,38 +26,17 @@
* in {...@code /bar/baz/Foo.ui.xml}. (To use a different template file, put
the
* {...@link UiTemplate} annotation on your UiBinder interface declaration to
point
* the code generator at it.)
- *
+ *
* @param <U> The type of the root object of the generated UI, typically a
* subclass of {...@link com.google.gwt.dom.client.Element} or
* {...@link com.google.gwt.user.client.ui.UIObject}
* @param <O> The type of the object that will own the generated UI
*/
public interface UiBinder<U, O> {
-
- /**
- * Creates and returns the root object of the UI. If possible the
creation of
- * the rest of the UI is deferred until {...@link #bindUi} is called.
- *
- * @deprecated The use case for this complexity never materialized. Use
- * {...@link #createAndBindUi}
- */
- @Deprecated
- U createUiRoot(O owner);
-
- /**
- * Completes the creation of a UI started with a call to {...@link
#createUiRoot},
- * and fills any owner fields tagged with {...@link UiField}.
- *
- * @deprecated The use case for this complexity never materialized. Use
- * {...@link #createAndBindUi}
- */
- @Deprecated
- void bindUi(U root, O owner);
-
/**
* Creates and returns the root object of the UI, and fills any fields
of owner
* tagged with {...@link UiField}.
- *
+ *
* @param owner the object whose {...@literal @}UiField needs will be filled
*/
U createAndBindUi(O owner);
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/parsers/CellPanelParser.java
Wed Aug 5 20:27:52 2009
+++ /trunk/user/src/com/google/gwt/uibinder/parsers/CellPanelParser.java
Tue Sep 1 15:14:29 2009
@@ -90,14 +90,14 @@
if (widget == null) {
writer.die("Cell must contain a single child widget");
}
- String childFieldName = writer.parseWidget(widget);
+ String childFieldName = writer.parseElementToField(widget);
writer.addStatement("%1$s.add(%2$s);", fieldName, childFieldName);
// Parse the cell tag's alignment & size attributes.
parseCellAttributes(child, fieldName, childFieldName, writer);
} else {
// It's just a normal child, so parse it as a widget.
- String childFieldName = writer.parseWidget(child);
+ String childFieldName = writer.parseElementToField(child);
writer.addStatement("%1$s.add(%2$s);", fieldName, childFieldName);
}
}
=======================================
---
/trunk/user/src/com/google/gwt/uibinder/parsers/DisclosurePanelParser.java
Wed Aug 5 20:27:52 2009
+++
/trunk/user/src/com/google/gwt/uibinder/parsers/DisclosurePanelParser.java
Tue Sep 1 15:14:29 2009
@@ -82,14 +82,14 @@
writer.die("In %s, DisclosurePanel cannot contain more than one
header widget.", elem);
}
child.consumeAttribute(headerAttributeName);
- headerFieldName = writer.parseWidget(child);
+ headerFieldName = writer.parseElementToField(child);
childIsHeader = true;
}
if (!childIsHeader) {
if (childFieldName != null) {
writer.die("In %s, DisclosurePanel cannot contain more than one
content widget.", elem);
}
- childFieldName = writer.parseWidget(child);
+ childFieldName = writer.parseElementToField(child);
}
}
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/parsers/DockPanelParser.java
Wed Aug 5 20:27:52 2009
+++ /trunk/user/src/com/google/gwt/uibinder/parsers/DockPanelParser.java
Tue Sep 1 15:14:29 2009
@@ -69,7 +69,7 @@
if (widget == null) {
writer.die("Dock must contain a single child widget.");
}
- String childFieldName = writer.parseWidget(widget);
+ String childFieldName = writer.parseElementToField(widget);
writer.addStatement("%1$s.add(%2$s, %3$s);", fieldName,
childFieldName, translated);
// And they can optionally have a width.
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/parsers/DomElementParser.java
Wed Aug 5 20:27:52 2009
+++ /trunk/user/src/com/google/gwt/uibinder/parsers/DomElementParser.java
Tue Sep 1 15:14:29 2009
@@ -21,36 +21,24 @@
import com.google.gwt.uibinder.rebind.XMLElement;
/**
- * Parses a raw dom element, as opposed to a widget or uiobject. Note that
this
- * parser does not get a crack at every dom element--it only handles the
case of
- * a dom element as ui root. Note also that it is intended to be used only
by
- * {...@link UiBinderWriter}, and will generate cast class exceptions if used
- * otherwise.
+ * Parses a dom element and all of its children. Note that this parser
does not
+ * make recursive calls to parse child elements, unlike what goes on with
widget
+ * parsers. Instead, we consume the inner html of the given element into
+ * a single string literal, used to instantiate the dom tree at run time.
*/
public class DomElementParser implements ElementParser {
public void parse(XMLElement elem, String fieldName, JClassType type,
UiBinderWriter writer) throws UnableToCompleteException {
HtmlInterpreter interpreter =
- new HtmlInterpreter(writer, "root", new
HtmlMessageInterpreter(writer,
- "root"));
-
- writer.setFieldInitializer(fieldName, "null");
+ new HtmlInterpreter(writer, fieldName, new
HtmlMessageInterpreter(writer,
+ fieldName));
interpreter.interpretElement(elem);
- String rootHtml = elem.consumeOpeningTag() + elem.getClosingTag();
- String rootType = getFQTypeName(type);
- writer.genRoot(String.format(
- "(%1$s) UiBinderUtil.fromHtml(\"%2$s\")", rootType, rootHtml));
-
- String innerHtml = elem.consumeInnerHtml(interpreter);
-
- if (innerHtml.trim().length() > 0) { // TODO(rjrjr) Really want this
check?
- writer.addStatement("root.setInnerHTML(\"%s\");", innerHtml);
- }
- }
-
- private String getFQTypeName(JClassType type) {
- return type.getPackage().getName() + "." + type.getName();
+
+ String html = elem.consumeOpeningTag() +
elem.consumeInnerHtml(interpreter)
+ + elem.getClosingTag();
+ writer.setFieldInitializer(fieldName, String.format(
+ "(%1$s) UiBinderUtil.fromHtml(\"%2$s\")",
type.getQualifiedSourceName(), html));
}
}
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/parsers/HasWidgetsParser.java
Wed Aug 5 20:27:52 2009
+++ /trunk/user/src/com/google/gwt/uibinder/parsers/HasWidgetsParser.java
Tue Sep 1 15:14:29 2009
@@ -32,7 +32,7 @@
UiBinderWriter writer) throws UnableToCompleteException {
// Parse children.
for (XMLElement child : elem.consumeChildElements()) {
- String childFieldName = writer.parseWidget(child);
+ String childFieldName = writer.parseElementToField(child);
writer.addStatement("%1$s.add(%2$s);", fieldName, childFieldName);
}
}
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/parsers/MenuBarParser.java Wed
Aug 5 20:27:52 2009
+++ /trunk/user/src/com/google/gwt/uibinder/parsers/MenuBarParser.java Tue
Sep 1 15:14:29 2009
@@ -51,7 +51,7 @@
}
}
- String itemFieldName = writer.parseWidget(child);
+ String itemFieldName = writer.parseElementToField(child);
writer.addStatement("%1$s.addItem(%2$s);", fieldName, itemFieldName);
}
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/parsers/MenuItemParser.java Wed
Aug 5 20:27:52 2009
+++ /trunk/user/src/com/google/gwt/uibinder/parsers/MenuItemParser.java Tue
Sep 1 15:14:29 2009
@@ -64,7 +64,7 @@
writer.die("In %s, only one MenuBar may be contained in a
MenuItem",
errorContext);
}
- menuBarField = writer.parseWidget(elem);
+ menuBarField = writer.parseElementToField(elem);
return "";
}
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/parsers/StackPanelParser.java
Wed Aug 5 20:27:52 2009
+++ /trunk/user/src/com/google/gwt/uibinder/parsers/StackPanelParser.java
Tue Sep 1 15:14:29 2009
@@ -39,7 +39,7 @@
stackItemLabel = child.consumeAttribute(variableAttributeName);
}
- String childFieldName = writer.parseWidget(child);
+ String childFieldName = writer.parseElementToField(child);
if (stackItemLabel == null) {
writer.addStatement("%1$s.add(%2$s);", fieldName, childFieldName);
} else {
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/parsers/TabPanelParser.java Wed
Aug 5 20:27:52 2009
+++ /trunk/user/src/com/google/gwt/uibinder/parsers/TabPanelParser.java Tue
Sep 1 15:14:29 2009
@@ -61,7 +61,7 @@
if (childFieldName != null) {
writer.die("gwt:Tab may only have a single child widget");
}
- childFieldName = writer.parseWidget(tabChild);
+ childFieldName = writer.parseElementToField(tabChild);
}
}
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/parsers/WidgetInterpreter.java
Wed Aug 5 20:27:52 2009
+++ /trunk/user/src/com/google/gwt/uibinder/parsers/WidgetInterpreter.java
Tue Sep 1 15:14:29 2009
@@ -73,7 +73,7 @@
}
public String interpretElement(XMLElement elem) throws
UnableToCompleteException {
- if (uiWriter.isWidget(elem)) {
+ if (uiWriter.isWidgetElement(elem)) {
// Allocate a local variable to hold the dom id for this widget. Note
// that idHolder is a local variable reference, not a string id. We
// have to generate the ids at runtime, not compile time, or else
@@ -102,7 +102,7 @@
*/
for (String idHolder : idToWidgetElement.keySet()) {
XMLElement childElem = idToWidgetElement.get(idHolder);
- String childField = uiWriter.parseWidget(childElem);
+ String childField = uiWriter.parseElementToField(childElem);
uiWriter.addInitStatement("%1$s.addAndReplaceElement(%2$s, %3$s);",
fieldName,
childField, idHolder);
}
=======================================
---
/trunk/user/src/com/google/gwt/uibinder/parsers/WidgetPlaceholderInterpreter.java
Wed Aug 5 20:27:52 2009
+++
/trunk/user/src/com/google/gwt/uibinder/parsers/WidgetPlaceholderInterpreter.java
Tue Sep 1 15:14:29 2009
@@ -77,11 +77,11 @@
public String interpretElement(XMLElement elem)
throws UnableToCompleteException {
- if (!uiWriter.isWidget(elem)) {
+ if (!uiWriter.isWidgetElement(elem)) {
return super.interpretElement(elem);
}
- JClassType type = uiWriter.findWidgetOrElementType(elem);
+ JClassType type = uiWriter.findFieldType(elem);
TypeOracle oracle = uiWriter.getOracle();
MessagesWriter mw = uiWriter.getMessages();
@@ -112,7 +112,7 @@
public String postProcess(String consumed) throws
UnableToCompleteException {
for (String idHolder : idToWidgetElement.keySet()) {
XMLElement childElem = idToWidgetElement.get(idHolder);
- String childField = uiWriter.parseWidget(childElem);
+ String childField = uiWriter.parseElementToField(childElem);
genSetWidgetTextCall(idHolder, childField);
uiWriter.addInitStatement("%1$s.addAndReplaceElement(%2$s, %3$s);",
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java Tue
Sep 1 11:53:48 2009
+++ /trunk/user/src/com/google/gwt/uibinder/rebind/FieldWriter.java Tue
Sep 1 15:14:29 2009
@@ -1,12 +1,12 @@
/*
* Copyright 2009 Google Inc.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may
not
* use this file except in compliance with the License. You may obtain a
copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -53,13 +53,6 @@
this.type = type;
this.name = name;
}
-
- /**
- * @return the fully qualified name of this field's type
- */
- public String getFullTypeName() {
- return type.getPackage().getName() + "." + type.getName();
- }
/**
* @return the type of this field
@@ -79,7 +72,7 @@
* Used to provide an initializer string to use instead of a
* {...@link com.google.gwt.core.client.GWT#create()} call. Note that this
is an
* RHS expression. Don't include the leading '=', and don't end it
with ';'.
- *
+ *
* @throws IllegalStateException on second attempt to set the initializer
*/
public void setInitializer(String initializer) {
@@ -112,7 +105,7 @@
/**
* Write the field delcaration.
- *
+ *
* @return false if unable to write for lack of a default constructor
*/
// TODO(rjrjr) This return code thing is silly. We should
@@ -138,10 +131,10 @@
return false;
}
initializer = String.format("(%1$s) GWT.create(%1$s.class)",
- getFullTypeName());
+ type.getQualifiedSourceName());
}
- w.write("%s %s = %s;", getFullTypeName(), name, initializer);
+ w.write("%s %s = %s;", type.getQualifiedSourceName(), name,
initializer);
this.written = true;
return true;
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/rebind/HandlerEvaluator.java
Wed Aug 5 20:27:52 2009
+++ /trunk/user/src/com/google/gwt/uibinder/rebind/HandlerEvaluator.java
Tue Sep 1 15:14:29 2009
@@ -63,8 +63,18 @@
*/
class HandlerEvaluator {
- private static final String HANDLER_BASE_NAME = "handler";
-
+ private static final String HANDLER_BASE_NAME =
+ "handlerMethodWithNameVeryUnlikelyToCollideWithUserFieldNames";
+ /*
+ * TODO(rjrjr) The correct fix is to put the handlers in a locally
defined
+ * class, making the generated code look like this:
+ *
+ *
+ *
http://docs.google.com/Doc?docid=0AQfnKgX9tAdgZGZ2cTM5YjdfMmQ4OTk0eGhz&hl=en
+ *
+ * But that needs to wait for a refactor to get most of this stuff out
of here
+ * and into com.google.gwt.uibinder.rebind.model
+ */
private int varCounter = 0;
private final TreeLogger logger;
@@ -222,7 +232,7 @@
*/
void writeAddHandler(IndentedWriter writer, String handlerVarName,
String addHandlerMethodName, String objectName) {
- writer.write("this.%1$s.%2$s(%3$s);", objectName,
+ writer.write("%1$s.%2$s(%3$s);", objectName,
addHandlerMethodName, handlerVarName);
}
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java Tue
Sep 1 11:53:48 2009
+++ /trunk/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java Tue
Sep 1 15:14:29 2009
@@ -1,12 +1,12 @@
/*
* Copyright 2008 Google Inc.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may
not
* use this file except in compliance with the License. You may obtain a
copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -48,12 +48,13 @@
/**
* Writer for UiBinder generated classes.
- *
+ *
* TODO(rdamazio): Refactor this, extract model classes, improve ordering
* guarantees, etc. TODO(rjrjr): Improve error messages
*/
+...@suppresswarnings("deprecation")
public class UiBinderWriter {
- private static final String GWT_URI = "urn:ui:com.google.gwt.uibinder";
+ private static final String BINDER_URI
= "urn:ui:com.google.gwt.uibinder";
private static final String BUNDLE_URI_SCHEME = "urn:with:";
private static final String PACKAGE_URI_SCHEME = "urn:import:";
@@ -141,7 +142,7 @@
/**
* Returns a list of the given type and all its superclasses and
implemented
* interfaces in a breadth-first traversal.
- *
+ *
* @param type the base type
* @return a breadth-first collection of its type hierarchy
*/
@@ -181,7 +182,7 @@
/**
* Class names of parsers for values of attributes with no namespace
prefix,
* keyed by method parameter signatures.
- *
+ *
* TODO(rjrjr) Seems like the attribute parsers belong in BeanParser,
which is
* the only thing that uses them.
*/
@@ -229,8 +230,6 @@
private String rendered;
- private String rootDeclaration;
-
UiBinderWriter(JClassType baseClass, String implClassName,
String templatePath, TypeOracle oracle, TreeLogger logger)
throws UnableToCompleteException {
@@ -282,7 +281,7 @@
* generate a unique dom id at runtime. Further code will be generated
to be
* run after widgets are instantiated, to use that dom id in a
getElementById
* call and assign the Element instance to its field.
- *
+ *
* @param fieldName The name of the field being declared
* @param parentElementExpression an expression to be evaluated at
runtime,
* which will return an Element that is an ancestor of this one
@@ -302,7 +301,7 @@
/**
* Declare a variable that will be filled at runtime with a unique id,
safe
* for use as a dom element's id attribute.
- *
+ *
* @return that variable's name.
*/
public String declareDomIdHolder() throws UnableToCompleteException {
@@ -332,14 +331,14 @@
* If this element has a gwt:field attribute, create a field for it of
the
* appropriate type, and return the field name. If no gwt:field
attribute is
* found, do nothing and return null
- *
+ *
* @return The new field name, or null if no field is created
*/
public String declareFieldIfNeeded(XMLElement elem)
throws UnableToCompleteException {
String fieldName = getFieldName(elem);
if (fieldName != null) {
- String typeName =
findWidgetOrElementType(elem).getQualifiedSourceName();
+ String typeName = findFieldType(elem).getQualifiedSourceName();
fieldManager.registerField(typeName, fieldName);
}
return fieldName;
@@ -378,15 +377,15 @@
/**
* Finds the JClassType that corresponds to this XMLElement, which must
be a
* Widget or an Element.
- *
+ *
* @throws UnableToCompleteException If no such widget class exists
* @throws RuntimeException if asked to handle a non-widget, non-DOM
element
*/
- public JClassType findWidgetOrElementType(XMLElement elem)
+ public JClassType findFieldType(XMLElement elem)
throws UnableToCompleteException {
String tagName = elem.getLocalName();
- if (!isWidget(elem)) {
+ if (!isWidgetElement(elem)) {
return findElementTypeForTag(tagName);
}
@@ -416,16 +415,6 @@
addStatement("%1$s.set%2$s(%3$s);", fieldName,
capitalizePropName(propName), value);
}
-
- /**
- * Provide the expression to generate only the root element of the UI.
If this
- * is called, the code to create the bulk of the ui will be in the
- * {...@link UiBinder#bindUi} method. If it is not called, all code to
create the
- * ui will be in the {...@link UiBinder#createUiRoot} method.
- */
- public void genRoot(String initString) {
- rootDeclaration = initString;
- }
/**
* Generates the code to set a string property.
@@ -450,7 +439,7 @@
if (params.length == 1) {
return new StrictAttributeParser();
}
-
+
return null;
}
@@ -472,7 +461,7 @@
/**
* Finds an attribute {...@link BundleAttributeParser} for the given xml
* attribute, if any, based on its namespace uri.
- *
+ *
* @return the parser or null
* @deprecated exists only to support {...@link BundleAttributeParser},
which
* will be leaving us soon.
@@ -525,33 +514,37 @@
return ownerClass;
}
- public boolean isWidget(XMLElement elem) {
+ public boolean isBinderElement(XMLElement elem) {
+ String uri = elem.getNamespaceUri();
+ return uri != null && BINDER_URI.equals(uri);
+ }
+
+ public boolean isWidgetElement(XMLElement elem) {
String uri = elem.getNamespaceUri();
return uri != null && uri.startsWith(PACKAGE_URI_SCHEME);
}
/**
- * Parses the widget associated with the specified element, and returns
the
- * name of the field (possibly private) that will hold the object it
- * describes.
- *
+ * Parses the object associated with the specified element, and returns
the
+ * name of the field (possibly private) that will hold it.
+ *
* @param elem the xml element to be parsed
* @return the name of the field containing the parsed widget
*/
- public String parseWidget(XMLElement elem) throws
UnableToCompleteException {
+ public String parseElementToField(XMLElement elem) throws
UnableToCompleteException {
if (elementParsers.isEmpty()) {
registerParsers();
}
// Get the class associated with this element.
- JClassType type = findWidgetOrElementType(elem);
+ JClassType type = findFieldType(elem);
// Declare its field.
String fieldName = declareField(type.getQualifiedSourceName(), elem);
FieldWriter field = fieldManager.lookup(fieldName);
- // Push the field that will hold this widget to the top of
parsedFieldStack
+ // Push the field that will hold this widget on top of the
parsedFieldStack
// to ensure that fields registered by its parsers will be noted as
// dependencies of the new widget. See registerField.
fieldManager.push(field);
@@ -567,7 +560,7 @@
/**
* Gives the writer the initializer to use for this field instead of the
* default GWT.create call.
- *
+ *
* @throws IllegalStateException if an initializer has already been set
*/
public void setFieldInitializer(String fieldName, String factoryMethod) {
@@ -591,7 +584,7 @@
* token, surrounded by plus signs. This is useful in strings to be
handed to
* setInnerHTML() and setText() calls, to allow a unique dom id
attribute or
* other runtime expression in the string.
- *
+ *
* @param expression
*/
public String tokenForExpression(String expression) {
@@ -618,8 +611,16 @@
* of its children being parsed as well).
*/
void parseDocument(Document doc) throws UnableToCompleteException {
+ JClassType uiBinderClass =
getOracle().findType(UiBinder.class.getName());
+ if (!baseClass.isAssignableTo(uiBinderClass)) {
+ die(baseClass.getName() + " must implement UiBinder");
+ }
+
Element documentElement = doc.getDocumentElement();
- this.rendered = tokenator.detokenate(parseElement(documentElement));
+ gwtPrefix = documentElement.lookupPrefix(BINDER_URI);
+
+ XMLElement elem = new XMLElement(documentElement, this);
+ this.rendered = tokenator.detokenate(parseDocumentElement(elem));
}
void write(PrintWriter printWriter) {
@@ -684,57 +685,9 @@
private MessagesWriter createMessagesWriter(String templatePath,
TreeLogger logger) {
- return new MessagesWriter(GWT_URI, logger, templatePath,
getPackageName(),
+ return new MessagesWriter(BINDER_URI, logger, templatePath,
getPackageName(),
this.implClassName);
}
-
- /**
- * Interprets <ui:with> elements.
- */
- private void createResource(XMLElement elem) throws
UnableToCompleteException {
- String resourceName = elem.consumeRequiredAttribute("name");
- String resourceTypeName = elem.consumeRequiredAttribute("type");
-
- JClassType resourceType = oracle.findType(resourceTypeName);
- if (resourceType == null) {
- die("In %s, no such type %s", elem, resourceTypeName);
- }
-
- if (elem.getAttributeCount() > 0) {
- die("In %s, should only find attributes \"name\" and \"type\"");
- }
-
- // TODO(rjrjr) This is redundant with BeanParser, I think. Probably
- // should refactor from here and there to FieldWriter
-
- FieldWriter fieldWriter = fieldManager.registerField(resourceTypeName,
- resourceName);
- OwnerField ownerField = getOwnerClass().getUiField(resourceName);
-
- // Perhaps it is provided via @UiField
-
- if (ownerField != null) {
- if (!resourceType.equals(ownerField.getType().getRawType())) {
- die("In %s, type must match %s", ownerField);
- }
-
- if (ownerField.isProvided()) {
- fieldWriter.setInitializer("owner." + ownerField.getName());
- return;
- }
- }
-
- // Nope. Maybe a @UiFactory will make it
-
- JMethod factoryMethod =
getOwnerClass().getUiFactoryMethod(resourceType);
- if (factoryMethod != null) {
- fieldWriter.setInitializer(String.format("owner.%s()",
- factoryMethod.getName()));
- }
-
- // If neither of the above, the FieldWriter's default GWT.create call
will
- // do just fine.
- }
/**
* Outputs a bundle resource for a given bundle attribute parser.
@@ -773,33 +726,17 @@
return elementClass;
}
-
- private void findResources(XMLElement binderElement)
- throws UnableToCompleteException {
- binderElement.consumeChildElements(new
XMLElement.Interpreter<Boolean>() {
- public Boolean interpretElement(XMLElement elem)
- throws UnableToCompleteException {
- if (!(GWT_URI.equals(elem.getNamespaceUri())
&& "with".equals(elem.getLocalName()))) {
- return false; // Not of interest, do not consume
- }
-
- createResource(elem);
-
- return true; // Yum
- }
- });
- }
/**
* Inspects this element for a gwt:field attribute. If one is found, the
* attribute is consumed and its value returned.
- *
+ *
* @return The field name declared by an element, or null if none is
declared
*/
private String getFieldName(XMLElement elem) throws
UnableToCompleteException {
String fieldName = null;
boolean hasOldSchoolId = false;
- if (elem.hasAttribute("id") && isWidget(elem)) {
+ if (elem.hasAttribute("id") && isWidgetElement(elem)) {
hasOldSchoolId = true;
// If an id is specified on the element, use that.
fieldName = elem.consumeAttribute("id");
@@ -855,7 +792,7 @@
/**
* Find a set of element parsers for the given ui type.
- *
+ *
* The list of parsers will be returned in order from most- to
least-specific.
*/
private Iterable<ElementParser> getParsersForClass(JClassType type) {
@@ -864,7 +801,7 @@
/*
* Let this non-widget parser go first (it finds <m:attribute/>
elements).
* Any other such should land here too.
- *
+ *
* TODO(rjrjr) Need a scheme to associate these with a namespace uri or
* something?
*/
@@ -890,11 +827,6 @@
return parsers;
}
-
- private String instanceBinderType() {
- return String.format("AbstractUiBinder.InstanceBinder<%s, %s>",
- uiRootType.getName(), uiOwnerType.getName());
- }
/**
* Writes a field setter if the field is not provided and the field
class is
@@ -917,37 +849,28 @@
}
}
- private String parseElement(Element documentElement)
+ /**
+ * Parse the document element and return the source of the Java
+ * class that will implement its UiBinder.
+ */
+ private String parseDocumentElement(XMLElement elem)
throws UnableToCompleteException {
- gwtPrefix = documentElement.lookupPrefix(GWT_URI);
-
- JClassType uiBinderClass =
getOracle().findType(UiBinder.class.getName());
- if (!baseClass.isAssignableTo(uiBinderClass)) {
- die(baseClass.getName() + " must implement UiBinder");
- }
-
- XMLElement elem = new XMLElement(documentElement, this);
- findResources(elem);
- messages.findMessagesConfig(elem);
- elem = elem.consumeSingleChildElement();
- String rootField = parseWidget(elem);
+
+ String rootField = new UiBinderParser(this, messages,
fieldManager).parse(
+ elem);
StringWriter stringWriter = new StringWriter();
IndentedWriter niceWriter = new IndentedWriter(
new PrintWriter(stringWriter));
- if (rootDeclaration != null) {
- writeBifurcatedBinder(niceWriter);
- } else {
- writeEagerBinder(niceWriter, rootField);
- }
+ writeBinder(niceWriter, rootField);
return stringWriter.toString();
}
/**
* Parses a package uri (i.e. package://com.google...).
- *
+ *
* @throws UnableToCompleteException on bad package name
*/
private JPackage parseNamespacePackage(String ns)
@@ -970,7 +893,6 @@
// TODO(rjrjr): Allow third-party parsers to register themselves
// automagically, according to http://b/issue?id=1867118
- // Parses the root element in UiBinder, for non-widget templates
addParser("com.google.gwt.dom.client.Element",
"com.google.gwt.uibinder.parsers.DomElementParser");
@@ -1016,59 +938,6 @@
niceWriter.write(s);
}
}
-
- /**
- * Writes a binder that creates a root element in
- * {...@link UiBinder#createUiRoot}, and the rest of the dom in
- * {...@link UiBinder#bindUi}. See a sample in
{...@link "http://go/gwt-bif-binder"}
- */
- private void writeBifurcatedBinder(IndentedWriter w)
- throws UnableToCompleteException {
- writePackage(w);
- writeImports(w);
- w.newline();
-
- writeClassOpen(w);
- writeStatics(w);
- w.newline();
-
- // createUiRoot method
- w.write("public %s createUiRoot(final %s owner) {",
uiRootType.getName(),
- uiOwnerType.getName());
- w.indent();
- w.write("return %s;", rootDeclaration);
- w.outdent();
- w.write("}");
- w.newline();
-
- // bindUi method
- w.write("public void bindUi(%s root, %s owner) {",
uiRootType.getName(),
- uiOwnerType.getName());
- w.indent();
-
- writeGwtFields(w);
- writeAddedStatements(w);
- writeInitStatements(w);
- writeFieldSetters(w);
- writeHandlers(w);
- w.newline();
-
- // close bindUi method
- w.outdent();
- w.write("}");
- w.newline();
-
- // Close class
- w.outdent();
- w.write("}");
- }
-
- private void writeClassOpen(IndentedWriter w) {
- w.write("public class %s extends AbstractUiBinder<%s, %s> \n"
- + "implements %s {", implClassName, uiRootType.getName(),
- uiOwnerType.getName(), baseClass.getName());
- w.indent();
- }
/**
* Writes a binder that creates the entire ui in {...@link
UiBinder#createUiRoot}
@@ -1082,16 +951,13 @@
* without the map, but am stumped.
* <p>
* See a sample in {...@link "http://go/gwt-eager-binder"}
- *
+ *
* @throws UnableToCompleteException
*/
- private void writeEagerBinder(IndentedWriter w, String rootField)
+ private void writeBinder(IndentedWriter w, String rootField)
throws UnableToCompleteException {
writePackage(w);
- // Add a few extra imports
- w.write("import java.util.HashMap;");
- w.write("import java.util.Map;");
writeImports(w);
w.newline();
@@ -1099,81 +965,40 @@
writeStatics(w);
w.newline();
- // Map from root objects to instance binders
- w.write("final Map<%1$s, %2$s> rootToBindCommand "
- + "= new HashMap<%1$s, %2$s>();", uiRootType.getName(),
- instanceBinderType());
- w.newline();
-
- // createUiRoot method
- w.write("public %s createUiRoot(final %s owner) {",
uiRootType.getName(),
+ // createAndBindUi method
+ w.write("public %s createAndBindUi(final %s owner) {",
uiRootType.getName(),
uiOwnerType.getName());
w.indent();
w.newline();
- // write instance binder anon class
- w.write("%1$s instanceBinder = new %1$s() {", instanceBinderType());
- w.indent();
writeGwtFields(w);
w.newline();
- w.write("public %s makeUi() {", uiRootType.getName());
- w.indent();
writeAddedStatements(w);
w.newline();
+
writeInitStatements(w);
w.newline();
+
writeHandlers(w);
w.newline();
- w.write("return %s;", rootField);
- w.outdent();
- w.write("}");
- w.newline();
-
- w.write("public void doBind(%s owner) {", uiOwnerType.getName());
- w.indent();
+
writeFieldSetters(w);
- w.outdent();
- w.write("}");
-
- // close instance binder anon class
- w.outdent();
- w.write("};");
- w.newline();
-
- // use instance binder to make the ui, and store the binder away
- // to wait for the call to bindUi
- w.write("%s root = instanceBinder.makeUi();", uiRootType.getName());
- w.write("rootToBindCommand.put(root, instanceBinder);");
- w.write("return root;");
-
- // close createUiRoot method
+
+ w.write("return %s;", rootField);
w.outdent();
w.write("}");
- w.newline();
-
- // bindUi method
- w.write("public void bindUi(%s root, %s owner) {",
uiRootType.getName(),
- uiOwnerType.getName());
- w.indent();
-
- // fetch command and run it
- w.write("%s instanceBinder = rootToBindCommand.remove(root);",
- instanceBinderType());
- w.write("assert instanceBinder != null : "
- + "\"Attempted to bind an unknown UI, or to bind the same one
twice\""
- + ";");
- w.write("instanceBinder.doBind(owner);");
-
- // close bindUiMethod
- w.outdent();
- w.write("}");
- w.newline();
// Close class
w.outdent();
w.write("}");
}
+
+ private void writeClassOpen(IndentedWriter w) {
+ w.write("public class %s implements UiBinder<%s, %s>, %s {",
implClassName,
+ uiRootType.getName(), uiOwnerType.getName(), baseClass.getName());
+ w.indent();
+ }
/**
* Write the statements to fill in the fields of the UI owner.
@@ -1211,7 +1036,7 @@
* gwt:field in the template. For those that have not had constructor
* generation suppressed, emit GWT.create() calls instantiating them (or
die
* if they have no default constructor).
- *
+ *
* @throws UnableToCompleteException on constructor problem
*/
private void writeGwtFields(IndentedWriter niceWriter)
@@ -1243,9 +1068,7 @@
private void writeImports(IndentedWriter w) {
w.write("import com.google.gwt.core.client.GWT;");
- w.write("import com.google.gwt.dom.client.Element;");
- w.write("import com.google.gwt.uibinder.client.AbstractUiBinder;");
- w.write("import com.google.gwt.uibinder.client.DomHolder;");
+ w.write("import com.google.gwt.uibinder.client.UiBinder;");
w.write("import com.google.gwt.uibinder.client.UiBinderUtil;");
w.write("import %s.%s;", uiRootType.getPackage().getName(),
uiRootType.getName());
=======================================
---
/trunk/user/src/com/google/gwt/uibinder/rebind/messages/PlaceholderInterpreter.java
Wed Aug 5 20:27:52 2009
+++
/trunk/user/src/com/google/gwt/uibinder/rebind/messages/PlaceholderInterpreter.java
Tue Sep 1 15:14:29 2009
@@ -86,7 +86,7 @@
return nextPlaceholder(name, example, value);
}
- if (uiWriter.isWidget(elem)) {
+ if (uiWriter.isWidgetElement(elem)) {
uiWriter.die("Found %s in a message that cannot contain widgets",
elem);
}
return null;
=======================================
---
/trunk/user/src/com/google/gwt/uibinder/rebind/model/OwnerFieldClass.java
Wed Aug 5 20:27:52 2009
+++
/trunk/user/src/com/google/gwt/uibinder/rebind/model/OwnerFieldClass.java
Tue Sep 1 15:14:29 2009
@@ -57,7 +57,7 @@
OwnerFieldClass clazz = FIELD_CLASSES.get(forType);
if (clazz == null) {
clazz = new OwnerFieldClass(forType);
- FIELD_CLASSES.put(forType, clazz);
+ FIELD_CLASSES.put(forType, clazz);
}
return clazz;
}
@@ -96,6 +96,10 @@
*/
public JMethod getSetter(String propertyName)
throws UnableToCompleteException {
+ // TODO(rjrjr) This fails for CheckBox#setValue(Boolean) because it
+ // also finds CheckBox#setValue(Boolean, Boolean). Must fix for 2.0,
+ // when CheckBox#setChecked will go away and CheckBox#setValue must be
used
+
if (ambiguousSetters != null &&
ambiguousSetters.contains(propertyName)) {
// TODO(rdamazio): proper logging
System.out.println("Ambiguous setter requested: " + rawType.getName()
@@ -215,7 +219,7 @@
for (String propertyName : allSetters.keySet()) {
Collection<JMethod> propertySetters = allSetters.get(propertyName);
JMethod setter = disambiguateSetters(propertySetters);
-
+
// If no setter could be disambiguated for this property, add it to
the
// set of ambiguous setters. This is later consulted if and only if
the
// setter is used.
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/sample/client/DomBasedUi.java
Wed Aug 5 20:27:52 2009
+++ /trunk/user/src/com/google/gwt/uibinder/sample/client/DomBasedUi.java
Tue Sep 1 15:14:29 2009
@@ -24,7 +24,6 @@
import com.google.gwt.resources.client.CssResource.Strict;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiTemplate;
/**
* Sample use of a {...@code UiBinder} with no dependency on
@@ -48,8 +47,6 @@
String bodyFont();
}
- @UiTemplate("DomBasedUi.ui.xml")
- // Actually, it will default to this name w/o annotation
interface Binder extends UiBinder<Element, DomBasedUi> {
}
@@ -60,34 +57,20 @@
@UiField SpanElement nameSpan;
@UiField Element tmElement;
-
- private final Element root;
- private final String yourNameHere;
-
- private boolean gotRoot = false;
+ @UiField Element root;
public DomBasedUi(String yourNameHere) {
- this.yourNameHere = yourNameHere;
- root = binder.createUiRoot(this);
-
// Inject only once.
if (!stylesInjected) {
StyleInjector.injectStylesheet(res.style().getText());
stylesInjected = true;
}
- // Or if you don't care about deferred rendering...
-// root = binder.createAndBindUi(this);
-// nameSpan.setInnerText(yourNameHere);
- // ...and delete gotRoot and the bulk of #getElement
+ binder.createAndBindUi(this);
+ nameSpan.setInnerText(yourNameHere);
}
public Element getRoot() {
- if (!gotRoot) {
- binder.bindUi(root, this);
- nameSpan.setInnerText(yourNameHere);
- gotRoot = true;
- }
return root;
}
}
=======================================
--- /trunk/user/src/com/google/gwt/uibinder/sample/client/DomBasedUi.ui.xml
Wed Aug 5 20:27:52 2009
+++ /trunk/user/src/com/google/gwt/uibinder/sample/client/DomBasedUi.ui.xml
Tue Sep 1 15:14:29 2009
@@ -14,14 +14,10 @@
<ui:UiBinder
xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:res='urn:with:com.google.gwt.uibinder.sample.client.DomBasedUi.Resources'
-
- ui:defaultLocale="en_us"
- ui:generateKeys="com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator"
- ui:generateFormat="com.google.gwt.i18n.rebind.format.PropertiesFormat"
- ui:generateFilename="myapp_translate_source"
- ui:generateLocales="default"
>
- <div res:class="style.bodyColor style.bodyFont">
+ <div ui:field='root' res:class="style.bodyColor style.bodyFont"
+ title="The title of this div is localizable">
+ <ui:attribute name='title'/>
Hello, <span ui:field="nameSpan" />.
<ui:msg>How goes it?</ui:msg>
<h2 res:class="style.bodyColor style.bodyFont">Placeholders in
localizables</h2>
--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---