diff options
author | Leif Åstrand <leif@vaadin.com> | 2012-08-22 17:07:22 +0300 |
---|---|---|
committer | Leif Åstrand <leif@vaadin.com> | 2012-08-22 19:25:32 +0300 |
commit | 02878bd07a38dc69fe415c7f60238817d2a7c434 (patch) | |
tree | ebae730a5108dca70be31039ebc6b30884436fa7 /client-compiler/src/com | |
parent | e345e1820aa0e0fe551cca86d6d28b906beb8937 (diff) | |
download | vaadin-framework-02878bd07a38dc69fe415c7f60238817d2a7c434.tar.gz vaadin-framework-02878bd07a38dc69fe415c7f60238817d2a7c434.zip |
Use ConnectorBundle for JSON encoding and decoding (#9371)
Diffstat (limited to 'client-compiler/src/com')
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()); } |