blob: f4c81dfa1016826a6f0f215539f8739523505244 [file] [log] [blame]
/*
* 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.Class''";
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.Class".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() {
}
}