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

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