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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049
  1. /*
  2. * Copyright 2000-2013 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.server.widgetsetutils;
  17. import java.io.PrintWriter;
  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collection;
  21. import java.util.Collections;
  22. import java.util.Comparator;
  23. import java.util.HashMap;
  24. import java.util.HashSet;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.Map.Entry;
  28. import java.util.Set;
  29. import com.google.gwt.core.client.GWT;
  30. import com.google.gwt.core.client.RunAsyncCallback;
  31. import com.google.gwt.core.ext.Generator;
  32. import com.google.gwt.core.ext.GeneratorContext;
  33. import com.google.gwt.core.ext.TreeLogger;
  34. import com.google.gwt.core.ext.TreeLogger.Type;
  35. import com.google.gwt.core.ext.UnableToCompleteException;
  36. import com.google.gwt.core.ext.typeinfo.JClassType;
  37. import com.google.gwt.core.ext.typeinfo.JMethod;
  38. import com.google.gwt.core.ext.typeinfo.JParameterizedType;
  39. import com.google.gwt.core.ext.typeinfo.JType;
  40. import com.google.gwt.core.ext.typeinfo.NotFoundException;
  41. import com.google.gwt.core.ext.typeinfo.TypeOracle;
  42. import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
  43. import com.google.gwt.user.rebind.SourceWriter;
  44. import com.vaadin.client.ServerConnector;
  45. import com.vaadin.client.metadata.ConnectorBundleLoader;
  46. import com.vaadin.client.metadata.InvokationHandler;
  47. import com.vaadin.client.metadata.ProxyHandler;
  48. import com.vaadin.client.metadata.TypeData;
  49. import com.vaadin.client.metadata.TypeDataStore;
  50. import com.vaadin.client.ui.UnknownComponentConnector;
  51. import com.vaadin.server.widgetsetutils.metadata.ClientRpcVisitor;
  52. import com.vaadin.server.widgetsetutils.metadata.ConnectorBundle;
  53. import com.vaadin.server.widgetsetutils.metadata.ConnectorInitVisitor;
  54. import com.vaadin.server.widgetsetutils.metadata.GeneratedSerializer;
  55. import com.vaadin.server.widgetsetutils.metadata.Property;
  56. import com.vaadin.server.widgetsetutils.metadata.ServerRpcVisitor;
  57. import com.vaadin.server.widgetsetutils.metadata.StateInitVisitor;
  58. import com.vaadin.server.widgetsetutils.metadata.TypeVisitor;
  59. import com.vaadin.server.widgetsetutils.metadata.WidgetInitVisitor;
  60. import com.vaadin.shared.annotations.Delayed;
  61. import com.vaadin.shared.annotations.DelegateToWidget;
  62. import com.vaadin.shared.communication.ClientRpc;
  63. import com.vaadin.shared.communication.ServerRpc;
  64. import com.vaadin.shared.ui.Connect;
  65. import com.vaadin.shared.ui.Connect.LoadStyle;
  66. public class ConnectorBundleLoaderFactory extends Generator {
  67. /**
  68. * Special SourceWriter that approximates the number of written bytes to
  69. * support splitting long methods into shorter chunks to avoid hitting the
  70. * 65535 byte limit.
  71. */
  72. private class SplittingSourceWriter implements SourceWriter {
  73. private final SourceWriter target;
  74. private final String baseName;
  75. private final int splitSize;
  76. private final List<String> methodNames;
  77. // Seems to be undercounted by about 15%
  78. private int approximateChars = 0;
  79. private int wrapCount = 0;
  80. public SplittingSourceWriter(SourceWriter target, String baseName,
  81. int splitSize) {
  82. this.target = target;
  83. this.baseName = baseName;
  84. this.splitSize = splitSize;
  85. methodNames = new ArrayList<String>();
  86. methodNames.add(baseName);
  87. }
  88. @Override
  89. public void beginJavaDocComment() {
  90. target.beginJavaDocComment();
  91. addChars(10);
  92. }
  93. private void addChars(int i) {
  94. approximateChars += i;
  95. }
  96. private void addChars(String s) {
  97. addChars(s.length());
  98. }
  99. private void addChars(String s, Object[] args) {
  100. addChars(String.format(s, args));
  101. }
  102. @Override
  103. public void commit(TreeLogger logger) {
  104. target.commit(logger);
  105. }
  106. @Override
  107. public void endJavaDocComment() {
  108. target.endJavaDocComment();
  109. addChars(10);
  110. }
  111. @Override
  112. public void indent() {
  113. target.indent();
  114. addChars(10);
  115. }
  116. @Override
  117. public void indentln(String s) {
  118. target.indentln(s);
  119. addChars(s);
  120. }
  121. @Override
  122. public void indentln(String s, Object... args) {
  123. target.indentln(s, args);
  124. addChars(s, args);
  125. }
  126. @Override
  127. public void outdent() {
  128. target.outdent();
  129. }
  130. @Override
  131. public void print(String s) {
  132. target.print(s);
  133. addChars(s);
  134. }
  135. @Override
  136. public void print(String s, Object... args) {
  137. target.print(s, args);
  138. addChars(s, args);
  139. }
  140. @Override
  141. public void println() {
  142. target.println();
  143. addChars(5);
  144. }
  145. @Override
  146. public void println(String s) {
  147. target.println(s);
  148. addChars(s);
  149. }
  150. @Override
  151. public void println(String s, Object... args) {
  152. target.println(s, args);
  153. addChars(s, args);
  154. }
  155. public void splitIfNeeded() {
  156. splitIfNeeded(false, null);
  157. }
  158. public void splitIfNeeded(boolean isNative, String params) {
  159. if (approximateChars > splitSize) {
  160. String newMethod = baseName + wrapCount++;
  161. String args = params == null ? "" : params;
  162. if (isNative) {
  163. outdent();
  164. println("}-*/;");
  165. println("private native void %s(%s) /*-{", newMethod, args);
  166. } else {
  167. println("%s();", newMethod);
  168. outdent();
  169. println("}");
  170. println("private void %s(%s) {", newMethod, args);
  171. }
  172. methodNames.add(newMethod);
  173. indent();
  174. approximateChars = 0;
  175. }
  176. }
  177. public List<String> getMethodNames() {
  178. return Collections.unmodifiableList(methodNames);
  179. }
  180. }
  181. @Override
  182. public String generate(TreeLogger logger, GeneratorContext context,
  183. String typeName) throws UnableToCompleteException {
  184. TypeOracle typeOracle = context.getTypeOracle();
  185. try {
  186. JClassType classType = typeOracle.getType(typeName);
  187. String packageName = classType.getPackage().getName();
  188. String className = classType.getSimpleSourceName() + "Impl";
  189. generateClass(logger, context, packageName, className, typeName);
  190. return packageName + "." + className;
  191. } catch (UnableToCompleteException e) {
  192. // Just rethrow
  193. throw e;
  194. } catch (Exception e) {
  195. logger.log(Type.ERROR, getClass() + " failed", e);
  196. throw new UnableToCompleteException();
  197. }
  198. }
  199. private void generateClass(TreeLogger logger, GeneratorContext context,
  200. String packageName, String className, String requestedType)
  201. throws Exception {
  202. PrintWriter printWriter = context.tryCreate(logger, packageName,
  203. className);
  204. if (printWriter == null) {
  205. return;
  206. }
  207. List<ConnectorBundle> bundles = buildBundles(logger,
  208. context.getTypeOracle());
  209. ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(
  210. packageName, className);
  211. composer.setSuperclass(requestedType);
  212. SourceWriter w = composer.createSourceWriter(context, printWriter);
  213. w.println("public void init() {");
  214. w.indent();
  215. for (ConnectorBundle bundle : bundles) {
  216. detectBadProperties(bundle, logger);
  217. String name = bundle.getName();
  218. boolean isEager = name
  219. .equals(ConnectorBundleLoader.EAGER_BUNDLE_NAME);
  220. w.print("addAsyncBlockLoader(new AsyncBundleLoader(\"");
  221. w.print(escape(name));
  222. w.print("\", ");
  223. w.print("new String[] {");
  224. for (Entry<JClassType, Set<String>> entry : bundle.getIdentifiers()
  225. .entrySet()) {
  226. Set<String> identifiers = entry.getValue();
  227. for (String id : identifiers) {
  228. w.print("\"");
  229. w.print(escape(id));
  230. w.print("\",");
  231. }
  232. }
  233. w.println("}) {");
  234. w.indent();
  235. w.print("protected void load(final ");
  236. w.print(TypeDataStore.class.getName());
  237. w.println(" store) {");
  238. w.indent();
  239. if (!isEager) {
  240. w.print(GWT.class.getName());
  241. w.print(".runAsync(");
  242. }
  243. w.println("new %s() {", RunAsyncCallback.class.getName());
  244. w.indent();
  245. w.println("public void onSuccess() {");
  246. w.indent();
  247. w.println("load();");
  248. w.println("%s.get().setLoaded(getName());",
  249. ConnectorBundleLoader.class.getName());
  250. // Close onSuccess method
  251. w.outdent();
  252. w.println("}");
  253. w.println("private void load() {");
  254. w.indent();
  255. String loadNativeJsBundle = "loadJsBundle";
  256. printBundleData(logger, w, bundle, loadNativeJsBundle);
  257. // Close load method
  258. w.outdent();
  259. w.println("}");
  260. // Separate method for loading native JS stuff (e.g. callbacks)
  261. String loadNativeJsMethodName = "loadNativeJs";
  262. w.println("private native void %s(%s store) /*-{",
  263. loadNativeJsMethodName, TypeDataStore.class.getName());
  264. w.indent();
  265. List<String> jsMethodNames = printJsBundleData(logger, w, bundle,
  266. loadNativeJsMethodName);
  267. w.outdent();
  268. w.println("}-*/;");
  269. // Call all generated native method inside one Java method to avoid
  270. // refercences inside native methods to each other
  271. w.println("private void %s(%s store) {", loadNativeJsBundle,
  272. TypeDataStore.class.getName());
  273. w.indent();
  274. printLoadJsBundleData(w, loadNativeJsBundle, jsMethodNames);
  275. w.outdent();
  276. w.println("}");
  277. // onFailure method declaration starts
  278. w.println("public void onFailure(Throwable reason) {");
  279. w.indent();
  280. w.println("%s.get().setLoadFailure(getName(), reason);",
  281. ConnectorBundleLoader.class.getName());
  282. w.outdent();
  283. w.println("}");
  284. // Close new RunAsyncCallback() {}
  285. w.outdent();
  286. w.print("}");
  287. if (isEager) {
  288. w.println(".onSuccess();");
  289. } else {
  290. w.println(");");
  291. }
  292. // Close load method
  293. w.outdent();
  294. w.println("}");
  295. // Close add(new ...
  296. w.outdent();
  297. w.println("});");
  298. }
  299. w.outdent();
  300. w.println("}");
  301. w.commit(logger);
  302. }
  303. private void printLoadJsBundleData(SourceWriter w, String methodName,
  304. List<String> methods) {
  305. SplittingSourceWriter writer = new SplittingSourceWriter(w, methodName,
  306. 30000);
  307. for (String method : methods) {
  308. writer.println("%s(store);", method);
  309. writer.splitIfNeeded();
  310. }
  311. }
  312. private void detectBadProperties(ConnectorBundle bundle, TreeLogger logger)
  313. throws UnableToCompleteException {
  314. Map<JClassType, Set<String>> definedProperties = new HashMap<JClassType, Set<String>>();
  315. for (Property property : bundle.getNeedsProperty()) {
  316. JClassType beanType = property.getBeanType();
  317. Set<String> usedPropertyNames = definedProperties.get(beanType);
  318. if (usedPropertyNames == null) {
  319. usedPropertyNames = new HashSet<String>();
  320. definedProperties.put(beanType, usedPropertyNames);
  321. }
  322. String name = property.getName();
  323. if (!usedPropertyNames.add(name)) {
  324. logger.log(Type.ERROR, beanType.getQualifiedSourceName()
  325. + " has multiple properties with the name " + name
  326. + ". This can happen if there are multiple "
  327. + "setters with identical names ignoring case.");
  328. throw new UnableToCompleteException();
  329. }
  330. if (!property.hasAccessorMethods()) {
  331. logger.log(Type.ERROR, beanType.getQualifiedSourceName()
  332. + " has the property '" + name
  333. + "' without getter defined.");
  334. throw new UnableToCompleteException();
  335. }
  336. }
  337. }
  338. private List<String> printJsBundleData(TreeLogger logger, SourceWriter w,
  339. ConnectorBundle bundle, String methodName) {
  340. SplittingSourceWriter writer = new SplittingSourceWriter(w, methodName,
  341. 30000);
  342. Set<Property> needsProperty = bundle.getNeedsProperty();
  343. for (Property property : needsProperty) {
  344. writer.println("var data = {");
  345. writer.indent();
  346. writer.println("setter: function(bean, value) {");
  347. writer.indent();
  348. property.writeSetterBody(logger, writer, "bean", "value");
  349. writer.outdent();
  350. writer.println("},");
  351. writer.println("getter: function(bean) {");
  352. writer.indent();
  353. property.writeGetterBody(logger, writer, "bean");
  354. writer.outdent();
  355. writer.println("}");
  356. writer.outdent();
  357. writer.println("};");
  358. // Method declaration
  359. writer.print(
  360. "store.@%s::setPropertyData(Ljava/lang/Class;Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)",
  361. TypeDataStore.class.getName());
  362. writer.println("(@%s::class, '%s', data);", property.getBeanType()
  363. .getQualifiedSourceName(), property.getName());
  364. writer.println();
  365. writer.splitIfNeeded(true,
  366. String.format("%s store", TypeDataStore.class.getName()));
  367. }
  368. return writer.getMethodNames();
  369. }
  370. private void printBundleData(TreeLogger logger, SourceWriter sourceWriter,
  371. ConnectorBundle bundle, String loadNativeJsMethodName)
  372. throws UnableToCompleteException {
  373. // Split into new load method when reaching approximately 30000 bytes
  374. SplittingSourceWriter w = new SplittingSourceWriter(sourceWriter,
  375. "load", 30000);
  376. writeSuperClasses(w, bundle);
  377. writeIdentifiers(w, bundle);
  378. writeGwtConstructors(w, bundle);
  379. writeReturnTypes(w, bundle);
  380. writeInvokers(w, bundle);
  381. writeParamTypes(w, bundle);
  382. writeProxys(w, bundle);
  383. writeDelayedInfo(w, bundle);
  384. w.println("%s(store);", loadNativeJsMethodName);
  385. // Must use Java code to generate Type data (because of Type[]), doing
  386. // this after the JS property data has been initialized
  387. writePropertyTypes(logger, w, bundle);
  388. writeSerializers(logger, w, bundle);
  389. writeDelegateToWidget(logger, w, bundle);
  390. }
  391. private void writeSuperClasses(SplittingSourceWriter w,
  392. ConnectorBundle bundle) {
  393. List<JClassType> needsSuperclass = new ArrayList<JClassType>(
  394. bundle.getNeedsSuperclass());
  395. // Emit in hierarchy order to ensure superclass is defined when
  396. // referenced
  397. Collections.sort(needsSuperclass, new Comparator<JClassType>() {
  398. @Override
  399. public int compare(JClassType type1, JClassType type2) {
  400. int depthDiff = getDepth(type1) - getDepth(type2);
  401. if (depthDiff != 0) {
  402. return depthDiff;
  403. } else {
  404. // Just something to get a stable compare
  405. return type1.getName().compareTo(type2.getName());
  406. }
  407. }
  408. private int getDepth(JClassType type) {
  409. int depth = 0;
  410. while (type != null) {
  411. depth++;
  412. type = type.getSuperclass();
  413. }
  414. return depth;
  415. }
  416. });
  417. for (JClassType jClassType : needsSuperclass) {
  418. JClassType superclass = jClassType.getSuperclass();
  419. while (superclass != null && !superclass.isPublic()) {
  420. superclass = superclass.getSuperclass();
  421. }
  422. String classLiteralString;
  423. if (superclass == null) {
  424. classLiteralString = "null";
  425. } else {
  426. classLiteralString = getClassLiteralString(superclass);
  427. }
  428. w.println("store.setSuperClass(%s, %s);",
  429. getClassLiteralString(jClassType), classLiteralString);
  430. }
  431. }
  432. private void writeDelegateToWidget(TreeLogger logger,
  433. SplittingSourceWriter w, ConnectorBundle bundle) {
  434. Set<Property> needsDelegateToWidget = bundle.getNeedsDelegateToWidget();
  435. for (Property property : needsDelegateToWidget) {
  436. w.println("store.setDelegateToWidget(%s, \"%s\", \"%s\");",
  437. getClassLiteralString(property.getBeanType()),
  438. property.getName(),
  439. property.getAnnotation(DelegateToWidget.class).value());
  440. w.splitIfNeeded();
  441. }
  442. }
  443. private void writeSerializers(TreeLogger logger, SplittingSourceWriter w,
  444. ConnectorBundle bundle) throws UnableToCompleteException {
  445. Map<JType, GeneratedSerializer> serializers = bundle.getSerializers();
  446. for (Entry<JType, GeneratedSerializer> entry : serializers.entrySet()) {
  447. JType type = entry.getKey();
  448. GeneratedSerializer serializer = entry.getValue();
  449. w.print("store.setSerializerFactory(");
  450. writeClassLiteral(w, type);
  451. w.print(", ");
  452. w.println("new Invoker() {");
  453. w.indent();
  454. w.println("public Object invoke(Object target, Object[] params) {");
  455. w.indent();
  456. serializer.writeSerializerInstantiator(logger, w);
  457. w.outdent();
  458. w.println("}");
  459. w.outdent();
  460. w.print("}");
  461. w.println(");");
  462. w.splitIfNeeded();
  463. }
  464. }
  465. private void writePropertyTypes(TreeLogger logger, SplittingSourceWriter w,
  466. ConnectorBundle bundle) {
  467. Set<Property> properties = bundle.getNeedsProperty();
  468. for (Property property : properties) {
  469. w.print("store.setPropertyType(");
  470. writeClassLiteral(w, property.getBeanType());
  471. w.print(", \"");
  472. w.print(escape(property.getName()));
  473. w.print("\", ");
  474. writeTypeCreator(w, property.getPropertyType());
  475. w.println(");");
  476. w.splitIfNeeded();
  477. }
  478. }
  479. private void writeDelayedInfo(SplittingSourceWriter w,
  480. ConnectorBundle bundle) {
  481. Map<JClassType, Set<JMethod>> needsDelayedInfo = bundle
  482. .getNeedsDelayedInfo();
  483. Set<Entry<JClassType, Set<JMethod>>> entrySet = needsDelayedInfo
  484. .entrySet();
  485. for (Entry<JClassType, Set<JMethod>> entry : entrySet) {
  486. JClassType type = entry.getKey();
  487. Set<JMethod> methods = entry.getValue();
  488. for (JMethod method : methods) {
  489. Delayed annotation = method.getAnnotation(Delayed.class);
  490. if (annotation != null) {
  491. w.print("store.setDelayed(");
  492. writeClassLiteral(w, type);
  493. w.print(", \"");
  494. w.print(escape(method.getName()));
  495. w.println("\");");
  496. if (annotation.lastOnly()) {
  497. w.print("store.setLastOnly(");
  498. writeClassLiteral(w, type);
  499. w.print(", \"");
  500. w.print(escape(method.getName()));
  501. w.println("\");");
  502. }
  503. w.splitIfNeeded();
  504. }
  505. }
  506. }
  507. }
  508. private void writeProxys(SplittingSourceWriter w, ConnectorBundle bundle) {
  509. Set<JClassType> needsProxySupport = bundle.getNeedsProxySupport();
  510. for (JClassType type : needsProxySupport) {
  511. w.print("store.setProxyHandler(");
  512. writeClassLiteral(w, type);
  513. w.print(", new ");
  514. w.print(ProxyHandler.class.getCanonicalName());
  515. w.println("() {");
  516. w.indent();
  517. w.println("public Object createProxy(final "
  518. + InvokationHandler.class.getName() + " handler) {");
  519. w.indent();
  520. w.print("return new ");
  521. w.print(type.getQualifiedSourceName());
  522. w.println("() {");
  523. w.indent();
  524. JMethod[] methods = type.getOverridableMethods();
  525. for (JMethod method : methods) {
  526. if (method.isAbstract()) {
  527. w.print("public ");
  528. w.print(method.getReturnType().getQualifiedSourceName());
  529. w.print(" ");
  530. w.print(method.getName());
  531. w.print("(");
  532. JType[] types = method.getParameterTypes();
  533. for (int i = 0; i < types.length; i++) {
  534. if (i != 0) {
  535. w.print(", ");
  536. }
  537. w.print(types[i].getQualifiedSourceName());
  538. w.print(" p");
  539. w.print(Integer.toString(i));
  540. }
  541. w.println(") {");
  542. w.indent();
  543. if (!method.getReturnType().getQualifiedSourceName()
  544. .equals("void")) {
  545. w.print("return ");
  546. }
  547. w.print("handler.invoke(this, ");
  548. w.print(TypeData.class.getCanonicalName());
  549. w.print(".getType(");
  550. writeClassLiteral(w, type);
  551. w.print(").getMethod(\"");
  552. w.print(escape(method.getName()));
  553. w.print("\"), new Object [] {");
  554. for (int i = 0; i < types.length; i++) {
  555. w.print("p" + i + ", ");
  556. }
  557. w.println("});");
  558. w.outdent();
  559. w.println("}");
  560. }
  561. }
  562. w.outdent();
  563. w.println("};");
  564. w.outdent();
  565. w.println("}");
  566. w.outdent();
  567. w.println("});");
  568. w.splitIfNeeded();
  569. }
  570. }
  571. private void writeParamTypes(SplittingSourceWriter w, ConnectorBundle bundle) {
  572. Map<JClassType, Set<JMethod>> needsParamTypes = bundle
  573. .getNeedsParamTypes();
  574. for (Entry<JClassType, Set<JMethod>> entry : needsParamTypes.entrySet()) {
  575. JClassType type = entry.getKey();
  576. Set<JMethod> methods = entry.getValue();
  577. for (JMethod method : methods) {
  578. w.print("store.setParamTypes(");
  579. writeClassLiteral(w, type);
  580. w.print(", \"");
  581. w.print(escape(method.getName()));
  582. w.print("\", new Type[] {");
  583. for (JType parameter : method.getParameterTypes()) {
  584. ConnectorBundleLoaderFactory.writeTypeCreator(w, parameter);
  585. w.print(", ");
  586. }
  587. w.println("});");
  588. w.splitIfNeeded();
  589. }
  590. }
  591. }
  592. private void writeInvokers(SplittingSourceWriter w, ConnectorBundle bundle) {
  593. Map<JClassType, Set<JMethod>> needsInvoker = bundle.getNeedsInvoker();
  594. for (Entry<JClassType, Set<JMethod>> entry : needsInvoker.entrySet()) {
  595. JClassType type = entry.getKey();
  596. Set<JMethod> methods = entry.getValue();
  597. for (JMethod method : methods) {
  598. w.print("store.setInvoker(");
  599. writeClassLiteral(w, type);
  600. w.print(", \"");
  601. w.print(escape(method.getName()));
  602. w.println("\", new Invoker() {");
  603. w.indent();
  604. w.println("public Object invoke(Object target, Object[] params) {");
  605. w.indent();
  606. JType returnType = method.getReturnType();
  607. boolean hasReturnType = !"void".equals(returnType
  608. .getQualifiedSourceName());
  609. if (hasReturnType) {
  610. w.print("return ");
  611. }
  612. JType[] parameterTypes = method.getParameterTypes();
  613. w.print("((" + type.getQualifiedSourceName() + ") target)."
  614. + method.getName() + "(");
  615. for (int i = 0; i < parameterTypes.length; i++) {
  616. JType parameterType = parameterTypes[i];
  617. if (i != 0) {
  618. w.print(", ");
  619. }
  620. String parameterTypeName = getBoxedTypeName(parameterType);
  621. w.print("(" + parameterTypeName + ") params[" + i + "]");
  622. }
  623. w.println(");");
  624. if (!hasReturnType) {
  625. w.println("return null;");
  626. }
  627. w.outdent();
  628. w.println("}");
  629. w.outdent();
  630. w.println("});");
  631. w.splitIfNeeded();
  632. }
  633. }
  634. }
  635. private void writeReturnTypes(SplittingSourceWriter w,
  636. ConnectorBundle bundle) {
  637. Map<JClassType, Set<JMethod>> methodReturnTypes = bundle
  638. .getMethodReturnTypes();
  639. for (Entry<JClassType, Set<JMethod>> entry : methodReturnTypes
  640. .entrySet()) {
  641. JClassType type = entry.getKey();
  642. Set<JMethod> methods = entry.getValue();
  643. for (JMethod method : methods) {
  644. // setReturnType(Class<?> type, String methodName, Type
  645. // returnType)
  646. w.print("store.setReturnType(");
  647. writeClassLiteral(w, type);
  648. w.print(", \"");
  649. w.print(escape(method.getName()));
  650. w.print("\", ");
  651. writeTypeCreator(w, method.getReturnType());
  652. w.println(");");
  653. w.splitIfNeeded();
  654. }
  655. }
  656. }
  657. private void writeGwtConstructors(SplittingSourceWriter w,
  658. ConnectorBundle bundle) {
  659. Set<JClassType> constructors = bundle.getGwtConstructors();
  660. for (JClassType type : constructors) {
  661. w.print("store.setConstructor(");
  662. writeClassLiteral(w, type);
  663. w.println(", new Invoker() {");
  664. w.indent();
  665. w.println("public Object invoke(Object target, Object[] params) {");
  666. w.indent();
  667. w.print("return ");
  668. w.print(GWT.class.getName());
  669. w.print(".create(");
  670. writeClassLiteral(w, type);
  671. w.println(");");
  672. w.outdent();
  673. w.println("}");
  674. w.outdent();
  675. w.println("});");
  676. w.splitIfNeeded();
  677. }
  678. }
  679. public static void writeClassLiteral(SourceWriter w, JType type) {
  680. w.print(getClassLiteralString(type));
  681. }
  682. public static String getClassLiteralString(JType type) {
  683. return type.getQualifiedSourceName() + ".class";
  684. }
  685. private void writeIdentifiers(SplittingSourceWriter w,
  686. ConnectorBundle bundle) {
  687. Map<JClassType, Set<String>> identifiers = bundle.getIdentifiers();
  688. for (Entry<JClassType, Set<String>> entry : identifiers.entrySet()) {
  689. Set<String> ids = entry.getValue();
  690. JClassType type = entry.getKey();
  691. for (String id : ids) {
  692. w.print("store.setClass(\"");
  693. w.print(escape(id));
  694. w.print("\", ");
  695. writeClassLiteral(w, type);
  696. w.println(");");
  697. w.splitIfNeeded();
  698. }
  699. }
  700. }
  701. private List<ConnectorBundle> buildBundles(TreeLogger logger,
  702. TypeOracle typeOracle) throws NotFoundException,
  703. UnableToCompleteException {
  704. Map<LoadStyle, Collection<JClassType>> connectorsByLoadStyle = new HashMap<LoadStyle, Collection<JClassType>>();
  705. for (LoadStyle loadStyle : LoadStyle.values()) {
  706. connectorsByLoadStyle.put(loadStyle, new ArrayList<JClassType>());
  707. }
  708. // Find all types with a valid mapping
  709. Collection<JClassType> selectedTypes = getConnectorsForWidgetset(
  710. logger, typeOracle);
  711. // Group by load style
  712. for (JClassType connectorSubtype : selectedTypes) {
  713. LoadStyle loadStyle = getLoadStyle(connectorSubtype);
  714. if (loadStyle != null) {
  715. connectorsByLoadStyle.get(loadStyle).add(connectorSubtype);
  716. }
  717. }
  718. List<ConnectorBundle> bundles = new ArrayList<ConnectorBundle>();
  719. Collection<TypeVisitor> visitors = getVisitors(typeOracle);
  720. ConnectorBundle eagerBundle = new ConnectorBundle(
  721. ConnectorBundleLoader.EAGER_BUNDLE_NAME, visitors, typeOracle);
  722. TreeLogger eagerLogger = logger.branch(Type.TRACE,
  723. "Populating eager bundle");
  724. // Eager connectors and all RPC interfaces are loaded by default
  725. eagerBundle.processTypes(eagerLogger,
  726. connectorsByLoadStyle.get(LoadStyle.EAGER));
  727. eagerBundle.processType(eagerLogger, typeOracle
  728. .findType(UnknownComponentConnector.class.getCanonicalName()));
  729. eagerBundle.processSubTypes(eagerLogger,
  730. typeOracle.getType(ClientRpc.class.getName()));
  731. eagerBundle.processSubTypes(eagerLogger,
  732. typeOracle.getType(ServerRpc.class.getName()));
  733. bundles.add(eagerBundle);
  734. ConnectorBundle deferredBundle = new ConnectorBundle(
  735. ConnectorBundleLoader.DEFERRED_BUNDLE_NAME, eagerBundle);
  736. TreeLogger deferredLogger = logger.branch(Type.TRACE,
  737. "Populating deferred bundle");
  738. deferredBundle.processTypes(deferredLogger,
  739. connectorsByLoadStyle.get(LoadStyle.DEFERRED));
  740. bundles.add(deferredBundle);
  741. Collection<JClassType> lazy = connectorsByLoadStyle.get(LoadStyle.LAZY);
  742. for (JClassType type : lazy) {
  743. ConnectorBundle bundle = new ConnectorBundle(type.getName(),
  744. eagerBundle);
  745. TreeLogger subLogger = logger.branch(Type.TRACE, "Populating "
  746. + type.getName() + " bundle");
  747. bundle.processType(subLogger, type);
  748. bundles.add(bundle);
  749. }
  750. return bundles;
  751. }
  752. /**
  753. * Returns the connector types that should be included in the widgetset.
  754. * This method can be overridden to create a widgetset only containing
  755. * selected connectors.
  756. * <p>
  757. * The default implementation finds all type implementing
  758. * {@link ServerConnector} that have a @{@link Connect} annotation. It also
  759. * checks that multiple connectors aren't connected to the same server-side
  760. * class.
  761. *
  762. * @param logger
  763. * the logger to which information can be logged
  764. * @param typeOracle
  765. * the type oracle that can be used for finding types
  766. * @return a collection of all the connector types that should be included
  767. * in the widgetset
  768. * @throws UnableToCompleteException
  769. * if the operation fails
  770. */
  771. protected Collection<JClassType> getConnectorsForWidgetset(
  772. TreeLogger logger, TypeOracle typeOracle)
  773. throws UnableToCompleteException {
  774. JClassType serverConnectorType;
  775. try {
  776. serverConnectorType = typeOracle.getType(ServerConnector.class
  777. .getName());
  778. } catch (NotFoundException e) {
  779. logger.log(Type.ERROR,
  780. "Can't find " + ServerConnector.class.getName());
  781. throw new UnableToCompleteException();
  782. }
  783. JClassType[] types = serverConnectorType.getSubtypes();
  784. Map<String, JClassType> mappings = new HashMap<String, JClassType>();
  785. // Keep track of what has happened to avoid logging intermediate state
  786. Map<JClassType, List<JClassType>> replaced = new HashMap<JClassType, List<JClassType>>();
  787. for (JClassType type : types) {
  788. Connect connectAnnotation = type.getAnnotation(Connect.class);
  789. if (connectAnnotation == null) {
  790. continue;
  791. }
  792. String identifier = connectAnnotation.value().getCanonicalName();
  793. JClassType previousMapping = mappings.put(identifier, type);
  794. if (previousMapping != null) {
  795. // There are multiple mappings, pick the subclass
  796. JClassType subclass;
  797. JClassType superclass;
  798. if (previousMapping.isAssignableFrom(type)) {
  799. subclass = type;
  800. superclass = previousMapping;
  801. } else if (type.isAssignableFrom(previousMapping)) {
  802. subclass = previousMapping;
  803. superclass = type;
  804. } else {
  805. // Neither inherits from the other - this is a conflict
  806. logger.log(
  807. Type.ERROR,
  808. "Conflicting @Connect mappings detected for "
  809. + identifier
  810. + ": "
  811. + type.getQualifiedSourceName()
  812. + " and "
  813. + previousMapping.getQualifiedSourceName()
  814. + ". There can only be multiple @Connect mappings for the same server-side type if one is the subclass of the other.");
  815. throw new UnableToCompleteException();
  816. }
  817. mappings.put(identifier, subclass);
  818. // Inherit any previous replacements
  819. List<JClassType> previousReplacements = replaced
  820. .remove(superclass);
  821. if (previousReplacements == null) {
  822. previousReplacements = new ArrayList<JClassType>();
  823. }
  824. previousReplacements.add(superclass);
  825. replaced.put(subclass, previousReplacements);
  826. }
  827. }
  828. // Log the final set of replacements
  829. for (Entry<JClassType, List<JClassType>> entry : replaced.entrySet()) {
  830. String msg = entry.getKey().getQualifiedSourceName() + " replaces ";
  831. List<JClassType> list = entry.getValue();
  832. for (int i = 0; i < list.size(); i++) {
  833. if (i != 0) {
  834. msg += ", ";
  835. }
  836. msg += list.get(i).getQualifiedSourceName();
  837. }
  838. logger.log(Type.INFO, msg);
  839. }
  840. // Return the types of the final mapping
  841. return mappings.values();
  842. }
  843. private Collection<TypeVisitor> getVisitors(TypeOracle oracle)
  844. throws NotFoundException {
  845. List<TypeVisitor> visitors = Arrays.<TypeVisitor> asList(
  846. new ConnectorInitVisitor(), new StateInitVisitor(),
  847. new WidgetInitVisitor(), new ClientRpcVisitor(),
  848. new ServerRpcVisitor());
  849. for (TypeVisitor typeVisitor : visitors) {
  850. typeVisitor.init(oracle);
  851. }
  852. return visitors;
  853. }
  854. protected LoadStyle getLoadStyle(JClassType connectorType) {
  855. Connect annotation = connectorType.getAnnotation(Connect.class);
  856. return annotation.loadStyle();
  857. }
  858. public static String getBoxedTypeName(JType type) {
  859. if (type.isPrimitive() != null) {
  860. // Used boxed types for primitives
  861. return type.isPrimitive().getQualifiedBoxedSourceName();
  862. } else {
  863. return type.getErasedType().getQualifiedSourceName();
  864. }
  865. }
  866. public static void writeTypeCreator(SourceWriter sourceWriter, JType type) {
  867. String typeName = ConnectorBundleLoaderFactory.getBoxedTypeName(type);
  868. JParameterizedType parameterized = type.isParameterized();
  869. if (parameterized != null) {
  870. sourceWriter.print("new Type(\"" + typeName + "\", ");
  871. sourceWriter.print("new Type[] {");
  872. JClassType[] typeArgs = parameterized.getTypeArgs();
  873. for (JClassType jClassType : typeArgs) {
  874. writeTypeCreator(sourceWriter, jClassType);
  875. sourceWriter.print(", ");
  876. }
  877. sourceWriter.print("}");
  878. } else {
  879. sourceWriter.print("new Type(" + typeName + ".class");
  880. }
  881. sourceWriter.print(")");
  882. }
  883. }