/* * 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.NotFoundException; import com.google.gwt.core.ext.typeinfo.JWildcardType.BoundType; 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.ExtendsRawGenericClass; import com.google.gwt.dev.javac.typemodel.test.GenericClass; 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.javac.typemodel.test.GenericClass.GenericInnerClass; 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; import java.util.Vector; /** * 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; } public JClassType getSubstitution(JClassType type) { return type.getSubstitutedType(parameterizedType); } } private final JClassType integerType; private final boolean logToConsole = false; private final ModuleContext moduleContext = new ModuleContext(logToConsole ? new PrintWriterTreeLogger() : TreeLogger.NULL, "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); } /** * Test method for * {@link com.google.gwt.core.ext.typeinfo.JParameterizedType#isAssignableFrom(JClassType)} * . */ @Override public void testIsAssignableFrom() throws NotFoundException { // Check that raw types can be assigned to a parameterized type JParameterizedType testType = getTestType(); JClassType rawType = testType.getRawType(); assertTrue(testType.isAssignableFrom(rawType)); TypeOracle oracle = moduleContext.getOracle(); JGenericType genericList = (JGenericType) oracle.getType(List.class.getName()); // ? JWildcardType unboundWildcard = oracle.getWildcardType(BoundType.EXTENDS, oracle.getJavaLangObject()); // ? extends Number JWildcardType numUpperBoundWildcard = oracle.getWildcardType( BoundType.EXTENDS, oracle.getType(Number.class.getName())); // List<?> JParameterizedType unboundList = oracle.getParameterizedType(genericList, new JClassType[]{unboundWildcard}); // List<? extends Number> JParameterizedType listOfExtendsNumber = oracle.getParameterizedType( genericList, new JClassType[]{numUpperBoundWildcard}); // List<?> should be assignable from List<? extends Number> assertTrue(unboundList.isAssignableFrom(listOfExtendsNumber)); assertFalse(unboundList.isAssignableTo(listOfExtendsNumber)); assertFalse(listOfExtendsNumber.isAssignableFrom(unboundList)); assertTrue(listOfExtendsNumber.isAssignableTo(unboundList)); // ? extends Integer JWildcardType intUpperBoundWildcard = oracle.getWildcardType( BoundType.EXTENDS, integerType); // List<? extends Integer> JParameterizedType listOfExtendsInteger = oracle.getParameterizedType( genericList, new JClassType[]{intUpperBoundWildcard}); // List<? extends Number> should be assignable from List<? extends Integer> assertTrue(listOfExtendsNumber.isAssignableFrom(listOfExtendsInteger)); assertFalse(listOfExtendsNumber.isAssignableTo(listOfExtendsInteger)); assertFalse(listOfExtendsInteger.isAssignableFrom(listOfExtendsNumber)); assertTrue(listOfExtendsInteger.isAssignableTo(listOfExtendsNumber)); // List<? super Integer> should be assignable from List<? super Number> JWildcardType numLowerBoundWildcard = oracle.getWildcardType( BoundType.SUPER, oracle.getType(Number.class.getName())); JWildcardType intLowerBoundWildcard = oracle.getWildcardType( BoundType.SUPER, integerType); // List<? super Number> JParameterizedType listOfSuperNumber = oracle.getParameterizedType( genericList, new JClassType[]{numLowerBoundWildcard}); // List<? super Interger> JParameterizedType listOfSuperInteger = oracle.getParameterizedType( genericList, new JClassType[]{intLowerBoundWildcard}); assertTrue(listOfSuperInteger.isAssignableFrom(listOfSuperNumber)); assertFalse(listOfSuperInteger.isAssignableTo(listOfSuperNumber)); assertFalse(listOfSuperNumber.isAssignableFrom(listOfSuperInteger)); assertTrue(listOfSuperNumber.isAssignableTo(listOfSuperInteger)); JParameterizedType listOfObject = oracle.getParameterizedType(genericList, new JClassType[]{oracle.getJavaLangObject()}); JClassType stringType = oracle.getType(String.class.getName()); JParameterizedType listOfString = oracle.getParameterizedType(genericList, new JClassType[]{stringType}); // List<Object> is not assignable from List<String> assertFalse(listOfObject.isAssignableFrom(listOfString)); // List<String> is not assignable from List<Object> assertFalse(listOfString.isAssignableFrom(listOfObject)); // List<List<String>> is not assignable from List<Vector<String>> JParameterizedType listOfListOfString = oracle.getParameterizedType( genericList, new JClassType[]{listOfString}); JGenericType genericVector = oracle.getType(Vector.class.getName()).isGenericType(); JParameterizedType vectorOfString = oracle.getParameterizedType( genericVector, new JClassType[]{stringType}); JParameterizedType listOfVectorOfString = oracle.getParameterizedType( genericList, new JClassType[]{vectorOfString}); assertFalse(listOfListOfString.isAssignableFrom(listOfVectorOfString)); assertFalse(listOfVectorOfString.isAssignableFrom(listOfListOfString)); // List<List> is not assignable from List<List<String>> JClassType listOfRawList = oracle.getParameterizedType(genericList, new JClassType[]{genericList.getRawType()}); assertFalse(listOfRawList.isAssignableFrom(listOfListOfString)); assertFalse(listOfListOfString.isAssignableFrom(listOfRawList)); JGenericType genericClass = oracle.getType(GenericClass.class.getName()).isGenericType(); JParameterizedType parameterizedGenericClass = oracle.getParameterizedType( genericClass, new JClassType[]{stringType}); JClassType extendsRawGenericClass = oracle.getType(ExtendsRawGenericClass.class.getName()); // GenericClass<String> is assignable from ExtendsRawGenericClass assertTrue(parameterizedGenericClass.isAssignableFrom(extendsRawGenericClass)); // ExtendsRawGenericClass is not assignable from GenericClass<String> assertFalse(extendsRawGenericClass.isAssignableFrom(parameterizedGenericClass)); // List<List<? extends Number>> JClassType listOfListOfExtendsNumber = oracle.getParameterizedType( genericList, new JClassType[]{listOfExtendsNumber}); // List<List<? extends Integer>> JClassType listOfListOfExtendsInteger = oracle.getParameterizedType( genericList, new JClassType[]{listOfExtendsInteger}); assertFalse(listOfListOfExtendsNumber.isAssignableFrom(listOfListOfExtendsInteger)); // List<Integer> JClassType listOfInteger = oracle.getParameterizedType(genericList, new JClassType[]{integerType}); // List<? extends Number> is assignable from List<Integer> assertTrue(listOfExtendsNumber.isAssignableFrom(listOfInteger)); assertFalse(listOfExtendsNumber.isAssignableFrom(listOfObject)); // List<? super Number> is not assignable from List<Integer> assertFalse(listOfSuperNumber.isAssignableFrom(listOfInteger)); // List<? super Number> is assignable from List<Object> assertTrue(listOfSuperNumber.isAssignableFrom(listOfObject)); } @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().getMethods())); } for (JMethod method : actual) { assertEquals("Method " + method.getReadableDeclaration() + " from type " + method.getEnclosingType().getQualifiedSourceName() + " was not expected", true, expectedMethods.remove(method)); } assertTrue(expectedMethods.isEmpty()); } }