summaryrefslogtreecommitdiffstats
path: root/client-compiler/src/com
diff options
context:
space:
mode:
authorLeif Åstrand <leif@vaadin.com>2012-08-22 17:07:22 +0300
committerLeif Åstrand <leif@vaadin.com>2012-08-22 19:25:32 +0300
commit02878bd07a38dc69fe415c7f60238817d2a7c434 (patch)
treeebae730a5108dca70be31039ebc6b30884436fa7 /client-compiler/src/com
parente345e1820aa0e0fe551cca86d6d28b906beb8937 (diff)
downloadvaadin-framework-02878bd07a38dc69fe415c7f60238817d2a7c434.tar.gz
vaadin-framework-02878bd07a38dc69fe415c7f60238817d2a7c434.zip
Use ConnectorBundle for JSON encoding and decoding (#9371)
Diffstat (limited to 'client-compiler/src/com')
-rw-r--r--client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorBundleLoaderFactory.java163
-rw-r--r--client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java484
-rw-r--r--client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java377
-rw-r--r--client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ArraySerializer.java90
-rw-r--r--client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ClientRpcVisitor.java6
-rw-r--r--client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorBundle.java345
-rw-r--r--client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/CustomSerializer.java43
-rw-r--r--client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/EnumSerializer.java58
-rw-r--r--client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/GeneratedSerializer.java26
-rw-r--r--client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/JsonSerializer.java88
-rw-r--r--client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/MethodProperty.java124
-rw-r--r--client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/Property.java84
-rw-r--r--client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ServerRpcVisitor.java6
-rw-r--r--client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/StateInitVisitor.java2
14 files changed, 983 insertions, 913 deletions
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorBundleLoaderFactory.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorBundleLoaderFactory.java
index 9f830d4a8d..9a5b83f460 100644
--- a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorBundleLoaderFactory.java
+++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/ConnectorBundleLoaderFactory.java
@@ -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
index 83e1c17881..0000000000
--- a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java
+++ /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
index ac22aa2e8a..0000000000
--- a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerMapGenerator.java
+++ /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
index 0000000000..1c729e9841
--- /dev/null
+++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ArraySerializer.java
@@ -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;");
+ }
+
+}
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ClientRpcVisitor.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ClientRpcVisitor.java
index 2f628b76cb..d5a78b86b9 100644
--- a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ClientRpcVisitor.java
+++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ClientRpcVisitor.java
@@ -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);
+ }
}
}
}
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorBundle.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorBundle.java
index e93c72aa2f..ad6b1eb102 100644
--- a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorBundle.java
+++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ConnectorBundle.java
@@ -4,62 +4,114 @@
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
index 0000000000..11252c4acb
--- /dev/null
+++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/CustomSerializer.java
@@ -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
index 0000000000..324b555300
--- /dev/null
+++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/EnumSerializer.java
@@ -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
index 0000000000..0cfde7748c
--- /dev/null
+++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/GeneratedSerializer.java
@@ -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
index 0000000000..accee84fe7
--- /dev/null
+++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/JsonSerializer.java
@@ -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
index 0000000000..f422205175
--- /dev/null
+++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/MethodProperty.java
@@ -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
index 0000000000..1714489db5
--- /dev/null
+++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/Property.java
@@ -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();
+ }
+
+}
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ServerRpcVisitor.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ServerRpcVisitor.java
index 5505c70dc3..4c0fc94500 100644
--- a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ServerRpcVisitor.java
+++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/ServerRpcVisitor.java
@@ -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);
+ }
}
}
}
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/StateInitVisitor.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/StateInitVisitor.java
index d22f03635b..49cf687412 100644
--- a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/StateInitVisitor.java
+++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/metadata/StateInitVisitor.java
@@ -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());
}