| /* |
| * Copyright 2007 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.typeinfo.JClassType; |
| import com.google.gwt.core.ext.typeinfo.JMethod; |
| import com.google.gwt.core.ext.typeinfo.JParameter; |
| import com.google.gwt.core.ext.typeinfo.JPrimitiveType; |
| import com.google.gwt.core.ext.typeinfo.JType; |
| import com.google.gwt.user.client.rpc.SerializationStreamReader; |
| import com.google.gwt.user.client.rpc.SerializationStreamWriter; |
| |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Checks that a custom serializer is valid. |
| */ |
| public class CustomFieldSerializerValidator { |
| private static final String NO_DESERIALIZE_METHOD = |
| "Custom Field Serializer ''{0}'' does not define a deserialize method: ''public static void deserialize({1} reader,{2} instance)''"; |
| private static final String NO_INSTANTIATE_METHOD = |
| "Custom Field Serializer ''{0}'' does not define an instantiate method: ''public static {1} instantiate({2} reader)''; but ''{1}'' is not default instantiable"; |
| private static final String NO_SERIALIZE_METHOD = |
| "Custom Field Serializer ''{0}'' does not define a serialize method: ''public static void serialize({1} writer,{2} instance)''"; |
| private static final String TOO_MANY_METHODS = |
| "Custom Field Serializer ''{0}'' defines too many methods named ''{1}''; please define only one method with that name"; |
| private static final String WRONG_CONCRETE_TYPE_RETURN = |
| "Custom Field Serializer ''{0}'' returns the wrong type from ''concreteType''; return type must be ''java.lang.String''"; |
| |
| public static JMethod getConcreteTypeMethod(JClassType serializer) { |
| return serializer.findMethod("concreteType", new JType[0]); |
| } |
| |
| public static JMethod getDeserializationMethod(JClassType serializer, JClassType serializee) { |
| return getMethod("deserialize", SerializationStreamReader.class.getName(), serializer, |
| serializee); |
| } |
| |
| public static JMethod getInstantiationMethod(JClassType serializer, JClassType serializee) { |
| JMethod[] overloads = serializer.getOverloads("instantiate"); |
| for (JMethod overload : overloads) { |
| JParameter[] parameters = overload.getParameters(); |
| |
| if (parameters.length != 1) { |
| // Different overload |
| continue; |
| } |
| |
| if (!parameters[0].getType().getQualifiedSourceName().equals( |
| SerializationStreamReader.class.getName())) { |
| // First param is not a stream class |
| continue; |
| } |
| |
| if (!isValidCustomFieldSerializerMethod(overload)) { |
| continue; |
| } |
| |
| JType type = overload.getReturnType(); |
| if (type.isPrimitive() != null) { |
| // Primitives are auto serialized so this can't be the right method |
| continue; |
| } |
| |
| // TODO: if isArray answered yes to isClass this cast would not be |
| // necessary |
| JClassType clazz = (JClassType) type; |
| if (clazz.isAssignableFrom(serializee)) { |
| return overload; |
| } |
| } |
| |
| return null; |
| } |
| |
| public static JMethod getSerializationMethod(JClassType serializer, JClassType serializee) { |
| return getMethod("serialize", SerializationStreamWriter.class.getName(), serializer, serializee); |
| } |
| |
| public static boolean hasDeserializationMethod(JClassType serializer, JClassType serializee) { |
| return getDeserializationMethod(serializer, serializee) != null; |
| } |
| |
| public static boolean hasInstantiationMethod(JClassType serializer, JClassType serializee) { |
| return getInstantiationMethod(serializer, serializee) != null; |
| } |
| |
| public static boolean hasSerializationMethod(JClassType serializer, JClassType serializee) { |
| return getSerializationMethod(serializer, serializee) != null; |
| } |
| |
| /** |
| * Returns a list of error messages associated with the custom field |
| * serializer. |
| * |
| * @param serializer the class which performs the serialization |
| * @param serializee the class being serialized |
| * @return list of error messages, if any, associated with the custom field |
| * serializer |
| */ |
| public static List<String> validate(JClassType serializer, JClassType serializee) { |
| List<String> reasons = new ArrayList<String>(); |
| |
| if (serializee.isEnum() != null) { |
| /* |
| * Enumerated types cannot have custom field serializers because it would |
| * introduce shared state between the client and the server via the |
| * enumerated constants. |
| */ |
| reasons.add("Enumerated types cannot have custom field serializers."); |
| return reasons; |
| } |
| |
| if (!hasDeserializationMethod(serializer, serializee)) { |
| // No valid deserialize method was found. |
| reasons.add(MessageFormat.format(NO_DESERIALIZE_METHOD, serializer.getQualifiedSourceName(), |
| SerializationStreamReader.class.getName(), serializee.getQualifiedSourceName())); |
| } else { |
| checkTooMany("deserialize", serializer, reasons); |
| } |
| |
| if (!hasSerializationMethod(serializer, serializee)) { |
| // No valid serialize method was found. |
| reasons.add(MessageFormat.format(NO_SERIALIZE_METHOD, serializer.getQualifiedSourceName(), |
| SerializationStreamWriter.class.getName(), serializee.getQualifiedSourceName())); |
| } else { |
| checkTooMany("serialize", serializer, reasons); |
| } |
| |
| if (!hasInstantiationMethod(serializer, serializee)) { |
| if (!serializee.isDefaultInstantiable() && !serializee.isAbstract()) { |
| // Not default instantiable and no instantiate method was found. |
| reasons.add(MessageFormat.format(NO_INSTANTIATE_METHOD, |
| serializer.getQualifiedSourceName(), serializee.getQualifiedSourceName(), |
| SerializationStreamReader.class.getName())); |
| } |
| } else { |
| checkTooMany("instantiate", serializer, reasons); |
| } |
| |
| JMethod concreteTypeMethod = getConcreteTypeMethod(serializer); |
| if (concreteTypeMethod != null) { |
| if (!"java.lang.String".equals(concreteTypeMethod.getReturnType().getQualifiedSourceName())) { |
| // Wrong return type. |
| reasons.add(MessageFormat.format(WRONG_CONCRETE_TYPE_RETURN, serializer |
| .getQualifiedSourceName())); |
| } else { |
| checkTooMany("concreteType", serializer, reasons); |
| } |
| } |
| |
| return reasons; |
| } |
| |
| private static void checkTooMany(String methodName, JClassType serializer, List<String> reasons) { |
| JMethod[] overloads = serializer.getOverloads(methodName); |
| if (overloads.length > 1) { |
| reasons.add(MessageFormat.format(TOO_MANY_METHODS, serializer.getQualifiedSourceName(), |
| methodName)); |
| } |
| } |
| |
| private static JMethod getMethod(String methodName, String streamClassName, |
| JClassType serializer, JClassType serializee) { |
| JMethod[] overloads = serializer.getOverloads(methodName); |
| for (JMethod overload : overloads) { |
| JParameter[] parameters = overload.getParameters(); |
| |
| if (parameters.length != 2) { |
| // Different overload |
| continue; |
| } |
| |
| if (!parameters[0].getType().getQualifiedSourceName().equals(streamClassName)) { |
| // First param is not a stream class |
| continue; |
| } |
| |
| JParameter serializeeParam = parameters[1]; |
| JType type = serializeeParam.getType(); |
| if (type.isPrimitive() != null) { |
| // Primitives are auto serialized so this can't be the right method |
| continue; |
| } |
| |
| // TODO: if isArray answered yes to isClass this cast would not be |
| // necessary |
| JClassType clazz = (JClassType) type; |
| if (clazz.isAssignableFrom(serializee)) { |
| if (isValidCustomFieldSerializerMethod(overload) |
| && overload.getReturnType() == JPrimitiveType.VOID) { |
| return overload; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| private static boolean isValidCustomFieldSerializerMethod(JMethod method) { |
| if (method == null) { |
| return false; |
| } |
| |
| if (!method.isStatic()) { |
| return false; |
| } |
| |
| if (!method.isPublic()) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| private CustomFieldSerializerValidator() { |
| } |
| } |