/* * 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.user.rebind.rpc; import com.google.gwt.core.ext.StubGeneratorContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JArrayType; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JGenericType; import com.google.gwt.core.ext.typeinfo.JParameterizedType; import com.google.gwt.core.ext.typeinfo.JPrimitiveType; import com.google.gwt.core.ext.typeinfo.JRawType; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.JTypeParameter; import com.google.gwt.core.ext.typeinfo.JWildcardType.BoundType; import com.google.gwt.core.ext.typeinfo.NotFoundException; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.dev.cfg.BindingProperty; import com.google.gwt.dev.cfg.ConfigurationProperty; import com.google.gwt.dev.cfg.ModuleDef; import com.google.gwt.dev.cfg.ModuleDefLoader; import com.google.gwt.dev.cfg.StaticPropertyOracle; import com.google.gwt.dev.javac.TypeOracleTestingUtils; import com.google.gwt.dev.javac.testing.impl.JavaResourceBase; import com.google.gwt.dev.javac.testing.impl.MockJavaResource; import com.google.gwt.dev.javac.testing.impl.StaticJavaResource; import com.google.gwt.dev.resource.Resource; import com.google.gwt.dev.util.log.PrintWriterTreeLogger; import com.google.gwt.user.rebind.rpc.testcases.client.AbstractSerializableTypes; import com.google.gwt.user.rebind.rpc.testcases.client.ClassWithTypeParameterThatErasesToObject; import com.google.gwt.user.rebind.rpc.testcases.client.ManualSerialization; import com.google.gwt.user.rebind.rpc.testcases.client.NoSerializableTypes; import com.google.gwt.user.rebind.rpc.testcases.client.NotAllSubtypesAreSerializable; import junit.framework.TestCase; import java.io.PrintWriter; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; /** * Used to test the {@link SerializableTypeOracleBuilder}. */ public class SerializableTypeOracleBuilderTest extends TestCase { /** * Just enough of a {@code GeneratorContext} to satisfy * {@code SerializableTypeOracleBuilder}. */ static class MockContext extends StubGeneratorContext { private TypeOracle typeOracle; MockContext(TypeOracle typeOracle) { this.typeOracle = typeOracle; } @Override public TypeOracle getTypeOracle() { return typeOracle; } @Override public boolean isProdMode() { return true; } } /** * Used to test the results produced by the {@link SerializableTypeOracle}. */ static class TypeInfo { boolean maybeInstantiated; final String sourceName; TypeInfo(String binaryName, boolean maybeInstantiated) { this.sourceName = makeSourceName(binaryName); this.maybeInstantiated = maybeInstantiated; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof TypeInfo)) { return false; } TypeInfo other = (TypeInfo) obj; return sourceName.equals(other.sourceName) && maybeInstantiated == other.maybeInstantiated; } @Override public int hashCode() { return sourceName.hashCode() * (maybeInstantiated ? 17 : 43); } @Override public String toString() { return "{ " + sourceName + ", " + Boolean.toString(maybeInstantiated) + " }"; } } private static final int EXPOSURE_DIRECT = TypeParameterExposureComputer.EXPOSURE_DIRECT; private static final int EXPOSURE_NONE = TypeParameterExposureComputer.EXPOSURE_NONE; private static TypeOracle sTypeOracle; /** * Mocks the source of the {@link GwtTransient} type in this package. */ private static void addCustomGwtTransient(Set<Resource> resources) { StringBuffer code = new StringBuffer(); code.append("package com.google.gwt.user.rebind.rpc;\n"); code.append("import java.lang.annotation.Retention;"); code.append("import java.lang.annotation.RetentionPolicy;"); code.append("@Retention(RetentionPolicy.RUNTIME)"); code.append("public @interface GwtTransient { }\n"); resources.add(new StaticJavaResource("com.google.gwt.user.rebind.rpc.GwtTransient", code)); } private static void addGwtTransient(Set<Resource> resources) { StringBuffer code = new StringBuffer(); code.append("package com.google.gwt.user.client.rpc;\n"); code.append("public @interface GwtTransient { }\n"); resources.add(new StaticJavaResource("com.google.gwt.user.client.rpc.GwtTransient", code)); } private static void addICRSE(Set<Resource> resources) { StringBuffer code = new StringBuffer(); code.append("package com.google.gwt.user.client.rpc;\n"); code.append("public class IncompatibleRemoteServiceException extends Throwable {\n"); code.append("}\n"); resources.add(new StaticJavaResource( "com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException", code)); } private static void addStandardClasses(Set<Resource> resources) { for (MockJavaResource resource : JavaResourceBase.getStandardResources()) { resources.add(resource); } addGwtTransient(resources); addICRSE(resources); } private static void assertFieldSerializable(SerializableTypeOracle so, JClassType type) { assertTrue(so.isSerializable(type)); } private static void assertInstantiable(SerializableTypeOracle so, JClassType type) { assertTrue(so.maybeInstantiated(type)); assertFieldSerializable(so, type); } private static void assertNotFieldSerializable(SerializableTypeOracle so, JClassType type) { assertFalse(so.isSerializable(type)); } private static void assertNotInstantiable(SerializableTypeOracle so, JClassType type) { assertFalse(so.maybeInstantiated(type)); } private static void assertNotInstantiableOrFieldSerializable(SerializableTypeOracle so, JClassType type) { assertNotInstantiable(so, type); assertNotFieldSerializable(so, type); } private static void assertSerializableTypes(SerializableTypeOracle so, JClassType... expectedTypes) { Set<JType> expectedSet = new TreeSet<JType>(SerializableTypeOracleBuilder.JTYPE_COMPARATOR); expectedSet.addAll(Arrays.asList(expectedTypes)); Set<JType> actualSet = new TreeSet<JType>(SerializableTypeOracleBuilder.JTYPE_COMPARATOR); JType[] actualTypes = so.getSerializableTypes(); actualSet.addAll(Arrays.asList(actualTypes)); assertTrue("Sets not equal. Expected=\n" + expectedSet + ", \nactual=\n" + actualSet, expectedSet.containsAll(actualSet) && actualSet.containsAll(expectedSet)); } private static TreeLogger createLogger() { PrintWriterTreeLogger logger = new PrintWriterTreeLogger(new PrintWriter(System.err, true)); logger.setMaxDetail(TreeLogger.ERROR); return logger; } private static SerializableTypeOracleBuilder createSerializableTypeOracleBuilder( TreeLogger logger, TypeOracle to) throws UnableToCompleteException { // Make an empty property oracle. StaticPropertyOracle propertyOracle = new StaticPropertyOracle(new BindingProperty[0], new String[0], new ConfigurationProperty[0]); return new SerializableTypeOracleBuilder(logger, propertyOracle, new MockContext(to)); } private static TypeInfo[] getActualTypeInfo(SerializableTypeOracle sto) { JType[] types = sto.getSerializableTypes(); TypeInfo[] actual = new TypeInfo[types.length]; for (int i = 0; i < types.length; ++i) { JType type = types[i]; actual[i] = new TypeInfo(type.getParameterizedQualifiedSourceName(), sto.maybeInstantiated(type)); } sort(actual); return actual; } private static TypeOracle getTestTypeOracle() throws UnableToCompleteException { if (sTypeOracle == null) { TreeLogger logger = createLogger(); ModuleDef moduleDef = ModuleDefLoader.createSyntheticModule(logger, "com.google.gwt.user.rebind.rpc.testcases.RebindRPCTestCases.JUnit", new String[] { "com.google.gwt.user.rebind.rpc.testcases.RebindRPCTestCases", "com.google.gwt.junit.JUnit"}, true); sTypeOracle = moduleDef.getCompilationState(logger).getTypeOracle(); } return sTypeOracle; } private static String makeSourceName(String binaryName) { return binaryName.replace('$', '.'); } private static void sort(TypeInfo[] typeInfos) { Arrays.sort(typeInfos, new Comparator<TypeInfo>() { @Override public int compare(TypeInfo ti1, TypeInfo ti2) { if (ti1 == ti2) { return 0; } return ti1.sourceName.compareTo(ti2.sourceName); } }); } private static String toString(TypeInfo[] typeInfos) { StringBuffer sb = new StringBuffer(); sb.append("["); for (int i = 0; i < typeInfos.length; ++i) { if (i != 0) { sb.append(","); } sb.append(typeInfos[i].toString()); sb.append("\n"); } sb.append("]"); return sb.toString(); } private static void validateSTO(SerializableTypeOracle sto, TypeInfo[] expected) { sort(expected); TypeInfo[] actual = getActualTypeInfo(sto); assertTrue("Expected: \n" + toString(expected) + ",\n Actual: \n" + toString(actual), Arrays .equals(expected, actual)); } /** * Test with a generic class whose type parameter is exposed only in certain * subclasses. * * NOTE: This test has been disabled because it requires a better pruner in * STOB. See SerializableTypeOracleBuilder.pruneUnreachableTypes(). */ public void disabledTestMaybeExposedParameter() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public abstract class List<T> implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("List", code)); } { StringBuilder code = new StringBuilder(); code.append("public class EmptyList<T> extends List<T> {\n"); code.append("}\n"); resources.add(new StaticJavaResource("EmptyList", code)); } { StringBuilder code = new StringBuilder(); code.append("public class LinkedList<T> extends List<T> {\n"); code.append(" T head;\n"); code.append(" LinkedList<T> next;\n"); code.append("}\n"); resources.add(new StaticJavaResource("LinkedList", code)); } { StringBuilder code = new StringBuilder(); code.append("public class CantSerialize {\n"); code.append("}\n"); resources.add(new StaticJavaResource("CantSerialize", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType list = to.getType("List").isGenericType(); JGenericType emptyList = to.getType("EmptyList").isGenericType(); JClassType cantSerialize = to.getType("CantSerialize"); JParameterizedType listOfCantSerialize = to.getParameterizedType(list, makeArray(cantSerialize)); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, listOfCantSerialize); SerializableTypeOracle so = sob.build(logger); assertFieldSerializable(so, listOfCantSerialize); assertSerializableTypes(so, list.getRawType(), emptyList.getRawType()); } /** * Tests abstract root types that are field serializable. * * @throws UnableToCompleteException * @throws NotFoundException */ public void testAbstractFieldSerializableRootType() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public abstract class A implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("A", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public abstract class B extends A {\n"); code.append("}\n"); resources.add(new StaticJavaResource("B", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class C extends B {\n"); code.append("}\n"); resources.add(new StaticJavaResource("C", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JClassType a = to.getType("A"); JClassType b = to.getType("B"); JClassType c = to.getType("C"); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, b); SerializableTypeOracle so = sob.build(logger); assertInstantiable(so, c); assertFieldSerializable(so, a); assertFieldSerializable(so, b); assertSerializableTypes(so, a, b, c); } /** * Tests that we do not violate java package restrictions when computing * serializable types. */ public void testAccessLevelsInJavaPackage() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("package java;\n"); code.append("import java.io.Serializable;\n"); code.append("public class A implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("java.A", code)); } { StringBuilder code = new StringBuilder(); code.append("package java;\n"); code.append("import java.io.Serializable;\n"); code.append("class B extends A {\n"); code.append("}\n"); resources.add(new StaticJavaResource("java.B", code)); } { StringBuilder code = new StringBuilder(); code.append("package java;\n"); code.append("import java.io.Serializable;\n"); code.append("public class C extends A {\n"); code.append("}\n"); resources.add(new StaticJavaResource("java.C", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JClassType a = to.getType("java.A"); JArrayType arrayOfA = to.getArrayType(a); JClassType c = to.getType("java.C"); JArrayType arrayOfC = to.getArrayType(c); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, arrayOfA); SerializableTypeOracle so = sob.build(logger); assertSerializableTypes(so, arrayOfA, arrayOfC, a, c); } /* * Tests arrays of parameterized types. */ public void testArrayOfParameterizedTypes() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { // A<T> exposes its param StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class A<T> implements Serializable {\n"); code.append(" T t;\n"); code.append("}\n"); resources.add(new StaticJavaResource("A", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class AList<T> implements Serializable {\n"); code.append(" A<T>[] as;\n"); code.append("}\n"); resources.add(new StaticJavaResource("AList", code)); } { // B<T> does not expose its param StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class B<T> implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("B", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class BList<T> implements Serializable {\n"); code.append(" B<T>[] bs;\n"); code.append("}\n"); resources.add(new StaticJavaResource("BList", code)); } { // A random serializable class StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class Ser1 implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Ser1", code)); } { // A random serializable class StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class Ser2 implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Ser2", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class Root implements Serializable {\n"); code.append(" AList<Ser1> alist;\n"); code.append(" BList<Ser2> blist;\n"); code.append("}\n"); resources.add(new StaticJavaResource("Root", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType a = to.getType("A").isGenericType(); JGenericType b = to.getType("B").isGenericType(); JGenericType alist = to.getType("AList").isGenericType(); JGenericType blist = to.getType("BList").isGenericType(); JClassType ser1 = to.getType("Ser1"); JClassType ser2 = to.getType("Ser2"); JClassType root = to.getType("Root"); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, root); assertEquals(EXPOSURE_DIRECT, sob.getTypeParameterExposure(a, 0)); assertEquals(EXPOSURE_NONE, sob.getTypeParameterExposure(b, 0)); assertEquals(EXPOSURE_DIRECT, sob.getTypeParameterExposure(alist, 0)); assertEquals(EXPOSURE_NONE, sob.getTypeParameterExposure(blist, 0)); SerializableTypeOracle so = sob.build(logger); JArrayType aArray = to.getArrayType(a.getRawType()); JArrayType bArray = to.getArrayType(b.getRawType()); assertSerializableTypes(so, root, alist.getRawType(), blist.getRawType(), aArray, bArray, a .getRawType(), b.getRawType(), ser1); assertInstantiable(so, alist.getRawType()); assertInstantiable(so, blist.getRawType()); assertInstantiable(so, a.getRawType()); assertInstantiable(so, b.getRawType()); assertInstantiable(so, aArray); assertInstantiable(so, bArray); assertInstantiable(so, ser1); assertNotInstantiableOrFieldSerializable(so, ser2); } /* * Tests arrays of type variables that do not cause infinite expansion. */ public void testArrayOfTypeParameter() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class A<T> implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("A", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class B<T> extends A<T> implements Serializable {\n"); code.append(" T[][] t;\n"); code.append("}\n"); resources.add(new StaticJavaResource("B", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class C<T> implements Serializable {\n"); code.append(" A<T[]> a1;\n"); code.append(" A<Ser> a2;\n"); code.append("}\n"); resources.add(new StaticJavaResource("C", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class Ser implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Ser", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType a = to.getType("A").isGenericType(); JGenericType b = to.getType("B").isGenericType(); JGenericType c = to.getType("C").isGenericType(); JClassType ser = to.getType("Ser"); JClassType javaLangString = to.getType(String.class.getName()); JParameterizedType cOfString = to.getParameterizedType(c, makeArray(javaLangString)); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, cOfString); assertEquals(2, sob.getTypeParameterExposure(a, 0)); assertEquals(2, sob.getTypeParameterExposure(b, 0)); assertEquals(3, sob.getTypeParameterExposure(c, 0)); SerializableTypeOracle so = sob.build(logger); JArrayType stringArray = to.getArrayType(javaLangString); JArrayType stringArrayArray = to.getArrayType(stringArray); JArrayType stringArrayArrayArray = to.getArrayType(stringArrayArray); JArrayType serArray = to.getArrayType(ser); JArrayType serArrayArray = to.getArrayType(serArray); assertSerializableTypes(so, a.getRawType(), b.getRawType(), c, javaLangString, stringArray, stringArrayArray, stringArrayArrayArray, ser, serArray, serArrayArray); assertInstantiable(so, a.getRawType()); assertInstantiable(so, b.getRawType()); assertInstantiable(so, c); assertInstantiable(so, stringArray); assertInstantiable(so, stringArrayArray); assertInstantiable(so, stringArrayArrayArray); assertInstantiable(so, serArray); assertInstantiable(so, serArrayArray); } /** * Tests the rules that govern whether a type qualifies for serialization. * * @throws UnableToCompleteException * @throws NotFoundException */ public void testClassQualifiesForSerialization() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("public class NotSerializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("NotSerializable", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class OuterClass {\n"); code.append(" static class StaticNested implements Serializable {};\n"); code.append(" class NonStaticNested implements Serializable {};\n"); code.append("}\n"); resources.add(new StaticJavaResource("OuterClass", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public abstract class AbstractSerializableClass implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("AbstractSerializableClass", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class NonDefaultInstantiableSerializable implements Serializable {\n"); code.append(" NonDefaultInstantiableSerializable(int i) {}\n"); code.append("}\n"); resources.add(new StaticJavaResource("NonDefaultInstantiableSerializable", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class PublicOuterClass {\n"); code.append(" private static class PrivateStaticInner {\n"); code.append(" public static class PublicStaticInnerInner implements Serializable {\n"); code.append(" }\n"); code.append(" }\n"); code.append(" static class DefaultStaticInner {\n"); code.append(" static class DefaultStaticInnerInner implements Serializable {\n"); code.append(" }\n"); code.append(" }\n"); code.append("}\n"); resources.add(new StaticJavaResource("PublicOuterClass", code)); } { StringBuilder code = new StringBuilder(); code.append("public enum EnumWithSubclasses {\n"); code.append(" A {\n"); code.append(" @Override\n"); code.append(" public String value() {\n"); code.append(" return \"X\";\n"); code.append(" }\n"); code.append(" },\n"); code.append(" B {\n"); code.append(" @Override\n"); code.append(" public String value() {\n"); code.append(" return \"Y\";\n"); code.append(" };\n"); code.append(" };\n"); code.append(" public abstract String value();\n"); code.append("};\n"); resources.add(new StaticJavaResource("EnumWithSubclasses", code)); } { StringBuilder code = new StringBuilder(); code.append("public enum EnumWithNonDefaultCtors {\n"); code.append(" A(\"X\"), B(\"Y\");\n"); code.append(" String value;"); code.append(" private EnumWithNonDefaultCtors(String value) {\n"); code.append(" this.value = value;\n"); code.append(" }\n"); code.append("};\n"); resources.add(new StaticJavaResource("EnumWithNonDefaultCtors", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); // Does not qualify because it is not declared to be auto or manually // serializable JClassType notSerializable = to.getType("NotSerializable"); ProblemReport problems = new ProblemReport(); assertFalse(sob.shouldConsiderFieldsForSerialization(notSerializable, problems)); // Static nested types qualify for serialization JClassType staticNested = to.getType("OuterClass.StaticNested"); problems = new ProblemReport(); assertTrue(sob.shouldConsiderFieldsForSerialization(staticNested, problems)); // Non-static nested types do not qualify for serialization JClassType nonStaticNested = to.getType("OuterClass.NonStaticNested"); problems = new ProblemReport(); assertFalse(sob.shouldConsiderFieldsForSerialization(nonStaticNested, problems)); // Abstract classes that implement Serializable should not qualify JClassType abstractSerializableClass = to.getType("AbstractSerializableClass"); problems = new ProblemReport(); assertTrue(sob.shouldConsiderFieldsForSerialization(abstractSerializableClass, problems)); problems = new ProblemReport(); assertFalse(SerializableTypeOracleBuilder .canBeInstantiated(abstractSerializableClass, problems)); // Non-default instantiable types should not qualify JClassType nonDefaultInstantiableSerializable = to.getType("NonDefaultInstantiableSerializable"); problems = new ProblemReport(); assertTrue(sob.shouldConsiderFieldsForSerialization(nonDefaultInstantiableSerializable, problems)); problems = new ProblemReport(); assertFalse(SerializableTypeOracleBuilder.canBeInstantiated(nonDefaultInstantiableSerializable, problems)); // SPublicStaticInnerInner is not accessible to classes in its package JClassType publicStaticInnerInner = to.getType("PublicOuterClass.PrivateStaticInner.PublicStaticInnerInner"); problems = new ProblemReport(); assertFalse(sob.shouldConsiderFieldsForSerialization(publicStaticInnerInner, problems)); // DefaultStaticInnerInner is visible to classes in its package JClassType defaultStaticInnerInner = to.getType("PublicOuterClass.DefaultStaticInner.DefaultStaticInnerInner"); problems = new ProblemReport(); assertTrue(sob.shouldConsiderFieldsForSerialization(defaultStaticInnerInner, problems)); // Enum with subclasses should qualify, but their subtypes should not JClassType enumWithSubclasses = to.getType("EnumWithSubclasses"); problems = new ProblemReport(); assertTrue(sob.shouldConsiderFieldsForSerialization(enumWithSubclasses, problems)); // There are no longer any enum subclasses in TypeOracle assertEquals(0, enumWithSubclasses.getSubtypes().length); // Enum that are not default instantiable should qualify JClassType enumWithNonDefaultCtors = to.getType("EnumWithNonDefaultCtors"); problems = new ProblemReport(); assertTrue(sob.shouldConsiderFieldsForSerialization(enumWithNonDefaultCtors, problems)); } /** * Tests that both the generic and raw forms of type that has a type parameter * that erases to object are not serializable. * * @throws NotFoundException */ public void testClassWithTypeParameterThatErasesToObject() throws NotFoundException, UnableToCompleteException { TreeLogger logger = createLogger(); TypeOracle typeOracle = getTestTypeOracle(); JRawType rawType = typeOracle.getType(ClassWithTypeParameterThatErasesToObject.class.getCanonicalName()) .isGenericType().getRawType(); // The raw form of the type should not be serializable. SerializableTypeOracleBuilder stob = createSerializableTypeOracleBuilder(logger, typeOracle); stob.addRootType(logger, rawType); try { stob.build(logger); fail("Expected an " + UnableToCompleteException.class.getSimpleName()); } catch (UnableToCompleteException ex) { // Expected to reach here } } /** * Test the situation where an abstract class has an unconstrained type * parameter but all of its concrete subclasses add helpful constraints to it. * * @throws NotFoundException * @throws UnableToCompleteException */ public void testConcreteClassesConstrainATypeParameter() throws NotFoundException, UnableToCompleteException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public abstract class Holder<T extends Serializable> implements Serializable {\n"); code.append(" T x;\n"); code.append("}\n"); resources.add(new StaticJavaResource("Holder", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class StringHolder extends Holder<String> implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("StringHolder", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class DateHolder extends Holder<Date> implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("DateHolder", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class Date implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Date", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class UnrelatedClass implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("UnrelatedClass", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType holder = to.getType("Holder").isGenericType(); JClassType stringHolder = to.getType("StringHolder"); JClassType dateHolder = to.getType("DateHolder"); JClassType unrelatedClass = to.getType("UnrelatedClass"); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, holder.getRawType()); SerializableTypeOracle so = sob.build(logger); JClassType string = to.getType(String.class.getCanonicalName()); JClassType date = to.getType("Date"); assertSerializableTypes(so, holder.getRawType(), stringHolder, dateHolder, string, date); assertFieldSerializable(so, holder.getRawType()); assertInstantiable(so, stringHolder); assertInstantiable(so, dateHolder); assertInstantiable(so, string); assertInstantiable(so, date); assertNotInstantiableOrFieldSerializable(so, unrelatedClass); } /** * Tests that a method signature which returns an Array type also includes the * possible covariant array types which could contain a serializable type. */ public void testCovariantArrays() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class Sup implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Sup", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class Sub extends Sup {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Sub", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JClassType sup = to.getType("Sup"); JClassType sub = to.getType("Sub"); JPrimitiveType primFloat = JPrimitiveType.FLOAT; JArrayType subArray = to.getArrayType(sub); JArrayType subArrayArray = to.getArrayType(subArray); JArrayType supArray = to.getArrayType(sup); JArrayType supArrayArray = to.getArrayType(supArray); JArrayType primFloatArray = to.getArrayType(primFloat); JArrayType primFloatArrayArray = to.getArrayType(primFloatArray); SerializableTypeOracleBuilder stob = createSerializableTypeOracleBuilder(logger, to); // adding Sub first exercises an extra code path in STOB stob.addRootType(logger, sub); stob.addRootType(logger, supArrayArray); stob.addRootType(logger, primFloatArrayArray); SerializableTypeOracle sto = stob.build(logger); assertSerializableTypes(sto, sup, sub, supArray, subArray, primFloatArray, supArrayArray, subArrayArray, primFloatArrayArray); assertInstantiable(sto, primFloatArrayArray); assertInstantiable(sto, primFloatArray); assertInstantiable(sto, subArrayArray); assertInstantiable(sto, subArray); assertInstantiable(sto, supArrayArray); assertInstantiable(sto, supArray); } /** * If the query type extends a raw type, be sure to pick up the parameters of * the raw subertype. * * @throws UnableToCompleteException * @throws NotFoundException */ public void testExtensionFromRaw1() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class HashSet<T extends SerClass> implements Serializable {\n"); code.append(" T[] x;\n"); code.append("}\n"); resources.add(new StaticJavaResource("HashSet", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class NameSet extends HashSet implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("NameSet", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class SerClass implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("SerClass", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class SerClassSub extends SerClass {\n"); code.append("}\n"); resources.add(new StaticJavaResource("SerClassSub", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType hashSet = to.getType("HashSet").isGenericType(); JClassType nameSet = to.getType("NameSet"); JClassType serClass = to.getType("SerClass"); JClassType serClassSub = to.getType("SerClassSub"); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, nameSet); SerializableTypeOracle so = sob.build(logger); JArrayType arrayOfSerClass = to.getArrayType(serClass); JArrayType arrayOfSerClassSub = to.getArrayType(serClassSub); assertSerializableTypes(so, hashSet.getRawType(), nameSet, serClass, serClassSub, arrayOfSerClass, arrayOfSerClassSub); assertFieldSerializable(so, hashSet.getRawType()); assertNotInstantiable(so, hashSet.getRawType()); assertInstantiable(so, nameSet); assertInstantiable(so, serClass); assertInstantiable(so, serClassSub); assertInstantiable(so, arrayOfSerClass); assertInstantiable(so, arrayOfSerClassSub); } /** * If a subtype of a root type extends from the raw version of that root type, * then when visiting the fields of the raw version, take advantage of * information from the original root type. * * @throws UnableToCompleteException * @throws NotFoundException */ public void testExtensionFromRaw2() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class HashSet<T extends Serializable> implements Serializable {\n"); code.append(" T[] x;\n"); code.append("}\n"); resources.add(new StaticJavaResource("HashSet", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class NameSet<T extends SerClass> extends HashSet implements Serializable {\n"); code.append(" T exposed;\n"); code.append("}\n"); resources.add(new StaticJavaResource("NameSet", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class SerClass implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("SerClass", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class SerClassSub extends SerClass {\n"); code.append("}\n"); resources.add(new StaticJavaResource("SerClassSub", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class UnrelatedClass implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("UnrelatedClass", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JClassType string = to.getType(String.class.getCanonicalName()); JGenericType hashSet = to.getType("HashSet").isGenericType(); JGenericType nameSet = to.getType("NameSet").isGenericType(); JClassType unrelatedClass = to.getType("UnrelatedClass"); JClassType serClass = to.getType("SerClass"); JClassType serClassSub = to.getType("SerClassSub"); JParameterizedType hashSetOfString = to.getParameterizedType(hashSet, makeArray(string)); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, hashSetOfString); SerializableTypeOracle so = sob.build(logger); JArrayType arrayOfString = to.getArrayType(string); assertSerializableTypes(so, hashSet.getRawType(), nameSet.getRawType(), string, arrayOfString, serClass, serClassSub); assertInstantiable(so, hashSet.getRawType()); assertInstantiable(so, nameSet.getRawType()); assertInstantiable(so, string); assertInstantiable(so, serClass); assertInstantiable(so, serClassSub); assertNotInstantiable(so, unrelatedClass); } /** * Expansion via parameterized types, where the type is exposed. */ public void testInfiniteParameterizedTypeExpansionCase1() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class A<T> implements Serializable {\n"); code.append(" B<T> b;\n"); code.append(" T x;\n"); code.append("}\n"); resources.add(new StaticJavaResource("A", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class B<T> extends A<T> implements Serializable {\n"); code.append(" A<B<T>> ab;\n"); code.append("}\n"); resources.add(new StaticJavaResource("B", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class SerializableArgument implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("SerializableArgument", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType a = to.getType("A").isGenericType(); JGenericType b = to.getType("B").isGenericType(); JClassType serializableArgument = to.getType("SerializableArgument"); JParameterizedType aOfString = to.getParameterizedType(a, makeArray(serializableArgument)); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, aOfString); SerializableTypeOracle so = sob.build(logger); assertInstantiable(so, a.getRawType()); assertInstantiable(so, b.getRawType()); assertInstantiable(so, serializableArgument); assertSerializableTypes(so, a.getRawType(), b.getRawType(), serializableArgument); } /** * Expansion via parameterized types, where the type is not actually exposed. */ public void testInfiniteParameterizedTypeExpansionCase2() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class A<T> implements Serializable {\n"); code.append(" B<T> b;\n"); code.append("}\n"); resources.add(new StaticJavaResource("A", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class B<T> extends A<T> implements Serializable {\n"); code.append(" A<B<T>> ab;\n"); code.append("}\n"); resources.add(new StaticJavaResource("B", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class UnusedSerializableArgument implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("UnusedSerializableArgument", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType a = to.getType("A").isGenericType(); JGenericType b = to.getType("B").isGenericType(); JClassType unusedSerializableArgument = to.getType("UnusedSerializableArgument"); JParameterizedType aOfString = to.getParameterizedType(a, makeArray(unusedSerializableArgument)); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, aOfString); SerializableTypeOracle so = sob.build(logger); assertInstantiable(so, a.getRawType()); assertInstantiable(so, b.getRawType()); assertNotInstantiableOrFieldSerializable(so, unusedSerializableArgument); assertSerializableTypes(so, a.getRawType(), b.getRawType()); } /* * Case 3: Expansion via array dimensions, but the type arguments are not * exposed so this case will succeed. */ public void testInfiniteParameterizedTypeExpansionCase3() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class A<T> implements Serializable {\n"); code.append(" B<T> b;\n"); code.append("}\n"); resources.add(new StaticJavaResource("A", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class B<T> extends A<T> implements Serializable {\n"); code.append(" A<T[]> ab;\n"); code.append("}\n"); resources.add(new StaticJavaResource("B", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType a = to.getType("A").isGenericType(); JGenericType b = to.getType("B").isGenericType(); JClassType javaLangString = to.getType(String.class.getName()); JParameterizedType aOfString = to.getParameterizedType(a, makeArray(javaLangString)); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, aOfString); SerializableTypeOracle so = sob.build(logger); assertInstantiable(so, a.getRawType()); assertInstantiable(so, b.getRawType()); assertNotInstantiableOrFieldSerializable(so, javaLangString); assertSerializableTypes(so, a.getRawType(), b.getRawType()); } /* * Case 4: Expansion via array dimensions, but the type arguments are exposed * so this case will fail. */ public void testInfiniteParameterizedTypeExpansionCase4() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class A<T> implements Serializable {\n"); code.append(" T t;\n"); code.append("}\n"); resources.add(new StaticJavaResource("A", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class B<T> extends A<T> implements Serializable {\n"); code.append(" A<T[]> ab;\n"); code.append("}\n"); resources.add(new StaticJavaResource("B", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType a = to.getType("A").isGenericType(); JGenericType b = to.getType("B").isGenericType(); JClassType javaLangString = to.getType(String.class.getName()); JParameterizedType aOfString = to.getParameterizedType(a, makeArray(javaLangString)); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); assertEquals(EXPOSURE_DIRECT, sob.getTypeParameterExposure(a, 0)); assertEquals(EXPOSURE_DIRECT, sob.getTypeParameterExposure(b, 0)); sob.addRootType(logger, aOfString); SerializableTypeOracle so = sob.build(logger); assertNotFieldSerializable(so, b.getRawType()); assertNotInstantiable(so, b.getRawType()); assertSerializableTypes(so, a.getRawType(), javaLangString); assertInstantiable(so, a.getRawType()); assertNotInstantiable(so, b.getRawType()); assertInstantiable(so, javaLangString); } /** * Tests that a manually serialized type with a field that is not serializable * does not cause the generator to fail. */ public void testManualSerialization() throws NotFoundException, UnableToCompleteException { TreeLogger logger = createLogger(); TypeOracle typeOracle = getTestTypeOracle(); SerializableTypeOracleBuilder stob = createSerializableTypeOracleBuilder(logger, typeOracle); JClassType a = typeOracle.getType(ManualSerialization.A.class.getCanonicalName()); JClassType b = typeOracle.getType(ManualSerialization.B.class.getCanonicalName()); stob.addRootType(logger, a); SerializableTypeOracle sto = stob.build(logger); assertInstantiable(sto, a); assertNotInstantiableOrFieldSerializable(sto, b); } /** * Tests that a raw List (missing gwt.typeArgs) will not result in a failure. * The set of types is not currently being checked. */ public void testMissingGwtTypeArgs() throws NotFoundException, UnableToCompleteException { TreeLogger logger = createLogger(); TypeOracle typeOracle = getTestTypeOracle(); JClassType rawList = typeOracle.getType(List.class.getName()); SerializableTypeOracleBuilder stob = createSerializableTypeOracleBuilder(logger, typeOracle); stob.addRootType(logger, rawList); SerializableTypeOracle sto = stob.build(logger); // TODO: This test should should be updated to use a controlled type oracle // then we can check the types. assertNotInstantiable(sto, rawList); } /** * Tests that the type constrainer can accurately detect when an interface * matches another type. */ public void testNonOverlappingInterfaces() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public interface Intf1 extends Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Intf1", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public interface Intf2 extends Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Intf2", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public interface Intf3 extends Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Intf3", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class Implements12 implements Intf1, Intf2 {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Implements12", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class ImplementsNeither implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("ImplementsNeither", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public interface List<T> extends Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("List", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class ListOfIntf1 implements List<Intf1> {\n"); code.append("}\n"); resources.add(new StaticJavaResource("ListOfIntf1", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class ListOfIntf2 implements List<Intf2> {\n"); code.append("}\n"); resources.add(new StaticJavaResource("ListOfIntf2", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class ListOfIntf3 implements List<Intf3> {\n"); code.append("}\n"); resources.add(new StaticJavaResource("ListOfIntf3", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class ListOfImplements12 implements List<Implements12> {\n"); code.append("}\n"); resources.add(new StaticJavaResource("ListOfImplements12", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class ListOfImplementsNeither implements List<ImplementsNeither> {\n"); code.append("}\n"); resources.add(new StaticJavaResource("ListOfImplementsNeither", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JClassType intf1 = to.getType("Intf1"); JClassType intf2 = to.getType("Intf2"); JClassType intf3 = to.getType("Intf3"); JClassType implements12 = to.getType("Implements12"); JClassType implementsNeither = to.getType("ImplementsNeither"); JGenericType list = to.getType("List").isGenericType(); JClassType listOfIntf1 = to.getType("ListOfIntf1"); JClassType listOfIntf2 = to.getType("ListOfIntf2"); JClassType listOfIntf3 = to.getType("ListOfIntf3"); JClassType listOfImplements12 = to.getType("ListOfImplements12"); JClassType listOfImplementsNeither = to.getType("ListOfImplementsNeither"); TypeConstrainer typeConstrainer = new TypeConstrainer(to); Map<JTypeParameter, JClassType> emptyConstraints = new HashMap<JTypeParameter, JClassType>(); assertTrue(typeConstrainer.typesMatch(intf1, intf2, emptyConstraints)); assertFalse(typeConstrainer.typesMatch(intf1, intf3, emptyConstraints)); assertTrue(typeConstrainer.typesMatch(implements12, intf1, emptyConstraints)); assertFalse(typeConstrainer.typesMatch(implements12, intf3, emptyConstraints)); assertFalse(typeConstrainer.typesMatch(implementsNeither, intf1, emptyConstraints)); JParameterizedType parameterizedListOfIntf1 = to.getParameterizedType(list, makeArray(intf1)); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, parameterizedListOfIntf1); SerializableTypeOracle so = sob.build(logger); assertSerializableTypes(so, listOfIntf1, listOfIntf2, listOfImplements12); assertNotFieldSerializable(so, listOfIntf3); assertNotFieldSerializable(so, listOfImplementsNeither); } /** * Tests that a method signature which has no serializable types will result * in a failure. */ public void testNoSerializableTypes() throws NotFoundException, UnableToCompleteException { TreeLogger logger = createLogger(); TypeOracle typeOracle = getTestTypeOracle(); JClassType a = typeOracle.getType(NoSerializableTypes.A.class.getCanonicalName()); SerializableTypeOracleBuilder stob = createSerializableTypeOracleBuilder(logger, typeOracle); stob.addRootType(logger, a); try { stob.build(logger); fail("Should have thrown an UnableToCompleteException"); } catch (UnableToCompleteException ex) { // expected to get here } } /** * Tests that a method signature which only has type whose inheritance * hiearchy has a mix of serializable and unserializable types will not cause * the generator fail. It also checks for the set of serializable types. */ public void testNotAllSubtypesAreSerializable() throws UnableToCompleteException, NotFoundException { TreeLogger logger = createLogger(); TypeOracle typeOracle = getTestTypeOracle(); JClassType a = typeOracle.getType(NotAllSubtypesAreSerializable.A.class.getCanonicalName()); SerializableTypeOracleBuilder stob = createSerializableTypeOracleBuilder(logger, typeOracle); stob.addRootType(logger, a); SerializableTypeOracle sto = stob.build(logger); TypeInfo[] expected = new TypeInfo[] { new TypeInfo(makeSourceName(NotAllSubtypesAreSerializable.B.class.getName()), true), new TypeInfo(makeSourceName(NotAllSubtypesAreSerializable.D.class.getName()), true)}; validateSTO(sto, expected); } /** * Tests that Object[] is not instantiable. */ public void testObjectArrayNotInstantiable() throws UnableToCompleteException { TreeLogger logger = createLogger(); TypeOracle typeOracle = getTestTypeOracle(); JArrayType objectArray = typeOracle.getArrayType(typeOracle.getJavaLangObject()); SerializableTypeOracleBuilder stob = createSerializableTypeOracleBuilder(logger, typeOracle); stob.addRootType(logger, objectArray); try { stob.build(logger); fail("Expected UnableToCompleteException"); } catch (UnableToCompleteException e) { // Should get here } } /** * Tests that Object is not considered instantiable. */ public void testObjectNotInstantiable() throws UnableToCompleteException { TreeLogger logger = createLogger(); TypeOracle typeOracle = getTestTypeOracle(); SerializableTypeOracleBuilder stob = createSerializableTypeOracleBuilder(logger, typeOracle); stob.addRootType(logger, typeOracle.getJavaLangObject()); try { stob.build(logger); fail("Expected UnableToCompleteException"); } catch (UnableToCompleteException e) { // Should get here } } /** * Tests that a method signature which only has abstract serializable types * fails. */ public void testOnlyAbstractSerializableTypes() throws UnableToCompleteException, NotFoundException { TreeLogger logger = createLogger(); TypeOracle typeOracle = getTestTypeOracle(); SerializableTypeOracleBuilder stob = createSerializableTypeOracleBuilder(logger, typeOracle); stob.addRootType(logger, typeOracle.getType(AbstractSerializableTypes.IFoo.class .getCanonicalName())); stob.addRootType(logger, typeOracle.getType(AbstractSerializableTypes.AbstractClass.class .getCanonicalName())); try { stob.build(logger); fail("Expected UnableToCompleteException"); } catch (UnableToCompleteException e) { // Should get here } } /** * Tests a hierarchy blending various serializable and non-serializable types. */ public void testProblemReporting() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("public interface TopInterface {\n"); code.append("}\n"); resources.add(new StaticJavaResource("TopInterface", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public abstract class AbstractSerializable implements\n"); code.append(" Serializable, TopInterface {\n"); code.append("}\n"); resources.add(new StaticJavaResource("AbstractSerializable", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public interface PureAbstractSerializable extends \n"); code.append(" Serializable, TopInterface {\n"); code.append("}\n"); resources.add(new StaticJavaResource("PureAbstractSerializable", code)); } { StringBuilder code = new StringBuilder(); code.append("public abstract class PureAbstractClass implements \n"); code.append(" PureAbstractSerializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("PureAbstractClass", code)); } { StringBuilder code = new StringBuilder(); code.append("public abstract class PureAbstractClassTwo extends \n"); code.append(" PureAbstractClass {\n"); code.append("}\n"); resources.add(new StaticJavaResource("PureAbstractClassTwo", code)); } { StringBuilder code = new StringBuilder(); code.append("public class ConcreteNonSerializable implements\n"); code.append(" TopInterface {\n"); code.append("}\n"); resources.add(new StaticJavaResource("ConcreteNonSerializable", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class ConcreteSerializable implements\n"); code.append(" Serializable, TopInterface {\n"); code.append("}\n"); resources.add(new StaticJavaResource("ConcreteSerializable", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class SubSerializable extends\n"); code.append(" ConcreteNonSerializable implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("SubSerializable", code)); } { StringBuilder code = new StringBuilder(); code.append("public class ConcreteBadCtor extends\n"); code.append(" AbstractSerializable {\n"); code.append(" public ConcreteBadCtor(int i) {\n"); code.append(" }\n"); code.append("}\n"); resources.add(new StaticJavaResource("ConcreteBadCtor", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); SerializableTypeOracleBuilder stob = createSerializableTypeOracleBuilder(logger, to); JClassType topInterface = to.getType("TopInterface"); stob.addRootType(logger, topInterface); ProblemReport problems = new ProblemReport(); assertTrue("TopInterface should be (partially) serializable", stob.computeTypeInstantiability( logger, topInterface, null, problems).hasInstantiableSubtypes()); assertTrue("TopInterface should be a serializable type", problems.getProblemsForType( topInterface).isEmpty()); assertTrue("AbstractSerializable should not be reported on", problems.getProblemsForType( to.getType("AbstractSerializable")).isEmpty()); assertTrue("PureAbstractSerializable should not be reported on", problems.getProblemsForType( to.getType("PureAbstractSerializable")).isEmpty()); assertTrue("PureAbstractClass should not be reported on", problems.getProblemsForType( to.getType("PureAbstractClass")).isEmpty()); assertFalse("ConcreteBadCtor should not be a serializable type", problems.getProblemsForType( to.getType("ConcreteBadCtor")).isEmpty()); assertFalse("ConcreteNonSerializable should not be a serializable type", problems .getProblemsForType(to.getType("ConcreteNonSerializable")).isEmpty()); assertTrue("ConcreteSerializable should be a serializable type", problems.getProblemsForType( to.getType("ConcreteSerializable")).isEmpty()); assertTrue("SubSerializable should be a serializable type", problems.getProblemsForType( to.getType("SubSerializable")).isEmpty()); problems = new ProblemReport(); assertFalse("PureAbstractClass should have no possible concrete implementation", stob .computeTypeInstantiability(logger, to.getType("PureAbstractClass"), null, problems) .hasInstantiableSubtypes()); assertTrue("PureAbstractClass should have a problem entry as the tested class", null != problems.getProblemsForType(to.getType("PureAbstractClass"))); problems = new ProblemReport(); assertFalse("PureAbstractSerializable should have no possible concrete implementation", stob .computeTypeInstantiability(logger, to.getType("PureAbstractSerializable"), null, problems) .hasInstantiableSubtypes()); assertFalse("PureAbstractSerializable should have a problem entry", problems .getProblemsForType(to.getType("PureAbstractSerializable")).isEmpty()); assertTrue("PureAbstractClassTwo should not have a problem entry as the middle class", problems .getProblemsForType(to.getType("PureAbstractClassTwo")).isEmpty()); assertTrue("PureAbstractClassTwo should not have an auxiliary entry as the middle class", problems.getAuxiliaryMessagesForType(to.getType("PureAbstractClassTwo")).isEmpty()); assertTrue("PureAbstractClass should not have a problem entry as the child class", problems .getProblemsForType(to.getType("PureAbstractClass")).isEmpty()); assertTrue("PureAbstractClass should not have an auxiliary entry as the child class", problems .getAuxiliaryMessagesForType(to.getType("PureAbstractClass")).isEmpty()); } /** * Tests that adding a raw collection as a root type pulls in the world. * * @throws UnableToCompleteException * @throws NotFoundException */ public void testRawCollection() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("import java.util.Collection;\n"); code.append("public interface List<T> extends Serializable, Collection<T> {\n"); code.append("}\n"); resources.add(new StaticJavaResource("List", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("import java.util.Collection;\n"); code.append("public class LinkedList<T> implements List<T> {\n"); code.append(" T head;"); code.append("}\n"); resources.add(new StaticJavaResource("LinkedList", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("import java.util.Collection;\n"); code.append("public class RandomClass implements Serializable {\n"); // TODO(mmendez): Lex, this should fail, but your fix will allow it if // we get here from a raw collection. // code.append(" Object obj;\n"); code.append("}\n"); resources.add(new StaticJavaResource("RandomClass", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType list = to.getType("List").isGenericType(); JGenericType linkedList = to.getType("LinkedList").isGenericType(); JClassType randomClass = to.getType("RandomClass"); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, list.getRawType()); SerializableTypeOracle so = sob.build(logger); assertInstantiable(so, linkedList.getRawType()); assertInstantiable(so, randomClass); } /** * Tests that raw type with type parameters that are instantiable are * themselves instantiable. * * @throws UnableToCompleteException * @throws NotFoundException */ public void testRawTypes() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class A<T extends SerializableClass> implements Serializable {\n"); code.append(" T x;\n"); code.append("}\n"); resources.add(new StaticJavaResource("A", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class SerializableClass implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("SerializableClass", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType a = to.getType("A").isGenericType(); JRawType rawA = a.getRawType(); JClassType serializableClass = to.getType("SerializableClass"); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, rawA); SerializableTypeOracle so = sob.build(logger); assertInstantiable(so, a.getRawType()); assertSerializableTypes(so, rawA, serializableClass); } /** * Tests that a type paramter that occurs within its bounds will not result in * infinite recursion. * * @throws UnableToCompleteException * @throws NotFoundException */ public void testRootTypeParameterWithSelfInBounds() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class A<Ta extends A<Ta>> implements Serializable {\n"); code.append(" Ta ta;\n"); code.append("}\n"); resources.add(new StaticJavaResource("A", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType a = to.getType("A").isGenericType(); JClassType rawA = a.getRawType(); JTypeParameter ta = a.getTypeParameters()[0]; SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, ta); SerializableTypeOracle so = sob.build(logger); assertSerializableTypes(so, rawA); } /** * Tests that type String[][] also pulls in String[]. */ public void testStringArrayArray() throws NotFoundException, UnableToCompleteException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class Data implements Serializable {\n"); code.append(" String justOneString;"); code.append(" String[][] stringsGalore;\n"); code.append("}\n"); resources.add(new StaticJavaResource("Data", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JClassType data = to.getType("Data"); JClassType string = to.getType(String.class.getCanonicalName()); JArrayType stringArray = to.getArrayType(string); JArrayType stringArrayArray = to.getArrayType(stringArray); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, data); SerializableTypeOracle so = sob.build(logger); assertSerializableTypes(so, data, string, stringArray, stringArrayArray); assertInstantiable(so, data); assertInstantiable(so, stringArrayArray); assertInstantiable(so, stringArray); } /* * Tests the isAssignable test for deciding whether a subclass should be * pulled in. */ public void testSubclassIsAssignable() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class A<T> implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("A", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class B extends A<String> implements Serializable {\n"); code.append(" Object o;\n"); code.append("}\n"); resources.add(new StaticJavaResource("B", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class C extends A<Ser> implements Serializable {\n"); // TODO: rejecting Ser requires a better pruner in STOB // code.append(" Ser ser;\n"); code.append("}\n"); resources.add(new StaticJavaResource("C", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class Ser implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Ser", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType a = to.getType("A").isGenericType(); JClassType b = to.getType("B"); JClassType c = to.getType("C"); JClassType ser = to.getType("Ser"); JClassType javaLangString = to.getType(String.class.getName()); JParameterizedType aOfString = to.getParameterizedType(a, makeArray(javaLangString)); SerializableTypeOracleBuilder stob = createSerializableTypeOracleBuilder(logger, to); stob.addRootType(logger, aOfString); assertEquals(EXPOSURE_NONE, stob.getTypeParameterExposure(a, 0)); SerializableTypeOracle so = stob.build(logger); assertSerializableTypes(so, a.getRawType()); assertInstantiable(so, a.getRawType()); assertNotInstantiableOrFieldSerializable(so, b); assertNotInstantiableOrFieldSerializable(so, c); assertNotInstantiableOrFieldSerializable(so, javaLangString); assertNotInstantiableOrFieldSerializable(so, ser); } /** * Tests subtypes that introduce new instantiable type parameters. * * @throws UnableToCompleteException * @throws NotFoundException */ public void testSubclassWithNewInstantiableTypeParameters() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class A implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("A", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class B<T extends C> extends A {\n"); code.append(" T c;\n"); code.append("}\n"); resources.add(new StaticJavaResource("B", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class C implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("C", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JClassType a = to.getType("A"); JRawType rawB = to.getType("B").isGenericType().getRawType(); JClassType c = to.getType("C"); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, a); SerializableTypeOracle so = sob.build(logger); assertInstantiable(so, a); assertSerializableTypes(so, a, rawB, c); } /** * Tests subtypes that introduce new uninstantiable type parameters as * compared to an implemented interface, where the root type is the interface. * * @throws UnableToCompleteException * @throws NotFoundException */ public void testSubclassWithNewTypeParameterComparedToAnImplementedInterface() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public interface Intf<T> extends Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Intf", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class Bar<T> implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Bar", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class Foo<T extends Ser> extends Bar<T> implements Intf<String> {\n"); code.append(" T x;\n"); code.append("}\n"); resources.add(new StaticJavaResource("Foo", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class Ser implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Ser", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType intf = to.getType("Intf").isGenericType(); JClassType foo = to.getType("Foo"); JClassType bar = to.getType("Bar"); JClassType intfOfString = to.getParameterizedType(intf, new JClassType[] {to.getType(String.class.getName())}); JClassType ser = to.getType("Ser"); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, intfOfString); SerializableTypeOracle so = sob.build(logger); /* * TODO(spoon): should also check that Intf<String> has instantiable * subclasses; currently the APIs for STOB and STO do not make this possible * to test */ assertInstantiable(so, ser); assertSerializableTypes(so, foo.getErasedType(), bar.getErasedType(), ser.getErasedType()); } /** * Tests subtypes that introduce new uninstantiable type parameters. * * @throws UnableToCompleteException * @throws NotFoundException */ public void testSubclassWithNewUninstantiableTypeParameters() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class A implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("A", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class B<T> extends A {\n"); code.append(" T x;\n"); code.append("}\n"); resources.add(new StaticJavaResource("B", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JClassType a = to.getType("A"); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, a); SerializableTypeOracle so = sob.build(logger); assertInstantiable(so, a); assertSerializableTypes(so, a); } /** * Tests that STOB skips transient fields. */ public void testTransient() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); addCustomGwtTransient(resources); { StringBuilder code = new StringBuilder(); code.append("import com.google.gwt.user.client.rpc.GwtTransient;\n"); code.append("import java.io.Serializable;\n"); code.append("public class A implements Serializable {\n"); code.append(" transient ServerOnly1 serverOnly1;\n"); code.append(" @GwtTransient ServerOnly2 serverOnly2;\n"); code.append(" @com.google.gwt.user.rebind.rpc.GwtTransient ServerOnly3 serverOnly3;\n"); code.append("}\n"); resources.add(new StaticJavaResource("A", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("class ServerOnly1 implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("ServerOnly1", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("class ServerOnly2 implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("ServerOnly2", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("class ServerOnly3 implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("ServerOnly3", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JClassType a = to.getType("A"); JClassType serverOnly1 = to.getType("ServerOnly1"); JClassType serverOnly2 = to.getType("ServerOnly2"); JClassType serverOnly3 = to.getType("ServerOnly3"); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, a); SerializableTypeOracle so = sob.build(logger); assertSerializableTypes(so, a); assertInstantiable(so, a); assertNotFieldSerializable(so, serverOnly1); assertNotFieldSerializable(so, serverOnly2); assertNotFieldSerializable(so, serverOnly3); } /** * Miscellaneous direct tests of {@link TypeConstrainer}. * * @throws NotFoundException */ public void testTypeConstrainer() throws NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("public interface Intf1 {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Intf1", code)); } { StringBuilder code = new StringBuilder(); code.append("public interface Intf2 {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Intf2", code)); } { StringBuilder code = new StringBuilder(); code.append("public interface Intf3 {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Intf3", code)); } { StringBuilder code = new StringBuilder(); code.append("public class Implements12 implements Intf1, Intf2 {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Implements12", code)); } { StringBuilder code = new StringBuilder(); code.append("public class ImplementsNeither {\n"); code.append("}\n"); resources.add(new StaticJavaResource("ImplementsNeither", code)); } { StringBuilder code = new StringBuilder(); code.append("public class Sup {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Sup", code)); } { StringBuilder code = new StringBuilder(); code.append("public class Sub extends Sup {\n"); code.append("}\n"); resources.add(new StaticJavaResource("Sub", code)); } { StringBuilder code = new StringBuilder(); code.append("public class Holder<T> {\n"); code.append(" T x;\n"); code.append("}\n"); resources.add(new StaticJavaResource("Holder", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JClassType intf1 = to.getType("Intf1"); JClassType intf2 = to.getType("Intf2"); JClassType intf3 = to.getType("Intf3"); JClassType implements12 = to.getType("Implements12"); JClassType implementsNeither = to.getType("ImplementsNeither"); JClassType string = to.getType(String.class.getCanonicalName()); JClassType sub = to.getType("Sub"); JClassType sup = to.getType("Sup"); JGenericType holder = to.getType("Holder").isGenericType(); JClassType arrayOfInt = to.getArrayType(JPrimitiveType.INT); JClassType arrayOfFloat = to.getArrayType(JPrimitiveType.FLOAT); JClassType arrayOfString = to.getArrayType(string); JClassType arrayOfWildExtString = to.getArrayType(to.getWildcardType(BoundType.EXTENDS, string)); JClassType holderOfString = to.getParameterizedType(holder, makeArray(to.getWildcardType(BoundType.EXTENDS, string))); JClassType holderOfSub = to.getParameterizedType(holder, makeArray(to.getWildcardType(BoundType.EXTENDS, sub))); JClassType holderOfSup = to.getParameterizedType(holder, makeArray(to.getWildcardType(BoundType.EXTENDS, sup))); TypeConstrainer typeConstrainer = new TypeConstrainer(to); Map<JTypeParameter, JClassType> emptyConstraints = new HashMap<JTypeParameter, JClassType>(); assertTrue(typeConstrainer.typesMatch(intf1, intf2, emptyConstraints)); assertFalse(typeConstrainer.typesMatch(intf1, intf3, emptyConstraints)); assertTrue(typeConstrainer.typesMatch(implements12, intf1, emptyConstraints)); assertFalse(typeConstrainer.typesMatch(implements12, intf3, emptyConstraints)); assertFalse(typeConstrainer.typesMatch(implementsNeither, intf1, emptyConstraints)); assertTrue(typeConstrainer.typesMatch(to.getJavaLangObject(), arrayOfString, emptyConstraints)); assertTrue(typeConstrainer.typesMatch(arrayOfString, to.getJavaLangObject(), emptyConstraints)); assertTrue(typeConstrainer.typesMatch(sub, sup, emptyConstraints)); assertTrue(typeConstrainer.typesMatch(sub, sub, emptyConstraints)); assertTrue(typeConstrainer.typesMatch(arrayOfFloat, arrayOfFloat, emptyConstraints)); assertFalse(typeConstrainer.typesMatch(arrayOfFloat, arrayOfInt, emptyConstraints)); assertFalse(typeConstrainer.typesMatch(arrayOfFloat, arrayOfString, emptyConstraints)); assertTrue(typeConstrainer.typesMatch(arrayOfString, arrayOfString, emptyConstraints)); assertTrue(typeConstrainer.typesMatch(arrayOfString, arrayOfWildExtString, emptyConstraints)); assertTrue(typeConstrainer.typesMatch(holderOfSub, holderOfSup, emptyConstraints)); assertFalse(typeConstrainer.typesMatch(holderOfSub, holderOfString, emptyConstraints)); assertTrue(typeConstrainer.typesMatch(holder.getRawType(), holderOfSub, emptyConstraints)); assertTrue(typeConstrainer.typesMatch(holderOfSub, holder.getRawType(), emptyConstraints)); assertFalse(typeConstrainer.typesMatch(holder.getRawType(), intf1, emptyConstraints)); assertTrue(emptyConstraints.isEmpty()); } /** * Tests root types that have type parameters. * * @throws UnableToCompleteException * @throws NotFoundException */ public void testTypeParametersInRootTypes1() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class A<T> implements Serializable {\n"); code.append(" T t;\n"); code.append("}\n"); resources.add(new StaticJavaResource("A", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class B implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("B", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class C<U extends B> {\n"); code.append("}\n"); resources.add(new StaticJavaResource("C", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType a = to.getType("A").isGenericType(); JRawType rawA = a.getRawType(); JClassType b = to.getType("B"); JGenericType c = to.getType("C").isGenericType(); JTypeParameter typeParam = c.getTypeParameters()[0]; JParameterizedType parameterizedType = to.getParameterizedType(a, new JClassType[] {typeParam}); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, parameterizedType); SerializableTypeOracle so = sob.build(logger); assertInstantiable(so, rawA); assertInstantiable(so, b); assertSerializableTypes(so, rawA, b); } /** * Tests root types that <em>are</em> type parameters. * * @throws UnableToCompleteException * @throws NotFoundException */ public void testTypeParametersInRootTypes2() throws UnableToCompleteException, NotFoundException { Set<Resource> resources = new HashSet<Resource>(); addStandardClasses(resources); { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public abstract class A<U extends B> implements Serializable {\n"); code.append(" <V extends C> V getFoo() { return null; }\n"); code.append("}\n"); resources.add(new StaticJavaResource("A", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class B implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("B", code)); } { StringBuilder code = new StringBuilder(); code.append("import java.io.Serializable;\n"); code.append("public class C implements Serializable {\n"); code.append("}\n"); resources.add(new StaticJavaResource("C", code)); } TreeLogger logger = createLogger(); TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, resources); JGenericType a = to.getType("A").isGenericType(); JClassType b = to.getType("B"); JClassType c = to.getType("C"); JTypeParameter u = a.getTypeParameters()[0]; JTypeParameter v = a.getMethod("getFoo", makeArray()).getReturnType().isTypeParameter(); SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(logger, to); sob.addRootType(logger, u); sob.addRootType(logger, v); SerializableTypeOracle so = sob.build(logger); assertSerializableTypes(so, b, c); assertInstantiable(so, b); assertInstantiable(so, c); assertNotInstantiableOrFieldSerializable(so, a.getRawType()); } private JClassType[] makeArray(JClassType... elements) { return elements; } }