Revision: 10058
Author: j...@google.com
Date: Fri Apr 22 09:21:14 2011
Log: Create a utility class for checking assignability of types for use
in finding compatible constructors/methods.
Review at http://gwt-code-reviews.appspot.com/1420808
Review by: rj...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=10058
Added:
/trunk/user/src/com/google/gwt/uibinder/rebind/TypeOracleUtils.java
/trunk/user/test/com/google/gwt/uibinder/rebind/TypeOracleUtilsTest.java
Modified:
/trunk/user/src/com/google/gwt/uibinder/elementparsers/DateLabelParser.java
/trunk/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java
=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/uibinder/rebind/TypeOracleUtils.java Fri
Apr 22 09:21:14 2011
@@ -0,0 +1,147 @@
+/*
+ * 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.JArrayType;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JConstructor;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JType;
+
+/**
+ * Utilities for functionality that really should be part of TypeOracle.
+ */
+public class TypeOracleUtils {
+
+ /**
+ * Check for a constructor which is compatible with the supplied argument
+ * types.
+ *
+ * @param type
+ * @param argTypes
+ * @return true if a constructor compatible with the supplied arguments
exists
+ */
+ public static boolean hasCompatibleConstructor(JClassType type, JType...
argTypes) {
+ // Note that this does not return the constructor, since that is a more
+ // complicated decision about finding the best matching arguments where
+ // more than one are compatible.
+ for (JConstructor ctor : type.getConstructors()) {
+ if (typesAreCompatible(ctor.getParameterTypes(), argTypes,
ctor.isVarArgs())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return true if the supplied argument type is assignment compatible
with a
+ * declared parameter type.
+ *
+ * @param paramType
+ * @param argType
+ * @return true if the argument type is compatible with the parameter
type
+ */
+ public static boolean typeIsCompatible(JType paramType, JType argType) {
+ if (paramType == argType) {
+ return true;
+ }
+ JClassType paramClass = paramType.isClassOrInterface();
+ if (paramClass != null) {
+ JClassType argClass = argType.isClassOrInterface();
+ return argClass != null && paramClass.isAssignableFrom(argClass);
+ }
+ JArrayType paramArray = paramType.isArray();
+ if (paramArray != null) {
+ JArrayType argArray = argType.isArray();
+ return argArray != null &&
typeIsCompatible(paramArray.getComponentType(),
+ argArray.getComponentType());
+ }
+ if (paramType instanceof JPrimitiveType && argType instanceof
JPrimitiveType) {
+ return isWideningPrimitiveConversion((JPrimitiveType) paramType,
(JPrimitiveType) argType);
+ }
+ // TODO: handle autoboxing?
+ return false;
+ }
+
+ /**
+ * Check if the types of supplied arguments are compatible with the
parameter
+ * types of a method.
+ *
+ * @param paramTypes
+ * @param argTypes
+ * @param varArgs true if the method is a varargs method
+ * @return true if all argument types are compatible with the parameter
types
+ */
+ public static boolean typesAreCompatible(JType[] paramTypes, JType[]
argTypes, boolean varArgs) {
+ int expectedArgs = paramTypes.length;
+ int actualArgs = argTypes.length;
+ int comparedArgs = expectedArgs;
+ if (varArgs) {
+ comparedArgs--;
+ if (actualArgs != expectedArgs |
| !typeIsCompatible(paramTypes[comparedArgs], argTypes[comparedArgs])) {
+ if (actualArgs < comparedArgs) {
+ return false;
+ }
+ JArrayType varargsArrayType = paramTypes[comparedArgs].isArray();
+ assert varargsArrayType != null;
+ JType varargsType = varargsArrayType.getComponentType();
+ for (int i = comparedArgs; i < actualArgs; ++i) {
+ if (!typeIsCompatible(varargsType, argTypes[i])) {
+ return false;
+ }
+ }
+ }
+ } else if (actualArgs != expectedArgs) {
+ return false;
+ }
+ for (int i = 0; i < comparedArgs; ++i) {
+ if (!typeIsCompatible(paramTypes[i], argTypes[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Check for a widening primitive conversion. See
+ * <a
href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25214">JLS
5.1.2</a>.
+ *
+ * @param paramType
+ * @param argType
+ * @return true if assigning argType to paramType is a widening
conversion
+ */
+ private static boolean isWideningPrimitiveConversion(JPrimitiveType
paramType, JPrimitiveType argType) {
+ switch (paramType) {
+ case DOUBLE:
+ return argType != JPrimitiveType.BOOLEAN;
+ case FLOAT:
+ return argType != JPrimitiveType.BOOLEAN && argType !=
JPrimitiveType.DOUBLE;
+ case LONG:
+ return argType != JPrimitiveType.BOOLEAN && argType !=
JPrimitiveType.DOUBLE
+ && argType != JPrimitiveType.FLOAT;
+ case INT:
+ return argType == JPrimitiveType.BYTE || argType ==
JPrimitiveType.SHORT
+ || argType == JPrimitiveType.CHAR;
+ case SHORT:
+ return argType == JPrimitiveType.BYTE;
+ default:
+ return false;
+ }
+ }
+
+ private TypeOracleUtils() {
+ }
+}
=======================================
--- /dev/null
+++
/trunk/user/test/com/google/gwt/uibinder/rebind/TypeOracleUtilsTest.java
Fri Apr 22 09:21:14 2011
@@ -0,0 +1,270 @@
+/*
+ * 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 static com.google.gwt.uibinder.rebind.TypeOracleUtils.*;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.typeinfo.JArrayType;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.NotFoundException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.javac.CompilationState;
+import com.google.gwt.dev.javac.CompilationStateBuilder;
+import com.google.gwt.dev.javac.testing.impl.JavaResourceBase;
+import com.google.gwt.dev.javac.testing.impl.MockJavaResource;
+import com.google.gwt.dev.resource.Resource;
+import com.google.gwt.dev.shell.FailErrorLogger;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Tests for {@link TypeOracleUtils}.
+ */
+public class TypeOracleUtilsTest extends TestCase {
+
+ protected static final MockJavaResource BAR = new
MockJavaResource("my.Bar") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package my;\n");
+ code.append("public class Bar {\n");
+ code.append(" public Bar(Child c) {}\n");
+ code.append(" public Bar(int i) {}\n");
+ code.append(" public Bar(boolean flag, Parent... ps) {}\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+
+ protected static final MockJavaResource BAZ = new
MockJavaResource("my.Baz") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package my;\n");
+ code.append("public class Baz {\n");
+ code.append(" public Baz(Parent[] ps) {}\n");
+ code.append(" public Baz(ParentInt... ps) {}\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+
+ protected static final MockJavaResource CHILD = new
MockJavaResource("my.Child") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package my;\n");
+ code.append("public class Child extends Parent implements ChildInt
{\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+
+ protected static final MockJavaResource CHILD_INT = new
MockJavaResource("my.ChildInt") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package my;\n");
+ code.append("public interface ChildInt extends ParentInt {\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+
+ protected static final MockJavaResource FOO = new
MockJavaResource("my.Foo") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package my;\n");
+ code.append("public class Foo {\n");
+ code.append(" public Foo() {}\n");
+ code.append(" public Foo(Parent p) {}\n");
+ code.append(" public Foo(double d) {}\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+
+ protected static final MockJavaResource PARENT = new
MockJavaResource("my.Parent") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package my;\n");
+ code.append("public class Parent implements ParentInt {\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+
+ protected static final MockJavaResource PARENT_INT = new
MockJavaResource("my.ParentInt") {
+ @Override
+ public CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package my;\n");
+ code.append("public interface ParentInt {\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+
+ private JClassType bar;
+
+ private JClassType baz;
+
+ private JClassType child;
+
+ private JArrayType childArray;
+
+ private JClassType childInt;
+
+ private JClassType foo;
+
+ private JClassType parent;
+
+ private JArrayType parentArray;
+
+ private JClassType parentInt;
+
+ private TypeOracle typeOracle;
+
+ /**
+ * Test method for {@link
TypeOracleUtils#hasCompatibleConstructor(JClassType, JType[])}.
+ */
+ public void testHasCompatibleConstructorBar() {
+ assertHasCtor(bar, child);
+ assertNoCtor(bar, childInt);
+ assertNoCtor(bar, parent);
+ assertHasCtor(bar, JPrimitiveType.INT);
+ assertHasCtor(bar, JPrimitiveType.CHAR);
+ assertNoCtor(bar, JPrimitiveType.FLOAT);
+ assertHasCtor(bar, JPrimitiveType.BOOLEAN, parent);
+ assertHasCtor(bar, JPrimitiveType.BOOLEAN, parent, child, parent);
+ assertHasCtor(bar, JPrimitiveType.BOOLEAN);
+ assertHasCtor(bar, JPrimitiveType.BOOLEAN, parentArray);
+ assertNoCtor(bar, parentArray);
+ assertHasCtor(bar, JPrimitiveType.BOOLEAN, childArray);
+ assertNoCtor(bar);
+ }
+
+ /**
+ * Test method for {@link
TypeOracleUtils#hasCompatibleConstructor(JClassType, JType[])}.
+ */
+ public void testHasCompatibleConstructorBaz() {
+ assertHasCtor(baz, parentArray);
+ assertHasCtor(baz, parentInt);
+ assertHasCtor(baz, parentInt, childInt);
+ assertHasCtor(baz, parent, child);
+ assertHasCtor(baz, childArray);
+ assertNoCtor(baz, JPrimitiveType.INT);
+ }
+
+ /**
+ * Test method for {@link
TypeOracleUtils#hasCompatibleConstructor(JClassType, JType[])}.
+ */
+ public void testHasCompatibleConstructorFoo() {
+ assertHasCtor(foo);
+ assertHasCtor(foo, parent);
+ assertHasCtor(foo, child);
+ assertNoCtor(foo, foo);
+ assertNoCtor(foo, child, child);
+ assertNoCtor(foo, JPrimitiveType.BOOLEAN);
+ assertHasCtor(foo, JPrimitiveType.DOUBLE);
+ assertHasCtor(foo, JPrimitiveType.INT);
+ }
+
+ /**
+ * Test method for {@link TypeOracleUtils#typeIsCompatible(JType,
JType)}.
+ */
+ public void testTypeIsCompatible() {
+ assertTrue("Parent should be compatible with itself",
typeIsCompatible(parent, parent));
+ assertTrue("Parent should be compatible with parentInt",
typeIsCompatible(parentInt, parent));
+ assertTrue("Child should be compatible with parent",
typeIsCompatible(parent, child));
+ assertFalse("Child should not be compatible with parent",
typeIsCompatible(child, parent));
+ assertTrue("Child should be compatible with itself",
typeIsCompatible(child, child));
+ assertFalse("Boolean should not be compatible with child",
typeIsCompatible(child,
+ JPrimitiveType.BOOLEAN));
+ assertFalse("Parent[] should not be compatible with parent",
typeIsCompatible(parent,
+ parentArray));
+ assertTrue("Parent[] should be compatible with itself",
typeIsCompatible(parentArray,
+ parentArray));
+ assertFalse("Parent[] should not be compatible with Child[]",
typeIsCompatible(childArray,
+ parentArray));
+ assertTrue("Child[] should be compatible with Parent[]",
typeIsCompatible(parentArray,
+ childArray));
+ }
+
+ @Override
+ protected void setUp() throws NotFoundException {
+ Set<Resource> resources = new HashSet<Resource>();
+
resources.addAll(Arrays.asList(JavaResourceBase.getStandardResources()));
+ resources.add(FOO);
+ resources.add(BAR);
+ resources.add(BAZ);
+ resources.add(PARENT);
+ resources.add(PARENT_INT);
+ resources.add(CHILD);
+ resources.add(CHILD_INT);
+ TreeLogger logger = new FailErrorLogger();
+ CompilationState state = CompilationStateBuilder.buildFrom(logger,
resources);
+ typeOracle = state.getTypeOracle();
+ foo = typeOracle.getType("my.Foo");
+ bar = typeOracle.getType("my.Bar");
+ baz = typeOracle.getType("my.Baz");
+ parent = typeOracle.getType("my.Parent");
+ parentInt = typeOracle.getType("my.ParentInt");
+ parentArray = typeOracle.getArrayType(parent);
+ child = typeOracle.getType("my.Child");
+ childInt = typeOracle.getType("my.ChildInt");
+ childArray = typeOracle.getArrayType(child);
+ }
+
+ private void assertHasCtor(JClassType type, JType... argTypes) {
+ if (hasCompatibleConstructor(type, argTypes)) {
+ return;
+ }
+ fail(buildCtorName("Should have found ", type, argTypes));
+ }
+
+ private void assertNoCtor(JClassType type, JType... argTypes) {
+ if (!hasCompatibleConstructor(type, argTypes)) {
+ return;
+ }
+ fail(buildCtorName("Should not have found ", type, argTypes));
+ }
+
+ private String buildCtorName(String msg, JClassType type, JType...
argTypes) {
+ StringBuilder buf = new StringBuilder();
+ buf.append(msg).append(type.getName()).append('(');
+ boolean first = true;
+ for (JType argType : argTypes) {
+ if (first) {
+ first = false;
+ } else {
+ buf.append(", ");
+ }
+ buf.append(argType.getSimpleSourceName());
+ }
+ buf.append(')');
+ return buf.toString();
+ }
+}
=======================================
---
/trunk/user/src/com/google/gwt/uibinder/elementparsers/DateLabelParser.java
Mon Nov 29 10:45:36 2010
+++
/trunk/user/src/com/google/gwt/uibinder/elementparsers/DateLabelParser.java
Fri Apr 22 09:21:14 2011
@@ -21,8 +21,9 @@
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.core.ext.typeinfo.TypeOracleException;
import com.google.gwt.i18n.client.DateTimeFormat;
-import com.google.gwt.i18n.client.TimeZone;
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
+import com.google.gwt.i18n.client.TimeZone;
+import com.google.gwt.uibinder.rebind.TypeOracleUtils;
import com.google.gwt.uibinder.rebind.UiBinderWriter;
import com.google.gwt.uibinder.rebind.XMLElement;
@@ -112,13 +113,13 @@
TypeOracle typeOracle, JClassType type) {
JType dateTimeFormatType =
typeOracle.findType(DateTimeFormat.class.getName());
JType timeZoneType = typeOracle.findType(TimeZone.class.getName());
- return type.findConstructor(new JType[] {dateTimeFormatType,
timeZoneType}) != null;
+ return TypeOracleUtils.hasCompatibleConstructor(type,
dateTimeFormatType, timeZoneType);
}
private boolean hasDateTimeFormatConstructor(TypeOracle typeOracle,
JClassType type) {
JType dateTimeFormatType =
typeOracle.findType(DateTimeFormat.class.getName());
- return type.findConstructor(new JType[] {dateTimeFormatType}) != null;
+ return TypeOracleUtils.hasCompatibleConstructor(type,
dateTimeFormatType);
}
private boolean hasTimeZone(XMLElement elem) {
=======================================
--- /trunk/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java Fri Jan
7 08:26:38 2011
+++ /trunk/user/test/com/google/gwt/uibinder/UiBinderJreSuite.java Fri Apr
22 09:21:14 2011
@@ -49,6 +49,7 @@
import com.google.gwt.uibinder.rebind.GwtResourceEntityResolverTest;
import com.google.gwt.uibinder.rebind.HandlerEvaluatorTest;
import com.google.gwt.uibinder.rebind.TokenatorTest;
+import com.google.gwt.uibinder.rebind.TypeOracleUtilsTest;
import com.google.gwt.uibinder.rebind.XMLElementTest;
import com.google.gwt.uibinder.rebind.model.OwnerClassTest;
import com.google.gwt.uibinder.rebind.model.OwnerFieldClassTest;
@@ -71,6 +72,7 @@
suite.addTestSuite(TokenatorTest.class);
suite.addTestSuite(XMLElementTest.class);
suite.addTestSuite(DesignTimeUtilsTest.class);
+ suite.addTestSuite(TypeOracleUtilsTest.class);
// model
suite.addTestSuite(OwnerClassTest.class);
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors