Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

SerializerMapGenerator.java 12KB

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