]> source.dussan.org Git - vaadin-framework.git/commitdiff
Use ConnectorBundle for JSON encoding and decoding (#9371)
authorLeif Åstrand <leif@vaadin.com>
Wed, 22 Aug 2012 14:07:22 +0000 (17:07 +0300)
committerLeif Åstrand <leif@vaadin.com>
Wed, 22 Aug 2012 16:25:32 +0000 (19:25 +0300)
24 files changed:
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorBundleLoaderFactory.java
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java [deleted file]
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java [deleted file]
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ArraySerializer.java [new file with mode: 0644]
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ClientRpcVisitor.java
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorBundle.java
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/CustomSerializer.java [new file with mode: 0644]
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/EnumSerializer.java [new file with mode: 0644]
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/GeneratedSerializer.java [new file with mode: 0644]
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/JsonSerializer.java [new file with mode: 0644]
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/MethodProperty.java [new file with mode: 0644]
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/Property.java [new file with mode: 0644]
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ServerRpcVisitor.java
client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/StateInitVisitor.java
client/src/com/vaadin/Vaadin.gwt.xml
client/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
client/src/com/vaadin/terminal/gwt/client/communication/JsonDecoder.java
client/src/com/vaadin/terminal/gwt/client/communication/JsonEncoder.java
client/src/com/vaadin/terminal/gwt/client/communication/SerializerMap.java [deleted file]
client/src/com/vaadin/terminal/gwt/client/communication/Type.java [deleted file]
client/src/com/vaadin/terminal/gwt/client/metadata/Invoker.java
client/src/com/vaadin/terminal/gwt/client/metadata/Property.java
client/src/com/vaadin/terminal/gwt/client/metadata/Type.java
client/src/com/vaadin/terminal/gwt/client/metadata/TypeDataStore.java

index 9f830d4a8df4ccd888487ec6748b045e3a2d63eb..9a5b83f46087c2d58c776a78eaea11dece708125 100644 (file)
@@ -9,6 +9,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -43,6 +44,8 @@ import com.vaadin.terminal.gwt.client.metadata.TypeDataStore;
 import com.vaadin.terminal.gwt.widgetsetutils.metadata.ClientRpcVisitor;
 import com.vaadin.terminal.gwt.widgetsetutils.metadata.ConnectorBundle;
 import com.vaadin.terminal.gwt.widgetsetutils.metadata.ConnectorInitVisitor;
+import com.vaadin.terminal.gwt.widgetsetutils.metadata.GeneratedSerializer;
+import com.vaadin.terminal.gwt.widgetsetutils.metadata.Property;
 import com.vaadin.terminal.gwt.widgetsetutils.metadata.ServerRpcVisitor;
 import com.vaadin.terminal.gwt.widgetsetutils.metadata.StateInitVisitor;
 import com.vaadin.terminal.gwt.widgetsetutils.metadata.TypeVisitor;
@@ -134,7 +137,7 @@ public class ConnectorBundleLoaderFactory extends Generator {
             w.println("public void load() {");
             w.indent();
 
-            printBundleData(w, bundle);
+            printBundleData(logger, w, bundle);
 
             // Close load method
             w.outdent();
@@ -165,7 +168,8 @@ public class ConnectorBundleLoaderFactory extends Generator {
         w.commit(logger);
     }
 
-    private void printBundleData(SourceWriter w, ConnectorBundle bundle) {
+    private void printBundleData(TreeLogger logger, SourceWriter w,
+            ConnectorBundle bundle) throws UnableToCompleteException {
         writeIdentifiers(w, bundle);
         writeGwtConstructors(w, bundle);
         writeReturnTypes(w, bundle);
@@ -173,6 +177,133 @@ public class ConnectorBundleLoaderFactory extends Generator {
         writeParamTypes(w, bundle);
         writeProxys(w, bundle);
         wirteDelayedInfo(w, bundle);
+        writeProperites(logger, w, bundle);
+        writePropertyTypes(w, bundle);
+        writeSetters(logger, w, bundle);
+        writeGetters(logger, w, bundle);
+        writeSerializers(logger, w, bundle);
+    }
+
+    private void writeSerializers(TreeLogger logger, SourceWriter w,
+            ConnectorBundle bundle) throws UnableToCompleteException {
+        Map<JType, GeneratedSerializer> serializers = bundle.getSerializers();
+        for (Entry<JType, GeneratedSerializer> entry : serializers.entrySet()) {
+            JType type = entry.getKey();
+            GeneratedSerializer serializer = entry.getValue();
+
+            w.print("store.setSerializerFactory(");
+            writeClassLiteral(w, type);
+            w.print(", ");
+            w.println("new Invoker() {");
+            w.indent();
+
+            w.println("public Object invoke(Object target, Object[] params) {");
+            w.indent();
+
+            serializer.writeSerializerInstantiator(logger, w);
+
+            w.outdent();
+            w.println("}");
+
+            w.outdent();
+            w.print("}");
+            w.println(");");
+        }
+    }
+
+    private void writeGetters(TreeLogger logger, SourceWriter w,
+            ConnectorBundle bundle) {
+        Set<Property> properties = bundle.getNeedsSetter();
+        for (Property property : properties) {
+            w.print("store.setGetter(");
+            writeClassLiteral(w, property.getBeanType());
+            w.print(", \"");
+            w.print(escape(property.getName()));
+            w.print("\", new Invoker() {");
+            w.indent();
+
+            w.println("public Object invoke(Object bean, Object[] params) {");
+            w.indent();
+
+            property.writeGetterBody(logger, w, "bean");
+            w.println();
+
+            w.outdent();
+            w.println("}");
+
+            w.outdent();
+            w.println("});");
+        }
+    }
+
+    private void writeSetters(TreeLogger logger, SourceWriter w,
+            ConnectorBundle bundle) {
+        Set<Property> properties = bundle.getNeedsSetter();
+        for (Property property : properties) {
+            w.print("store.setSetter(");
+            writeClassLiteral(w, property.getBeanType());
+            w.print(", \"");
+            w.print(escape(property.getName()));
+            w.println("\", new Invoker() {");
+            w.indent();
+
+            w.println("public Object invoke(Object bean, Object[] params) {");
+            w.indent();
+
+            property.writeSetterBody(logger, w, "bean", "params[0]");
+
+            w.println("return null;");
+
+            w.outdent();
+            w.println("}");
+
+            w.outdent();
+            w.println("});");
+        }
+    }
+
+    private void writePropertyTypes(SourceWriter w, ConnectorBundle bundle) {
+        Set<Property> properties = bundle.getNeedsType();
+        for (Property property : properties) {
+            w.print("store.setPropertyType(");
+            writeClassLiteral(w, property.getBeanType());
+            w.print(", \"");
+            w.print(escape(property.getName()));
+            w.print("\", ");
+            writeTypeCreator(w, property.getPropertyType());
+            w.println(");");
+        }
+    }
+
+    private void writeProperites(TreeLogger logger, SourceWriter w,
+            ConnectorBundle bundle) throws UnableToCompleteException {
+        Set<JClassType> needsPropertyListing = bundle.getNeedsPropertyListing();
+        for (JClassType type : needsPropertyListing) {
+            w.print("store.setProperties(");
+            writeClassLiteral(w, type);
+            w.print(", new String[] {");
+
+            Set<String> usedPropertyNames = new HashSet<String>();
+            Collection<Property> properties = bundle.getProperties(type);
+            for (Property property : properties) {
+                String name = property.getName();
+                if (!usedPropertyNames.add(name)) {
+                    logger.log(
+                            Type.ERROR,
+                            type.getQualifiedSourceName()
+                                    + " has multiple properties with the name "
+                                    + name
+                                    + ". This can happen if there are multiple setters with identical names exect casing.");
+                    throw new UnableToCompleteException();
+                }
+
+                w.print("\"");
+                w.print(name);
+                w.print("\", ");
+            }
+
+            w.println("});");
+        }
     }
 
     private void wirteDelayedInfo(SourceWriter w, ConnectorBundle bundle) {
@@ -187,14 +318,14 @@ public class ConnectorBundleLoaderFactory extends Generator {
                 Delayed annotation = method.getAnnotation(Delayed.class);
                 if (annotation != null) {
                     w.print("store.setDelayed(");
-                    printClassLiteral(w, type);
+                    writeClassLiteral(w, type);
                     w.print(", \"");
                     w.print(escape(method.getName()));
                     w.println("\");");
 
                     if (annotation.lastonly()) {
                         w.print("store.setLastonly(");
-                        printClassLiteral(w, type);
+                        writeClassLiteral(w, type);
                         w.print(", \"");
                         w.print(escape(method.getName()));
                         w.println("\");");
@@ -208,7 +339,7 @@ public class ConnectorBundleLoaderFactory extends Generator {
         Set<JClassType> needsProxySupport = bundle.getNeedsProxySupport();
         for (JClassType type : needsProxySupport) {
             w.print("store.setProxyHandler(");
-            printClassLiteral(w, type);
+            writeClassLiteral(w, type);
             w.print(", new ");
             w.print(ProxyHandler.class.getCanonicalName());
             w.println("() {");
@@ -253,7 +384,7 @@ public class ConnectorBundleLoaderFactory extends Generator {
                     w.print("handler.invoke(this, ");
                     w.print(TypeData.class.getCanonicalName());
                     w.print(".getType(");
-                    printClassLiteral(w, type);
+                    writeClassLiteral(w, type);
                     w.print(").getMethod(\"");
                     w.print(escape(method.getName()));
                     w.print("\"), new Object [] {");
@@ -288,7 +419,7 @@ public class ConnectorBundleLoaderFactory extends Generator {
             Set<JMethod> methods = entry.getValue();
             for (JMethod method : methods) {
                 w.print("store.setParamTypes(");
-                printClassLiteral(w, type);
+                writeClassLiteral(w, type);
                 w.print(", \"");
                 w.print(escape(method.getName()));
                 w.print("\", new Type[] {");
@@ -312,7 +443,7 @@ public class ConnectorBundleLoaderFactory extends Generator {
             Set<JMethod> methods = entry.getValue();
             for (JMethod method : methods) {
                 w.print("store.setInvoker(");
-                printClassLiteral(w, type);
+                writeClassLiteral(w, type);
                 w.print(", \"");
                 w.print(escape(method.getName()));
                 w.println("\", new Invoker() {");
@@ -368,7 +499,7 @@ public class ConnectorBundleLoaderFactory extends Generator {
                 // setReturnType(Class<?> type, String methodName, Type
                 // returnType)
                 w.print("store.setReturnType(");
-                printClassLiteral(w, type);
+                writeClassLiteral(w, type);
                 w.print(", \"");
                 w.print(escape(method.getName()));
                 w.print("\", ");
@@ -382,7 +513,7 @@ public class ConnectorBundleLoaderFactory extends Generator {
         Set<JClassType> constructors = bundle.getGwtConstructors();
         for (JClassType type : constructors) {
             w.print("store.setConstructor(");
-            printClassLiteral(w, type);
+            writeClassLiteral(w, type);
             w.println(", new Invoker() {");
             w.indent();
 
@@ -392,7 +523,7 @@ public class ConnectorBundleLoaderFactory extends Generator {
             w.print("return ");
             w.print(GWT.class.getName());
             w.print(".create(");
-            printClassLiteral(w, type);
+            writeClassLiteral(w, type);
             w.println(");");
 
             w.outdent();
@@ -403,7 +534,7 @@ public class ConnectorBundleLoaderFactory extends Generator {
         }
     }
 
-    private void printClassLiteral(SourceWriter w, JClassType type) {
+    public static void writeClassLiteral(SourceWriter w, JType type) {
         w.print(type.getQualifiedSourceName());
         w.print(".class");
     }
@@ -417,7 +548,7 @@ public class ConnectorBundleLoaderFactory extends Generator {
                 w.print("store.setClass(\"");
                 w.print(escape(id));
                 w.print("\", ");
-                printClassLiteral(w, type);
+                writeClassLiteral(w, type);
                 w.println(");");
             }
         }
@@ -450,7 +581,7 @@ public class ConnectorBundleLoaderFactory extends Generator {
         Collection<TypeVisitor> visitors = getVisitors(typeOracle);
 
         ConnectorBundle eagerBundle = new ConnectorBundle(
-                ConnectorBundleLoader.EAGER_BUNDLE_NAME, visitors);
+                ConnectorBundleLoader.EAGER_BUNDLE_NAME, visitors, typeOracle);
         TreeLogger eagerLogger = logger.branch(Type.TRACE,
                 "Populating eager bundle");
 
@@ -515,9 +646,9 @@ public class ConnectorBundleLoaderFactory extends Generator {
 
     public static void writeTypeCreator(SourceWriter sourceWriter, JType type) {
         String typeName = ConnectorBundleLoaderFactory.getBoxedTypeName(type);
-        sourceWriter.print("new Type(\"" + typeName + "\", ");
         JParameterizedType parameterized = type.isParameterized();
         if (parameterized != null) {
+            sourceWriter.print("new Type(\"" + typeName + "\", ");
             sourceWriter.print("new Type[] {");
             JClassType[] typeArgs = parameterized.getTypeArgs();
             for (JClassType jClassType : typeArgs) {
@@ -526,7 +657,7 @@ public class ConnectorBundleLoaderFactory extends Generator {
             }
             sourceWriter.print("}");
         } else {
-            sourceWriter.print("null");
+            sourceWriter.print("new Type(" + typeName + ".class");
         }
         sourceWriter.print(")");
     }
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java
deleted file mode 100644 (file)
index 83e1c17..0000000
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- * Copyright 2011 Vaadin Ltd.
- * 
- * 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.vaadin.terminal.gwt.widgetsetutils;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.ext.Generator;
-import com.google.gwt.core.ext.GeneratorContext;
-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.JArrayType;
-import com.google.gwt.core.ext.typeinfo.JClassType;
-import com.google.gwt.core.ext.typeinfo.JEnumConstant;
-import com.google.gwt.core.ext.typeinfo.JEnumType;
-import com.google.gwt.core.ext.typeinfo.JMethod;
-import com.google.gwt.core.ext.typeinfo.JPackage;
-import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
-import com.google.gwt.core.ext.typeinfo.JType;
-import com.google.gwt.core.ext.typeinfo.TypeOracleException;
-import com.google.gwt.json.client.JSONArray;
-import com.google.gwt.json.client.JSONObject;
-import com.google.gwt.json.client.JSONString;
-import com.google.gwt.json.client.JSONValue;
-import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
-import com.google.gwt.user.rebind.SourceWriter;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.communication.DiffJSONSerializer;
-import com.vaadin.terminal.gwt.client.communication.JSONSerializer;
-import com.vaadin.terminal.gwt.client.communication.JsonDecoder;
-import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
-import com.vaadin.terminal.gwt.client.communication.SerializerMap;
-
-/**
- * GWT generator for creating serializer classes for custom classes sent from
- * server to client.
- * 
- * Only fields with a correspondingly named setter are deserialized.
- * 
- * @since 7.0
- */
-public class SerializerGenerator extends Generator {
-
-    private static final String SUBTYPE_SEPARATOR = "___";
-
-    @Override
-    public String generate(TreeLogger logger, GeneratorContext context,
-            String typeName) throws UnableToCompleteException {
-        JClassType type;
-        try {
-            type = (JClassType) context.getTypeOracle().parse(typeName);
-        } catch (TypeOracleException e1) {
-            logger.log(Type.ERROR, "Could not find type " + typeName, e1);
-            throw new UnableToCompleteException();
-        }
-        String serializerClassName = getSerializerSimpleClassName(type);
-        try {
-            // Generate class source code
-            generateClass(logger, context, type,
-                    getSerializerPackageName(type), serializerClassName);
-        } catch (Exception e) {
-            logger.log(TreeLogger.ERROR, "SerializerGenerator failed for "
-                    + type.getQualifiedSourceName(), e);
-            throw new UnableToCompleteException();
-        }
-
-        // return the fully qualifed name of the class generated
-        return getFullyQualifiedSerializerClassName(type);
-    }
-
-    /**
-     * Generate source code for a VaadinSerializer implementation.
-     * 
-     * @param logger
-     *            Logger object
-     * @param context
-     *            Generator context
-     * @param type
-     * @param beanTypeName
-     *            bean type for which the serializer is to be generated
-     * @param beanSerializerTypeName
-     *            name of the serializer class to generate
-     * @throws UnableToCompleteException
-     */
-    private void generateClass(TreeLogger logger, GeneratorContext context,
-            JClassType type, String serializerPackageName,
-            String serializerClassName) throws UnableToCompleteException {
-        // get print writer that receives the source code
-        PrintWriter printWriter = null;
-        printWriter = context.tryCreate(logger, serializerPackageName,
-                serializerClassName);
-
-        // print writer if null, source code has ALREADY been generated
-        if (printWriter == null) {
-            return;
-        }
-        boolean isEnum = (type.isEnum() != null);
-        boolean isArray = (type.isArray() != null);
-
-        String qualifiedSourceName = type.getQualifiedSourceName();
-        logger.log(Type.DEBUG, "Processing serializable type "
-                + qualifiedSourceName + "...");
-
-        // init composer, set class properties, create source writer
-        ClassSourceFileComposerFactory composer = null;
-        composer = new ClassSourceFileComposerFactory(serializerPackageName,
-                serializerClassName);
-        composer.addImport(GWT.class.getName());
-        composer.addImport(JSONValue.class.getName());
-        composer.addImport(com.vaadin.terminal.gwt.client.metadata.Type.class
-                .getName());
-        // composer.addImport(JSONObject.class.getName());
-        // composer.addImport(VPaintableMap.class.getName());
-        composer.addImport(JsonDecoder.class.getName());
-        // composer.addImport(VaadinSerializer.class.getName());
-
-        if (isEnum || isArray) {
-            composer.addImplementedInterface(JSONSerializer.class.getName()
-                    + "<" + qualifiedSourceName + ">");
-        } else {
-            composer.addImplementedInterface(DiffJSONSerializer.class.getName()
-                    + "<" + qualifiedSourceName + ">");
-        }
-
-        SourceWriter sourceWriter = composer.createSourceWriter(context,
-                printWriter);
-        sourceWriter.indent();
-
-        // Serializer
-
-        // public JSONValue serialize(Object value,
-        // ApplicationConnection connection) {
-        sourceWriter.println("public " + JSONValue.class.getName()
-                + " serialize(" + qualifiedSourceName + " value, "
-                + ApplicationConnection.class.getName() + " connection) {");
-        sourceWriter.indent();
-        // MouseEventDetails castedValue = (MouseEventDetails) value;
-        sourceWriter.println(qualifiedSourceName + " castedValue = ("
-                + qualifiedSourceName + ") value;");
-
-        if (isEnum) {
-            writeEnumSerializer(logger, sourceWriter, type);
-        } else if (isArray) {
-            writeArraySerializer(logger, sourceWriter, type.isArray());
-        } else {
-            writeBeanSerializer(logger, sourceWriter, type);
-        }
-        // }
-        sourceWriter.outdent();
-        sourceWriter.println("}");
-        sourceWriter.println();
-
-        // Updater
-        // public void update(T target, Type type, JSONValue jsonValue,
-        // ApplicationConnection connection);
-        if (!isEnum && !isArray) {
-            sourceWriter.println("public void update(" + qualifiedSourceName
-                    + " target, Type type, " + JSONValue.class.getName()
-                    + " jsonValue, " + ApplicationConnection.class.getName()
-                    + " connection) {");
-            sourceWriter.indent();
-
-            writeBeanDeserializer(logger, sourceWriter, type);
-
-            sourceWriter.outdent();
-            sourceWriter.println("}");
-        }
-
-        // Deserializer
-        // T deserialize(Type type, JSONValue jsonValue, ApplicationConnection
-        // connection);
-        sourceWriter.println("public " + qualifiedSourceName
-                + " deserialize(Type type, " + JSONValue.class.getName()
-                + " jsonValue, " + ApplicationConnection.class.getName()
-                + " connection) {");
-        sourceWriter.indent();
-
-        if (isEnum) {
-            writeEnumDeserializer(logger, sourceWriter, type.isEnum());
-        } else if (isArray) {
-            writeArrayDeserializer(logger, sourceWriter, type.isArray());
-        } else {
-            sourceWriter.println(qualifiedSourceName + " target = GWT.create("
-                    + qualifiedSourceName + ".class);");
-            sourceWriter
-                    .println("update(target, type, jsonValue, connection);");
-            // return target;
-            sourceWriter.println("return target;");
-        }
-        sourceWriter.outdent();
-        sourceWriter.println("}");
-
-        // End of class
-        sourceWriter.outdent();
-        sourceWriter.println("}");
-
-        // commit generated class
-        context.commit(logger, printWriter);
-        logger.log(TreeLogger.INFO, "Generated Serializer class "
-                + getFullyQualifiedSerializerClassName(type));
-    }
-
-    private void writeEnumDeserializer(TreeLogger logger,
-            SourceWriter sourceWriter, JEnumType enumType) {
-        sourceWriter.println("String enumIdentifier = (("
-                + JSONString.class.getName() + ")jsonValue).stringValue();");
-        for (JEnumConstant e : enumType.getEnumConstants()) {
-            sourceWriter.println("if (\"" + e.getName()
-                    + "\".equals(enumIdentifier)) {");
-            sourceWriter.indent();
-            sourceWriter.println("return " + enumType.getQualifiedSourceName()
-                    + "." + e.getName() + ";");
-            sourceWriter.outdent();
-            sourceWriter.println("}");
-        }
-        sourceWriter.println("return null;");
-    }
-
-    private void writeArrayDeserializer(TreeLogger logger,
-            SourceWriter sourceWriter, JArrayType type) {
-        JType leafType = type.getLeafType();
-        int rank = type.getRank();
-
-        sourceWriter.println(JSONArray.class.getName()
-                + " jsonArray = jsonValue.isArray();");
-
-        // Type value = new Type[jsonArray.size()][][];
-        sourceWriter.print(type.getQualifiedSourceName() + " value = new "
-                + leafType.getQualifiedSourceName() + "[jsonArray.size()]");
-        for (int i = 1; i < rank; i++) {
-            sourceWriter.print("[]");
-        }
-        sourceWriter.println(";");
-
-        sourceWriter.println("for(int i = 0 ; i < value.length; i++) {");
-        sourceWriter.indent();
-
-        JType componentType = type.getComponentType();
-
-        sourceWriter.print("value[i] = ("
-                + ConnectorBundleLoaderFactory
-                        .getBoxedTypeName(componentType) + ") "
-                + JsonDecoder.class.getName() + ".decodeValue(");
-        ConnectorBundleLoaderFactory.writeTypeCreator(sourceWriter,
-                componentType);
-        sourceWriter.print(", jsonArray.get(i), null, connection)");
-
-        sourceWriter.println(";");
-
-        sourceWriter.outdent();
-        sourceWriter.println("}");
-
-        sourceWriter.println("return value;");
-    }
-
-    private void writeBeanDeserializer(TreeLogger logger,
-            SourceWriter sourceWriter, JClassType beanType) {
-        String beanQualifiedSourceName = beanType.getQualifiedSourceName();
-
-        // JSONOBject json = (JSONObject)jsonValue;
-        sourceWriter.println(JSONObject.class.getName() + " json = ("
-                + JSONObject.class.getName() + ")jsonValue;");
-
-        for (JMethod method : getSetters(beanType)) {
-            String setterName = method.getName();
-            String baseName = setterName.substring(3);
-            String fieldName = getTransportFieldName(baseName); // setZIndex()
-                                                                // -> zIndex
-            JType setterParameterType = method.getParameterTypes()[0];
-
-            logger.log(Type.DEBUG, "* Processing field " + fieldName + " in "
-                    + beanQualifiedSourceName + " (" + beanType.getName() + ")");
-
-            // if (json.containsKey("height")) {
-            sourceWriter.println("if (json.containsKey(\"" + fieldName
-                    + "\")) {");
-            sourceWriter.indent();
-            String jsonFieldName = "json_" + fieldName;
-            // JSONValue json_Height = json.get("height");
-            sourceWriter.println("JSONValue " + jsonFieldName
-                    + " = json.get(\"" + fieldName + "\");");
-
-            String fieldType;
-            String getterName = "get" + baseName;
-            JPrimitiveType primitiveType = setterParameterType.isPrimitive();
-            if (primitiveType != null) {
-                // This is a primitive type -> must used the boxed type
-                fieldType = primitiveType.getQualifiedBoxedSourceName();
-                if (primitiveType == JPrimitiveType.BOOLEAN) {
-                    getterName = "is" + baseName;
-                }
-            } else {
-                fieldType = setterParameterType.getQualifiedSourceName();
-            }
-
-            // String referenceValue = target.getHeight();
-            sourceWriter.println(fieldType + " referenceValue = target."
-                    + getterName + "();");
-
-            // target.setHeight((String)
-            // JsonDecoder.decodeValue(jsonFieldValue,referenceValue, idMapper,
-            // connection));
-            sourceWriter.print("target." + setterName + "((" + fieldType + ") "
-                    + JsonDecoder.class.getName() + ".decodeValue(");
-            ConnectorBundleLoaderFactory.writeTypeCreator(sourceWriter,
-                    setterParameterType);
-            sourceWriter.println(", " + jsonFieldName
-                    + ", referenceValue, connection));");
-
-            // } ... end of if contains
-            sourceWriter.outdent();
-            sourceWriter.println("}");
-        }
-    }
-
-    private void writeEnumSerializer(TreeLogger logger,
-            SourceWriter sourceWriter, JClassType beanType) {
-        // return new JSONString(castedValue.name());
-        sourceWriter.println("return new " + JSONString.class.getName()
-                + "(castedValue.name());");
-    }
-
-    private void writeArraySerializer(TreeLogger logger,
-            SourceWriter sourceWriter, JArrayType array) {
-        sourceWriter.println(JSONArray.class.getName() + " values = new "
-                + JSONArray.class.getName() + "();");
-        JType componentType = array.getComponentType();
-        // JPrimitiveType primitive = componentType.isPrimitive();
-        sourceWriter.println("for (int i = 0; i < castedValue.length; i++) {");
-        sourceWriter.indent();
-        sourceWriter.print("values.set(i, ");
-        sourceWriter.print(JsonEncoder.class.getName()
-                + ".encode(castedValue[i], false, connection)");
-        sourceWriter.println(");");
-        sourceWriter.outdent();
-        sourceWriter.println("}");
-        sourceWriter.println("return values;");
-    }
-
-    private void writeBeanSerializer(TreeLogger logger,
-            SourceWriter sourceWriter, JClassType beanType)
-            throws UnableToCompleteException {
-
-        // JSONObject json = new JSONObject();
-        sourceWriter.println(JSONObject.class.getName() + " json = new "
-                + JSONObject.class.getName() + "();");
-
-        HashSet<String> usedFieldNames = new HashSet<String>();
-
-        for (JMethod setterMethod : getSetters(beanType)) {
-            String setterName = setterMethod.getName();
-            String fieldName = getTransportFieldName(setterName.substring(3)); // setZIndex()
-            // -> zIndex
-            if (!usedFieldNames.add(fieldName)) {
-                logger.log(
-                        TreeLogger.ERROR,
-                        "Can't encode "
-                                + beanType.getQualifiedSourceName()
-                                + " as it has multiple fields with the name "
-                                + fieldName.toLowerCase()
-                                + ". This can happen if only casing distinguishes one property name from another.");
-                throw new UnableToCompleteException();
-            }
-            String getterName = findGetter(beanType, setterMethod);
-
-            if (getterName == null) {
-                logger.log(TreeLogger.ERROR, "No getter found for " + fieldName
-                        + ". Serialization will likely fail");
-            }
-            // json.put("button",
-            // JsonEncoder.encode(castedValue.getButton(), false, idMapper,
-            // connection));
-            sourceWriter.println("json.put(\"" + fieldName + "\",  "
-                    + JsonEncoder.class.getName() + ".encode(castedValue."
-                    + getterName + "(), false, connection));");
-        }
-        // return json;
-        sourceWriter.println("return json;");
-
-    }
-
-    private static String getTransportFieldName(String baseName) {
-        return Character.toLowerCase(baseName.charAt(0))
-                + baseName.substring(1);
-    }
-
-    private String findGetter(JClassType beanType, JMethod setterMethod) {
-        JType setterParameterType = setterMethod.getParameterTypes()[0];
-        String fieldName = setterMethod.getName().substring(3);
-        if (setterParameterType.getQualifiedSourceName().equals(
-                boolean.class.getName())) {
-            return "is" + fieldName;
-        } else {
-            return "get" + fieldName;
-        }
-    }
-
-    /**
-     * Returns a list of all setters found in the beanType or its parent class
-     * 
-     * @param beanType
-     *            The type to check
-     * @return A list of setter methods from the class and its parents
-     */
-    protected static List<JMethod> getSetters(JClassType beanType) {
-
-        List<JMethod> setterMethods = new ArrayList<JMethod>();
-
-        while (beanType != null
-                && !beanType.getQualifiedSourceName().equals(
-                        Object.class.getName())) {
-            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);
-            }
-            beanType = beanType.getSuperclass();
-        }
-
-        return setterMethods;
-    }
-
-    private static String getSerializerSimpleClassName(JClassType beanType) {
-        return getSimpleClassName(beanType) + "_Serializer";
-    }
-
-    private static String getSimpleClassName(JType type) {
-        JArrayType arrayType = type.isArray();
-        if (arrayType != null) {
-            return "Array" + getSimpleClassName(arrayType.getComponentType());
-        }
-        JClassType classType = type.isClass();
-        if (classType != null && classType.isMemberType()) {
-            // Assumed to be static sub class
-            String baseName = getSimpleClassName(classType.getEnclosingType());
-            String name = baseName + SUBTYPE_SEPARATOR
-                    + type.getSimpleSourceName();
-            return name;
-        }
-        return type.getSimpleSourceName();
-    }
-
-    public static String getFullyQualifiedSerializerClassName(JClassType type) {
-        return getSerializerPackageName(type) + "."
-                + getSerializerSimpleClassName(type);
-    }
-
-    private static String getSerializerPackageName(JClassType type) {
-        JPackage typePackage = type.getPackage();
-        if (typePackage == null) {
-            return SerializerMap.class.getPackage().getName();
-        } else {
-            String packageName = typePackage.getName();
-            // Dev mode classloader gets unhappy for some java packages
-            if (packageName.startsWith("java.")) {
-                packageName = "com.vaadin." + packageName;
-            }
-            return packageName;
-        }
-    }
-}
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java
deleted file mode 100644 (file)
index ac22aa2..0000000
+++ /dev/null
@@ -1,377 +0,0 @@
-/*
- * Copyright 2011 Vaadin Ltd.
- * 
- * 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.vaadin.terminal.gwt.widgetsetutils;
-
-import java.io.PrintWriter;
-import java.io.Serializable;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import com.google.gwt.core.ext.Generator;
-import com.google.gwt.core.ext.GeneratorContext;
-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.JArrayType;
-import com.google.gwt.core.ext.typeinfo.JClassType;
-import com.google.gwt.core.ext.typeinfo.JMethod;
-import com.google.gwt.core.ext.typeinfo.JParameterizedType;
-import com.google.gwt.core.ext.typeinfo.JType;
-import com.google.gwt.core.ext.typeinfo.NotFoundException;
-import com.google.gwt.core.ext.typeinfo.TypeOracle;
-import com.google.gwt.json.client.JSONValue;
-import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
-import com.google.gwt.user.rebind.SourceWriter;
-import com.vaadin.shared.communication.ClientRpc;
-import com.vaadin.shared.communication.ServerRpc;
-import com.vaadin.shared.communication.SharedState;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.communication.JSONSerializer;
-import com.vaadin.terminal.gwt.client.communication.SerializerMap;
-
-/**
- * GWT generator that creates a {@link SerializerMap} implementation (mapper
- * from type string to serializer instance) and serializer classes for all
- * subclasses of {@link SharedState}.
- * 
- * @since 7.0
- */
-public class SerializerMapGenerator extends Generator {
-
-    private static final String FAIL_IF_NOT_SERIALIZABLE = "vFailIfNotSerializable";
-    private String packageName;
-    private String className;
-
-    @Override
-    public String generate(TreeLogger logger, GeneratorContext context,
-            String typeName) throws UnableToCompleteException {
-
-        try {
-            TypeOracle typeOracle = context.getTypeOracle();
-            Set<JClassType> typesNeedingSerializers = findTypesNeedingSerializers(
-                    typeOracle, logger);
-            checkForUnserializableTypes(typesNeedingSerializers, typeOracle,
-                    logger);
-            Set<JClassType> typesWithExistingSerializers = findTypesWithExistingSerializers(
-                    typeOracle, logger);
-            Set<JClassType> serializerMappings = new HashSet<JClassType>();
-            serializerMappings.addAll(typesNeedingSerializers);
-            serializerMappings.addAll(typesWithExistingSerializers);
-            // 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
-            generateSerializerMap(serializerMappings, logger, context);
-
-            SerializerGenerator sg = new SerializerGenerator();
-            for (JClassType type : typesNeedingSerializers) {
-                sg.generate(logger, context, type.getQualifiedSourceName());
-            }
-        } catch (Exception e) {
-            logger.log(TreeLogger.ERROR,
-                    "SerializerMapGenerator creation failed", e);
-            throw new UnableToCompleteException();
-        }
-        // return the fully qualifed name of the class generated
-        return packageName + "." + className;
-    }
-
-    /**
-     * Emits a warning for all classes that are used in communication but do not
-     * implement java.io.Serializable. Implementing java.io.Serializable is not
-     * needed for communication but for the server side Application to be
-     * serializable i.e. work in GAE for instance.
-     * 
-     * @param typesNeedingSerializers
-     * @param typeOracle
-     * @param logger
-     * @throws UnableToCompleteException
-     */
-    private void checkForUnserializableTypes(
-            Set<JClassType> typesNeedingSerializers, TypeOracle typeOracle,
-            TreeLogger logger) throws UnableToCompleteException {
-        JClassType javaSerializable = typeOracle.findType(Serializable.class
-                .getName());
-        for (JClassType type : typesNeedingSerializers) {
-            if (type.isArray() != null) {
-                // Don't check for arrays
-                continue;
-            }
-            boolean serializable = type.isAssignableTo(javaSerializable);
-            if (!serializable) {
-                boolean abortCompile = "true".equals(System
-                        .getProperty(FAIL_IF_NOT_SERIALIZABLE));
-                logger.log(
-                        abortCompile ? Type.ERROR : Type.WARN,
-                        type
-                                + " is used in RPC or shared state but does not implement "
-                                + Serializable.class.getName()
-                                + ". Communication will work but the Application on server side cannot be serialized if it refers to objects of this type. "
-                                + "If the system property "
-                                + FAIL_IF_NOT_SERIALIZABLE
-                                + " is set to \"true\", this causes the compilation to fail instead of just emitting a warning.");
-                if (abortCompile) {
-                    throw new UnableToCompleteException();
-                }
-            }
-        }
-    }
-
-    private Set<JClassType> findTypesWithExistingSerializers(
-            TypeOracle typeOracle, TreeLogger logger)
-            throws UnableToCompleteException {
-        JClassType serializerInterface = typeOracle
-                .findType(JSONSerializer.class.getName());
-        JType[] deserializeParamTypes = new JType[] {
-                typeOracle
-                        .findType(com.vaadin.terminal.gwt.client.metadata.Type.class
-                                .getName()),
-                typeOracle.findType(JSONValue.class.getName()),
-                typeOracle.findType(ApplicationConnection.class.getName()) };
-        String deserializeMethodName = "deserialize";
-        try {
-            serializerInterface.getMethod(deserializeMethodName,
-                    deserializeParamTypes);
-        } catch (NotFoundException e) {
-            logger.log(Type.ERROR, "Could not find " + deserializeMethodName
-                    + " in " + serializerInterface);
-            throw new UnableToCompleteException();
-        }
-
-        Set<JClassType> types = new HashSet<JClassType>();
-        for (JClassType serializer : serializerInterface.getSubtypes()) {
-            JMethod deserializeMethod = serializer.findMethod(
-                    deserializeMethodName, deserializeParamTypes);
-            if (deserializeMethod == null) {
-                logger.log(Type.DEBUG, "Could not find "
-                        + deserializeMethodName + " in " + serializer);
-                continue;
-            }
-            JType returnType = deserializeMethod.getReturnType();
-            logger.log(Type.DEBUG, "Found " + deserializeMethodName
-                    + " with return type " + returnType + " in " + serializer);
-
-            types.add(returnType.isClass());
-        }
-        return types;
-    }
-
-    /**
-     * Generate source code for SerializerMapImpl
-     * 
-     * @param typesNeedingSerializers
-     * 
-     * @param logger
-     *            Logger object
-     * @param context
-     *            Generator 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);
-        // print writer if null, source code has ALREADY been generated
-        if (printWriter == null) {
-            return;
-        }
-        Date date = new Date();
-        TypeOracle typeOracle = context.getTypeOracle();
-
-        // init composer, set class properties, create source writer
-        ClassSourceFileComposerFactory composer = null;
-        composer = new ClassSourceFileComposerFactory(packageName, className);
-        composer.addImport("com.google.gwt.core.client.GWT");
-        composer.addImplementedInterface(SerializerMap.class.getName());
-        SourceWriter sourceWriter = composer.createSourceWriter(context,
-                printWriter);
-        sourceWriter.indent();
-
-        sourceWriter.println("public " + JSONSerializer.class.getName()
-                + " getSerializer(String type) {");
-        sourceWriter.indent();
-
-        // TODO cache serializer instances in a map
-        for (JClassType type : typesNeedingSerializers) {
-            sourceWriter.print("if (type.equals(\""
-                    + type.getQualifiedSourceName() + "\")");
-            if (type instanceof JArrayType) {
-                // Also add binary name to support encoding based on
-                // object.getClass().getName()
-                sourceWriter.print("||type.equals(\"" + type.getJNISignature()
-                        + "\")");
-            }
-            sourceWriter.println(") {");
-            sourceWriter.indent();
-            String serializerName = SerializerGenerator
-                    .getFullyQualifiedSerializerClassName(type);
-            sourceWriter.println("return GWT.create(" + serializerName
-                    + ".class);");
-            sourceWriter.outdent();
-            sourceWriter.println("}");
-            logger.log(Type.INFO, "Configured serializer (" + serializerName
-                    + ") for " + type.getName());
-        }
-        sourceWriter
-                .println("throw new RuntimeException(\"No serializer found for class \"+type);");
-        sourceWriter.outdent();
-        sourceWriter.println("}");
-
-        // close generated class
-        sourceWriter.outdent();
-        sourceWriter.println("}");
-        // commit generated class
-        context.commit(logger, printWriter);
-        logger.log(Type.INFO,
-                "Done. (" + (new Date().getTime() - date.getTime()) / 1000
-                        + "seconds)");
-
-    }
-
-    public Set<JClassType> findTypesNeedingSerializers(TypeOracle typeOracle,
-            TreeLogger logger) {
-        logger.log(Type.DEBUG, "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());
-        types.add(serializerType);
-        JClassType[] serializerSubtypes = serializerType.getSubtypes();
-        for (JClassType type : serializerSubtypes) {
-            types.add(type);
-        }
-
-        // Serializer classes might also be needed for RPC methods
-        for (Class<?> cls : new Class[] { ServerRpc.class, ClientRpc.class }) {
-            JClassType rpcType = typeOracle.findType(cls.getName());
-            JClassType[] serverRpcSubtypes = rpcType.getSubtypes();
-            for (JClassType type : serverRpcSubtypes) {
-                addMethodParameterTypes(type, types, logger);
-            }
-        }
-
-        // Add all types used from/in the types
-        for (Object t : types.toArray()) {
-            findSubTypesNeedingSerializers((JClassType) t, types);
-        }
-        logger.log(Type.DEBUG, "Serializable data types: " + types.toString());
-
-        return types;
-    }
-
-    private void addMethodParameterTypes(JClassType classContainingMethods,
-            Set<JClassType> types, TreeLogger logger) {
-        for (JMethod method : classContainingMethods.getMethods()) {
-            if (method.getName().equals("initRpc")) {
-                continue;
-            }
-            for (JType type : method.getParameterTypes()) {
-                addTypeIfNeeded(types, type);
-            }
-        }
-    }
-
-    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];
-            addTypeIfNeeded(serializableTypes, setterType);
-        }
-    }
-
-    private void addTypeIfNeeded(Set<JClassType> serializableTypes, JType type) {
-        if (serializableTypes.contains(type)) {
-            return;
-        }
-        JParameterizedType parametrized = type.isParameterized();
-        if (parametrized != null) {
-            for (JClassType parameterType : parametrized.getTypeArgs()) {
-                addTypeIfNeeded(serializableTypes, parameterType);
-            }
-        }
-
-        if (serializationHandledByFramework(type)) {
-            return;
-        }
-
-        if (serializableTypes.contains(type)) {
-            return;
-        }
-
-        JClassType typeClass = type.isClass();
-        if (typeClass != null) {
-            // setterTypeClass is null at least for List<String>. It is
-            // possible that we need to handle the cases somehow, for
-            // instance for List<MyObject>.
-            serializableTypes.add(typeClass);
-            findSubTypesNeedingSerializers(typeClass, serializableTypes);
-        }
-
-        // Generate (n-1)-dimensional array serializer for n-dimensional array
-        JArrayType arrayType = type.isArray();
-        if (arrayType != null) {
-            serializableTypes.add(arrayType);
-            addTypeIfNeeded(serializableTypes, arrayType.getComponentType());
-        }
-
-    }
-
-    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);
-        frameworkHandledTypes.add(List.class);
-        frameworkHandledTypes.add(Set.class);
-        frameworkHandledTypes.add(Byte.class);
-        frameworkHandledTypes.add(Character.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.isPrimitive() != null) {
-            return true;
-        }
-
-        String qualifiedName = setterType.getQualifiedSourceName();
-        for (Class<?> cls : frameworkHandledTypes) {
-            if (qualifiedName.equals(cls.getName())) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-}
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ArraySerializer.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ArraySerializer.java
new file mode 100644 (file)
index 0000000..1c729e9
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ * 
+ * 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.vaadin.terminal.gwt.widgetsetutils.metadata;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.typeinfo.JArrayType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.vaadin.terminal.gwt.client.communication.JsonDecoder;
+import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
+import com.vaadin.terminal.gwt.widgetsetutils.ConnectorBundleLoaderFactory;
+
+public class ArraySerializer extends JsonSerializer {
+
+    private final JArrayType arrayType;
+
+    public ArraySerializer(JArrayType arrayType) {
+        super(arrayType);
+        this.arrayType = arrayType;
+    }
+
+    @Override
+    protected void printDeserializerBody(TreeLogger logger, SourceWriter w,
+            String type, String jsonValue, String connection) {
+        JType leafType = arrayType.getLeafType();
+        int rank = arrayType.getRank();
+
+        w.println(JSONArray.class.getName() + " jsonArray = " + jsonValue
+                + ".isArray();");
+
+        // Type value = new Type[jsonArray.size()][][];
+        w.print(arrayType.getQualifiedSourceName() + " value = new "
+                + leafType.getQualifiedSourceName() + "[jsonArray.size()]");
+        for (int i = 1; i < rank; i++) {
+            w.print("[]");
+        }
+        w.println(";");
+
+        w.println("for(int i = 0 ; i < value.length; i++) {");
+        w.indent();
+
+        JType componentType = arrayType.getComponentType();
+
+        w.print("value[i] = ("
+                + ConnectorBundleLoaderFactory.getBoxedTypeName(componentType)
+                + ") " + JsonDecoder.class.getName() + ".decodeValue(");
+        ConnectorBundleLoaderFactory.writeTypeCreator(w, componentType);
+        w.print(", jsonArray.get(i), null, " + connection + ")");
+
+        w.println(";");
+
+        w.outdent();
+        w.println("}");
+
+        w.println("return value;");
+    }
+
+    @Override
+    protected void printSerializerBody(TreeLogger logger, SourceWriter w,
+            String value, String applicationConnection) {
+        w.println(JSONArray.class.getName() + " values = new "
+                + JSONArray.class.getName() + "();");
+        // JPrimitiveType primitive = componentType.isPrimitive();
+        w.println("for (int i = 0; i < " + value + ".length; i++) {");
+        w.indent();
+        w.print("values.set(i, ");
+        w.print(JsonEncoder.class.getName() + ".encode(" + value
+                + "[i], false, " + applicationConnection + ")");
+        w.println(");");
+        w.outdent();
+        w.println("}");
+        w.println("return values;");
+    }
+
+}
index 2f628b76cb6508d0a82c540bbfe99567aeb06b28..d5a78b86b9f901434b2b4325522b35feba83537e 100644 (file)
@@ -21,6 +21,7 @@ import java.util.Set;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JType;
 
 public class ClientRpcVisitor extends TypeVisitor {
     @Override
@@ -33,6 +34,11 @@ public class ClientRpcVisitor extends TypeVisitor {
             for (JMethod method : methods) {
                 bundle.setNeedsInvoker(type, method);
                 bundle.setNeedsParamTypes(type, method);
+
+                JType[] parameterTypes = method.getParameterTypes();
+                for (JType paramType : parameterTypes) {
+                    bundle.setNeedsSerialize(paramType);
+                }
             }
         }
     }
index e93c72aa2f4b2e8d1e10cc83adba1b994d90958e..ad6b1eb102afe45e8543467b138d0b1ad213879e 100644 (file)
 
 package com.vaadin.terminal.gwt.widgetsetutils.metadata;
 
+import java.io.Serializable;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 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.JArrayType;
 import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JEnumType;
 import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JParameterizedType;
+import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.json.client.JSONValue;
 import com.vaadin.shared.communication.ClientRpc;
 import com.vaadin.shared.communication.ServerRpc;
 import com.vaadin.shared.ui.Connect;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
 import com.vaadin.terminal.gwt.client.ComponentConnector;
 import com.vaadin.terminal.gwt.client.ServerConnector;
+import com.vaadin.terminal.gwt.client.communication.JSONSerializer;
 
 public class ConnectorBundle {
+    private static final String FAIL_IF_NOT_SERIALIZABLE = "vFailIfNotSerializable";
+
     private final String name;
     private final ConnectorBundle previousBundle;
+    private final Collection<TypeVisitor> visitors;
+    private final Map<JType, JClassType> customSerializers;
 
+    private final Set<JType> hasSerializeSupport = new HashSet<JType>();
+    private final Set<JType> needsSerializeSupport = new HashSet<JType>();
+    private final Map<JType, GeneratedSerializer> serializers = new HashMap<JType, GeneratedSerializer>();
+
+    private final Set<JClassType> needsPropertyList = new HashSet<JClassType>();
     private final Set<JClassType> needsGwtConstructor = new HashSet<JClassType>();
-    private final Map<JClassType, Set<String>> identifiers = new HashMap<JClassType, Set<String>>();
     private final Set<JClassType> visitedTypes = new HashSet<JClassType>();
-    private final Set<JClassType> visitQueue = new HashSet<JClassType>();
-    private final Map<JClassType, Set<JMethod>> needsReturnType = new HashMap<JClassType, Set<JMethod>>();
-
-    private final Collection<TypeVisitor> visitors;
-
     private final Set<JClassType> needsProxySupport = new HashSet<JClassType>();
+
+    private final Map<JClassType, Set<String>> identifiers = new HashMap<JClassType, Set<String>>();
+    private final Map<JClassType, Set<JMethod>> needsReturnType = new HashMap<JClassType, Set<JMethod>>();
     private final Map<JClassType, Set<JMethod>> needsInvoker = new HashMap<JClassType, Set<JMethod>>();
     private final Map<JClassType, Set<JMethod>> needsParamTypes = new HashMap<JClassType, Set<JMethod>>();
     private final Map<JClassType, Set<JMethod>> needsDelayedInfo = new HashMap<JClassType, Set<JMethod>>();
 
+    private final Set<Property> needsSetter = new HashSet<Property>();
+    private final Set<Property> needsType = new HashSet<Property>();
+    private final Set<Property> needsGetter = new HashSet<Property>();
+
     private ConnectorBundle(String name, ConnectorBundle previousBundle,
-            Collection<TypeVisitor> visitors) {
+            Collection<TypeVisitor> visitors,
+            Map<JType, JClassType> customSerializers) {
         this.name = name;
         this.previousBundle = previousBundle;
         this.visitors = visitors;
+        this.customSerializers = customSerializers;
     }
 
     public ConnectorBundle(String name, ConnectorBundle previousBundle) {
-        this(name, previousBundle, previousBundle.visitors);
-    }
+        this(name, previousBundle, previousBundle.visitors,
+                previousBundle.customSerializers);
+    }
+
+    public ConnectorBundle(String name, Collection<TypeVisitor> visitors,
+            TypeOracle oracle) throws NotFoundException {
+        this(name, null, visitors, findCustomSerializers(oracle));
+    }
+
+    private static Map<JType, JClassType> findCustomSerializers(
+            TypeOracle oracle) throws NotFoundException {
+        Map<JType, JClassType> serializers = new HashMap<JType, JClassType>();
+
+        JClassType serializerInterface = oracle.findType(JSONSerializer.class
+                .getName());
+        JType[] deserializeParamTypes = new JType[] {
+                oracle.findType(com.vaadin.terminal.gwt.client.metadata.Type.class
+                        .getName()),
+                oracle.findType(JSONValue.class.getName()),
+                oracle.findType(ApplicationConnection.class.getName()) };
+        String deserializeMethodName = "deserialize";
+        // Just test that the method exists
+        serializerInterface.getMethod(deserializeMethodName,
+                deserializeParamTypes);
+
+        for (JClassType serializer : serializerInterface.getSubtypes()) {
+            JMethod deserializeMethod = serializer.findMethod(
+                    deserializeMethodName, deserializeParamTypes);
+            if (deserializeMethod == null) {
+                continue;
+            }
+            JType returnType = deserializeMethod.getReturnType();
 
-    public ConnectorBundle(String name, Collection<TypeVisitor> visitors) {
-        this(name, null, visitors);
+            serializers.put(returnType, serializer);
+        }
+        return serializers;
     }
 
     public void setNeedsGwtConstructor(JClassType type) {
         if (!needsGwtConstructor(type)) {
-            ensureVisited(type);
             needsGwtConstructor.add(type);
         }
     }
@@ -75,7 +127,6 @@ public class ConnectorBundle {
 
     public void setIdentifier(JClassType type, String identifier) {
         if (!hasIdentifier(type, identifier)) {
-            ensureVisited(type);
             addMapping(identifiers, type, identifier);
         }
     }
@@ -114,8 +165,13 @@ public class ConnectorBundle {
 
     public void processType(TreeLogger logger, JClassType type)
             throws UnableToCompleteException {
-        ensureVisited(type);
-        purgeQueue(logger);
+        if (!isTypeVisited(type)) {
+            for (TypeVisitor typeVisitor : visitors) {
+                invokeVisitor(logger, type, typeVisitor);
+            }
+            visitedTypes.add(type);
+            purgeSerializeSupportQueue(logger);
+        }
     }
 
     private boolean isTypeVisited(JClassType type) {
@@ -126,31 +182,200 @@ public class ConnectorBundle {
         }
     }
 
-    private void ensureVisited(JClassType type) {
-        if (!isTypeVisited(type)) {
-            visitQueue.add(type);
+    private void purgeSerializeSupportQueue(TreeLogger logger)
+            throws UnableToCompleteException {
+        while (!needsSerializeSupport.isEmpty()) {
+            Iterator<JType> iterator = needsSerializeSupport.iterator();
+            JType type = iterator.next();
+            iterator.remove();
+
+            if (hasSserializeSupport(type)) {
+                continue;
+            }
+
+            addSerializeSupport(logger, type);
         }
     }
 
-    private void purgeQueue(TreeLogger logger) throws UnableToCompleteException {
-        while (!visitQueue.isEmpty()) {
-            Iterator<JClassType> iterator = visitQueue.iterator();
-            JClassType type = iterator.next();
-            iterator.remove();
+    private void addSerializeSupport(TreeLogger logger, JType type)
+            throws UnableToCompleteException {
+        hasSerializeSupport.add(type);
 
-            if (isTypeVisited(type)) {
-                continue;
+        JParameterizedType parametrized = type.isParameterized();
+        if (parametrized != null) {
+            for (JClassType parameterType : parametrized.getTypeArgs()) {
+                setNeedsSerialize(parameterType);
             }
+        }
 
-            // Mark as visited before visiting to avoid adding to queue again
-            visitedTypes.add(type);
+        if (serializationHandledByFramework(type)) {
+            return;
+        }
 
-            for (TypeVisitor typeVisitor : visitors) {
-                invokeVisitor(logger, type, typeVisitor);
+        JClassType customSerializer = customSerializers.get(type);
+        JClassType typeAsClass = type.isClass();
+        JEnumType enumType = type.isEnum();
+        JArrayType arrayType = type.isArray();
+
+        if (customSerializer != null) {
+            logger.log(Type.INFO, "Will serialize " + type + " using "
+                    + customSerializer.getName());
+            setSerializer(type, new CustomSerializer(customSerializer));
+        } else if (arrayType != null) {
+            logger.log(Type.INFO, "Will serialize " + type + " as an array");
+            setSerializer(type, new ArraySerializer(arrayType));
+            setNeedsSerialize(arrayType.getComponentType());
+        } else if (enumType != null) {
+            logger.log(Type.INFO, "Will serialize " + type + " as an enum");
+            setSerializer(type, new EnumSerializer(enumType));
+        } else if (typeAsClass != null) {
+            // Bean
+            checkSerializable(logger, typeAsClass);
+
+            logger.log(Type.INFO, "Will serialize " + type + " as a bean");
+
+            setNeedsPropertyList(typeAsClass);
+
+            for (Property property : getProperties(typeAsClass)) {
+                setNeedsGwtConstructor(property.getBeanType());
+                setNeedsSetter(property);
+
+                // Getters needed for reading previous value that should be
+                // passed to sub encoder
+                setNeedsGetter(property);
+                setNeedsType(property);
+
+                JType propertyType = property.getPropertyType();
+                setNeedsSerialize(propertyType);
             }
         }
     }
 
+    private void checkSerializable(TreeLogger logger, JClassType type)
+            throws UnableToCompleteException {
+        JClassType javaSerializable = type.getOracle().findType(
+                Serializable.class.getName());
+        boolean serializable = type.isAssignableTo(javaSerializable);
+        if (!serializable) {
+            boolean abortCompile = "true".equals(System
+                    .getProperty(FAIL_IF_NOT_SERIALIZABLE));
+            logger.log(
+                    abortCompile ? Type.ERROR : Type.WARN,
+                    type
+                            + " is used in RPC or shared state but does not implement "
+                            + Serializable.class.getName()
+                            + ". Communication will work but the Application on server side cannot be serialized if it refers to objects of this type. "
+                            + "If the system property "
+                            + FAIL_IF_NOT_SERIALIZABLE
+                            + " is set to \"true\", this causes the compilation to fail instead of just emitting a warning.");
+            if (abortCompile) {
+                throw new UnableToCompleteException();
+            }
+        }
+    }
+
+    private void setSerializer(JType type, GeneratedSerializer serializer) {
+        if (!hasSerializer(type)) {
+            serializers.put(type, serializer);
+        }
+    }
+
+    private boolean hasSerializer(JType type) {
+        if (serializers.containsKey(type)) {
+            return true;
+        } else {
+            return previousBundle != null && previousBundle.hasSerializer(type);
+        }
+    }
+
+    public Map<JType, GeneratedSerializer> getSerializers() {
+        return Collections.unmodifiableMap(serializers);
+    }
+
+    private void setNeedsGetter(Property property) {
+        if (!isNeedsGetter(property)) {
+            needsGetter.add(property);
+        }
+    }
+
+    private boolean isNeedsGetter(Property property) {
+        if (needsGetter.contains(property)) {
+            return true;
+        } else {
+            return previousBundle != null
+                    && previousBundle.isNeedsGetter(property);
+        }
+    }
+
+    public Set<Property> getNeedsGetter() {
+        return Collections.unmodifiableSet(needsGetter);
+    }
+
+    private void setNeedsType(Property property) {
+        if (!isNeedsType(property)) {
+            needsType.add(property);
+        }
+    }
+
+    public Set<Property> getNeedsType() {
+        return Collections.unmodifiableSet(needsType);
+    }
+
+    private boolean isNeedsType(Property property) {
+        if (needsType.contains(property)) {
+            return true;
+        } else {
+            return previousBundle != null
+                    && previousBundle.isNeedsType(property);
+        }
+    }
+
+    public void setNeedsSetter(Property property) {
+        if (!isNeedsSetter(property)) {
+            needsSetter.add(property);
+        }
+    }
+
+    private boolean isNeedsSetter(Property property) {
+        if (needsSetter.contains(property)) {
+            return true;
+        } else {
+            return previousBundle != null
+                    && previousBundle.isNeedsSetter(property);
+        }
+    }
+
+    public Set<Property> getNeedsSetter() {
+        return Collections.unmodifiableSet(needsSetter);
+    }
+
+    private void setNeedsPropertyList(JClassType type) {
+        if (!isNeedsPropertyList(type)) {
+            needsPropertyList.add(type);
+        }
+    }
+
+    private boolean isNeedsPropertyList(JClassType type) {
+        if (needsPropertyList.contains(type)) {
+            return true;
+        } else {
+            return previousBundle != null
+                    && previousBundle.isNeedsPropertyList(type);
+        }
+    }
+
+    public Set<JClassType> getNeedsPropertyListing() {
+        return Collections.unmodifiableSet(needsPropertyList);
+    }
+
+    public Collection<Property> getProperties(JClassType type) {
+        HashSet<Property> properties = new HashSet<Property>();
+
+        properties.addAll(MethodProperty.findProperties(type));
+
+        return properties;
+    }
+
     private void invokeVisitor(TreeLogger logger, JClassType type,
             TypeVisitor typeVisitor) throws UnableToCompleteException {
         TreeLogger subLogger = logger.branch(Type.TRACE,
@@ -158,9 +383,11 @@ public class ConnectorBundle {
                         + typeVisitor.getClass().getSimpleName());
         if (isConnectedConnector(type)) {
             typeVisitor.visitConnector(subLogger, type, this);
-        } else if (isClientRpc(type)) {
+        }
+        if (isClientRpc(type)) {
             typeVisitor.visitClientRpc(subLogger, type, this);
-        } else if (isServerRpc(type)) {
+        }
+        if (isServerRpc(type)) {
             typeVisitor.visitServerRpc(subLogger, type, this);
         }
     }
@@ -172,7 +399,6 @@ public class ConnectorBundle {
 
     public void setNeedsReturnType(JClassType type, JMethod method) {
         if (!isNeedsReturnType(type, method)) {
-            ensureVisited(type);
             addMapping(needsReturnType, type, method);
         }
     }
@@ -221,7 +447,6 @@ public class ConnectorBundle {
 
     public void setNeedsInvoker(JClassType type, JMethod method) {
         if (!isNeedsInvoker(type, method)) {
-            ensureVisited(type);
             addMapping(needsInvoker, type, method);
         }
     }
@@ -254,7 +479,6 @@ public class ConnectorBundle {
 
     public void setNeedsParamTypes(JClassType type, JMethod method) {
         if (!isNeedsParamTypes(type, method)) {
-            ensureVisited(type);
             addMapping(needsParamTypes, type, method);
         }
     }
@@ -274,7 +498,6 @@ public class ConnectorBundle {
 
     public void setNeedsProxySupport(JClassType type) {
         if (!isNeedsProxySupport(type)) {
-            ensureVisited(type);
             needsProxySupport.add(type);
         }
     }
@@ -294,7 +517,6 @@ public class ConnectorBundle {
 
     public void setNeedsDelayedInfo(JClassType type, JMethod method) {
         if (!isNeedsDelayedInfo(type, method)) {
-            ensureVisited(type);
             addMapping(needsDelayedInfo, type, method);
         }
     }
@@ -311,4 +533,55 @@ public class ConnectorBundle {
     public Map<JClassType, Set<JMethod>> getNeedsDelayedInfo() {
         return Collections.unmodifiableMap(needsDelayedInfo);
     }
+
+    public void setNeedsSerialize(JType type) {
+        if (!hasSserializeSupport(type)) {
+            needsSerializeSupport.add(type);
+        }
+    }
+
+    private static 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);
+        frameworkHandledTypes.add(List.class);
+        frameworkHandledTypes.add(Set.class);
+        frameworkHandledTypes.add(Byte.class);
+        frameworkHandledTypes.add(Character.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.isPrimitive() != null) {
+            return true;
+        }
+
+        String qualifiedName = setterType.getQualifiedSourceName();
+        for (Class<?> cls : frameworkHandledTypes) {
+            if (qualifiedName.equals(cls.getName())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private boolean hasSserializeSupport(JType type) {
+        if (hasSerializeSupport.contains(type)) {
+            return true;
+        } else {
+            return previousBundle != null
+                    && previousBundle.hasSserializeSupport(type);
+        }
+    }
 }
\ No newline at end of file
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/CustomSerializer.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/CustomSerializer.java
new file mode 100644 (file)
index 0000000..11252c4
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ * 
+ * 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.vaadin.terminal.gwt.widgetsetutils.metadata;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.vaadin.terminal.gwt.widgetsetutils.ConnectorBundleLoaderFactory;
+
+public class CustomSerializer implements GeneratedSerializer {
+
+    private final JClassType serializerType;
+
+    public CustomSerializer(JClassType serializerType) {
+        this.serializerType = serializerType;
+    }
+
+    @Override
+    public void writeSerializerInstantiator(TreeLogger logger, SourceWriter w)
+            throws UnableToCompleteException {
+        w.print("return ");
+        w.print(GWT.class.getCanonicalName());
+        w.print(".create(");
+        ConnectorBundleLoaderFactory.writeClassLiteral(w, serializerType);
+        w.println(");");
+    }
+}
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/EnumSerializer.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/EnumSerializer.java
new file mode 100644 (file)
index 0000000..324b555
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ * 
+ * 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.vaadin.terminal.gwt.widgetsetutils.metadata;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.typeinfo.JEnumConstant;
+import com.google.gwt.core.ext.typeinfo.JEnumType;
+import com.google.gwt.json.client.JSONString;
+import com.google.gwt.user.rebind.SourceWriter;
+
+public class EnumSerializer extends JsonSerializer {
+
+    private final JEnumType enumType;
+
+    public EnumSerializer(JEnumType type) {
+        super(type);
+        enumType = type;
+    }
+
+    @Override
+    protected void printDeserializerBody(TreeLogger logger, SourceWriter w,
+            String type, String jsonValue, String connection) {
+        w.println("String enumIdentifier = ((" + JSONString.class.getName()
+                + ")" + jsonValue + ").stringValue();");
+        for (JEnumConstant e : enumType.getEnumConstants()) {
+            w.println("if (\"" + e.getName() + "\".equals(enumIdentifier)) {");
+            w.indent();
+            w.println("return " + enumType.getQualifiedSourceName() + "."
+                    + e.getName() + ";");
+            w.outdent();
+            w.println("}");
+        }
+        w.println("return null;");
+    }
+
+    @Override
+    protected void printSerializerBody(TreeLogger logger, SourceWriter w,
+            String value, String applicationConnection) {
+        // return new JSONString(castedValue.name());
+        w.println("return new " + JSONString.class.getName() + "(" + value
+                + ".name());");
+    }
+
+}
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/GeneratedSerializer.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/GeneratedSerializer.java
new file mode 100644 (file)
index 0000000..0cfde77
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ * 
+ * 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.vaadin.terminal.gwt.widgetsetutils.metadata;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.user.rebind.SourceWriter;
+
+public interface GeneratedSerializer {
+    public void writeSerializerInstantiator(TreeLogger logger, SourceWriter w)
+            throws UnableToCompleteException;
+}
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/JsonSerializer.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/JsonSerializer.java
new file mode 100644 (file)
index 0000000..accee84
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ * 
+ * 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.vaadin.terminal.gwt.widgetsetutils.metadata;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.json.client.JSONValue;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.communication.JSONSerializer;
+
+public abstract class JsonSerializer implements GeneratedSerializer {
+
+    private final JType type;
+
+    public JsonSerializer(JType type) {
+        this.type = type;
+    }
+
+    @Override
+    public void writeSerializerInstantiator(TreeLogger logger, SourceWriter w)
+            throws UnableToCompleteException {
+
+        w.print("return new ");
+        w.print(JSONSerializer.class.getCanonicalName());
+        w.print("<");
+        w.print(type.getQualifiedSourceName());
+        w.println(">() {");
+        w.indent();
+
+        writeSerializerBody(logger, w);
+
+        w.outdent();
+        w.println("};");
+    }
+
+    protected void writeSerializerBody(TreeLogger logger, SourceWriter w) {
+        String qualifiedSourceName = type.getQualifiedSourceName();
+        w.println("public " + JSONValue.class.getName() + " serialize("
+                + qualifiedSourceName + " value, "
+                + ApplicationConnection.class.getName() + " connection) {");
+        w.indent();
+        // MouseEventDetails castedValue = (MouseEventDetails) value;
+        w.println(qualifiedSourceName + " castedValue = ("
+                + qualifiedSourceName + ") value;");
+
+        printSerializerBody(logger, w, "castedValue", "connection");
+
+        // End of serializer method
+        w.outdent();
+        w.println("}");
+
+        // Deserializer
+        // T deserialize(Type type, JSONValue jsonValue, ApplicationConnection
+        // connection);
+        w.println("public " + qualifiedSourceName + " deserialize(Type type, "
+                + JSONValue.class.getName() + " jsonValue, "
+                + ApplicationConnection.class.getName() + " connection) {");
+        w.indent();
+
+        printDeserializerBody(logger, w, "type", "jsonValue", "connection");
+
+        w.outdent();
+        w.println("}");
+    }
+
+    protected abstract void printDeserializerBody(TreeLogger logger,
+            SourceWriter w, String type, String jsonValue, String connection);
+
+    protected abstract void printSerializerBody(TreeLogger logger,
+            SourceWriter w, String value, String applicationConnection);
+
+}
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/MethodProperty.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/MethodProperty.java
new file mode 100644 (file)
index 0000000..f422205
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ * 
+ * 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.vaadin.terminal.gwt.widgetsetutils.metadata;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import com.google.gwt.core.ext.TreeLogger;
+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.user.rebind.SourceWriter;
+
+public class MethodProperty extends Property {
+
+    private final JMethod setter;
+
+    private MethodProperty(JClassType beanType, JMethod setter) {
+        super(getTransportFieldName(setter), beanType, setter
+                .getParameterTypes()[0]);
+        this.setter = setter;
+    }
+
+    public static Collection<MethodProperty> findProperties(JClassType type) {
+        Collection<MethodProperty> properties = new ArrayList<MethodProperty>();
+
+        List<JMethod> setters = getSetters(type);
+        for (JMethod setter : setters) {
+            properties.add(new MethodProperty(type, setter));
+        }
+
+        return properties;
+    }
+
+    /**
+     * Returns a list of all setters found in the beanType or its parent class
+     * 
+     * @param beanType
+     *            The type to check
+     * @return A list of setter methods from the class and its parents
+     */
+    private static List<JMethod> getSetters(JClassType beanType) {
+        List<JMethod> setterMethods = new ArrayList<JMethod>();
+
+        while (beanType != null
+                && !beanType.getQualifiedSourceName().equals(
+                        Object.class.getName())) {
+            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);
+            }
+            beanType = beanType.getSuperclass();
+        }
+
+        return setterMethods;
+    }
+
+    @Override
+    public void writeSetterBody(TreeLogger logger, SourceWriter w,
+            String beanVariable, String valueVariable) {
+        w.print("((");
+        w.print(getBeanType().getQualifiedSourceName());
+        w.print(") ");
+        w.print(beanVariable);
+        w.print(").");
+        w.print(setter.getName());
+        w.print("((");
+        w.print(getUnboxedPropertyTypeName());
+        w.print(") ");
+        w.print(valueVariable);
+        w.println(");");
+    }
+
+    @Override
+    public void writeGetterBody(TreeLogger logger, SourceWriter w,
+            String beanVariable) {
+        w.print("return ((");
+        w.print(getBeanType().getQualifiedSourceName());
+        w.print(") ");
+        w.print(beanVariable);
+        w.print(").");
+        w.print(findGetter(getBeanType(), setter));
+        w.print("();");
+    }
+
+    private String findGetter(JClassType beanType, JMethod setterMethod) {
+        JType setterParameterType = setterMethod.getParameterTypes()[0];
+        String fieldName = setterMethod.getName().substring(3);
+        if (setterParameterType.getQualifiedSourceName().equals(
+                boolean.class.getName())) {
+            return "is" + fieldName;
+        } else {
+            return "get" + fieldName;
+        }
+    }
+
+    private static String getTransportFieldName(JMethod setter) {
+        String baseName = setter.getName().substring(3);
+        return Character.toLowerCase(baseName.charAt(0))
+                + baseName.substring(1);
+    }
+
+}
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/Property.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/Property.java
new file mode 100644 (file)
index 0000000..1714489
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ * 
+ * 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.vaadin.terminal.gwt.widgetsetutils.metadata;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.user.rebind.SourceWriter;
+
+public abstract class Property {
+    private final String name;
+    private final JClassType beanType;
+    private final JType propertyType;
+
+    protected Property(String name, JClassType beanType, JType propertyType) {
+        this.name = name;
+        this.beanType = beanType;
+        this.propertyType = propertyType;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public JType getPropertyType() {
+        return propertyType;
+    }
+
+    public String getUnboxedPropertyTypeName() {
+        JType propertyType = getPropertyType();
+        JPrimitiveType primitive = propertyType.isPrimitive();
+        if (primitive != null) {
+            return primitive.getQualifiedBoxedSourceName();
+        } else {
+            return propertyType.getQualifiedSourceName();
+        }
+    }
+
+    public JClassType getBeanType() {
+        return beanType;
+    }
+
+    public abstract void writeSetterBody(TreeLogger logger, SourceWriter w,
+            String beanVariable, String valueVariable);
+
+    public abstract void writeGetterBody(TreeLogger logger, SourceWriter w,
+            String beanVariable);
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        } else if (obj instanceof Property) {
+            Property other = (Property) obj;
+            return other.getClass() == getClass()
+                    && other.getBeanType().equals(getBeanType())
+                    && other.getName().equals(getName());
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return getClass().hashCode() * 31 ^ 2 + getBeanType().hashCode() * 31
+                + getName().hashCode();
+    }
+
+}
index 5505c70dc3c2bd50ff899a221e19b846ec0dce02..4c0fc94500abfa874206c3bfa7a5c94ffab157cb 100644 (file)
@@ -21,6 +21,7 @@ import java.util.Set;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JType;
 
 public class ServerRpcVisitor extends TypeVisitor {
     @Override
@@ -35,6 +36,11 @@ public class ServerRpcVisitor extends TypeVisitor {
                 JMethod[] methods = subType.getMethods();
                 for (JMethod method : methods) {
                     bundle.setNeedsDelayedInfo(type, method);
+
+                    JType[] parameterTypes = method.getParameterTypes();
+                    for (JType paramType : parameterTypes) {
+                        bundle.setNeedsSerialize(paramType);
+                    }
                 }
             }
         }
index d22f03635b1736b7c342dfba9b6dee2082ae984d..49cf6874127e12f6590c6fa25d139095b80125a6 100644 (file)
@@ -16,6 +16,8 @@ public class StateInitVisitor extends TypeVisitor {
         JMethod getState = findInheritedMethod(type, "getState");
         bundle.setNeedsReturnType(type, getState);
 
+        bundle.setNeedsSerialize(getState.getReturnType());
+
         JType stateType = getState.getReturnType();
         bundle.setNeedsGwtConstructor(stateType.isClass());
     }
index fe9643232e1c799492cdb377219418c4c03fa5c3..44357b24a05fbdba7eaad6532a8fa9e1b5c253d3 100644 (file)
                <when-type-is class="com.google.gwt.core.client.impl.SchedulerImpl" />
        </replace-with>
 
-       <!-- Generators for serializators for classes used in communication between 
-               server and client -->
-       <generate-with
-               class="com.vaadin.terminal.gwt.widgetsetutils.SerializerMapGenerator">
-               <when-type-is
-                       class="com.vaadin.terminal.gwt.client.communication.SerializerMap" />
-       </generate-with>
-
        <replace-with class="com.vaadin.terminal.gwt.client.VDebugConsole">
                <when-type-is class="com.vaadin.terminal.gwt.client.Console" />
        </replace-with>
index 9b58eb1295dc901ab9eb2137dd7cb409c13b18b4..7e1c505fe9fb5b8803655ba279e0e8ae740299da 100644 (file)
@@ -65,7 +65,6 @@ import com.vaadin.terminal.gwt.client.communication.HasJavaScriptConnectorHelper
 import com.vaadin.terminal.gwt.client.communication.JsonDecoder;
 import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
 import com.vaadin.terminal.gwt.client.communication.RpcManager;
-import com.vaadin.terminal.gwt.client.communication.SerializerMap;
 import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
 import com.vaadin.terminal.gwt.client.extensions.AbstractExtensionConnector;
 import com.vaadin.terminal.gwt.client.metadata.ConnectorBundleLoader;
@@ -106,8 +105,6 @@ public class ApplicationConnection {
 
     public static final char VAR_ESCAPE_CHARACTER = '\u001b';
 
-    private static SerializerMap serializerMap;
-
     /**
      * A string that, if found in a non-JSON response to a UIDL request, will
      * cause the browser to refresh the page. If followed by a colon, optional
@@ -215,7 +212,6 @@ public class ApplicationConnection {
         rpcManager = GWT.create(RpcManager.class);
         layoutManager = GWT.create(LayoutManager.class);
         layoutManager.setConnection(this);
-        serializerMap = GWT.create(SerializerMap.class);
     }
 
     public void init(WidgetSet widgetSet, ApplicationConfiguration cnf) {
@@ -2577,8 +2573,4 @@ public class ApplicationConnection {
     LayoutManager getLayoutManager() {
         return layoutManager;
     }
-
-    public SerializerMap getSerializerMap() {
-        return serializerMap;
-    }
 }
index ef5090ec18289b2d53f46a697f9b7b58b6a9c810..a98d08c36843c5ff2f2061e3d03f7b2ecbe6243d 100644 (file)
@@ -31,6 +31,8 @@ import com.google.gwt.json.client.JSONValue;
 import com.vaadin.shared.Connector;
 import com.vaadin.terminal.gwt.client.ApplicationConnection;
 import com.vaadin.terminal.gwt.client.ConnectorMap;
+import com.vaadin.terminal.gwt.client.metadata.NoDataException;
+import com.vaadin.terminal.gwt.client.metadata.Property;
 import com.vaadin.terminal.gwt.client.metadata.Type;
 
 /**
@@ -106,18 +108,42 @@ public class JsonDecoder {
 
     private static Object decodeObject(Type type, JSONValue jsonValue,
             Object target, ApplicationConnection connection) {
-        JSONSerializer<Object> serializer = connection.getSerializerMap()
-                .getSerializer(type.getBaseTypeName());
-        // TODO handle case with no serializer found
-        // Currently getSerializer throws exception if not found
-
-        if (target != null && serializer instanceof DiffJSONSerializer<?>) {
-            DiffJSONSerializer<Object> diffSerializer = (DiffJSONSerializer<Object>) serializer;
-            diffSerializer.update(target, type, jsonValue, connection);
-            return target;
+        JSONSerializer<Object> serializer = (JSONSerializer<Object>) type
+                .findSerializer();
+        if (serializer != null) {
+            if (target != null && serializer instanceof DiffJSONSerializer<?>) {
+                DiffJSONSerializer<Object> diffSerializer = (DiffJSONSerializer<Object>) serializer;
+                diffSerializer.update(target, type, jsonValue, connection);
+                return target;
+            } else {
+                Object object = serializer.deserialize(type, jsonValue,
+                        connection);
+                return object;
+            }
         } else {
-            Object object = serializer.deserialize(type, jsonValue, connection);
-            return object;
+            try {
+                Collection<Property> properties = type.getProperties();
+                if (target == null) {
+                    target = type.createInstance();
+                }
+                JSONObject jsonObject = jsonValue.isObject();
+
+                for (Property property : properties) {
+                    JSONValue encodedPropertyValue = jsonObject.get(property
+                            .getName());
+                    if (encodedPropertyValue == null) {
+                        continue;
+                    }
+                    Object propertyReference = property.getValue(target);
+                    Object decodedValue = decodeValue(property.getType(),
+                            encodedPropertyValue, propertyReference, connection);
+                    property.setValue(target, decodedValue);
+                }
+                return target;
+            } catch (NoDataException e) {
+                throw new RuntimeException("Can not deserialize "
+                        + type.getSignature(), e);
+            }
         }
     }
 
index 3730cad4c30c70386fbe501f6ceca2413d7d38fb..9b28da8b348cb008556d4c354dfc2733ddf10e36 100644 (file)
@@ -33,6 +33,9 @@ import com.vaadin.shared.Connector;
 import com.vaadin.shared.JsonConstants;
 import com.vaadin.shared.communication.UidlValue;
 import com.vaadin.terminal.gwt.client.ApplicationConnection;
+import com.vaadin.terminal.gwt.client.metadata.NoDataException;
+import com.vaadin.terminal.gwt.client.metadata.Property;
+import com.vaadin.terminal.gwt.client.metadata.Type;
 
 /**
  * Encoder for converting RPC parameters and other values to JSON for transfer
@@ -99,12 +102,33 @@ public class JsonEncoder {
             } else {
                 // Try to find a generated serializer object, class name is the
                 // type
-                transportType = value.getClass().getName();
-                JSONSerializer serializer = connection.getSerializerMap()
-                        .getSerializer(transportType);
+                Type type = new Type(value.getClass());
+
+                JSONSerializer<Object> serializer = (JSONSerializer<Object>) type
+                        .findSerializer();
+                if (serializer != null) {
+                    return serializer.serialize(value, connection);
+                } else {
+                    try {
+                        Collection<Property> properties = type.getProperties();
+
+                        JSONObject jsonObject = new JSONObject();
+                        for (Property property : properties) {
+                            Object propertyValue = property.getValue(value);
+                            JSONValue encodedPropertyValue = encode(
+                                    propertyValue, restrictToInternalTypes,
+                                    connection);
+                            jsonObject.put(property.getName(),
+                                    encodedPropertyValue);
+                        }
+                        return jsonObject;
+
+                    } catch (NoDataException e) {
+                        throw new RuntimeException("Can not encode "
+                                + type.getSignature(), e);
+                    }
+                }
 
-                // TODO handle case with no serializer found
-                return serializer.serialize(value, connection);
             }
         }
     }
diff --git a/client/src/com/vaadin/terminal/gwt/client/communication/SerializerMap.java b/client/src/com/vaadin/terminal/gwt/client/communication/SerializerMap.java
deleted file mode 100644 (file)
index 77df4c7..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2011 Vaadin Ltd.
- * 
- * 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.vaadin.terminal.gwt.client.communication;
-
-/**
- * Provide a mapping from a type (communicated between the server and the
- * client) and a {@link JSONSerializer} instance.
- * 
- * An implementation of this class is created at GWT compilation time by
- * SerializerMapGenerator, so this interface can be instantiated with
- * GWT.create().
- * 
- * @since 7.0
- */
-public interface SerializerMap {
-
-    /**
-     * Returns a serializer instance for a given type.
-     * 
-     * @param type
-     *            type communicated on between the server and the client
-     *            (currently fully qualified class name)
-     * @return serializer instance, not null
-     * @throws RuntimeException
-     *             if no serializer is found
-     */
-    // TODO better error handling in javadoc and in generator
-    public JSONSerializer getSerializer(String type);
-
-}
diff --git a/client/src/com/vaadin/terminal/gwt/client/communication/Type.java b/client/src/com/vaadin/terminal/gwt/client/communication/Type.java
deleted file mode 100644 (file)
index ff93234..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/* 
- * Copyright 2011 Vaadin Ltd.
- * 
- * 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.vaadin.terminal.gwt.client.communication;
-
-public class Type {
-    private final String baseTypeName;
-    private final Type[] parameterTypes;
-
-    public Type(String baseTypeName, Type[] parameterTypes) {
-        this.baseTypeName = baseTypeName;
-        this.parameterTypes = parameterTypes;
-    }
-
-    public String getBaseTypeName() {
-        return baseTypeName;
-    }
-
-    public Type[] getParameterTypes() {
-        return parameterTypes;
-    }
-
-    @Override
-    public String toString() {
-        String string = baseTypeName;
-        if (parameterTypes != null) {
-            string += '<';
-            for (int i = 0; i < parameterTypes.length; i++) {
-                if (i != 0) {
-                    string += ',';
-                }
-                string += parameterTypes[i].toString();
-            }
-            string += '>';
-        }
-
-        return string;
-    }
-
-}
index 5f6a839da65e5fe5b35acf6f8be0fb433cedbc8a..33e8776429108a0b95a8c206fdef0fe88001c244 100644 (file)
@@ -5,5 +5,5 @@
 package com.vaadin.terminal.gwt.client.metadata;
 
 public interface Invoker {
-    public Object invoke(Object target, Object[] params);
+    public Object invoke(Object target, Object... params);
 }
index 30d864a43f8effa28caf6f54aef6d565e6c0f888..082d032e643d1c35ac0e5515e724d00a995c58e5 100644 (file)
@@ -5,16 +5,20 @@
 package com.vaadin.terminal.gwt.client.metadata;
 
 public class Property {
-    private final Type type;
+    private final Type bean;
     private final String name;
 
-    public Property(Type type, String name) {
-        this.type = type;
+    public Property(Type bean, String name) {
+        this.bean = bean;
         this.name = name;
     }
 
     public Object getValue(Object bean) throws NoDataException {
-        return TypeDataStore.getGetter(this).invoke(bean, null);
+        return TypeDataStore.getGetter(this).invoke(bean);
+    }
+
+    public void setValue(Object bean, Object value) throws NoDataException {
+        TypeDataStore.getSetter(this).invoke(bean, value);
     }
 
     public String getDelegateToWidgetMethod() {
@@ -29,8 +33,12 @@ public class Property {
         }
     }
 
+    public Type getType() throws NoDataException {
+        return TypeDataStore.getType(this);
+    }
+
     public String getSignature() {
-        return type.toString() + "." + name;
+        return bean.toString() + "." + name;
     }
 
     @Override
@@ -50,4 +58,13 @@ public class Property {
         return getSignature().hashCode();
     }
 
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        return getSignature();
+    }
+
 }
index 2dc5182845c03d961b28a5d161b39fae5c7bce1a..d869cc259900c6d7f7ba0746d7f989f8c6f734a4 100644 (file)
@@ -3,6 +3,10 @@
  */
 package com.vaadin.terminal.gwt.client.metadata;
 
+import java.util.Collection;
+
+import com.vaadin.terminal.gwt.client.communication.JSONSerializer;
+
 public class Type {
     private final String name;
     private final Type[] parameterTypes;
@@ -27,13 +31,17 @@ public class Type {
 
     public Object createInstance() throws NoDataException {
         Invoker invoker = TypeDataStore.getConstructor(this);
-        return invoker.invoke(null, null);
+        return invoker.invoke(null);
     }
 
     public Method getMethod(String name) {
         return new Method(this, name);
     }
 
+    public Collection<Property> getProperties() throws NoDataException {
+        return TypeDataStore.getProperties(this);
+    }
+
     public Property getProperty(String propertyName) {
         return new Property(this, propertyName);
     }
@@ -82,4 +90,8 @@ public class Type {
                 .createProxy(invokationHandler);
     }
 
+    public JSONSerializer<?> findSerializer() {
+        return TypeDataStore.findSerializer(this);
+    }
+
 }
index f056e46d2d7d76c48298437e9c0fd9a8b74ab8be..55740f69dab4d65c81499c99ea057b312c6326fc 100644 (file)
@@ -4,17 +4,23 @@
 
 package com.vaadin.terminal.gwt.client.metadata;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
+import com.vaadin.terminal.gwt.client.communication.JSONSerializer;
+
 public class TypeDataStore {
     private static final String CONSTRUCTOR_NAME = "!new";
 
     private final Map<String, Class<?>> identifiers = new HashMap<String, Class<?>>();
 
+    private final Map<Type, Invoker> serializerFactories = new HashMap<Type, Invoker>();
     private final Map<Type, ProxyHandler> proxyHandlers = new HashMap<Type, ProxyHandler>();
+    private final Map<Type, Collection<Property>> properties = new HashMap<Type, Collection<Property>>();
 
     private final Set<Method> delayedMethods = new HashSet<Method>();
     private final Set<Method> lastonlyMethods = new HashSet<Method>();
@@ -23,6 +29,8 @@ public class TypeDataStore {
     private final Map<Method, Invoker> invokers = new HashMap<Method, Invoker>();
     private final Map<Method, Type[]> paramTypes = new HashMap<Method, Type[]>();
 
+    private final Map<Property, Type> propertyTypes = new HashMap<Property, Type>();
+    private final Map<Property, Invoker> setters = new HashMap<Property, Invoker>();
     private final Map<Property, Invoker> getters = new HashMap<Property, Invoker>();
     private final Map<Property, String> delegateToWidget = new HashMap<Property, String>();
 
@@ -85,6 +93,10 @@ public class TypeDataStore {
         return getter;
     }
 
+    public void setGetter(Class<?> clazz, String propertyName, Invoker invoker) {
+        getters.put(new Property(getType(clazz), propertyName), invoker);
+    }
+
     public static String getDelegateToWidget(Property property) {
         return get().delegateToWidget.get(property);
     }
@@ -148,4 +160,61 @@ public class TypeDataStore {
     public void setLastonly(Class<?> clazz, String methodName) {
         lastonlyMethods.add(getType(clazz).getMethod(methodName));
     }
+
+    public static Collection<Property> getProperties(Type type)
+            throws NoDataException {
+        Collection<Property> properties = get().properties.get(type);
+        if (properties == null) {
+            throw new NoDataException("No property list for "
+                    + type.getSignature());
+        }
+        return properties;
+    }
+
+    public void setProperties(Class<?> clazz, String[] propertyNames) {
+        Set<Property> properties = new HashSet<Property>();
+        Type type = getType(clazz);
+        for (String name : propertyNames) {
+            properties.add(new Property(type, name));
+        }
+        this.properties.put(type, Collections.unmodifiableSet(properties));
+    }
+
+    public static Type getType(Property property) throws NoDataException {
+        Type type = get().propertyTypes.get(property);
+        if (type == null) {
+            throw new NoDataException("No return type for "
+                    + property.getSignature());
+        }
+        return type;
+    }
+
+    public void setPropertyType(Class<?> clazz, String propertName, Type type) {
+        propertyTypes.put(new Property(getType(clazz), propertName), type);
+    }
+
+    public static Invoker getSetter(Property property) throws NoDataException {
+        Invoker setter = get().setters.get(property);
+        if (setter == null) {
+            throw new NoDataException("No setter for "
+                    + property.getSignature());
+        }
+        return setter;
+    }
+
+    public void setSetter(Class<?> clazz, String propertyName, Invoker setter) {
+        setters.put(new Property(getType(clazz), propertyName), setter);
+    }
+
+    public void setSerializerFactory(Class<?> clazz, Invoker factory) {
+        serializerFactories.put(getType(clazz), factory);
+    }
+
+    public static JSONSerializer<?> findSerializer(Type type) {
+        Invoker factoryCreator = get().serializerFactories.get(type);
+        if (factoryCreator == null) {
+            return null;
+        }
+        return (JSONSerializer<?>) factoryCreator.invoke(null);
+    }
 }