| /* |
| * 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.model; |
| |
| import com.google.gwt.core.ext.UnableToCompleteException; |
| import com.google.gwt.core.ext.typeinfo.JClassType; |
| import com.google.gwt.core.ext.typeinfo.JConstructor; |
| import com.google.gwt.core.ext.typeinfo.JMethod; |
| import com.google.gwt.core.ext.typeinfo.JParameter; |
| import com.google.gwt.core.ext.typeinfo.JPrimitiveType; |
| import com.google.gwt.core.ext.typeinfo.JType; |
| import com.google.gwt.dev.util.Pair; |
| import com.google.gwt.uibinder.client.UiChild; |
| import com.google.gwt.uibinder.client.UiConstructor; |
| import com.google.gwt.uibinder.rebind.JClassTypeAdapter; |
| import com.google.gwt.uibinder.rebind.MortalLogger; |
| import com.google.gwt.uibinder.rebind.UiBinderContext; |
| import com.google.gwt.user.client.ui.CheckBox; |
| import com.google.gwt.user.client.ui.HTML; |
| |
| import junit.framework.TestCase; |
| |
| import java.util.Map; |
| |
| /** |
| * Tests for descriptors of potential owner field classes. |
| */ |
| public class OwnerFieldClassTest extends TestCase { |
| |
| private JClassTypeAdapter gwtTypeAdapter; |
| private UiBinderContext uiBinderCtx; |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| uiBinderCtx = new UiBinderContext(); |
| gwtTypeAdapter = new JClassTypeAdapter(); |
| } |
| |
| public void testOwnerFieldClass() throws Exception { |
| // Get the JType for an HTML |
| JClassType htmlType = gwtTypeAdapter.adaptJavaClass(HTML.class); |
| |
| // Now get its field class model |
| OwnerFieldClass fieldClass = OwnerFieldClass.getFieldClass(htmlType, |
| MortalLogger.NULL, uiBinderCtx); |
| |
| // Check the class model properties |
| assertEquals(htmlType, fieldClass.getRawType()); |
| assertNull(fieldClass.getUiConstructor()); |
| |
| // simple property: visible="" maps to setVisible |
| JMethod visibleSetter = fieldClass.getSetter("visible"); |
| assertMethod(visibleSetter, "setVisible", JPrimitiveType.BOOLEAN); |
| |
| // all-upper-case property, Java Bean naming (all-upper-cased) |
| JMethod htmlSetter = fieldClass.getSetter("HTML"); |
| assertMethod(htmlSetter, "setHTML", |
| gwtTypeAdapter.adaptJavaClass(String.class)); |
| |
| // all-upper-case property, GWT-legacy naming with the first char |
| // lower-cased |
| htmlSetter = fieldClass.getSetter("hTML"); |
| assertMethod(htmlSetter, "setHTML", |
| gwtTypeAdapter.adaptJavaClass(String.class)); |
| |
| // Check that the same instance of the model is returned if asked again |
| assertSame(fieldClass, |
| OwnerFieldClass.getFieldClass(htmlType, MortalLogger.NULL, uiBinderCtx)); |
| } |
| |
| /** |
| * Class with lots of setters for testing. |
| */ |
| @SuppressWarnings("unused") |
| // We know these methods are unused |
| private static class SettersTestClass { |
| // No ambiguity in these setters |
| public void setBlaBla(int x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| public void setBlaBle(String x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| // To be used in subclass test |
| public void setBlaBla2(int x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| public void setBlaBle2(String x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| public void setBli2(int x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| public void setBli2(double x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| // Ambiguous, String parameter should win |
| public void setBle(int y) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| public void setBle(String y) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| // Ambiguous with no winner |
| public void setBli(int y) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| public void setBli(double y) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| // Not considered setters |
| void setNothing(int x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| public void set() { |
| } |
| |
| public int setNothing2(String x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| public void notASetter(String x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| public static void setStatic(String x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| } |
| |
| /** |
| * |
| * base class for setters disambiguation tests. |
| * |
| */ |
| public class baseSetters { |
| public baseSetters() { |
| } |
| |
| // setvalue1 is not ambiguous |
| public void setValue1(@SuppressWarnings("unused") boolean b) { |
| } |
| |
| public void setValue1(@SuppressWarnings("unused") Boolean b) { |
| } |
| |
| // derived wins |
| public void setValue2(@SuppressWarnings("unused") Integer b) { |
| } |
| |
| // this overload wins |
| public void setValue3(@SuppressWarnings("unused") int b) { |
| } |
| |
| // this is not ambiguous since derived |
| // has the exact same signature |
| public void setValue4(@SuppressWarnings("unused") int b) { |
| } |
| |
| // setvalue5 is ambiguous |
| public void setValue5(@SuppressWarnings("unused") float f) { |
| } |
| |
| public void setValue5(@SuppressWarnings("unused") double d) { |
| } |
| |
| // string always wins |
| public void setValue6(@SuppressWarnings("unused") String s) { |
| } |
| |
| public void setValue6(@SuppressWarnings("unused") char s) { |
| } |
| |
| public void setValue6(@SuppressWarnings("unused") Object s) { |
| } |
| |
| // primitive wins |
| public void setValue7(@SuppressWarnings("unused") int s) { |
| } |
| |
| public void setValue7(@SuppressWarnings("unused") StringBuffer s) { |
| } |
| } |
| |
| /** |
| * |
| * derived class for setter disambiguation tests. |
| * |
| */ |
| public class derivedSetters extends baseSetters { |
| public derivedSetters() { |
| super(); |
| } |
| |
| public void setValue2(@SuppressWarnings("unused") int b) { |
| } |
| |
| public void setValue3(@SuppressWarnings("unused") Integer b) { |
| } |
| |
| @Override |
| public void setValue4(int b) { |
| } |
| } |
| |
| /** |
| * Regression test. |
| */ |
| public void testCheckBoxValueSetters() throws Exception { |
| JClassType cbClassType = gwtTypeAdapter.adaptJavaClass(CheckBox.class); |
| OwnerFieldClass settersClass = OwnerFieldClass.getFieldClass(cbClassType, |
| MortalLogger.NULL, uiBinderCtx); |
| JMethod setValueSetter = settersClass.getSetter("value"); |
| assertNotNull(setValueSetter); |
| } |
| |
| public void testDisambiguateClassHierarchySettersBase() throws Exception { |
| // ensure that primitive types win over boxed primitive types. |
| JClassType baseClassType = gwtTypeAdapter.adaptJavaClass(baseSetters.class); |
| OwnerFieldClass settersClass = OwnerFieldClass.getFieldClass(baseClassType, |
| MortalLogger.NULL, uiBinderCtx); |
| JMethod setValueSetter = settersClass.getSetter("value1"); |
| assertNotNull(setValueSetter); |
| } |
| |
| public void testDisambiguateClassHierarchySettersDerived() throws Exception { |
| // ensure that primitive types win over boxed primitive types |
| // in a class hierarchy. |
| JClassType derivedClass = gwtTypeAdapter.adaptJavaClass(derivedSetters.class); |
| OwnerFieldClass settersClass = OwnerFieldClass.getFieldClass(derivedClass, |
| MortalLogger.NULL, uiBinderCtx); |
| |
| // base.value1(boolean) and base.value1(Boolean) is never ambiguous |
| // must return boolean |
| assertNotNull(settersClass.getSetter("value1")); |
| assertMethod(settersClass.getSetter("value1"), "setValue1", JPrimitiveType.BOOLEAN); |
| |
| // base.value2(Integer) and derived.value2(int) is not ambiguous - must be int |
| assertNotNull(settersClass.getSetter("value2")); |
| assertMethod(settersClass.getSetter("value2"), "setValue2", JPrimitiveType.INT); |
| |
| // base.value3 (int) and derived.value3(Integer) is not ambiguous - must be int. |
| assertNotNull(settersClass.getSetter("value3")); |
| |
| // base.value4(int) and derived.value4(int) is not ambiguous. |
| assertNotNull(settersClass.getSetter("value4")); |
| |
| // base.value5(float) and base.value5(double) is ambiguous |
| try { |
| settersClass.getSetter("value5"); |
| fail("Expected exception not thrown"); |
| } catch (UnableToCompleteException utce) { |
| // Expected |
| } |
| |
| // value6 has multiple overload but string always wins |
| // base.value6(string), base.value6(char) and base.value6(object) |
| assertNotNull(settersClass.getSetter("value6")); |
| assertMethod(settersClass.getSetter("value6"), "setValue6", |
| gwtTypeAdapter.adaptJavaClass(String.class)); |
| |
| // base.value7(object) and base.value7(int) is not ambiguous - must be int. |
| assertNotNull(settersClass.getSetter("value7")); |
| assertMethod(settersClass.getSetter("value7"), "setValue7", |
| JPrimitiveType.INT); |
| } |
| |
| public void testOwnerFieldClass_setters() throws Exception { |
| JClassType settersType = gwtTypeAdapter.adaptJavaClass(SettersTestClass.class); |
| JClassType stringType = gwtTypeAdapter.adaptJavaClass(String.class); |
| OwnerFieldClass settersClass = OwnerFieldClass.getFieldClass(settersType, |
| MortalLogger.NULL, uiBinderCtx); |
| assertEquals(settersType, settersClass.getRawType()); |
| assertNull(settersClass.getUiConstructor()); |
| |
| JMethod blaBlaSetter = settersClass.getSetter("blaBla"); |
| assertMethod(blaBlaSetter, "setBlaBla", JPrimitiveType.INT); |
| JMethod blaBleSetter = settersClass.getSetter("blaBle"); |
| assertMethod(blaBleSetter, "setBlaBle", stringType); |
| |
| assertNull(settersClass.getSetter("nothing")); |
| assertNull(settersClass.getSetter("nothing2")); |
| assertNull(settersClass.getSetter("notASetter")); |
| assertNull(settersClass.getSetter("aSetter")); |
| assertNull(settersClass.getSetter("static")); |
| } |
| |
| public void testOwnerFieldClass_ambiguousSetters() throws Exception { |
| JClassType settersType = gwtTypeAdapter.adaptJavaClass(SettersTestClass.class); |
| JClassType stringType = gwtTypeAdapter.adaptJavaClass(String.class); |
| OwnerFieldClass settersClass = OwnerFieldClass.getFieldClass(settersType, |
| MortalLogger.NULL, uiBinderCtx); |
| assertEquals(settersType, settersClass.getRawType()); |
| |
| JMethod bleSetter = settersClass.getSetter("ble"); |
| assertMethod(bleSetter, "setBle", stringType); |
| |
| try { |
| settersClass.getSetter("bli"); |
| fail("Expected exception not thrown"); |
| } catch (UnableToCompleteException utce) { |
| // Expected |
| } |
| } |
| |
| /** |
| * Class with overridden setters for testing. |
| */ |
| @SuppressWarnings("unused") |
| // We know these methods are unused |
| private static class OverriddenSettersTestClass extends SettersTestClass { |
| // Simple override of parent method |
| @Override |
| public void setBlaBla(int x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| // setBlaBle is not overridden |
| |
| // Subclass adds ambiguity, String from this class wins |
| public void setBlaBla2(String x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| // Subclass adds ambiguity, String from superclass wins |
| public void setBlaBle2(int x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| // setBle had settled ambiguity, this shouldn't change it |
| public void setBle(double x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| // setBli2 ambiguous in superclass only |
| |
| // setBlo us ambiguous here only |
| public void setBlo(int x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| public void setBlo(double x) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| // Solves superclass ambiguity |
| public void setBli(String y) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| } |
| |
| public void testOwnerFieldClass_overriddenSetters() throws Exception { |
| JClassType settersType = gwtTypeAdapter.adaptJavaClass(OverriddenSettersTestClass.class); |
| JClassType stringType = gwtTypeAdapter.adaptJavaClass(String.class); |
| OwnerFieldClass settersClass = OwnerFieldClass.getFieldClass(settersType, |
| MortalLogger.NULL, uiBinderCtx); |
| assertEquals(settersType, settersClass.getRawType()); |
| |
| // setBlaBla is not ambiguous, though overridden |
| JMethod blaBlaSetter = settersClass.getSetter("blaBla"); |
| assertMethod(blaBlaSetter, "setBlaBla", JPrimitiveType.INT); |
| |
| // setBlaBle is not overridden, works from superclass |
| JMethod blaBleSetter = settersClass.getSetter("blaBle"); |
| assertMethod(blaBleSetter, "setBlaBle", stringType); |
| |
| // setBlaBla2 is not ambiguous, subclass String wins |
| JMethod blaBla2Setter = settersClass.getSetter("blaBla2"); |
| assertMethod(blaBla2Setter, "setBlaBla2", stringType); |
| |
| // setBlaBle2 is not ambiguous, superclass String wins |
| JMethod blaBle2Setter = settersClass.getSetter("blaBle2"); |
| assertMethod(blaBle2Setter, "setBlaBle2", stringType); |
| |
| // setBle is disambiguated and overridden |
| JMethod bleSetter = settersClass.getSetter("ble"); |
| assertMethod(bleSetter, "setBle", stringType); |
| |
| // setBli was ambiguous in the superclass, subclass String settles it |
| JMethod bliSetter = settersClass.getSetter("bli"); |
| assertMethod(bliSetter, "setBli", stringType); |
| |
| // setBli2 is ambiguous in the superclass |
| try { |
| settersClass.getSetter("bli2"); |
| fail("Expected exception not thrown"); |
| } catch (UnableToCompleteException utce) { |
| // Expected |
| } |
| |
| // setBlo is ambiguous in the subclass |
| try { |
| settersClass.getSetter("blo"); |
| fail("Expected exception not thrown"); |
| } catch (UnableToCompleteException utce) { |
| // Expected |
| } |
| |
| // Ignored superclass setters are still ignored |
| assertNull(settersClass.getSetter("nothing")); |
| assertNull(settersClass.getSetter("nothing2")); |
| assertNull(settersClass.getSetter("notASetter")); |
| assertNull(settersClass.getSetter("aSetter")); |
| assertNull(settersClass.getSetter("static")); |
| } |
| |
| /** |
| * Class with a {@link UiChild}-annotated methods. |
| */ |
| @SuppressWarnings("unused") |
| // We know these methods are unused |
| private static class UiChildClass { |
| public UiChildClass() { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| @UiChild |
| void addChild(Object child) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| @UiChild(tagname = "second", limit = 4) |
| void doesNotStartWithAdd(Object child) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| } |
| |
| public void testOwnerFieldClass_withUiChildren() throws Exception { |
| JClassType parentType = gwtTypeAdapter.adaptJavaClass(UiChildClass.class); |
| OwnerFieldClass parentClass = OwnerFieldClass.getFieldClass(parentType, |
| MortalLogger.NULL, uiBinderCtx); |
| assertEquals(parentType, parentClass.getRawType()); |
| |
| Map<String, Pair<JMethod, Integer>> childMethods = parentClass.getUiChildMethods(); |
| assertNotNull(childMethods); |
| assertEquals(2, childMethods.size()); |
| |
| Pair<JMethod, Integer> childPair = childMethods.get("child"); |
| assertEquals("addChild", childPair.left.getName()); |
| assertEquals(Integer.valueOf(-1), childPair.right); |
| |
| Pair<JMethod, Integer> secondPair = childMethods.get("second"); |
| assertEquals("doesNotStartWithAdd", secondPair.left.getName()); |
| assertEquals(Integer.valueOf(4), secondPair.right); |
| } |
| |
| public void testOwnerFieldClass_withNoUiChildren() throws Exception { |
| JClassType parentType = gwtTypeAdapter.adaptJavaClass(Object.class); |
| OwnerFieldClass parentClass = OwnerFieldClass.getFieldClass(parentType, |
| MortalLogger.NULL, uiBinderCtx); |
| assertEquals(parentType, parentClass.getRawType()); |
| |
| Map<String, Pair<JMethod, Integer>> childMethods = parentClass.getUiChildMethods(); |
| assertNotNull(childMethods); |
| assertEquals(0, childMethods.size()); |
| } |
| |
| /** |
| * Class with {@link UiChild}-annotated methods. |
| */ |
| @SuppressWarnings("unused") |
| // We know these methods are unused |
| private static class UiChildWithPoorMethodNames { |
| public UiChildWithPoorMethodNames() { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| @UiChild |
| void poorlyNamedMethodWithoutTag(Object child) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| } |
| |
| public void testOwnerFieldClass_withBadlyNamedMethod() { |
| JClassType parentType = gwtTypeAdapter.adaptJavaClass(UiChildWithPoorMethodNames.class); |
| try { |
| OwnerFieldClass.getFieldClass(parentType, MortalLogger.NULL, uiBinderCtx); |
| fail("Class should error because @UiChild method has invalid name (and no tag specified)."); |
| } catch (UnableToCompleteException expected) { |
| } |
| } |
| |
| /** |
| * Class with a {@link UiConstructor}-annotated constructor. |
| */ |
| @SuppressWarnings("unused") |
| // We know these methods are unused |
| private static class UiConstructorClass { |
| @UiConstructor |
| public UiConstructorClass(boolean visible) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| } |
| |
| public void testOwnerFieldClass_withUiConstructor() throws Exception { |
| JClassType constructorsType = gwtTypeAdapter.adaptJavaClass(UiConstructorClass.class); |
| OwnerFieldClass constructorsClass = OwnerFieldClass.getFieldClass( |
| constructorsType, MortalLogger.NULL, uiBinderCtx); |
| assertEquals(constructorsType, constructorsClass.getRawType()); |
| |
| JConstructor constructor = constructorsClass.getUiConstructor(); |
| assertNotNull(constructor); |
| assertEquals(constructorsType, constructor.getEnclosingType()); |
| |
| JParameter[] parameters = constructor.getParameters(); |
| assertEquals(1, parameters.length); |
| assertEquals(JPrimitiveType.BOOLEAN, parameters[0].getType()); |
| } |
| |
| /** |
| * Class with (disallowed) multiple constructors annotated with |
| * {@link UiConstructor}. |
| */ |
| @SuppressWarnings("unused") |
| // We know these methods are unused |
| private static class MultiUiConstructorsClass { |
| @UiConstructor |
| public MultiUiConstructorsClass(boolean visible) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| |
| @UiConstructor |
| public MultiUiConstructorsClass(String size) { |
| throw new UnsupportedOperationException("Should never get called"); |
| } |
| } |
| |
| public void testOwnerFieldClass_withMultipleUiConstructors() { |
| JClassType constructorsType = gwtTypeAdapter.adaptJavaClass(MultiUiConstructorsClass.class); |
| |
| try { |
| OwnerFieldClass.getFieldClass(constructorsType, MortalLogger.NULL, |
| uiBinderCtx); |
| fail("Expected exception not thrown"); |
| } catch (UnableToCompleteException utce) { |
| // Expected |
| } |
| } |
| |
| /** |
| * Asserts that the given method has the proper name and parameters. |
| * |
| * @param method the actual method |
| * @param methodName the expected method name |
| * @param parameterTypes the expected parameter types |
| */ |
| private void assertMethod(JMethod method, String methodName, |
| JType... parameterTypes) { |
| assertNotNull(method); |
| assertEquals(methodName, method.getName()); |
| JParameter[] parameters = method.getParameters(); |
| assertEquals(parameterTypes.length, parameters.length); |
| for (int i = 0; i < parameters.length; i++) { |
| assertEquals("Parameter " + i + " of method " + methodName |
| + " mismatch. Expected" + parameterTypes[i].getSimpleSourceName() |
| + "; actual: " + parameters[i].getType().getSimpleSourceName(), |
| parameterTypes[i], parameters[i].getType()); |
| } |
| } |
| } |