]> source.dussan.org Git - vaadin-framework.git/commitdiff
#8442 Serialize also nested beans, #8441 Allow using static inner
authorArtur Signell <artur@vaadin.com>
Tue, 28 Feb 2012 16:28:59 +0000 (18:28 +0200)
committerArtur Signell <artur@vaadin.com>
Tue, 28 Feb 2012 16:28:59 +0000 (18:28 +0200)
classes for communication

src/com/vaadin/terminal/gwt/server/JsonCodec.java
src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java
src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java

index d8964f85a644f8ced1fb16e55f568760bce675e0..09afea3f741321c69cb5bbe3148d4a8e053e52f2 100644 (file)
@@ -19,7 +19,6 @@ import com.vaadin.external.json.JSONException;
 import com.vaadin.external.json.JSONObject;
 import com.vaadin.terminal.Paintable;
 import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
-import com.vaadin.terminal.gwt.client.communication.SharedState;
 
 /**
  * Decoder for converting RPC parameters and other values from JSON in transfer
@@ -153,9 +152,6 @@ public class JsonCodec implements Serializable {
             // TODO as undefined type?
             return combineTypeAndValue(JsonEncoder.VTYPE_UNDEFINED,
                     JSONObject.NULL);
-        } else if (value instanceof SharedState) {
-            return combineTypeAndValue(value.getClass().getName(),
-                    encodeObject(value, idMapper));
         } else if (value instanceof String[]) {
             String[] array = (String[]) value;
             JSONArray jsonArray = new JSONArray();
@@ -181,9 +177,14 @@ public class JsonCodec implements Serializable {
             Paintable paintable = (Paintable) value;
             return combineTypeAndValue(JsonEncoder.VTYPE_PAINTABLE,
                     idMapper.getPaintableId(paintable));
-        } else {
+        } else if (getTransportType(value) != JsonEncoder.VTYPE_UNDEFINED) {
             return combineTypeAndValue(getTransportType(value),
                     String.valueOf(value));
+        } else {
+            // Any object that we do not know how to encode we encode by looping
+            // through fields
+            return combineTypeAndValue(value.getClass().getCanonicalName(),
+                    encodeObject(value, idMapper));
         }
     }
 
index d7af52b10d671238bdeec689af1565a94fb13499..0beed2abf039ce59e7ab2264f186415f0e88d7bd 100644 (file)
@@ -5,7 +5,9 @@
 package com.vaadin.terminal.gwt.widgetsetutils;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.List;
 
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.ext.Generator;
@@ -24,6 +26,7 @@ import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
 import com.google.gwt.user.rebind.SourceWriter;
 import com.vaadin.terminal.gwt.client.ConnectorMap;
 import com.vaadin.terminal.gwt.client.communication.JsonDecoder;
+import com.vaadin.terminal.gwt.client.communication.SerializerMap;
 import com.vaadin.terminal.gwt.client.communication.VaadinSerializer;
 
 /**
@@ -36,29 +39,27 @@ import com.vaadin.terminal.gwt.client.communication.VaadinSerializer;
  */
 public class SerializerGenerator extends Generator {
 
-    private String packageName;
-    private String beanSerializerClassName;
+    private static final String SUBTYPE_SEPARATOR = "___";
+    private static String beanSerializerPackageName = SerializerMap.class
+            .getPackage().getName();
 
     @Override
     public String generate(TreeLogger logger, GeneratorContext context,
             String beanTypeName) throws UnableToCompleteException {
-        String beanSerializerTypeName = beanTypeName + "_Serializer";
+        JClassType beanType = context.getTypeOracle().findType(beanTypeName);
+        String beanSerializerClassName = getSerializerSimpleClassName(beanType);
         try {
-            TypeOracle typeOracle = context.getTypeOracle();
-
-            // get classType and save instance variables
-            JClassType classType = typeOracle.getType(beanTypeName);
-            packageName = classType.getPackage().getName();
-            beanSerializerClassName = classType.getSimpleSourceName()
-                    + "_Serializer";
             // Generate class source code
-            generateClass(logger, context, beanTypeName, beanSerializerTypeName);
+            generateClass(logger, context, beanType, beanSerializerPackageName,
+                    beanSerializerClassName);
         } catch (Exception e) {
             logger.log(TreeLogger.ERROR, "SerializerGenerator failed for "
-                    + beanTypeName, e);
+                    + beanType.getQualifiedSourceName(), e);
         }
         // return the fully qualifed name of the class generated
-        return packageName + "." + beanSerializerClassName;
+        logger.log(TreeLogger.INFO, "Generated Serializer class "
+                + getFullyQualifiedSerializerClassName(beanType));
+        return getFullyQualifiedSerializerClassName(beanType);
     }
 
     /**
@@ -68,17 +69,19 @@ public class SerializerGenerator extends Generator {
      *            Logger object
      * @param context
      *            Generator context
+     * @param beanType
      * @param beanTypeName
      *            bean type for which the serializer is to be generated
      * @param beanSerializerTypeName
      *            name of the serializer class to generate
      */
     private void generateClass(TreeLogger logger, GeneratorContext context,
-            String beanTypeName, String beanSerializerTypeName) {
+            JClassType beanType, String serializerPackageName,
+            String serializerClassName) {
         // get print writer that receives the source code
         PrintWriter printWriter = null;
-        printWriter = context.tryCreate(logger, packageName,
-                beanSerializerClassName);
+        printWriter = context.tryCreate(logger, serializerPackageName,
+                serializerClassName);
 
         // print writer if null, source code has ALREADY been generated
         if (printWriter == null) {
@@ -86,13 +89,14 @@ public class SerializerGenerator extends Generator {
         }
         Date date = new Date();
         TypeOracle typeOracle = context.getTypeOracle();
-        logger.log(Type.DEBUG, "Processing serializable type " + beanTypeName
-                + "...");
+        String beanQualifiedSourceName = beanType.getQualifiedSourceName();
+        logger.log(Type.DEBUG, "Processing serializable type "
+                + beanQualifiedSourceName + "...");
 
         // init composer, set class properties, create source writer
         ClassSourceFileComposerFactory composer = null;
-        composer = new ClassSourceFileComposerFactory(packageName,
-                beanSerializerClassName);
+        composer = new ClassSourceFileComposerFactory(serializerPackageName,
+                serializerClassName);
         composer.addImport(GWT.class.getName());
         composer.addImport(JSONArray.class.getName());
         // composer.addImport(JSONObject.class.getName());
@@ -106,34 +110,27 @@ public class SerializerGenerator extends Generator {
                 printWriter);
         sourceWriter.indent();
 
-        sourceWriter.println("public " + beanTypeName + " deserialize("
-                + JSONObject.class.getName() + " jsonValue, "
+        sourceWriter.println("public " + beanQualifiedSourceName
+                + " deserialize(" + JSONObject.class.getName() + " jsonValue, "
                 + ConnectorMap.class.getName() + " idMapper) {");
         sourceWriter.indent();
 
         // VButtonState state = GWT.create(VButtonState.class);
-        sourceWriter.println(beanTypeName + " state = GWT.create("
-                + beanTypeName + ".class);");
-        JClassType beanType = typeOracle.findType(beanTypeName);
+        sourceWriter.println(beanQualifiedSourceName + " state = GWT.create("
+                + beanQualifiedSourceName + ".class);");
         JClassType objectType = typeOracle.findType("java.lang.Object");
         while (!objectType.equals(beanType)) {
-            for (JMethod method : beanType.getMethods()) {
-                // Process all setters that have corresponding fields
-                if (!method.isPublic() || method.isStatic()
-                        || !method.getName().startsWith("set")
-                        || method.getParameterTypes().length != 1) {
-                    // Not setter, skip to next method
-                    continue;
-
-                }
+            for (JMethod method : getSetters(beanType)) {
                 String setterName = method.getName();
                 String capitalizedFieldName = setterName.substring(3);
                 String fieldName = decapitalize(capitalizedFieldName);
                 JType setterParameterType = method.getParameterTypes()[0];
 
-                logger.log(Type.DEBUG, "* Processing field " + fieldName
-                        + " in " + beanTypeName + " (" + beanType.getName()
-                        + ")");
+                logger.log(
+                        Type.INFO,
+                        "* Processing field " + fieldName + " in "
+                                + beanQualifiedSourceName + " ("
+                                + beanType.getName() + ")");
 
                 String jsonFieldName = "json" + capitalizedFieldName;
                 // JSONArray jsonHeight = (JSONArray) jsonValue.get("height");
@@ -178,7 +175,45 @@ public class SerializerGenerator extends Generator {
 
     }
 
+    protected static List<JMethod> getSetters(JClassType beanType) {
+
+        List<JMethod> setterMethods = new ArrayList<JMethod>();
+
+        for (JMethod method : beanType.getMethods()) {
+            // Process all setters that have corresponding fields
+            if (!method.isPublic() || method.isStatic()
+                    || !method.getName().startsWith("set")
+                    || method.getParameterTypes().length != 1) {
+                // Not setter, skip to next method
+                continue;
+            }
+            setterMethods.add(method);
+        }
+
+        return setterMethods;
+    }
+
     private String decapitalize(String name) {
         return name.substring(0, 1).toLowerCase() + name.substring(1);
     }
+
+    private static String getSerializerSimpleClassName(JClassType beanType) {
+        return getSimpleClassName(beanType) + "_Serializer";
+    }
+
+    private static String getSimpleClassName(JClassType type) {
+        if (type.isMemberType()) {
+            // Assumed to be static sub class
+            String baseName = getSimpleClassName(type.getEnclosingType());
+            String name = baseName + SUBTYPE_SEPARATOR
+                    + type.getSimpleSourceName();
+            return name;
+        }
+        return type.getSimpleSourceName();
+    }
+
+    public static String getFullyQualifiedSerializerClassName(JClassType type) {
+        return beanSerializerPackageName + "."
+                + getSerializerSimpleClassName(type);
+    }
 }
index 3536cbd1177a3d5d6186ed9552a865aee3e69bed..2d5e2b704fcb36e6494cfcaa61d4949623323f88 100644 (file)
@@ -6,6 +6,9 @@ package com.vaadin.terminal.gwt.widgetsetutils;
 
 import java.io.PrintWriter;
 import java.util.Date;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 
 import com.google.gwt.core.ext.Generator;
 import com.google.gwt.core.ext.GeneratorContext;
@@ -13,6 +16,8 @@ import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.TreeLogger.Type;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
 import com.google.gwt.user.rebind.SourceWriter;
@@ -38,20 +43,18 @@ public class SerializerMapGenerator extends Generator {
 
         try {
             TypeOracle typeOracle = context.getTypeOracle();
+            Set<JClassType> typesNeedingSerializers = findTypesNeedingSerializers(
+                    typeOracle, logger);
 
             // get classType and save instance variables
             JClassType classType = typeOracle.getType(typeName);
             packageName = classType.getPackage().getName();
             className = classType.getSimpleSourceName() + "Impl";
             // Generate class source code for SerializerMapImpl
-            generateClass(logger, context);
+            generateSerializerMap(typesNeedingSerializers, logger, context);
 
-            // Generate serializer classes for each subclass of SharedState
-            JClassType serializerType = typeOracle.findType(SharedState.class
-                    .getName());
-            JClassType[] serializerSubtypes = serializerType.getSubtypes();
             SerializerGenerator sg = new SerializerGenerator();
-            for (JClassType type : serializerSubtypes) {
+            for (JClassType type : typesNeedingSerializers) {
                 sg.generate(logger, context, type.getQualifiedSourceName());
             }
         } catch (Exception e) {
@@ -65,12 +68,15 @@ public class SerializerMapGenerator extends Generator {
     /**
      * Generate source code for WidgetMapImpl
      * 
+     * @param typesNeedingSerializers
+     * 
      * @param logger
      *            Logger object
      * @param context
      *            Generator context
      */
-    private void generateClass(TreeLogger logger, GeneratorContext context) {
+    private void generateSerializerMap(Set<JClassType> typesNeedingSerializers,
+            TreeLogger logger, GeneratorContext context) {
         // get print writer that receives the source code
         PrintWriter printWriter = null;
         printWriter = context.tryCreate(logger, packageName, className);
@@ -79,12 +85,8 @@ public class SerializerMapGenerator extends Generator {
         if (printWriter == null) {
             return;
         }
-        logger.log(Type.INFO, "Detecting serializable data types...");
         Date date = new Date();
         TypeOracle typeOracle = context.getTypeOracle();
-        JClassType serializerType = typeOracle.findType(SharedState.class
-                .getName());
-        JClassType[] serializerSubtypes = serializerType.getSubtypes();
 
         // init composer, set class properties, create source writer
         ClassSourceFileComposerFactory composer = null;
@@ -100,15 +102,18 @@ public class SerializerMapGenerator extends Generator {
         sourceWriter.indent();
 
         // TODO cache serializer instances in a map
-        for (JClassType type : serializerSubtypes) {
+        for (JClassType type : typesNeedingSerializers) {
             sourceWriter.println("if (type.equals(\""
                     + type.getQualifiedSourceName() + "\")) {");
             sourceWriter.indent();
-            sourceWriter.println("return GWT.create("
-                    + type.getQualifiedSourceName() + "_Serializer.class);");
+            String serializerName = SerializerGenerator
+                    .getFullyQualifiedSerializerClassName(type);
+            sourceWriter.println("return GWT.create(" + serializerName
+                    + ".class);");
             sourceWriter.outdent();
             sourceWriter.println("}");
-            logger.log(Type.INFO, "Configured serializer for " + type.getName());
+            logger.log(Type.INFO, "Configured serializer (" + serializerName
+                    + ") for " + type.getName());
         }
         sourceWriter
                 .println("throw new RuntimeException(\"No serializer found for class \"+type);");
@@ -125,4 +130,84 @@ public class SerializerMapGenerator extends Generator {
                         + "seconds)");
 
     }
+
+    public Set<JClassType> findTypesNeedingSerializers(TypeOracle typeOracle,
+            TreeLogger logger) {
+        logger.log(Type.INFO, "Detecting serializable data types...");
+
+        HashSet<JClassType> types = new HashSet<JClassType>();
+
+        // Generate serializer classes for each subclass of SharedState
+        JClassType serializerType = typeOracle.findType(SharedState.class
+                .getName());
+        JClassType[] serializerSubtypes = serializerType.getSubtypes();
+        for (JClassType type : serializerSubtypes) {
+            types.add(type);
+        }
+
+        // Add all types used from/in the determined types
+        for (Object t : types.toArray()) {
+            findSubTypesNeedingSerializers((JClassType) t, types);
+        }
+        logger.log(Type.INFO, "Serializable data types: " + types.toString());
+
+        return types;
+    }
+
+    public void findSubTypesNeedingSerializers(JClassType type,
+            Set<JClassType> serializableTypes) {
+        // Find all setters and look at their parameter type to determine if a
+        // new serializer is needed
+        for (JMethod setterMethod : SerializerGenerator.getSetters(type)) {
+            // The one and only parameter for the setter
+            JType setterType = setterMethod.getParameterTypes()[0];
+
+            if (serializableTypes.contains(setterType)) {
+                continue;
+            }
+            if (serializationHandledByFramework(setterType)) {
+                continue;
+            }
+
+            serializableTypes.add(setterType.isClass());
+        }
+    }
+
+    Set<Class<?>> frameworkHandledTypes = new HashSet<Class<?>>();
+    {
+        frameworkHandledTypes.add(String.class);
+        frameworkHandledTypes.add(Boolean.class);
+        frameworkHandledTypes.add(Integer.class);
+        frameworkHandledTypes.add(Float.class);
+        frameworkHandledTypes.add(Double.class);
+        frameworkHandledTypes.add(Long.class);
+        frameworkHandledTypes.add(Enum.class);
+        frameworkHandledTypes.add(String[].class);
+        frameworkHandledTypes.add(Object[].class);
+        frameworkHandledTypes.add(Map.class);
+
+    }
+
+    private boolean serializationHandledByFramework(JType setterType) {
+        // Some types are handled by the framework at the moment. See #8449
+        // This method should be removed at some point.
+        if (setterType.isArray() != null) {
+            return true;
+        }
+        if (setterType.isEnum() != null) {
+            return true;
+        }
+        if (setterType.isPrimitive() != null) {
+            return true;
+        }
+
+        String qualifiedName = setterType.getQualifiedSourceName();
+        for (Class<?> cls : frameworkHandledTypes) {
+            if (qualifiedName.equals(cls.getName())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 }