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

Reply via email to