Add <ui:import> to UiBinder to allow access to static fields. Patch by: bobv Review by: jgw Suggested by: sonnyf Review at http://gwt-code-reviews.appspot.com/781801 git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8595 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java index 8293a35..b950a24 100644 --- a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java +++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
@@ -17,7 +17,10 @@ import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JField; import com.google.gwt.core.ext.typeinfo.JMethod; +import com.google.gwt.core.ext.typeinfo.JPrimitiveType; +import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.resources.client.CssResource; import com.google.gwt.resources.client.DataResource; @@ -39,28 +42,35 @@ public class UiBinderParser { private enum Resource { - data { + DATA { @Override void create(UiBinderParser parser, XMLElement elem) throws UnableToCompleteException { parser.createData(elem); } }, - image { + IMAGE { @Override void create(UiBinderParser parser, XMLElement elem) throws UnableToCompleteException { parser.createImage(elem); } }, - style { + IMPORT { + @Override + void create(UiBinderParser parser, XMLElement elem) + throws UnableToCompleteException { + parser.createImport(elem); + } + }, + STYLE { @Override void create(UiBinderParser parser, XMLElement elem) throws UnableToCompleteException { parser.createStyle(elem); } }, - with { + WITH { @Override void create(UiBinderParser parser, XMLElement elem) throws UnableToCompleteException { @@ -74,8 +84,9 @@ private static final String FLIP_RTL_ATTRIBUTE = "flipRtl"; private static final String FIELD_ATTRIBUTE = "field"; - private static final String SOURCE_ATTRIBUTE = "src"; private static final String REPEAT_STYLE_ATTRIBUTE = "repeatStyle"; + private static final String SOURCE_ATTRIBUTE = "src"; + private static final String TYPE_ATTRIBUTE = "type"; // TODO(rjrjr) Make all the ElementParsers receive their dependencies via // constructor like this one does, and make this an ElementParser. I want @@ -130,7 +141,7 @@ private JClassType consumeCssResourceType(XMLElement elem) throws UnableToCompleteException { - String typeName = elem.consumeRawAttribute("type", null); + String typeName = elem.consumeRawAttribute(TYPE_ATTRIBUTE, null); if (typeName == null) { return cssResourceType; } @@ -140,7 +151,7 @@ private JClassType consumeTypeAttribute(XMLElement elem) throws UnableToCompleteException { - String resourceTypeName = elem.consumeRequiredRawAttribute("type"); + String resourceTypeName = elem.consumeRequiredRawAttribute(TYPE_ATTRIBUTE); JClassType resourceType = oracle.findType(resourceTypeName); if (resourceType == null) { @@ -194,6 +205,49 @@ } /** + * Process <code><ui:import field="com.example.Blah.CONSTANT"></code>. + */ + private void createImport(XMLElement elem) throws UnableToCompleteException { + String rawFieldName = elem.consumeRequiredRawAttribute(FIELD_ATTRIBUTE); + if (elem.getAttributeCount() > 0) { + writer.die(elem, "Should only find attribute \"%s\"", FIELD_ATTRIBUTE); + } + + int idx = rawFieldName.lastIndexOf('.'); + if (idx < 1) { + writer.die(elem, "Attribute %s does not look like a static import " + + "reference", FIELD_ATTRIBUTE); + } + String enclosingName = rawFieldName.substring(0, idx); + String constantName = rawFieldName.substring(idx + 1); + + JClassType enclosingType = oracle.findType(enclosingName); + if (enclosingType == null) { + writer.die(elem, "Unable to locate type %s", enclosingName); + } + + if ("*".equals(constantName)) { + for (JField field : enclosingType.getFields()) { + if (!field.isStatic()) { + continue; + } else if (field.isPublic()) { + // OK + } else if (field.isProtected() || field.isPrivate()) { + continue; + } else if (!enclosingType.getPackage().equals( + writer.getOwnerClass().getOwnerType().getPackage())) { + // package-protected in another package + continue; + } + createSingleImport(elem, enclosingType, enclosingName + "." + + field.getName(), field.getName()); + } + } else { + createSingleImport(elem, enclosingType, rawFieldName, constantName); + } + } + + /** * Interprets <ui:with> elements. */ private void createResource(XMLElement elem) throws UnableToCompleteException { @@ -235,6 +289,31 @@ */ } + private void createSingleImport(XMLElement elem, JClassType enclosingType, + String rawFieldName, String constantName) + throws UnableToCompleteException { + JField field = enclosingType.findField(constantName); + if (field == null) { + writer.die(elem, "Unable to locate a field named %s in %s", constantName, + enclosingType.getQualifiedSourceName()); + } else if (!field.isStatic()) { + writer.die(elem, "Field %s in type %s is not static", constantName, + enclosingType.getQualifiedSourceName()); + } + + JType importType = field.getType(); + JClassType fieldType; + if (importType instanceof JPrimitiveType) { + fieldType = oracle.findType(((JPrimitiveType) importType).getQualifiedBoxedSourceName()); + } else { + fieldType = (JClassType) importType; + } + + FieldWriter fieldWriter = fieldManager.registerField(fieldType, + constantName); + fieldWriter.setInitializer(rawFieldName); + } + private void createStyle(XMLElement elem) throws UnableToCompleteException { String body = elem.consumeUnescapedInnerText(); String[] source = elem.consumeRawArrayAttribute(SOURCE_ATTRIBUTE); @@ -283,8 +362,8 @@ if (writer.isBinderElement(elem)) { try { - Resource.valueOf(elem.getLocalName()).create(UiBinderParser.this, - elem); + Resource.valueOf(elem.getLocalName().toUpperCase()).create( + UiBinderParser.this, elem); } catch (IllegalArgumentException e) { writer.die(elem, "Unknown tag %s, or is not appropriate as a top level element",
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/OwnerClass.java b/user/src/com/google/gwt/uibinder/rebind/model/OwnerClass.java index f3a453f..94eac5e 100644 --- a/user/src/com/google/gwt/uibinder/rebind/model/OwnerClass.java +++ b/user/src/com/google/gwt/uibinder/rebind/model/OwnerClass.java
@@ -67,6 +67,8 @@ private final MortalLogger logger; + private final JClassType ownerType; + /** * Constructor. * @@ -75,11 +77,16 @@ */ public OwnerClass(JClassType ownerType, MortalLogger logger) throws UnableToCompleteException { this.logger = logger; + this.ownerType = ownerType; findUiFields(ownerType); findUiFactories(ownerType); findUiHandlers(ownerType); } + public JClassType getOwnerType() { + return ownerType; + } + /** * Returns the method annotated with @UiFactory which returns the given type. *
diff --git a/user/test/com/google/gwt/uibinder/test/client/Constants.java b/user/test/com/google/gwt/uibinder/test/client/Constants.java new file mode 100644 index 0000000..e387a7a --- /dev/null +++ b/user/test/com/google/gwt/uibinder/test/client/Constants.java
@@ -0,0 +1,42 @@ +/* + * Copyright 2010 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.test.client; + +/** + * Used to test static imports in UiBinder templates. + */ +public class Constants { + /** + * Tests enum imports. + */ + public enum MyEnum { + ENUM_1, ENUM_2; + } + + /** + * Used to test a wildcard import. + */ + public static class Inner { + String instance = "instance"; + public static String CONST_BAR = "Bar"; + static String CONST_BAZ = "Baz"; + protected static String PROTECTED = "protected"; + @SuppressWarnings("unused") + private static String PRIVATE = "private"; + } + + public static String CONST_FOO = "Foo"; +}
diff --git a/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java b/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java index d9ef1f9..58e33b3 100644 --- a/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java +++ b/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java
@@ -448,19 +448,19 @@ * AbsolutePanelParserTest and AbsolutePanelTest are enough to make up for * the lack. Leaving this here as a warning to the next guy. */ -// { -// Widget w = widgetUi.myAbsolutePanelItemA; -// assertNotNull("Widget exists", w); -// assertEquals("Widget has left", 1, p.getWidgetLeft(w)); -// assertEquals("Widget has top", 2, p.getWidgetTop(w)); -// } -// -// { -// Widget w = widgetUi.myAbsolutePanelItemC; -// assertNotNull("Widget exists", w); -// assertEquals("Widget has left", 10, p.getWidgetLeft(w)); -// assertEquals("Widget has top", 20, p.getWidgetTop(w)); -// } + // { + // Widget w = widgetUi.myAbsolutePanelItemA; + // assertNotNull("Widget exists", w); + // assertEquals("Widget has left", 1, p.getWidgetLeft(w)); + // assertEquals("Widget has top", 2, p.getWidgetTop(w)); + // } + // + // { + // Widget w = widgetUi.myAbsolutePanelItemC; + // assertNotNull("Widget exists", w); + // assertEquals("Widget has left", 10, p.getWidgetLeft(w)); + // assertEquals("Widget has top", 20, p.getWidgetTop(w)); + // } } public void testStringAttributeIgnoresStaticSetter() { @@ -538,6 +538,16 @@ widgetUi.simpleSpriteParagraph.getOffsetHeight()); } + public void testStaticImport() { + assertEquals(Constants.CONST_FOO, + widgetUi.bracedParagraph.getAttribute("foo")); + assertEquals(Constants.Inner.CONST_BAR + " " + Constants.Inner.CONST_BAZ, + widgetUi.bracedParagraph.getAttribute("bar")); + assertEquals(Constants.MyEnum.ENUM_1.name() + " " + + Constants.MyEnum.ENUM_2.name(), + widgetUi.bracedParagraph.getAttribute("enum")); + } + public void suppressForIEfail_testBizarrelyElementedWidgets() { assertInOrder(widgetUi.widgetCrazyTable.getInnerHTML().toLowerCase(), "<td>they have been known</td>", "<td>to write widgets</td>",
diff --git a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml index 702cdb8..1376eaa 100644 --- a/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml +++ b/user/test/com/google/gwt/uibinder/test/client/WidgetBasedUi.ui.xml
@@ -72,6 +72,17 @@ for a resource to provide arbitrary objects to arbitrary attributes (look for FooLabel) </ui:with> +<ui:import field='com.google.gwt.uibinder.test.client.Constants.CONST_FOO'> + Tests the static import of a single constant into the local namespace. +</ui:import> + +<ui:import field='com.google.gwt.uibinder.test.client.Constants.Inner.*'> + Tests the static import of multiple constants into the local namespace. +</ui:import> + +<ui:import field='com.google.gwt.uibinder.test.client.Constants.MyEnum.*'> + Tests the static import of an enum into the local namespace. +</ui:import> <!-- Tests creating a CssResource from an external file. --> @@ -164,7 +175,10 @@ </gwt:Dock> <gwt:Dock direction='CENTER'> <gwt:HTMLPanel> - <p ui:field='bracedParagraph' fnord='blah di blah {{foo: "bar"} di blah'><ui:msg>This is a demonstration and test bed of GWT's shiny UiBinder + <p ui:field='bracedParagraph' fnord='blah di blah {{foo: "bar"} di blah' + foo='{CONST_FOO}' bar='{CONST_BAR} {CONST_BAZ}' + enum='{ENUM_1.name} {ENUM_2.name}' ><ui:msg>This is a + demonstration and test bed of GWT's shiny UiBinder package. At the moment it works mainly as described in <a href="http://code.google.com/p/google-web-toolkit-incubator/wiki/DeclarativeUi" ui:ph="oldBlogLink">