You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SerializerGenerator.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.widgetsetutils;
  5. import java.io.PrintWriter;
  6. import java.util.ArrayList;
  7. import java.util.Date;
  8. import java.util.List;
  9. import com.google.gwt.core.client.GWT;
  10. import com.google.gwt.core.ext.Generator;
  11. import com.google.gwt.core.ext.GeneratorContext;
  12. import com.google.gwt.core.ext.TreeLogger;
  13. import com.google.gwt.core.ext.TreeLogger.Type;
  14. import com.google.gwt.core.ext.UnableToCompleteException;
  15. import com.google.gwt.core.ext.typeinfo.JClassType;
  16. import com.google.gwt.core.ext.typeinfo.JEnumConstant;
  17. import com.google.gwt.core.ext.typeinfo.JEnumType;
  18. import com.google.gwt.core.ext.typeinfo.JMethod;
  19. import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
  20. import com.google.gwt.core.ext.typeinfo.JType;
  21. import com.google.gwt.core.ext.typeinfo.TypeOracle;
  22. import com.google.gwt.json.client.JSONArray;
  23. import com.google.gwt.json.client.JSONObject;
  24. import com.google.gwt.json.client.JSONString;
  25. import com.google.gwt.json.client.JSONValue;
  26. import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
  27. import com.google.gwt.user.rebind.SourceWriter;
  28. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  29. import com.vaadin.terminal.gwt.client.ConnectorMap;
  30. import com.vaadin.terminal.gwt.client.communication.DiffJSONSerializer;
  31. import com.vaadin.terminal.gwt.client.communication.JSONSerializer;
  32. import com.vaadin.terminal.gwt.client.communication.JsonDecoder;
  33. import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
  34. import com.vaadin.terminal.gwt.client.communication.SerializerMap;
  35. /**
  36. * GWT generator for creating serializer classes for custom classes sent from
  37. * server to client.
  38. *
  39. * Only fields with a correspondingly named setter are deserialized.
  40. *
  41. * @since 7.0
  42. */
  43. public class SerializerGenerator extends Generator {
  44. private static final String SUBTYPE_SEPARATOR = "___";
  45. private static String beanSerializerPackageName = SerializerMap.class
  46. .getPackage().getName();
  47. @Override
  48. public String generate(TreeLogger logger, GeneratorContext context,
  49. String beanTypeName) throws UnableToCompleteException {
  50. JClassType beanType = context.getTypeOracle().findType(beanTypeName);
  51. String beanSerializerClassName = getSerializerSimpleClassName(beanType);
  52. try {
  53. // Generate class source code
  54. generateClass(logger, context, beanType, beanSerializerPackageName,
  55. beanSerializerClassName);
  56. } catch (Exception e) {
  57. logger.log(TreeLogger.ERROR, "SerializerGenerator failed for "
  58. + beanType.getQualifiedSourceName(), e);
  59. throw new UnableToCompleteException();
  60. }
  61. // return the fully qualifed name of the class generated
  62. return getFullyQualifiedSerializerClassName(beanType);
  63. }
  64. /**
  65. * Generate source code for a VaadinSerializer implementation.
  66. *
  67. * @param logger
  68. * Logger object
  69. * @param context
  70. * Generator context
  71. * @param beanType
  72. * @param beanTypeName
  73. * bean type for which the serializer is to be generated
  74. * @param beanSerializerTypeName
  75. * name of the serializer class to generate
  76. */
  77. private void generateClass(TreeLogger logger, GeneratorContext context,
  78. JClassType beanType, String serializerPackageName,
  79. String serializerClassName) {
  80. // get print writer that receives the source code
  81. PrintWriter printWriter = null;
  82. printWriter = context.tryCreate(logger, serializerPackageName,
  83. serializerClassName);
  84. // print writer if null, source code has ALREADY been generated
  85. if (printWriter == null) {
  86. return;
  87. }
  88. boolean isEnum = (beanType.isEnum() != null);
  89. Date date = new Date();
  90. TypeOracle typeOracle = context.getTypeOracle();
  91. String beanQualifiedSourceName = beanType.getQualifiedSourceName();
  92. logger.log(Type.DEBUG, "Processing serializable type "
  93. + beanQualifiedSourceName + "...");
  94. // init composer, set class properties, create source writer
  95. ClassSourceFileComposerFactory composer = null;
  96. composer = new ClassSourceFileComposerFactory(serializerPackageName,
  97. serializerClassName);
  98. composer.addImport(GWT.class.getName());
  99. composer.addImport(JSONArray.class.getName());
  100. composer.addImport(com.vaadin.terminal.gwt.client.communication.Type.class
  101. .getName());
  102. // composer.addImport(JSONObject.class.getName());
  103. // composer.addImport(VPaintableMap.class.getName());
  104. composer.addImport(JsonDecoder.class.getName());
  105. // composer.addImport(VaadinSerializer.class.getName());
  106. if (isEnum) {
  107. composer.addImplementedInterface(JSONSerializer.class.getName()
  108. + "<" + beanQualifiedSourceName + ">");
  109. } else {
  110. composer.addImplementedInterface(DiffJSONSerializer.class.getName()
  111. + "<" + beanQualifiedSourceName + ">");
  112. }
  113. SourceWriter sourceWriter = composer.createSourceWriter(context,
  114. printWriter);
  115. sourceWriter.indent();
  116. // Serializer
  117. // public JSONValue serialize(Object value, ConnectorMap idMapper,
  118. // ApplicationConnection connection) {
  119. sourceWriter.println("public " + JSONValue.class.getName()
  120. + " serialize(" + beanQualifiedSourceName + " value, "
  121. + ConnectorMap.class.getName() + " idMapper, "
  122. + ApplicationConnection.class.getName() + " connection) {");
  123. sourceWriter.indent();
  124. // MouseEventDetails castedValue = (MouseEventDetails) value;
  125. sourceWriter.println(beanQualifiedSourceName + " castedValue = ("
  126. + beanQualifiedSourceName + ") value;");
  127. if (isEnum) {
  128. writeEnumSerializer(logger, sourceWriter, beanType);
  129. } else {
  130. writeBeanSerializer(logger, sourceWriter, beanType);
  131. }
  132. // }
  133. sourceWriter.outdent();
  134. sourceWriter.println("}");
  135. sourceWriter.println();
  136. // Updater
  137. // public void update(T target, Type type, JSONValue jsonValue,
  138. // ApplicationConnection connection);
  139. if (!isEnum) {
  140. sourceWriter.println("public void update("
  141. + beanQualifiedSourceName + " target, Type type, "
  142. + JSONValue.class.getName() + " jsonValue, "
  143. + ApplicationConnection.class.getName() + " connection) {");
  144. sourceWriter.indent();
  145. writeBeanDeserializer(logger, sourceWriter, beanType, true);
  146. sourceWriter.outdent();
  147. sourceWriter.println("}");
  148. }
  149. // Deserializer
  150. // T deserialize(Type type, JSONValue jsonValue, ApplicationConnection
  151. // connection);
  152. sourceWriter.println("public " + beanQualifiedSourceName
  153. + " deserialize(Type type, " + JSONValue.class.getName()
  154. + " jsonValue, " + ApplicationConnection.class.getName()
  155. + " connection) {");
  156. sourceWriter.indent();
  157. if (isEnum) {
  158. writeEnumDeserializer(logger, sourceWriter, beanType.isEnum());
  159. } else {
  160. writeBeanDeserializer(logger, sourceWriter, beanType, false);
  161. }
  162. sourceWriter.println("}");
  163. sourceWriter.outdent();
  164. // End of class
  165. sourceWriter.println("}");
  166. sourceWriter.outdent();
  167. // commit generated class
  168. context.commit(logger, printWriter);
  169. logger.log(TreeLogger.INFO, "Generated Serializer class "
  170. + getFullyQualifiedSerializerClassName(beanType));
  171. }
  172. private void writeEnumDeserializer(TreeLogger logger,
  173. SourceWriter sourceWriter, JEnumType enumType) {
  174. sourceWriter.println("String enumIdentifier = (("
  175. + JSONString.class.getName() + ")jsonValue).stringValue();");
  176. for (JEnumConstant e : enumType.getEnumConstants()) {
  177. sourceWriter.println("if (\"" + e.getName()
  178. + "\".equals(enumIdentifier)) {");
  179. sourceWriter.indent();
  180. sourceWriter.println("return " + enumType.getQualifiedSourceName()
  181. + "." + e.getName() + ";");
  182. sourceWriter.outdent();
  183. sourceWriter.println("}");
  184. }
  185. sourceWriter.println("return null;");
  186. }
  187. private void writeBeanDeserializer(TreeLogger logger,
  188. SourceWriter sourceWriter, JClassType beanType, boolean update) {
  189. String beanQualifiedSourceName = beanType.getQualifiedSourceName();
  190. if (!update) {
  191. sourceWriter.println(beanQualifiedSourceName
  192. + " target = GWT.create(" + beanQualifiedSourceName
  193. + ".class);");
  194. }
  195. // JSONOBject json = (JSONObject)jsonValue;
  196. sourceWriter.println(JSONObject.class.getName() + " json = ("
  197. + JSONObject.class.getName() + ")jsonValue;");
  198. for (JMethod method : getSetters(beanType)) {
  199. String setterName = method.getName();
  200. String fieldName = setterName.substring(3); // setZIndex() -> ZIndex
  201. JType setterParameterType = method.getParameterTypes()[0];
  202. logger.log(Type.DEBUG, "* Processing field " + fieldName + " in "
  203. + beanQualifiedSourceName + " (" + beanType.getName() + ")");
  204. // if (json.containsKey("height")) {
  205. sourceWriter.println("if (json.containsKey(\"" + fieldName
  206. + "\")) {");
  207. sourceWriter.indent();
  208. String jsonFieldName = "json_" + fieldName;
  209. // JSONArray json_Height = (JSONArray) json.get("height");
  210. sourceWriter.println("JSONArray " + jsonFieldName
  211. + " = (JSONArray) json.get(\"" + fieldName + "\");");
  212. String fieldType;
  213. String getterName = "get" + fieldName;
  214. JPrimitiveType primitiveType = setterParameterType.isPrimitive();
  215. if (primitiveType != null) {
  216. // This is a primitive type -> must used the boxed type
  217. fieldType = primitiveType.getQualifiedBoxedSourceName();
  218. if (primitiveType == JPrimitiveType.BOOLEAN) {
  219. getterName = "is" + fieldName;
  220. }
  221. } else {
  222. fieldType = setterParameterType.getQualifiedSourceName();
  223. }
  224. // String referenceValue = target.getHeight();
  225. sourceWriter.println(fieldType + " referenceValue = target."
  226. + getterName + "();");
  227. // target.setHeight((String)
  228. // JsonDecoder.decodeValue(jsonFieldValue,referenceValue, idMapper,
  229. // connection));
  230. sourceWriter.print("target." + setterName + "((" + fieldType + ") "
  231. + JsonDecoder.class.getName() + ".decodeValue(");
  232. GeneratedRpcMethodProviderGenerator.writeTypeCreator(sourceWriter,
  233. setterParameterType);
  234. sourceWriter.println(", " + jsonFieldName
  235. + ", referenceValue, connection));");
  236. // } ... end of if contains
  237. sourceWriter.outdent();
  238. sourceWriter.println("}");
  239. }
  240. if (!update) {
  241. // return target;
  242. sourceWriter.println("return target;");
  243. }
  244. }
  245. private void writeEnumSerializer(TreeLogger logger,
  246. SourceWriter sourceWriter, JClassType beanType) {
  247. // return new JSONString(castedValue.name());
  248. sourceWriter.println("return new " + JSONString.class.getName()
  249. + "(castedValue.name());");
  250. }
  251. private void writeBeanSerializer(TreeLogger logger,
  252. SourceWriter sourceWriter, JClassType beanType) {
  253. // JSONObject json = new JSONObject();
  254. sourceWriter.println(JSONObject.class.getName() + " json = new "
  255. + JSONObject.class.getName() + "();");
  256. for (JMethod setterMethod : getSetters(beanType)) {
  257. String setterName = setterMethod.getName();
  258. String fieldName = setterName.substring(3); // setZIndex() -> ZIndex
  259. String getterName = findGetter(beanType, setterMethod);
  260. if (getterName == null) {
  261. logger.log(TreeLogger.ERROR, "No getter found for " + fieldName
  262. + ". Serialization will likely fail");
  263. }
  264. // json.put("button",
  265. // JsonEncoder.encode(castedValue.getButton(), false, idMapper,
  266. // connection));
  267. sourceWriter.println("json.put(\"" + fieldName + "\", "
  268. + JsonEncoder.class.getName() + ".encode(castedValue."
  269. + getterName + "(), false, idMapper, connection));");
  270. }
  271. // return json;
  272. sourceWriter.println("return json;");
  273. }
  274. private String findGetter(JClassType beanType, JMethod setterMethod) {
  275. JType setterParameterType = setterMethod.getParameterTypes()[0];
  276. String fieldName = setterMethod.getName().substring(3);
  277. if (setterParameterType.getQualifiedSourceName().equals(
  278. boolean.class.getName())) {
  279. return "is" + fieldName;
  280. } else {
  281. return "get" + fieldName;
  282. }
  283. }
  284. /**
  285. * Returns a list of all setters found in the beanType or its parent class
  286. *
  287. * @param beanType
  288. * The type to check
  289. * @return A list of setter methods from the class and its parents
  290. */
  291. protected static List<JMethod> getSetters(JClassType beanType) {
  292. List<JMethod> setterMethods = new ArrayList<JMethod>();
  293. while (beanType != null
  294. && !beanType.getQualifiedSourceName().equals(
  295. Object.class.getName())) {
  296. for (JMethod method : beanType.getMethods()) {
  297. // Process all setters that have corresponding fields
  298. if (!method.isPublic() || method.isStatic()
  299. || !method.getName().startsWith("set")
  300. || method.getParameterTypes().length != 1) {
  301. // Not setter, skip to next method
  302. continue;
  303. }
  304. setterMethods.add(method);
  305. }
  306. beanType = beanType.getSuperclass();
  307. }
  308. return setterMethods;
  309. }
  310. private static String getSerializerSimpleClassName(JClassType beanType) {
  311. return getSimpleClassName(beanType) + "_Serializer";
  312. }
  313. private static String getSimpleClassName(JClassType type) {
  314. if (type.isMemberType()) {
  315. // Assumed to be static sub class
  316. String baseName = getSimpleClassName(type.getEnclosingType());
  317. String name = baseName + SUBTYPE_SEPARATOR
  318. + type.getSimpleSourceName();
  319. return name;
  320. }
  321. return type.getSimpleSourceName();
  322. }
  323. public static String getFullyQualifiedSerializerClassName(JClassType type) {
  324. return beanSerializerPackageName + "."
  325. + getSerializerSimpleClassName(type);
  326. }
  327. }