/* @VaadinApache2LicenseForJavaFiles@ */ package com.vaadin.terminal.gwt.widgetsetutils; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Date; import java.util.List; import com.google.gwt.core.client.GWT; import com.google.gwt.core.ext.Generator; 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.JClassType; import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.JPrimitiveType; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONObject; 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.ConnectorMap; 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 = "___"; private static String beanSerializerPackageName = SerializerMap.class .getPackage().getName(); @Override public String generate(TreeLogger logger, GeneratorContext context, String beanTypeName) throws UnableToCompleteException { JClassType beanType = context.getTypeOracle().findType(beanTypeName); String beanSerializerClassName = getSerializerSimpleClassName(beanType); try { // Generate class source code generateClass(logger, context, beanType, beanSerializerPackageName, beanSerializerClassName); } catch (Exception e) { logger.log(TreeLogger.ERROR, "SerializerGenerator failed for " + beanType.getQualifiedSourceName(), e); throw new UnableToCompleteException(); } // return the fully qualifed name of the class generated return getFullyQualifiedSerializerClassName(beanType); } /** * Generate source code for a VaadinSerializer implementation. * * @param logger * Logger object * @param context * Generator context * @param beanType * @param beanTypeName * bean type for which the serializer is to be generated * @param beanSerializerTypeName * name of the serializer class to generate */ private void generateClass(TreeLogger logger, GeneratorContext context, JClassType beanType, String serializerPackageName, String serializerClassName) { // 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; } Date date = new Date(); TypeOracle typeOracle = context.getTypeOracle(); String beanQualifiedSourceName = beanType.getQualifiedSourceName(); logger.log(Type.DEBUG, "Processing serializable type " + beanQualifiedSourceName + "..."); // init composer, set class properties, create source writer ClassSourceFileComposerFactory composer = null; composer = new ClassSourceFileComposerFactory(serializerPackageName, serializerClassName); composer.addImport(GWT.class.getName()); composer.addImport(JSONArray.class.getName()); // composer.addImport(JSONObject.class.getName()); // composer.addImport(VPaintableMap.class.getName()); composer.addImport(JsonDecoder.class.getName()); // composer.addImport(VaadinSerializer.class.getName()); composer.addImplementedInterface(JSONSerializer.class.getName()); SourceWriter sourceWriter = composer.createSourceWriter(context, printWriter); sourceWriter.indent(); // Serializer // public JSONValue serialize(Object value, ConnectorMap idMapper, // ApplicationConnection connection) { sourceWriter.println("public " + JSONObject.class.getName() + " serialize(" + Object.class.getName() + " value, " + ConnectorMap.class.getName() + " idMapper, " + ApplicationConnection.class.getName() + " connection) {"); sourceWriter.indent(); // MouseEventDetails castedValue = (MouseEventDetails) value; sourceWriter.println(beanQualifiedSourceName + " castedValue = (" + beanQualifiedSourceName + ") value;"); // JSONObject json = new JSONObject(); sourceWriter.println(JSONObject.class.getName() + " json = new " + JSONObject.class.getName() + "();"); for (JMethod setterMethod : getSetters(beanType)) { String setterName = setterMethod.getName(); String fieldName = setterName.substring(3); // setZindex() -> ZIndex 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(), idMapper, // connection)); sourceWriter.println("json.put(\"" + fieldName + "\", " + JsonEncoder.class.getName() + ".encode(castedValue." + getterName + "(), idMapper, connection));"); } // return json; sourceWriter.println("return json;"); // } sourceWriter.println("}"); // Deserializer sourceWriter.println("public " + beanQualifiedSourceName + " deserialize(" + JSONObject.class.getName() + " jsonValue, " + ConnectorMap.class.getName() + " idMapper, " + ApplicationConnection.class.getName() + " connection) {"); sourceWriter.indent(); // VButtonState state = GWT.create(VButtonState.class); sourceWriter.println(beanQualifiedSourceName + " state = GWT.create(" + beanQualifiedSourceName + ".class);"); for (JMethod method : getSetters(beanType)) { String setterName = method.getName(); String fieldName = setterName.substring(3); // setZIndex() -> ZIndex JType setterParameterType = method.getParameterTypes()[0]; logger.log(Type.DEBUG, "* Processing field " + fieldName + " in " + beanQualifiedSourceName + " (" + beanType.getName() + ")"); String jsonFieldName = "json_" + fieldName; // JSONArray json_Height = (JSONArray) jsonValue.get("height"); sourceWriter.println("JSONArray " + jsonFieldName + " = (JSONArray) jsonValue.get(\"" + fieldName + "\");"); // state.setHeight((String) // JsonDecoder.decodeValue(jsonFieldValue,idMapper, connection)); String fieldType; JPrimitiveType primitiveType = setterParameterType.isPrimitive(); if (primitiveType != null) { // This is a primitive type -> must used the boxed type fieldType = primitiveType.getQualifiedBoxedSourceName(); } else { fieldType = setterParameterType.getQualifiedSourceName(); } sourceWriter.println("state." + setterName + "((" + fieldType + ") " + JsonDecoder.class.getName() + ".decodeValue(" + jsonFieldName + ", idMapper, connection));"); } // return state; sourceWriter.println("return state;"); sourceWriter.println("}"); sourceWriter.outdent(); // End of class sourceWriter.println("}"); sourceWriter.outdent(); // commit generated class context.commit(logger, printWriter); logger.log(TreeLogger.INFO, "Generated Serializer class " + getFullyQualifiedSerializerClassName(beanType)); } 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 getSetters(JClassType beanType) { List setterMethods = new ArrayList(); 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 String decapitalize(String name) { return name.substring(0, 1).toLowerCase() + name.substring(1); } private static String getSerializerSimpleClassName(JClassType beanType) { return getSimpleClassName(beanType) + "_Serializer"; } private static String getSimpleClassName(JClassType type) { if (type.isMemberType()) { // Assumed to be static sub class String baseName = getSimpleClassName(type.getEnclosingType()); String name = baseName + SUBTYPE_SEPARATOR + type.getSimpleSourceName(); return name; } return type.getSimpleSourceName(); } public static String getFullyQualifiedSerializerClassName(JClassType type) { return beanSerializerPackageName + "." + getSerializerSimpleClassName(type); } }