| /* |
| * 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); |
| } |
| } |
| } |
| } |