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.

SerializerMapGenerator.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.widgetsetutils;
  5. import java.io.PrintWriter;
  6. import java.util.Date;
  7. import java.util.HashSet;
  8. import java.util.List;
  9. import java.util.Map;
  10. import java.util.Set;
  11. import com.google.gwt.core.ext.Generator;
  12. import com.google.gwt.core.ext.GeneratorContext;
  13. import com.google.gwt.core.ext.TreeLogger;
  14. import com.google.gwt.core.ext.TreeLogger.Type;
  15. import com.google.gwt.core.ext.UnableToCompleteException;
  16. import com.google.gwt.core.ext.typeinfo.JClassType;
  17. import com.google.gwt.core.ext.typeinfo.JMethod;
  18. import com.google.gwt.core.ext.typeinfo.JType;
  19. import com.google.gwt.core.ext.typeinfo.TypeOracle;
  20. import com.google.gwt.json.client.JSONObject;
  21. import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
  22. import com.google.gwt.user.rebind.SourceWriter;
  23. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  24. import com.vaadin.terminal.gwt.client.ConnectorMap;
  25. import com.vaadin.terminal.gwt.client.communication.ClientRpc;
  26. import com.vaadin.terminal.gwt.client.communication.JSONSerializer;
  27. import com.vaadin.terminal.gwt.client.communication.SerializerMap;
  28. import com.vaadin.terminal.gwt.client.communication.ServerRpc;
  29. import com.vaadin.terminal.gwt.client.communication.SharedState;
  30. /**
  31. * GWT generator that creates a {@link SerializerMap} implementation (mapper
  32. * from type string to serializer instance) and serializer classes for all
  33. * subclasses of {@link SharedState}.
  34. *
  35. * @since 7.0
  36. */
  37. public class SerializerMapGenerator extends Generator {
  38. private String packageName;
  39. private String className;
  40. @Override
  41. public String generate(TreeLogger logger, GeneratorContext context,
  42. String typeName) throws UnableToCompleteException {
  43. try {
  44. TypeOracle typeOracle = context.getTypeOracle();
  45. Set<JClassType> typesNeedingSerializers = findTypesNeedingSerializers(
  46. typeOracle, logger);
  47. Set<JClassType> typesWithExistingSerializers = findTypesWithExistingSerializers(
  48. typeOracle, logger);
  49. Set<JClassType> serializerMappings = new HashSet<JClassType>();
  50. serializerMappings.addAll(typesNeedingSerializers);
  51. serializerMappings.addAll(typesWithExistingSerializers);
  52. // get classType and save instance variables
  53. JClassType classType = typeOracle.getType(typeName);
  54. packageName = classType.getPackage().getName();
  55. className = classType.getSimpleSourceName() + "Impl";
  56. // Generate class source code for SerializerMapImpl
  57. generateSerializerMap(serializerMappings, logger, context);
  58. SerializerGenerator sg = new SerializerGenerator();
  59. for (JClassType type : typesNeedingSerializers) {
  60. sg.generate(logger, context, type.getQualifiedSourceName());
  61. }
  62. } catch (Exception e) {
  63. logger.log(TreeLogger.ERROR,
  64. "SerializerMapGenerator creation failed", e);
  65. }
  66. // return the fully qualifed name of the class generated
  67. return packageName + "." + className;
  68. }
  69. private Set<JClassType> findTypesWithExistingSerializers(
  70. TypeOracle typeOracle, TreeLogger logger) {
  71. JClassType serializerInterface = typeOracle
  72. .findType(JSONSerializer.class.getName());
  73. Set<JClassType> types = new HashSet<JClassType>();
  74. for (JClassType serializer : serializerInterface.getSubtypes()) {
  75. JType[] deserializeParamTypes = new JType[] {
  76. typeOracle.findType(JSONObject.class.getName()),
  77. typeOracle.findType(ConnectorMap.class.getName()),
  78. typeOracle.findType(ApplicationConnection.class.getName()) };
  79. JMethod deserializeMethod = serializer.findMethod("deserialize",
  80. deserializeParamTypes);
  81. if (deserializeMethod == null) {
  82. continue;
  83. }
  84. types.add(deserializeMethod.getReturnType().isClass());
  85. }
  86. return types;
  87. }
  88. /**
  89. * Generate source code for SerializerMapImpl
  90. *
  91. * @param typesNeedingSerializers
  92. *
  93. * @param logger
  94. * Logger object
  95. * @param context
  96. * Generator context
  97. */
  98. private void generateSerializerMap(Set<JClassType> typesNeedingSerializers,
  99. TreeLogger logger, GeneratorContext context) {
  100. // get print writer that receives the source code
  101. PrintWriter printWriter = null;
  102. printWriter = context.tryCreate(logger, packageName, className);
  103. // print writer if null, source code has ALREADY been generated
  104. if (printWriter == null) {
  105. return;
  106. }
  107. Date date = new Date();
  108. TypeOracle typeOracle = context.getTypeOracle();
  109. // init composer, set class properties, create source writer
  110. ClassSourceFileComposerFactory composer = null;
  111. composer = new ClassSourceFileComposerFactory(packageName, className);
  112. composer.addImport("com.google.gwt.core.client.GWT");
  113. composer.addImplementedInterface(SerializerMap.class.getName());
  114. SourceWriter sourceWriter = composer.createSourceWriter(context,
  115. printWriter);
  116. sourceWriter.indent();
  117. sourceWriter.println("public " + JSONSerializer.class.getName()
  118. + " getSerializer(String type) {");
  119. sourceWriter.indent();
  120. // TODO cache serializer instances in a map
  121. for (JClassType type : typesNeedingSerializers) {
  122. sourceWriter.println("if (type.equals(\""
  123. + type.getQualifiedSourceName() + "\")) {");
  124. sourceWriter.indent();
  125. String serializerName = SerializerGenerator
  126. .getFullyQualifiedSerializerClassName(type);
  127. sourceWriter.println("return GWT.create(" + serializerName
  128. + ".class);");
  129. sourceWriter.outdent();
  130. sourceWriter.println("}");
  131. logger.log(Type.INFO, "Configured serializer (" + serializerName
  132. + ") for " + type.getName());
  133. }
  134. sourceWriter
  135. .println("throw new RuntimeException(\"No serializer found for class \"+type);");
  136. sourceWriter.outdent();
  137. sourceWriter.println("}");
  138. // close generated class
  139. sourceWriter.outdent();
  140. sourceWriter.println("}");
  141. // commit generated class
  142. context.commit(logger, printWriter);
  143. logger.log(Type.INFO,
  144. "Done. (" + (new Date().getTime() - date.getTime()) / 1000
  145. + "seconds)");
  146. }
  147. public Set<JClassType> findTypesNeedingSerializers(TypeOracle typeOracle,
  148. TreeLogger logger) {
  149. logger.log(Type.INFO, "Detecting serializable data types...");
  150. HashSet<JClassType> types = new HashSet<JClassType>();
  151. // Generate serializer classes for each subclass of SharedState
  152. JClassType serializerType = typeOracle.findType(SharedState.class
  153. .getName());
  154. JClassType[] serializerSubtypes = serializerType.getSubtypes();
  155. for (JClassType type : serializerSubtypes) {
  156. types.add(type);
  157. }
  158. // Serializer classes might also be needed for RPC methods
  159. for (Class<?> cls : new Class[] { ServerRpc.class, ClientRpc.class }) {
  160. JClassType rpcType = typeOracle.findType(cls.getName());
  161. JClassType[] serverRpcSubtypes = rpcType.getSubtypes();
  162. for (JClassType type : serverRpcSubtypes) {
  163. addMethodParameterTypes(type, types);
  164. }
  165. }
  166. // Add all types used from/in the types
  167. for (Object t : types.toArray()) {
  168. findSubTypesNeedingSerializers((JClassType) t, types);
  169. }
  170. logger.log(Type.INFO, "Serializable data types: " + types.toString());
  171. return types;
  172. }
  173. private void addMethodParameterTypes(JClassType classContainingMethods,
  174. HashSet<JClassType> types) {
  175. for (JMethod method : classContainingMethods.getMethods()) {
  176. if (method.getName().equals("initRpc")) {
  177. continue;
  178. }
  179. for (JType type : method.getParameterTypes()) {
  180. JClassType t = type.isClass();
  181. JClassType interfaceType = type.isInterface();
  182. if (t != null) {
  183. types.add(t);
  184. } else if (interfaceType != null) {
  185. types.add(interfaceType);
  186. } else {
  187. System.err.println("Unknown method parameter type: "
  188. + type.getQualifiedSourceName());
  189. }
  190. }
  191. }
  192. }
  193. public void findSubTypesNeedingSerializers(JClassType type,
  194. Set<JClassType> serializableTypes) {
  195. // Find all setters and look at their parameter type to determine if a
  196. // new serializer is needed
  197. for (JMethod setterMethod : SerializerGenerator.getSetters(type)) {
  198. // The one and only parameter for the setter
  199. JType setterType = setterMethod.getParameterTypes()[0];
  200. if (serializableTypes.contains(setterType)) {
  201. continue;
  202. }
  203. if (serializationHandledByFramework(setterType)) {
  204. continue;
  205. }
  206. JClassType setterTypeClass = setterType.isClass();
  207. if (setterTypeClass != null) {
  208. // setterTypeClass is null at least for List<String>. It is
  209. // possible that we need to handle the cases somehow, for
  210. // instance for List<MyObject>.
  211. serializableTypes.add(setterTypeClass);
  212. findSubTypesNeedingSerializers(type, serializableTypes);
  213. }
  214. }
  215. }
  216. Set<Class<?>> frameworkHandledTypes = new HashSet<Class<?>>();
  217. {
  218. frameworkHandledTypes.add(String.class);
  219. frameworkHandledTypes.add(Boolean.class);
  220. frameworkHandledTypes.add(Integer.class);
  221. frameworkHandledTypes.add(Float.class);
  222. frameworkHandledTypes.add(Double.class);
  223. frameworkHandledTypes.add(Long.class);
  224. frameworkHandledTypes.add(Enum.class);
  225. frameworkHandledTypes.add(String[].class);
  226. frameworkHandledTypes.add(Object[].class);
  227. frameworkHandledTypes.add(Map.class);
  228. frameworkHandledTypes.add(List.class);
  229. frameworkHandledTypes.add(Set.class);
  230. }
  231. private boolean serializationHandledByFramework(JType setterType) {
  232. // Some types are handled by the framework at the moment. See #8449
  233. // This method should be removed at some point.
  234. if (setterType.isArray() != null) {
  235. return true;
  236. }
  237. if (setterType.isEnum() != null) {
  238. return true;
  239. }
  240. if (setterType.isPrimitive() != null) {
  241. return true;
  242. }
  243. String qualifiedName = setterType.getQualifiedSourceName();
  244. for (Class<?> cls : frameworkHandledTypes) {
  245. if (qualifiedName.equals(cls.getName())) {
  246. return true;
  247. }
  248. }
  249. return false;
  250. }
  251. }