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 8.7KB

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