/* * Copyright 2008 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.dev.javac.typemodel; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.JWildcardType.BoundType; import com.google.gwt.core.ext.typeinfo.NotFoundException; import com.google.gwt.dev.javac.TypeOracleTestingUtils; import com.google.gwt.dev.javac.testing.impl.JavaResourceBase; import com.google.gwt.dev.javac.typemodel.test.Base; import com.google.gwt.dev.javac.typemodel.test.Derived; import com.google.gwt.dev.javac.typemodel.test.GenericClass; import com.google.gwt.dev.javac.typemodel.test.GenericClass.GenericInnerClass; import com.google.gwt.dev.javac.typemodel.test.MyCustomList; import com.google.gwt.dev.javac.typemodel.test.MyIntegerList; import com.google.gwt.dev.javac.typemodel.test.MyList; import com.google.gwt.dev.resource.Resource; import com.google.gwt.dev.util.log.PrintWriterTreeLogger; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Test for {@link JParameterizedType}. */ public class JParameterizedTypeTest extends JDelegatingClassTypeTestBase { /** * Helper for verifying parameterized substitutions. */ static class ParameterizedSubstitution implements Substitution { private final JParameterizedType parameterizedType; public ParameterizedSubstitution(JParameterizedType parameterizedType) { this.parameterizedType = parameterizedType; } @Override public JClassType getSubstitution(JClassType type) { return type.getSubstitutedType(parameterizedType); } } private static TreeLogger createTreeLogger() { final boolean logToConsole = false; return logToConsole ? new PrintWriterTreeLogger() : TreeLogger.NULL; } private final JClassType integerType; private final ModuleContext moduleContext = new ModuleContext(createTreeLogger(), "com.google.gwt.dev.javac.typemodel.TypeOracleTest"); public JParameterizedTypeTest() throws UnableToCompleteException, NotFoundException { integerType = moduleContext.getOracle().getType(Integer.class.getName()); } @Override public void testFindNestedType() { // TODO: complete this test method } /** * Checks that GenericClass<Integer> ends up with the correct substitutions. * * @throws NotFoundException */ public void testGenericClass_Integer() throws NotFoundException { TypeOracle oracle = moduleContext.getOracle(); JGenericType genericType = getGenericTestType(); JClassType type = oracle.getParameterizedType(genericType, new JClassType[]{integerType}); JParameterizedType parameterizedType = type.isParameterized(); validateTypeSubstitution(genericType, parameterizedType, new ParameterizedSubstitution(parameterizedType)); } public void testGenericClass_LowerBoundWildcard() throws NotFoundException { TypeOracle oracle = moduleContext.getOracle(); JGenericType genericType = getGenericTestType(); JWildcardType lowerBoundWildcard = oracle.getWildcardType(BoundType.SUPER, integerType); JClassType type = oracle.getParameterizedType(genericType, new JClassType[]{lowerBoundWildcard}); JParameterizedType parameterizedType = type.isParameterized(); validateTypeSubstitution(genericType, parameterizedType, new ParameterizedSubstitution(parameterizedType)); } public void testGenericClass_UnboundWildcard() throws NotFoundException { TypeOracle oracle = moduleContext.getOracle(); JGenericType genericType = getGenericTestType(); JWildcardType upperBoundWildcard = oracle.getWildcardType( BoundType.EXTENDS, oracle.getJavaLangObject()); JClassType type = oracle.getParameterizedType(genericType, new JClassType[]{upperBoundWildcard}); JParameterizedType parameterizedType = type.isParameterized(); validateTypeSubstitution(genericType, parameterizedType, new ParameterizedSubstitution(parameterizedType)); } public void testGenericClass_UpperBoundWildcard() throws NotFoundException { TypeOracle oracle = moduleContext.getOracle(); JGenericType genericType = getGenericTestType(); JWildcardType upperBoundWildcard = oracle.getWildcardType( BoundType.EXTENDS, integerType); JClassType type = oracle.getParameterizedType(genericType, new JClassType[]{upperBoundWildcard}); JParameterizedType parameterizedType = type.isParameterized(); validateTypeSubstitution(genericType, parameterizedType, new ParameterizedSubstitution(parameterizedType)); } /** * Test method for * {@link com.google.gwt.core.ext.typeinfo.JParameterizedType#getEnclosingType()} * . * * @throws NotFoundException */ @Override public void testGetEnclosingType() throws NotFoundException { JParameterizedType testType = getTestType(); // Check that GenericClass<Integer> is not nested assertNull(testType.getEnclosingType()); /* * Check that GenericClass<Integer>.GenericInnerClass<Boolean> has // * GenericClass<Integer> as its // enclosing type */ JParameterizedType parameterizedInnerClass = getInnerParameterizedType(); assertEquals(testType, parameterizedInnerClass.getEnclosingType()); } @Override public void testGetInheritableMethods() throws NotFoundException { // Tested via testOverridableMethods_Base, testOverridableMethods_Derived, // testOverridableMethods_Derived_Integer } @Override public void testGetNestedType() { // TODO: complete this test method } /** * Test method for * {@link com.google.gwt.core.ext.typeinfo.JParameterizedType#getNestedTypes()} * . * * @throws NotFoundException */ @Override public void testGetNestedTypes() throws NotFoundException { JParameterizedType cut = getTestType(); JParameterizedType innerCut = getInnerParameterizedType(); // Check that inner parameterized types don't appear in the parent's nested // type set assertEquals(0, cut.getNestedTypes().length); try { cut.getNestedType(innerCut.getSimpleSourceName()); fail("Type " + cut.getQualifiedSourceName() + " should report that it has no nested types"); } catch (NotFoundException ex) { // Expected to get here } } @Override public void testGetOverridableMethods() throws NotFoundException { // Tested via testOverridableMethods_Base, testOverridableMethods_Derived, // testOverridableMethods_Derived_Integer } /** * Tests the subtypes of MyList<Integer>. These should be: * <ul> * <li><code>MyIntegerList</code></li> * <li><code>MyCustomList<? extends Serializable, Integer></code></li> * </ul> * * @throws NotFoundException */ @Override public void testGetSubtypes() throws NotFoundException { TypeOracle oracle = moduleContext.getOracle(); JGenericType genericType = oracle.getType(MyList.class.getName()).isGenericType(); JParameterizedType parameterizedType = oracle.getParameterizedType( genericType, new JClassType[]{integerType}); JClassType[] actualSubtypes = parameterizedType.getSubtypes(); JGenericType myCustomListType = oracle.getType(MyCustomList.class.getName()).isGenericType(); JParameterizedType parameterizedMyCustomList = oracle.getParameterizedType( myCustomListType, new JClassType[]{ oracle.getWildcardType(BoundType.EXTENDS, oracle.getType(Serializable.class.getName())), integerType}); JClassType[] expected = { oracle.getType(MyIntegerList.class.getName()), parameterizedMyCustomList}; validateEquals(expected, actualSubtypes); } @Override public void testIsAssignableFrom() throws NotFoundException { assertType("List").isAssignableFrom("List"); assertType("List").isAssignableFrom("List<?>"); assertType("List").isAssignableFrom("List<? extends Number>"); assertType("List").isAssignableFrom("List<String>"); assertType("List").isAssignableFrom("ExtendsRawList"); assertType("List<?>").isAssignableFrom("List"); assertType("List<?>").isAssignableFrom("List<?>"); assertType("List<?>").isAssignableFrom("List<? extends Number>"); assertType("List<?>").isAssignableFrom("List<String>"); assertType("List<?>").isAssignableFrom("ExtendsRawList"); assertType("List<? extends Number>").isAssignableFrom("List"); assertType("List<? extends Number>").isAssignableFrom("List<Integer>"); assertType("List<? extends Number>").isAssignableFrom("List<? extends Integer>"); assertType("List<? extends Number>").isNOTAssignableFrom("List<?>"); assertType("List<? extends Number>").isNOTAssignableFrom("List<Object>"); assertType("List<? extends Integer>").isAssignableFrom("List<? extends Integer>"); assertType("List<? extends Integer>").isNOTAssignableFrom("List<? extends Number>"); assertType("List<? super Number>").isAssignableFrom("List"); assertType("List<? super Number>").isAssignableFrom("List<Object>"); assertType("List<? super Number>").isAssignableFrom("List<Number>"); assertType("List<? super Number>").isAssignableFrom("List<? super Number>"); assertType("List<? super Number>").isNOTAssignableFrom("List<Integer>"); assertType("List<? super Number>").isNOTAssignableFrom("List<String>"); assertType("List<? super Number>").isNOTAssignableFrom("List<?>"); assertType("List<? super Number>").isNOTAssignableFrom("List<? super Integer>"); assertType("List<? super Integer>").isAssignableFrom("List<? super Object>"); assertType("List<? super Integer>").isAssignableFrom("List<? super Number>"); assertType("List<Object>").isAssignableFrom("List"); assertType("List<Object>").isAssignableFrom("List<Object>"); assertType("List<String>").isAssignableFrom("ExtendsRawList"); assertType("List<Object>").isNOTAssignableFrom("List<String>"); assertType("List<String>").isNOTAssignableFrom("List<Object>"); assertType("List<List>").isAssignableFrom("List"); assertType("List<List>").isAssignableFrom("List<List>"); assertType("List<List>").isNOTAssignableFrom("List<List<?>>"); assertType("List<List>").isNOTAssignableFrom("List<List<String>>"); assertType("List<List>").isNOTAssignableFrom("List<? extends List<String>>"); assertType("List<List<?>>").isAssignableFrom("List"); assertType("List<List<?>>").isAssignableFrom("List<List<?>>"); assertType("List<List<?>>").isAssignableFrom("List<List<? extends Object>>"); assertType("List<List<?>>").isNOTAssignableFrom("List<List>"); assertType("List<List<?>>").isNOTAssignableFrom("List<List<String>>"); assertType("List<List<String>>").isAssignableFrom("List"); assertType("List<List<String>>").isAssignableFrom("List<List<String>>"); assertType("List<List<String>>").isNOTAssignableFrom("List<List>"); assertType("List<List<String>>").isNOTAssignableFrom("List<List<?>>"); assertType("List<List<String>>").isNOTAssignableFrom("List<List<Object>>"); assertType("List<Collection<String>>").isNOTAssignableFrom("List<List<String>>"); assertType("List<? extends Collection<String>>").isAssignableFrom("List<List<String>>"); assertType("List<List<? extends Number>>").isNOTAssignableFrom("List<List<Integer>>"); assertType("Map<?, ?>").isAssignableFrom("Map"); assertType("Map<?, ?>").isAssignableFrom("Map<String, String>"); assertType("Map<?, ?>").isAssignableFrom("Map<String, Integer>"); assertType("Map<?, String>").isAssignableFrom("Map"); assertType("Map<?, String>").isAssignableFrom("Map<String, String>"); assertType("Map<?, String>").isNOTAssignableFrom("Map<String, Integer>"); assertType("Map<String, String>").isAssignableFrom("Map"); assertType("Map<String, String>").isAssignableFrom("Map<String, String>"); assertType("Map<String, String>").isNOTAssignableFrom("Map<String, Integer>"); } @Override public void testIsAssignableTo() throws NotFoundException { // This is covered as part of testIsAssignableFrom } public void testOverridableMethods_Base() throws NotFoundException { TypeOracle oracle = moduleContext.getOracle(); JClassType type = oracle.getType(Base.class.getName()); JGenericType genericType = type.isGenericType(); assertNotNull(genericType); List<JMethod> expected = new ArrayList<JMethod>( Arrays.asList(type.getOverloads("m"))); List<JMethod> actual = new ArrayList<JMethod>( Arrays.asList(type.getInheritableMethods())); validateInheritableOrOverridableMethods(expected, actual, true); actual = new ArrayList<JMethod>(Arrays.asList(type.getOverridableMethods())); validateInheritableOrOverridableMethods(expected, actual, true); } public void testOverridableMethods_Derived() throws NotFoundException { TypeOracle oracle = moduleContext.getOracle(); JClassType type = oracle.getType(Derived.class.getName()); JGenericType genericType = type.isGenericType(); assertNotNull(genericType); JClassType supertype = type.getSuperclass(); JParameterizedType paramType = supertype.isParameterized(); // JGenericType genericSuperType = paramType.getBaseType().isGenericType(); assertNotNull(paramType); List<JMethod> expected = new ArrayList<JMethod>(); expected.addAll(Arrays.asList(genericType.getOverloads("m"))); expected.add(paramType.getMethod("m", new JType[]{paramType.getTypeArgs()[0]})); List<JMethod> actual = new ArrayList<JMethod>( Arrays.asList(type.getInheritableMethods())); validateInheritableOrOverridableMethods(expected, actual, true); actual = new ArrayList<JMethod>(Arrays.asList(type.getOverridableMethods())); validateInheritableOrOverridableMethods(expected, actual, true); } public void testOverridableMethods_Derived_Integer() throws NotFoundException { TypeOracle oracle = moduleContext.getOracle(); JClassType type = oracle.getType(Derived.class.getName()); JGenericType genericType = type.isGenericType(); assertNotNull(genericType); JParameterizedType paramType = oracle.getParameterizedType(genericType, new JClassType[]{integerType}); List<JMethod> expected = new ArrayList<JMethod>(); expected.addAll(Arrays.asList(paramType.getOverloads("m"))); List<JMethod> actual = new ArrayList<JMethod>( Arrays.asList(paramType.getInheritableMethods())); validateInheritableOrOverridableMethods(expected, actual, true); actual = new ArrayList<JMethod>( Arrays.asList(paramType.getOverridableMethods())); validateInheritableOrOverridableMethods(expected, actual, true); } /** * Returns the <code>TypeOracle</code> type for {@link GenericClass}. * * @return <code>TypeOracle</code> type for {@link GenericClass} * @throws NotFoundException */ protected JGenericType getGenericTestType() throws NotFoundException { TypeOracle oracle = moduleContext.getOracle(); JClassType type = oracle.getType(GenericClass.class.getName()); assertNotNull(type.isGenericType()); return type.isGenericType(); } @Override protected Substitution getSubstitution() throws NotFoundException { return new ParameterizedSubstitution(getTestType()); } @Override protected JParameterizedType getTestType() throws NotFoundException { JGenericType type = getGenericTestType(); return moduleContext.getOracle().getParameterizedType(type, null, new JClassType[]{integerType}); } /** * Returns the type for GenericClass<Integer>.GenericInnerClass<Boolean>. * * @throws NotFoundException * @return type for GenericClass<Integer>.GenericInnerClass<Boolean> */ private JParameterizedType getInnerParameterizedType() throws NotFoundException { JParameterizedType cut = getTestType(); TypeOracle oracle = moduleContext.getOracle(); JGenericType innerGenericClass = cut.getBaseType().getNestedType( GenericInnerClass.class.getSimpleName()).isGenericType(); JClassType booleanType = oracle.getType(Boolean.class.getName()); /* * Check that GenericClass<Integer>.GenericInnerClass<Boolean> has * GenericClass<Integer> as its enclosing type */ // JParameterizedType parameterizedInnerClass = oracle.getParameterizedType( innerGenericClass, cut, new JClassType[]{booleanType}); return parameterizedInnerClass; } private void validateInheritableOrOverridableMethods(List<JMethod> expected, List<JMethod> actual, boolean addObjectMethods) { Set<JMethod> expectedMethods = new HashSet<JMethod>(); expectedMethods.addAll(expected); if (addObjectMethods) { TypeOracle oracle = moduleContext.getOracle(); expectedMethods.addAll( Arrays.asList(oracle.getJavaLangObject().getOverridableMethods())); } for (JMethod method : actual) { assertEquals("Method " + method.getReadableDeclaration() + " from type " + method.getEnclosingType().getQualifiedSourceName() + " was not expected", true, expectedMethods.remove(method)); } assertTrue(expectedMethods.isEmpty()); } private static TypeAssignabilityAsserter assertType(final String type) { return new TypeAssignabilityAsserter(type); } // TODO(goktug): make this utilized in more tests private static class TypeAssignabilityAsserter { private String type; public TypeAssignabilityAsserter(String type) { this.type = type; } public void isAssignableFrom(String from) { assertTrue(isAssignableFromTo(from, type)); } public void isNOTAssignableFrom(String from) { assertFalse(isAssignableFromTo(from, type)); } private boolean isAssignableFromTo(final String fromType, final String toType) { // Compile the code snippet to extract the types final String helperClassName = "ParameterizedTestHelper"; Set<Resource> resources = new HashSet<Resource>(); resources.addAll(Arrays.asList(JavaResourceBase.getStandardResources())); resources.add(JavaResourceBase.createMockJavaResource(helperClassName, "import java.util.*;", "public class " + helperClassName + " {", fromType + " from() { return null; }", toType + " to() { return null; }", "}")); resources.add(JavaResourceBase.createMockJavaResource("ExtendsRawComparable", "public interface ExtendsRawComparable extends Comparable {}")); resources.add(JavaResourceBase.createMockJavaResource("NonRecursiveComparable", "public interface NonRecursiveComparable extends Comparable<Number> {}")); resources.add(JavaResourceBase.createMockJavaResource("ExtendsRawList", "import java.util.*;", "public interface ExtendsRawList extends List {}")); JClassType to; JClassType from; try { // Compile and get helper type JClassType helperType = (JClassType) TypeOracleTestingUtils.buildTypeOracle( createTreeLogger(), resources).getType(helperClassName); to = (JClassType) helperType.getMethod("to", TypeOracle.NO_JCLASSES).getReturnType(); from = (JClassType) helperType.getMethod("from", TypeOracle.NO_JCLASSES).getReturnType(); } catch (NotFoundException e) { throw new AssertionError("Possible compilation error. Enable logToConsole for more info"); } // Check the assignability boolean assignableFrom = to.isAssignableFrom(from); boolean assignableTo = from.isAssignableTo(to); assertEquals(assignableFrom, assignableTo); return assignableFrom; } } }