Promoting LazyDomElement to be used externally. LazyDomElement can be used to boost rendering time. Today, html elements marked with "ui:field" need to call getElementById() by the time the template is created even if is not used. LazyDomElement delays this. Review at http://gwt-code-reviews.appspot.com/1427809 git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10129 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/uibinder/client/LazyDomElement.java b/user/src/com/google/gwt/uibinder/client/LazyDomElement.java new file mode 100644 index 0000000..10bb9c9 --- /dev/null +++ b/user/src/com/google/gwt/uibinder/client/LazyDomElement.java
@@ -0,0 +1,77 @@ +/* + * Copyright 2011 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.Document; +import com.google.gwt.dom.client.Element; + +/** + * Wraps a call to a DOM element. LazyDomElement can boost performance of html + * elements and delay calls to getElementById() to when the element is actually + * used. But note that it will throw a RuntimeException in case the element is + * accessed but not yet attached in the DOM tree. + * + * Usage example: + * + * <b>Template:</b> + * <pre> + * <gwt:HTMLPanel> + * <div ui:field="myDiv" /> + * </gwt:HTMLPanel> + * </pre> + * + * <b>Class:</b> + * <pre> + * @UiField LazyDomElement<DivElement> myDiv; + * + * public setText(String text) { + * myDiv.get().setInnerHtml(text); + * } + * </pre> + * + * @param <T> the Element type associated + */ +public class LazyDomElement<T extends Element> { + + private T element; + private final String domId; + + /** + * Creates an instance to fetch the element with the given id. + */ + public LazyDomElement(String domId) { + this.domId = domId; + } + + /** + * Returns the dom element. + * + * @return the dom element + * @throws RuntimeException if the element cannot be found + */ + public T get() { + if (element == null) { + element = Document.get().getElementById(domId).cast(); + if (element == null) { + throw new RuntimeException("Cannot find element with id \"" + domId + + "\". Perhaps it is not attached to the document body."); + } + element.removeAttribute("id"); + } + return element; + } +}
diff --git a/user/src/com/google/gwt/uibinder/client/UiBinderUtil.java b/user/src/com/google/gwt/uibinder/client/UiBinderUtil.java index 05cd36f..7e96db3 100644 --- a/user/src/com/google/gwt/uibinder/client/UiBinderUtil.java +++ b/user/src/com/google/gwt/uibinder/client/UiBinderUtil.java
@@ -28,27 +28,6 @@ public class UiBinderUtil { /** - * A helper class to enable lazy creation of DOM elements. - */ - public static class LazyDomElement { - - private Element element; - private final String domId; - - public LazyDomElement(String domId) { - this.domId = domId; - } - - public Element get() { - if (element == null) { - element = Document.get().getElementById(domId).cast(); - element.removeAttribute("id"); - } - return element; - } - } - - /** * Temporary attachment record that keeps track of where an element was * before attachment. Use the detach method to put things back. *
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java index 1b9ba33..c10083c 100644 --- a/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java +++ b/user/src/com/google/gwt/uibinder/elementparsers/WidgetInterpreter.java
@@ -16,9 +16,9 @@ package com.google.gwt.uibinder.elementparsers; import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.uibinder.client.UiBinderUtil.LazyDomElement; import com.google.gwt.uibinder.rebind.FieldManager; import com.google.gwt.uibinder.rebind.FieldWriter; +import com.google.gwt.uibinder.client.LazyDomElement; import com.google.gwt.uibinder.rebind.UiBinderWriter; import com.google.gwt.uibinder.rebind.XMLElement; @@ -97,8 +97,8 @@ // Register a DOM id field. String lazyDomElementPath = LazyDomElement.class.getCanonicalName(); FieldWriter elementWriter = fieldManager.registerField(lazyDomElementPath, elementPointer); - elementWriter.setInitializer(String.format("new %s(%s)", - lazyDomElementPath, fieldManager.convertFieldToGetter(idHolder))); + elementWriter.setInitializer(String.format("new %s<Element>(%s)", + lazyDomElementPath, fieldManager.convertFieldToGetter(idHolder))); // Add attach/detach sections for this element. fieldWriter.addAttachStatement("%s.get();",
diff --git a/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java b/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java index 93f0090..bdbb66d 100644 --- a/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java +++ b/user/src/com/google/gwt/uibinder/elementparsers/WidgetPlaceholderInterpreter.java
@@ -18,9 +18,9 @@ import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.TypeOracle; -import com.google.gwt.uibinder.client.UiBinderUtil.LazyDomElement; import com.google.gwt.uibinder.rebind.FieldManager; import com.google.gwt.uibinder.rebind.FieldWriter; +import com.google.gwt.uibinder.client.LazyDomElement; import com.google.gwt.uibinder.rebind.UiBinderWriter; import com.google.gwt.uibinder.rebind.XMLElement; import com.google.gwt.uibinder.rebind.messages.MessageWriter; @@ -131,7 +131,7 @@ String elementPointer = idHolder + "Element"; FieldWriter elementWriter = fieldManager.registerField( lazyDomElementPath, elementPointer); - elementWriter.setInitializer(String.format("new %s(%s)", + elementWriter.setInitializer(String.format("new %s<Element>(%s)", lazyDomElementPath, fieldManager.convertFieldToGetter(idHolder))); // Add attach/detach sections for this element.
diff --git a/user/src/com/google/gwt/uibinder/rebind/FieldManager.java b/user/src/com/google/gwt/uibinder/rebind/FieldManager.java index 5abd2a9..fee8dca 100644 --- a/user/src/com/google/gwt/uibinder/rebind/FieldManager.java +++ b/user/src/com/google/gwt/uibinder/rebind/FieldManager.java
@@ -21,6 +21,7 @@ import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.uibinder.rebind.model.ImplicitCssResource; +import com.google.gwt.uibinder.rebind.model.OwnerField; import com.google.gwt.uibinder.rebind.model.OwnerClass; import java.util.Arrays; @@ -211,6 +212,27 @@ } /** + * Register a new field for {@link com.google.gwt.uibinder.client.LazyDomElement} + * types. LazyDomElement fields can only be associated with html elements. Example: + * + * <li>LazyDomElement<DivElement> -> <div></li> + * <li>LazyDomElement<Element> -> <div></li> + * <li>LazyDomElement<SpanElement> -> <span></li> + * + * @param templateFieldType the html type to bind, eg, SpanElement, DivElement, etc + * @param ownerField the field instance + */ + public FieldWriter registerFieldForLazyDomElement(JClassType templateFieldType, + OwnerField ownerField) throws UnableToCompleteException { + if (ownerField == null) { + throw new RuntimeException("Cannot register a null owner field for LazyDomElement."); + } + FieldWriter field = new FieldWriterOfLazyDomElement( + templateFieldType, ownerField, logger); + return registerField(ownerField.getName(), field); + } + + /** * Used to declare fields of a type (other than CssResource) that is to be * generated. If your field will hold a reference of an existing type, see * {@link #registerField}. For generated CssResources, see
diff --git a/user/src/com/google/gwt/uibinder/rebind/FieldWriterOfLazyDomElement.java b/user/src/com/google/gwt/uibinder/rebind/FieldWriterOfLazyDomElement.java new file mode 100644 index 0000000..66aba1c --- /dev/null +++ b/user/src/com/google/gwt/uibinder/rebind/FieldWriterOfLazyDomElement.java
@@ -0,0 +1,72 @@ +/* + * Copyright 2011 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.JParameterizedType; +import com.google.gwt.uibinder.rebind.model.OwnerField; + +/** + * Implementation of FieldWriter for a {@link com.google.gwt.uibinder.client.LazyDomElement}. + */ +public class FieldWriterOfLazyDomElement extends AbstractFieldWriter { + + /** + * The field type for @UiField LazyDomElement<T>. + */ + private final JParameterizedType ownerFieldType; + + /** + * The T parameter in LazyDomElement<T>. + */ + private final JClassType parameterType; + + public FieldWriterOfLazyDomElement(JClassType templateFieldType, + OwnerField ownerField, MortalLogger logger) throws UnableToCompleteException { + super(ownerField.getName(), FieldWriterType.DEFAULT, logger); + + // ownerFieldType null means LazyDomElement is not parameterized. + this.ownerFieldType = ownerField.getRawType().isParameterized(); + if (ownerFieldType == null) { + logger.die("LazyDomElement must be of type LazyDomElement<? extends Element>."); + } + + // Parameterized LazyDomElement<T> must match its respective html element. + // Example: + // DivElement -> div + // SpanElement -> span + parameterType = ownerFieldType.getTypeArgs()[0]; + if (!templateFieldType.isAssignableTo(parameterType)) { + logger.die("Field %s is %s<%s>, must be %s<%s>.", ownerField.getName(), + ownerFieldType.getQualifiedSourceName(), parameterType, + ownerFieldType.getQualifiedSourceName(), templateFieldType); + } + } + + public JClassType getAssignableType() { + return ownerFieldType; + } + + public JClassType getInstantiableType() { + return ownerFieldType; + } + + public String getQualifiedSourceName() { + return ownerFieldType.getQualifiedSourceName() + + "<" + parameterType.getQualifiedSourceName() + ">"; + } +}
diff --git a/user/src/com/google/gwt/uibinder/rebind/FieldWriterType.java b/user/src/com/google/gwt/uibinder/rebind/FieldWriterType.java index 2747e24..df4e88c 100644 --- a/user/src/com/google/gwt/uibinder/rebind/FieldWriterType.java +++ b/user/src/com/google/gwt/uibinder/rebind/FieldWriterType.java
@@ -21,9 +21,10 @@ */ enum FieldWriterType { - GENERATED_BUNDLE(4), - GENERATED_CSS(3), - IMPORTED(2), // ui:with clauses. + GENERATED_BUNDLE(5), + GENERATED_CSS(4), + IMPORTED(3), // ui:with clauses. + DOM_ID_HOLDER(2), DEFAULT(1); /**
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java index 1496c59..8e32ab0 100644 --- a/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java +++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderWriter.java
@@ -25,8 +25,8 @@ import com.google.gwt.uibinder.attributeparsers.AttributeParsers; import com.google.gwt.uibinder.attributeparsers.BundleAttributeParser; import com.google.gwt.uibinder.attributeparsers.BundleAttributeParsers; +import com.google.gwt.uibinder.client.LazyDomElement; import com.google.gwt.uibinder.client.UiBinder; -import com.google.gwt.uibinder.client.UiBinderUtil.LazyDomElement; import com.google.gwt.uibinder.elementparsers.AttributeMessageParser; import com.google.gwt.uibinder.elementparsers.BeanParser; import com.google.gwt.uibinder.elementparsers.ElementParser; @@ -198,6 +198,8 @@ private final JClassType attachableClassType; + private final JClassType lazyDomElementClass; + private final OwnerClass ownerClass; private final FieldManager fieldManager; @@ -279,6 +281,7 @@ uiOwnerType = typeArgs[1]; attachableClassType = oracle.findType(Attachable.class.getCanonicalName()); + lazyDomElementClass = oracle.findType(LazyDomElement.class.getCanonicalName()); ownerClass = new OwnerClass(uiOwnerType, logger, uiBinderCtx); bundleClass = new ImplicitClientBundle(baseClass.getPackage().getName(), @@ -373,13 +376,26 @@ if (useLazyWidgetBuilders) { // Create and initialize the dom field with LazyDomElement. FieldWriter field = fieldManager.require(fieldName); - field.setInitializer(formatCode("new %s(%s).get().cast()", - LazyDomElement.class.getCanonicalName(), - fieldManager.convertFieldToGetter(name))); - // The dom must be created by its ancestor. - fieldManager.require(ancestorField).addAttachStatement( - fieldManager.convertFieldToGetter(fieldName) + ";"); + /** + * But if the owner field is an instance of LazyDomElement then the code + * can be optimized, no cast is needed and the getter doesn't need to be + * called in its ancestral. + */ + if (isOwnerFieldLazyDomElement(fieldName)) { + field.setInitializer(formatCode("new %s(%s)", + field.getQualifiedSourceName(), + fieldManager.convertFieldToGetter(name))); + } else { + + field.setInitializer(formatCode("new %s(%s).get().cast()", + LazyDomElement.class.getCanonicalName(), + fieldManager.convertFieldToGetter(name))); + + // The dom must be created by its ancestor. + fieldManager.require(ancestorField).addAttachStatement( + fieldManager.convertFieldToGetter(fieldName) + ";"); + } } else { setFieldInitializer(fieldName, "null"); addInitStatement( @@ -399,7 +415,7 @@ */ public String declareDomIdHolder() throws UnableToCompleteException { String domHolderName = "domId" + domId++; - FieldWriter domField = fieldManager.registerField( + FieldWriter domField = fieldManager.registerField(FieldWriterType.DOM_ID_HOLDER, oracle.findType(String.class.getName()), domHolderName); domField.setInitializer("com.google.gwt.dom.client.Document.get().createUniqueId()"); @@ -442,7 +458,16 @@ throws UnableToCompleteException { String fieldName = getFieldName(elem); if (fieldName != null) { - fieldManager.registerField(findFieldType(elem), fieldName); + + /** + * We can switch types if useLazyWidgetBuilders is enabled and the + * respective owner field is a LazyDomElement. + */ + if (useLazyWidgetBuilders && isOwnerFieldLazyDomElement(fieldName)) { + fieldManager.registerFieldForLazyDomElement(findFieldType(elem), ownerClass.getUiField(fieldName)); + } else { + fieldManager.registerField(findFieldType(elem), fieldName); + } } return fieldName; } @@ -673,6 +698,18 @@ return uri != null && UiBinderGenerator.BINDER_URI.equals(uri); } + /** + * Checks whether the given owner field name is a LazyDomElement or not. + */ + public boolean isOwnerFieldLazyDomElement(String fieldName) { + OwnerField ownerField = ownerClass.getUiField(fieldName); + if (ownerField == null) { + return false; + } + + return lazyDomElementClass.isAssignableFrom(ownerField.getType().getRawType()); + } + public boolean isWidgetElement(XMLElement elem) { String uri = elem.getNamespaceUri(); return uri != null && uri.startsWith(PACKAGE_URI_SCHEME);
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/OwnerField.java b/user/src/com/google/gwt/uibinder/rebind/model/OwnerField.java index 2240e82..1acdf7a 100644 --- a/user/src/com/google/gwt/uibinder/rebind/model/OwnerField.java +++ b/user/src/com/google/gwt/uibinder/rebind/model/OwnerField.java
@@ -76,6 +76,14 @@ } /** + * Gets the type associated with this field. + */ + public JClassType getRawType() { + // This shorten getType().getRawType() and make tests easier. + return getType().getRawType(); + } + + /** * Returns a descriptor for the type of the field. */ public OwnerFieldClass getType() { @@ -95,4 +103,4 @@ return String.format("%s.%s#%s", fieldType.getRawType().getPackage(), fieldType.getRawType().getName(), name); } -} \ No newline at end of file +}
diff --git a/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java b/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java index 101b416..9db81ab 100644 --- a/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java +++ b/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
@@ -45,7 +45,9 @@ import com.google.gwt.uibinder.elementparsers.HasTreeItemsParserTest; import com.google.gwt.uibinder.elementparsers.UIObjectParserTest; import com.google.gwt.uibinder.rebind.DesignTimeUtilsTest; +import com.google.gwt.uibinder.rebind.FieldWriterOfExistingTypeTest; import com.google.gwt.uibinder.rebind.FieldWriterOfGeneratedCssResourceTest; +import com.google.gwt.uibinder.rebind.FieldWriterOfLazyDomElementTest; import com.google.gwt.uibinder.rebind.GwtResourceEntityResolverTest; import com.google.gwt.uibinder.rebind.HandlerEvaluatorTest; import com.google.gwt.uibinder.rebind.TokenatorTest; @@ -66,7 +68,9 @@ TestSuite suite = new TestSuite("UiBinder tests that require the JRE"); // rebind + suite.addTestSuite(FieldWriterOfExistingTypeTest.class); suite.addTestSuite(FieldWriterOfGeneratedCssResourceTest.class); + suite.addTestSuite(FieldWriterOfLazyDomElementTest.class); suite.addTestSuite(GwtResourceEntityResolverTest.class); suite.addTestSuite(HandlerEvaluatorTest.class); suite.addTestSuite(TokenatorTest.class);
diff --git a/user/test/com/google/gwt/uibinder/rebind/FieldWriterOfExistingTypeTest.java b/user/test/com/google/gwt/uibinder/rebind/FieldWriterOfExistingTypeTest.java new file mode 100644 index 0000000..0b959c6 --- /dev/null +++ b/user/test/com/google/gwt/uibinder/rebind/FieldWriterOfExistingTypeTest.java
@@ -0,0 +1,105 @@ +/* + * Copyright 2011 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.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JGenericType; +import com.google.gwt.core.ext.typeinfo.JRawType; + +import junit.framework.TestCase; + +import static org.easymock.EasyMock.expect; + +import org.easymock.EasyMock; +import org.easymock.IMocksControl; + +/** + * Tests for FieldWriterOfExistingType. + */ +public class FieldWriterOfExistingTypeTest extends TestCase { + + private static final String FIELD_NAME = "field_name"; + private static final String QUALIFIED_SOURCE_NAME = "qualified_source_name"; + private static final String ARG_QUALIFIED_SOURCE_NAME = "arg_qualified_source_name"; + + private IMocksControl control; + + private JClassType type; + + @Override + public void setUp() throws Exception { + super.setUp(); + + control = EasyMock.createControl(); + + type = control.createMock(JClassType.class); + } + + /** + * Null type not allowed, must fail. + */ + public void testNullType() throws Exception { + control.replay(); + try { + FieldWriter field = new FieldWriterOfExistingType( + FieldWriterType.DEFAULT, null, FIELD_NAME, MortalLogger.NULL); + fail("Expected exception not thrown."); + } catch (IllegalArgumentException e) { + // Expected + } + control.verify(); + } + + /** + * Generic type. + */ + public void testGenericType() throws Exception { + JGenericType genericType = control.createMock(JGenericType.class); + expect(type.isGenericType()).andReturn(genericType); + + JRawType rawType = control.createMock(JRawType.class); + expect(genericType.getRawType()).andReturn(rawType); + + expect(rawType.getQualifiedSourceName()).andReturn(QUALIFIED_SOURCE_NAME); + + control.replay(); + FieldWriter field = new FieldWriterOfExistingType( + FieldWriterType.DEFAULT, type, FIELD_NAME, MortalLogger.NULL); + + assertSame(rawType, field.getAssignableType()); + assertSame(rawType, field.getInstantiableType()); + assertEquals(QUALIFIED_SOURCE_NAME, field.getQualifiedSourceName()); + control.verify(); + } + + /** + * Non generic type. + */ + public void testNonGenericType() throws Exception { + expect(type.isGenericType()).andReturn(null); + + expect(type.getQualifiedSourceName()).andReturn(QUALIFIED_SOURCE_NAME); + + control.replay(); + FieldWriter field = new FieldWriterOfExistingType( + FieldWriterType.DEFAULT, type, FIELD_NAME, MortalLogger.NULL); + + assertSame(type, field.getAssignableType()); + assertSame(type, field.getInstantiableType()); + assertEquals(QUALIFIED_SOURCE_NAME, field.getQualifiedSourceName()); + control.verify(); + } +}
diff --git a/user/test/com/google/gwt/uibinder/rebind/FieldWriterOfLazyDomElementTest.java b/user/test/com/google/gwt/uibinder/rebind/FieldWriterOfLazyDomElementTest.java new file mode 100644 index 0000000..4c540ab --- /dev/null +++ b/user/test/com/google/gwt/uibinder/rebind/FieldWriterOfLazyDomElementTest.java
@@ -0,0 +1,135 @@ +/* + * Copyright 2011 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.JParameterizedType; +import com.google.gwt.uibinder.rebind.model.OwnerField; + +import junit.framework.TestCase; + +import static org.easymock.EasyMock.expect; + +import org.easymock.EasyMock; +import org.easymock.IMocksControl; + +/** + * Tests for FieldWriterOfLazyDomElement. + */ +public class FieldWriterOfLazyDomElementTest extends TestCase { + + private static final String FIELD_NAME = "field_name"; + private static final String QUALIFIED_SOURCE_NAME = "qualified_source_name"; + private static final String ARG_QUALIFIED_SOURCE_NAME = "arg_qualified_source_name"; + + private IMocksControl control; + + private JClassType templateFieldType; + private OwnerField ownerField; + private JClassType ownerFieldType; + + @Override + public void setUp() throws Exception { + super.setUp(); + + control = EasyMock.createControl(); + + templateFieldType = control.createMock(JClassType.class); + ownerField = control.createMock(OwnerField.class); + ownerFieldType = control.createMock(JClassType.class); + + expect(ownerField.getName()).andStubReturn(FIELD_NAME); + expect(ownerField.getRawType()).andStubReturn(ownerFieldType); + } + + /** + * Not parameterized LazyDomElement must fail. Example: + * <pre> + * @UiField LazyDomElement el; + * </pre> + */ + public void testLazyDomElementNotParameterized() throws Exception { + expect(ownerFieldType.isParameterized()).andReturn(null); + + control.replay(); + try { + FieldWriter field = new FieldWriterOfLazyDomElement( + templateFieldType, ownerField, MortalLogger.NULL); + fail("Expected exception not thrown."); + } catch (UnableToCompleteException utce) { + // Expected + } + control.verify(); + } + + /** + * LazyDomElement has parameter but it's not assignable to the template field + * type. Example: + * <pre> + * @UiField LazyDomElement<DivElement> el; + * </pre> + * + * but in the template 'el' is defined as: + * <pre> + * <span ui:field='el' /> + * </pre> + */ + public void testLazyDomElementIncompatibleParameter() throws Exception { + JParameterizedType parameterClass = control.createMock(JParameterizedType.class); + expect(ownerFieldType.isParameterized()).andReturn(parameterClass); + + JClassType arg = control.createMock(JClassType.class); + expect(parameterClass.getTypeArgs()).andReturn(new JClassType[] { arg }); + + expect(templateFieldType.isAssignableTo(arg)).andReturn(false); + expect(parameterClass.getQualifiedSourceName()).andStubReturn(QUALIFIED_SOURCE_NAME); + + control.replay(); + try { + FieldWriter field = new FieldWriterOfLazyDomElement( + templateFieldType, ownerField, MortalLogger.NULL); + fail("Expected exception not thrown."); + } catch (UnableToCompleteException utce) { + // Expected + } + control.verify(); + } + + /** + * The success test, everything works fine. + */ + public void testLazyDomElementCompatibleType() throws Exception { + JParameterizedType parameterClass = control.createMock(JParameterizedType.class); + expect(ownerFieldType.isParameterized()).andReturn(parameterClass); + + JClassType arg = control.createMock(JClassType.class); + expect(parameterClass.getTypeArgs()).andReturn(new JClassType[] { arg }); + + expect(templateFieldType.isAssignableTo(arg)).andReturn(true); + + expect(parameterClass.getQualifiedSourceName()).andStubReturn(QUALIFIED_SOURCE_NAME); + expect(arg.getQualifiedSourceName()).andReturn(ARG_QUALIFIED_SOURCE_NAME); + + control.replay(); + FieldWriter field = new FieldWriterOfLazyDomElement(templateFieldType, ownerField, MortalLogger.NULL); + assertSame(parameterClass, field.getAssignableType()); + assertSame(parameterClass, field.getInstantiableType()); + assertEquals(QUALIFIED_SOURCE_NAME + "<" + ARG_QUALIFIED_SOURCE_NAME + ">", + field.getQualifiedSourceName()); + control.verify(); + } +}