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.

ConnectorBundleLoaderFactory.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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.Arrays;
  8. import java.util.Collection;
  9. import java.util.HashMap;
  10. import java.util.List;
  11. import java.util.Map;
  12. import java.util.Map.Entry;
  13. import java.util.Set;
  14. import com.google.gwt.core.client.GWT;
  15. import com.google.gwt.core.ext.Generator;
  16. import com.google.gwt.core.ext.GeneratorContext;
  17. import com.google.gwt.core.ext.TreeLogger;
  18. import com.google.gwt.core.ext.TreeLogger.Type;
  19. import com.google.gwt.core.ext.UnableToCompleteException;
  20. import com.google.gwt.core.ext.typeinfo.JClassType;
  21. import com.google.gwt.core.ext.typeinfo.JMethod;
  22. import com.google.gwt.core.ext.typeinfo.JParameterizedType;
  23. import com.google.gwt.core.ext.typeinfo.JType;
  24. import com.google.gwt.core.ext.typeinfo.NotFoundException;
  25. import com.google.gwt.core.ext.typeinfo.TypeOracle;
  26. import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
  27. import com.google.gwt.user.rebind.SourceWriter;
  28. import com.vaadin.shared.communication.ClientRpc;
  29. import com.vaadin.shared.communication.ServerRpc;
  30. import com.vaadin.shared.ui.Connect;
  31. import com.vaadin.shared.ui.Connect.LoadStyle;
  32. import com.vaadin.terminal.gwt.client.ServerConnector;
  33. import com.vaadin.terminal.gwt.client.metadata.ConnectorBundleLoader;
  34. import com.vaadin.terminal.gwt.client.metadata.TypeDataBundle;
  35. import com.vaadin.terminal.gwt.client.metadata.TypeDataStore;
  36. import com.vaadin.terminal.gwt.widgetsetutils.metadata.ClientRpcVisitor;
  37. import com.vaadin.terminal.gwt.widgetsetutils.metadata.ConnectorBundle;
  38. import com.vaadin.terminal.gwt.widgetsetutils.metadata.ConnectorInitVisitor;
  39. import com.vaadin.terminal.gwt.widgetsetutils.metadata.StateInitVisitor;
  40. import com.vaadin.terminal.gwt.widgetsetutils.metadata.TypeVisitor;
  41. import com.vaadin.terminal.gwt.widgetsetutils.metadata.WidgetInitVisitor;
  42. public class ConnectorBundleLoaderFactory extends Generator {
  43. @Override
  44. public String generate(TreeLogger logger, GeneratorContext context,
  45. String typeName) throws UnableToCompleteException {
  46. TypeOracle typeOracle = context.getTypeOracle();
  47. try {
  48. JClassType classType = typeOracle.getType(typeName);
  49. String packageName = classType.getPackage().getName();
  50. String className = classType.getSimpleSourceName() + "Impl";
  51. generateClass(logger, context, packageName, className, typeName);
  52. return packageName + "." + className;
  53. } catch (UnableToCompleteException e) {
  54. // Just rethrow
  55. throw e;
  56. } catch (Exception e) {
  57. logger.log(Type.ERROR, getClass() + " failed", e);
  58. throw new UnableToCompleteException();
  59. }
  60. }
  61. private void generateClass(TreeLogger logger, GeneratorContext context,
  62. String packageName, String className, String requestedType)
  63. throws Exception {
  64. PrintWriter printWriter = context.tryCreate(logger, packageName,
  65. className);
  66. if (printWriter == null) {
  67. return;
  68. }
  69. List<ConnectorBundle> bundles = buildBundles(logger,
  70. context.getTypeOracle());
  71. ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(
  72. packageName, className);
  73. composer.setSuperclass(requestedType);
  74. SourceWriter w = composer.createSourceWriter(context, printWriter);
  75. w.println("public void init() {");
  76. w.indent();
  77. for (ConnectorBundle bundle : bundles) {
  78. String name = bundle.getName();
  79. boolean isEager = name
  80. .equals(ConnectorBundleLoader.EAGER_BUNDLE_NAME);
  81. w.print("addAsyncBlockLoader(new AsyncBundleLoader(\"");
  82. w.print(escape(name));
  83. w.print("\", ");
  84. w.print("new String[] {");
  85. for (Entry<JClassType, Set<String>> entry : bundle.getIdentifiers()
  86. .entrySet()) {
  87. Set<String> identifiers = entry.getValue();
  88. for (String id : identifiers) {
  89. w.print("\"");
  90. w.print(escape(id));
  91. w.print("\",");
  92. }
  93. }
  94. w.println("}) {");
  95. w.indent();
  96. w.print("protected void load(final ");
  97. w.print(TypeDataStore.class.getName());
  98. w.println(" store) {");
  99. w.indent();
  100. if (!isEager) {
  101. w.print(GWT.class.getName());
  102. w.print(".runAsync(");
  103. }
  104. w.print("new ");
  105. w.print(TypeDataBundle.class.getName());
  106. w.println("(getName()) {");
  107. w.indent();
  108. w.println("public void load() {");
  109. w.indent();
  110. printBundleData(w, bundle);
  111. // Close load method
  112. w.outdent();
  113. w.println("}");
  114. // Close new TypeDataBundle() {}
  115. w.outdent();
  116. w.print("}");
  117. if (isEager) {
  118. w.println(".onSuccess();");
  119. } else {
  120. w.println(");");
  121. }
  122. // Close load method
  123. w.outdent();
  124. w.println("}");
  125. // Close add(new ...
  126. w.outdent();
  127. w.println("});");
  128. }
  129. w.outdent();
  130. w.println("}");
  131. w.commit(logger);
  132. }
  133. private void printBundleData(SourceWriter w, ConnectorBundle bundle) {
  134. writeIdentifiers(w, bundle);
  135. writeGwtConstructors(w, bundle);
  136. writeReturnTypes(w, bundle);
  137. writeInvokers(w, bundle);
  138. writeParamTypes(w, bundle);
  139. }
  140. private void writeParamTypes(SourceWriter w, ConnectorBundle bundle) {
  141. Map<JClassType, Set<JMethod>> needsParamTypes = bundle
  142. .getNeedsParamTypes();
  143. for (Entry<JClassType, Set<JMethod>> entry : needsParamTypes.entrySet()) {
  144. JClassType type = entry.getKey();
  145. Set<JMethod> methods = entry.getValue();
  146. for (JMethod method : methods) {
  147. w.println("store.setParamTypes(");
  148. printClassLiteral(w, type);
  149. w.print(", \"");
  150. w.print(escape(method.getName()));
  151. w.println("\", new Type[] {");
  152. for (JType parameter : method.getParameterTypes()) {
  153. ConnectorBundleLoaderFactory.writeTypeCreator(w, parameter);
  154. w.print(", ");
  155. }
  156. w.println("});");
  157. }
  158. }
  159. }
  160. private void writeInvokers(SourceWriter w, ConnectorBundle bundle) {
  161. Map<JClassType, Set<JMethod>> needsInvoker = bundle.getNeedsInvoker();
  162. for (Entry<JClassType, Set<JMethod>> entry : needsInvoker.entrySet()) {
  163. JClassType type = entry.getKey();
  164. Set<JMethod> methods = entry.getValue();
  165. for (JMethod method : methods) {
  166. w.print("store.setInvoker(");
  167. printClassLiteral(w, type);
  168. w.print(", \"");
  169. w.print(escape(method.getName()));
  170. w.println("\", new Invoker() {");
  171. w.indent();
  172. w.println("public Object invoke(Object target, Object[] params) {");
  173. w.indent();
  174. JType returnType = method.getReturnType();
  175. boolean hasReturnType = !"void".equals(returnType
  176. .getQualifiedSourceName());
  177. if (hasReturnType) {
  178. w.print("return ");
  179. }
  180. JType[] parameterTypes = method.getParameterTypes();
  181. w.print("((" + type.getQualifiedSourceName() + ") target)."
  182. + method.getName() + "(");
  183. for (int i = 0; i < parameterTypes.length; i++) {
  184. JType parameterType = parameterTypes[i];
  185. if (i != 0) {
  186. w.print(", ");
  187. }
  188. String parameterTypeName = getBoxedTypeName(parameterType);
  189. w.print("(" + parameterTypeName + ") params[" + i + "]");
  190. }
  191. w.println(");");
  192. if (!hasReturnType) {
  193. w.println("return null;");
  194. }
  195. w.outdent();
  196. w.println("}");
  197. w.outdent();
  198. w.println("});");
  199. }
  200. }
  201. }
  202. private void writeReturnTypes(SourceWriter w, ConnectorBundle bundle) {
  203. Map<JClassType, Set<JMethod>> methodReturnTypes = bundle
  204. .getMethodReturnTypes();
  205. for (Entry<JClassType, Set<JMethod>> entry : methodReturnTypes
  206. .entrySet()) {
  207. JClassType type = entry.getKey();
  208. Set<JMethod> methods = entry.getValue();
  209. for (JMethod method : methods) {
  210. // setReturnType(Class<?> type, String methodName, Type
  211. // returnType)
  212. w.print("store.setReturnType(");
  213. printClassLiteral(w, type);
  214. w.print(", \"");
  215. w.print(escape(method.getName()));
  216. w.print("\", ");
  217. writeTypeCreator(w, method.getReturnType());
  218. w.println(");");
  219. }
  220. }
  221. }
  222. private void writeGwtConstructors(SourceWriter w, ConnectorBundle bundle) {
  223. Set<JClassType> constructors = bundle.getGwtConstructors();
  224. for (JClassType type : constructors) {
  225. w.print("store.setConstructor(");
  226. printClassLiteral(w, type);
  227. w.print(", new Invoker() {");
  228. w.indent();
  229. w.println("public Object invoke(Object target, Object[] params) {");
  230. w.indent();
  231. w.print("return ");
  232. w.print(GWT.class.getName());
  233. w.print(".create(");
  234. printClassLiteral(w, type);
  235. w.println(");");
  236. w.outdent();
  237. w.println("}");
  238. w.outdent();
  239. w.println("});");
  240. }
  241. }
  242. private void printClassLiteral(SourceWriter w, JClassType type) {
  243. w.print(type.getQualifiedSourceName());
  244. w.print(".class");
  245. }
  246. private void writeIdentifiers(SourceWriter w, ConnectorBundle bundle) {
  247. Map<JClassType, Set<String>> identifiers = bundle.getIdentifiers();
  248. for (Entry<JClassType, Set<String>> entry : identifiers.entrySet()) {
  249. Set<String> ids = entry.getValue();
  250. JClassType type = entry.getKey();
  251. for (String id : ids) {
  252. w.print("store.setClass(\"");
  253. w.print(escape(id));
  254. w.print("\", ");
  255. printClassLiteral(w, type);
  256. w.println(");");
  257. }
  258. }
  259. }
  260. private List<ConnectorBundle> buildBundles(TreeLogger logger,
  261. TypeOracle typeOracle) throws NotFoundException,
  262. UnableToCompleteException {
  263. Map<LoadStyle, Collection<JClassType>> connectorsByLoadStyle = new HashMap<LoadStyle, Collection<JClassType>>();
  264. for (LoadStyle loadStyle : LoadStyle.values()) {
  265. connectorsByLoadStyle.put(loadStyle, new ArrayList<JClassType>());
  266. }
  267. JClassType connectorType = typeOracle.getType(ServerConnector.class
  268. .getName());
  269. JClassType[] subtypes = connectorType.getSubtypes();
  270. for (JClassType connectorSubtype : subtypes) {
  271. if (!connectorSubtype.isAnnotationPresent(Connect.class)) {
  272. continue;
  273. }
  274. LoadStyle loadStyle = getLoadStyle(connectorSubtype);
  275. if (loadStyle != null) {
  276. connectorsByLoadStyle.get(loadStyle).add(connectorSubtype);
  277. }
  278. }
  279. List<ConnectorBundle> bundles = new ArrayList<ConnectorBundle>();
  280. Collection<TypeVisitor> visitors = getVisitors(typeOracle);
  281. ConnectorBundle eagerBundle = new ConnectorBundle(
  282. ConnectorBundleLoader.EAGER_BUNDLE_NAME, visitors);
  283. TreeLogger eagerLogger = logger.branch(Type.TRACE,
  284. "Populating eager bundle");
  285. // Eager connectors and all RPC interfaces are loaded by default
  286. eagerBundle.processTypes(eagerLogger,
  287. connectorsByLoadStyle.get(LoadStyle.EAGER));
  288. eagerBundle.processSubTypes(eagerLogger,
  289. typeOracle.getType(ClientRpc.class.getName()));
  290. eagerBundle.processSubTypes(eagerLogger,
  291. typeOracle.getType(ServerRpc.class.getName()));
  292. bundles.add(eagerBundle);
  293. ConnectorBundle deferredBundle = new ConnectorBundle(
  294. ConnectorBundleLoader.DEFERRED_BUNDLE_NAME, eagerBundle);
  295. TreeLogger deferredLogger = logger.branch(Type.TRACE,
  296. "Populating deferred bundle");
  297. deferredBundle.processTypes(deferredLogger,
  298. connectorsByLoadStyle.get(LoadStyle.DEFERRED));
  299. bundles.add(deferredBundle);
  300. Collection<JClassType> lazy = connectorsByLoadStyle.get(LoadStyle.LAZY);
  301. for (JClassType type : lazy) {
  302. ConnectorBundle bundle = new ConnectorBundle(type.getName(),
  303. eagerBundle);
  304. TreeLogger subLogger = logger.branch(Type.TRACE, "Populating "
  305. + type.getName() + " bundle");
  306. bundle.processType(subLogger, type);
  307. bundles.add(bundle);
  308. }
  309. return bundles;
  310. }
  311. private Collection<TypeVisitor> getVisitors(TypeOracle oracle)
  312. throws NotFoundException {
  313. List<TypeVisitor> visitors = Arrays.<TypeVisitor> asList(
  314. new ConnectorInitVisitor(), new StateInitVisitor(),
  315. new WidgetInitVisitor(), new ClientRpcVisitor());
  316. for (TypeVisitor typeVisitor : visitors) {
  317. typeVisitor.init(oracle);
  318. }
  319. return visitors;
  320. }
  321. protected LoadStyle getLoadStyle(JClassType connectorType) {
  322. Connect annotation = connectorType.getAnnotation(Connect.class);
  323. return annotation.loadStyle();
  324. }
  325. public static String getBoxedTypeName(JType type) {
  326. if (type.isPrimitive() != null) {
  327. // Used boxed types for primitives
  328. return type.isPrimitive().getQualifiedBoxedSourceName();
  329. } else {
  330. return type.getErasedType().getQualifiedSourceName();
  331. }
  332. }
  333. public static void writeTypeCreator(SourceWriter sourceWriter, JType type) {
  334. String typeName = ConnectorBundleLoaderFactory.getBoxedTypeName(type);
  335. sourceWriter.print("new Type(\"" + typeName + "\", ");
  336. JParameterizedType parameterized = type.isParameterized();
  337. if (parameterized != null) {
  338. sourceWriter.print("new Type[] {");
  339. JClassType[] typeArgs = parameterized.getTypeArgs();
  340. for (JClassType jClassType : typeArgs) {
  341. writeTypeCreator(sourceWriter, jClassType);
  342. sourceWriter.print(", ");
  343. }
  344. sourceWriter.print("}");
  345. } else {
  346. sourceWriter.print("null");
  347. }
  348. sourceWriter.print(")");
  349. }
  350. }