blob: d5f01a0d18195eb661a35b3d14053acb7d3e78ac [file] [log] [blame]
/*
* 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;
}
}