blob: 0d6f826d0c262083c45e804123667ebeb02ec617 [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.TreeLogger;
import com.google.gwt.core.ext.typeinfo.JArrayType;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JEnumConstant;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.util.Util;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.zip.CRC32;
/**
* Utilities used for implementing serialization.
*/
public class SerializationUtils {
static final Comparator<JField> FIELD_COMPARATOR = new Comparator<JField>() {
public int compare(JField f1, JField f2) {
return f1.getName().compareTo(f2.getName());
}
};
static final String GENERATED_FIELD_SERIALIZER_SUFFIX = "_FieldSerializer";
static final String TYPE_SERIALIZER_SUFFIX = "_TypeSerializer";
static final Set<String> TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES =
new HashSet<String>();
static {
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Boolean");
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Byte");
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Character");
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Double");
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Exception");
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Float");
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Integer");
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Long");
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Object");
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Short");
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.String");
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.add("java.lang.Throwable");
/*
* Work around for incompatible type hierarchy (and therefore signature)
* between JUnit3 and JUnit4.
*/
TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES
.add("junit.framework.AssertionFailedError");
}
/**
* Returns the binary name of a type. This is the same name that would be
* returned by {@link Class#getName()} for this type.
*
* @param type TypeOracle type to get the name for
* @return binary name for a type
*/
public static String getRpcTypeName(JType type) {
JPrimitiveType primitiveType = type.isPrimitive();
if (primitiveType != null) {
return primitiveType.getJNISignature();
}
JArrayType arrayType = type.isArray();
if (arrayType != null) {
JType component = arrayType.getComponentType();
if (component.isClassOrInterface() != null) {
return "[L" + getRpcTypeName(arrayType.getComponentType()) + ";";
} else {
return "[" + getRpcTypeName(arrayType.getComponentType());
}
}
JParameterizedType parameterizedType = type.isParameterized();
if (parameterizedType != null) {
return getRpcTypeName(parameterizedType.getBaseType());
}
JClassType classType = type.isClassOrInterface();
assert (classType != null);
JClassType enclosingType = classType.getEnclosingType();
if (enclosingType != null) {
return getRpcTypeName(enclosingType) + "$" + classType.getSimpleSourceName();
}
return classType.getQualifiedSourceName();
}
/**
* Returns the set of fields that are serializable for a given class type.
* This method does not consider any superclass fields.
*
* @param typeOracle the type oracle
* @param classType the class for which we want serializable fields
* @return array of fields that meet the serialization criteria
*/
public static JField[] getSerializableFields(TypeOracle typeOracle, JClassType classType) {
assert (classType != null);
List<JField> fields = new ArrayList<JField>();
JField[] declFields = classType.getFields();
assert (declFields != null);
for (JField field : declFields) {
if (SerializableTypeOracleBuilder
.shouldConsiderForSerialization(TreeLogger.NULL, true, field)) {
fields.add(field);
}
}
Collections.sort(fields, FIELD_COMPARATOR);
return fields.toArray(new JField[fields.size()]);
}
/**
* Returns the name of the field serializer for a particular type. This name
* can be either the name of a custom field serializer or that of a generated
* field serializer. If the type is not serializable then it can return null.
*
* @param type the type that is going to be serialized
* @return the fully qualified name of the field serializer for the given type
*/
static String getFieldSerializerName(TypeOracle typeOracle, JType type) {
JClassType customSerializer =
SerializableTypeOracleBuilder.findCustomFieldSerializer(typeOracle, type);
if (customSerializer != null) {
return customSerializer.getQualifiedSourceName();
}
assert (type.isClassOrInterface() != null || type.isArray() != null);
JClassType classType = (JClassType) type;
return getStandardSerializerName(classType);
}
/**
* Returns the serialization signature for a type.
*
* @param instanceType
* @return a string representing the serialization signature of a type
*/
static String getSerializationSignature(TypeOracle typeOracle, JType type)
throws RuntimeException {
CRC32 crc = new CRC32();
try {
generateSerializationSignature(typeOracle, type, crc);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Could not compute the serialization signature", e);
}
return Long.toString(crc.getValue());
}
/**
* Returns the name of the generated field serializer.
*/
static String getStandardSerializerName(JClassType classType) {
String[] name =
Shared.synthesizeTopLevelClassName(classType,
SerializationUtils.GENERATED_FIELD_SERIALIZER_SUFFIX);
if (name[0].length() > 0) {
String serializerName = name[0] + "." + name[1];
if (SerializableTypeOracleBuilder.isInStandardJavaPackage(classType.getQualifiedSourceName())) {
/*
* Don't generate code into java packages. If you do Development Mode
* CompilingClassLoader will fail to resolve references to the generated
* code.
*/
serializerName = "com.google.gwt.user.client.rpc.core." + serializerName;
}
return serializerName;
} else {
return name[1];
}
}
/**
* Returns the qualified name of the type serializer class for the given
* service interface.
*
* @param serviceIntf service interface
* @return name of the type serializer that handles the service interface
*/
static String getTypeSerializerQualifiedName(JClassType serviceIntf)
throws IllegalArgumentException {
if (serviceIntf.isInterface() == null) {
throw new IllegalArgumentException(serviceIntf.getQualifiedSourceName()
+ " is not a service interface");
}
String[] name = Shared.synthesizeTopLevelClassName(serviceIntf, TYPE_SERIALIZER_SUFFIX);
if (name[0].length() > 0) {
return name[0] + "." + name[1];
} else {
return name[1];
}
}
/**
* Returns the simple name of the type serializer class for the given service
* interface.
*
* @param serviceIntf service interface
* @return the simple name of the type serializer class
*/
static String getTypeSerializerSimpleName(JClassType serviceIntf) throws IllegalArgumentException {
if (serviceIntf.isInterface() == null) {
throw new IllegalArgumentException(serviceIntf.getQualifiedSourceName()
+ " is not a service interface");
}
String[] name = Shared.synthesizeTopLevelClassName(serviceIntf, TYPE_SERIALIZER_SUFFIX);
return name[1];
}
private static boolean excludeImplementationFromSerializationSignature(JType instanceType) {
if (TYPES_WHOSE_IMPLEMENTATION_IS_EXCLUDED_FROM_SIGNATURES.contains(instanceType
.getQualifiedSourceName())) {
return true;
}
return false;
}
private static void generateSerializationSignature(TypeOracle typeOracle, JType type, CRC32 crc)
throws UnsupportedEncodingException {
JParameterizedType parameterizedType = type.isParameterized();
if (parameterizedType != null) {
generateSerializationSignature(typeOracle, parameterizedType.getRawType(), crc);
return;
}
String serializedTypeName = getRpcTypeName(type);
crc.update(serializedTypeName.getBytes(Util.DEFAULT_ENCODING));
if (excludeImplementationFromSerializationSignature(type)) {
return;
}
JClassType customSerializer =
SerializableTypeOracleBuilder.findCustomFieldSerializer(typeOracle, type);
if (customSerializer != null) {
generateSerializationSignature(typeOracle, customSerializer, crc);
} else if (type.isArray() != null) {
JArrayType isArray = type.isArray();
generateSerializationSignature(typeOracle, isArray.getComponentType(), crc);
} else if (type.isEnum() != null) {
List<JEnumConstant> constants = Arrays.asList(type.isEnum().getEnumConstants());
// Make sure the list is sorted; the getEnumConstants contract doesn't guarantees it.
Collections.sort(constants, new Comparator<JEnumConstant>() {
@Override
public int compare(JEnumConstant o1, JEnumConstant o2) {
int i1 = o1.getOrdinal();
int i2 = o2.getOrdinal();
if (i1 < i2) {
return -1;
} else if (i1 > i2) {
return 1;
} else {
return 0;
}
}
});
for (JEnumConstant constant : constants) {
crc.update(constant.getName().getBytes(Util.DEFAULT_ENCODING));
}
} else if (type.isClassOrInterface() != null) {
JClassType isClassOrInterface = type.isClassOrInterface();
JField[] fields = getSerializableFields(typeOracle, isClassOrInterface);
for (JField field : fields) {
assert (field != null);
crc.update(field.getName().getBytes(Util.DEFAULT_ENCODING));
crc.update(getRpcTypeName(field.getType()).getBytes(Util.DEFAULT_ENCODING));
}
JClassType superClass = isClassOrInterface.getSuperclass();
if (superClass != null) {
generateSerializationSignature(typeOracle, superClass, crc);
}
}
}
}