1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.google.gwt.uibinder.rebind;
17
18 import java.io.PrintWriter;
19 import java.io.StringWriter;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.HashMap;
23 import java.util.LinkedList;
24 import java.util.List;
25 import java.util.Locale;
26 import java.util.Map;
27
28 import org.vectomatic.dom.svg.OMSVGElement;
29 import org.vectomatic.dom.svg.utils.SVGConstants;
30 import org.w3c.dom.Document;
31 import org.w3c.dom.Element;
32
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.JParameter;
37 import com.google.gwt.core.ext.typeinfo.JParameterizedType;
38 import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
39 import com.google.gwt.core.ext.typeinfo.JType;
40 import com.google.gwt.core.ext.typeinfo.JTypeParameter;
41 import com.google.gwt.core.ext.typeinfo.TypeOracle;
42 import com.google.gwt.dev.resource.ResourceOracle;
43 import com.google.gwt.dom.client.NativeEvent;
44 import com.google.gwt.dom.client.TagName;
45 import com.google.gwt.event.dom.client.DomEvent;
46 import com.google.gwt.event.dom.client.DomEvent.Type;
47 import com.google.gwt.resources.client.ClientBundle;
48 import com.google.gwt.resources.rg.GssResourceGenerator.GssOptions;
49 import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
50 import com.google.gwt.uibinder.attributeparsers.AttributeParsers;
51 import com.google.gwt.uibinder.client.ElementParserToUse;
52 import com.google.gwt.uibinder.client.LazyDomElement;
53 import com.google.gwt.uibinder.client.UiBinder;
54 import com.google.gwt.uibinder.client.UiHandler;
55 import com.google.gwt.uibinder.client.UiRenderer;
56 import com.google.gwt.uibinder.client.impl.AbstractUiRenderer;
57 import com.google.gwt.uibinder.elementparsers.AttributeMessageParser;
58 import com.google.gwt.uibinder.elementparsers.BeanParser;
59 import com.google.gwt.uibinder.elementparsers.ElementParser;
60 import com.google.gwt.uibinder.elementparsers.IsEmptyParser;
61 import com.google.gwt.uibinder.elementparsers.UiChildParser;
62 import com.google.gwt.uibinder.rebind.messages.MessagesWriter;
63 import com.google.gwt.uibinder.rebind.model.HtmlTemplateMethodWriter;
64 import com.google.gwt.uibinder.rebind.model.HtmlTemplatesWriter;
65 import com.google.gwt.uibinder.rebind.model.ImplicitClientBundle;
66 import com.google.gwt.uibinder.rebind.model.ImplicitCssResource;
67 import com.google.gwt.uibinder.rebind.model.OwnerClass;
68 import com.google.gwt.uibinder.rebind.model.OwnerField;
69 import com.google.gwt.user.client.ui.IsRenderable;
70 import com.google.gwt.user.client.ui.IsWidget;
71 import com.google.gwt.user.client.ui.RenderableStamper;
72
73 import org.w3c.dom.Document;
74 import org.w3c.dom.Element;
75
76 import java.beans.Introspector;
77 import java.io.PrintWriter;
78 import java.io.StringWriter;
79 import java.lang.reflect.InvocationTargetException;
80 import java.util.ArrayList;
81 import java.util.Arrays;
82 import java.util.Collection;
83 import java.util.HashMap;
84 import java.util.HashSet;
85 import java.util.Iterator;
86 import java.util.LinkedList;
87 import java.util.List;
88 import java.util.Locale;
89 import java.util.Map;
90
91
92
93
94 public class UiBinderWriter implements Statements {
95
96 private static final String SAFE_VAR_PREFIX =
97 "somethingUnlikelyToCollideWithParamNamesWefio";
98
99 private static final String UI_RENDERER_DISPATCHER_PREFIX = "UiRendererDispatcherFor";
100
101 private static final String PACKAGE_URI_SCHEME = "urn:import:";
102
103
104
105 private static final String CLIENT_BUNDLE_FIELD =
106 "clientBundleFieldNameUnlikelyToCollideWithUserSpecifiedFieldOkay";
107
108 public static String asCommaSeparatedList(String... args) {
109 StringBuilder b = new StringBuilder();
110 for (String arg : args) {
111 if (b.length() > 0) {
112 b.append(", ");
113 }
114 b.append(arg);
115 }
116
117 return b.toString();
118 }
119
120
121
122
123
124 public static String escapeAttributeText(String text) {
125 text = escapeText(text, false);
126
127
128
129
130
131 text = text.replaceAll("'", "'");
132 return text;
133 }
134
135
136
137
138
139 public static String escapeText(String text, boolean preserveWhitespace) {
140
141
142
143 text = text.replaceAll("&", "&");
144 text = text.replaceAll("<", "<");
145 text = text.replaceAll(">", ">");
146
147 if (!preserveWhitespace) {
148 text = text.replaceAll("\\s+", " ");
149 }
150
151 return escapeTextForJavaStringLiteral(text);
152 }
153
154
155
156
157
158 public static String escapeTextForJavaStringLiteral(String text) {
159 text = text.replace("\\", "\\\\");
160 text = text.replace("\"", "\\\"");
161 text = text.replace("\n", "\\n");
162
163 return text;
164 }
165
166
167
168
169
170
171
172
173 static Iterable<JClassType> getClassHierarchyBreadthFirst(JClassType type) {
174 LinkedList<JClassType> list = new LinkedList<JClassType>();
175 LinkedList<JClassType> q = new LinkedList<JClassType>();
176
177 q.add(type);
178 while (!q.isEmpty()) {
179
180 JClassType curType = q.removeFirst();
181 list.add(curType);
182
183
184
185 for (JClassType intf : curType.getImplementedInterfaces()) {
186 q.add(intf);
187 }
188
189 JClassType superClass = curType.getSuperclass();
190 if (superClass != null) {
191 q.add(superClass);
192 }
193 }
194
195 return list;
196 }
197
198 private static String capitalizePropName(String propName) {
199 return propName.substring(0, 1).toUpperCase(Locale.ROOT) + propName.substring(1);
200 }
201
202
203
204
205 private static JMethod[] findEventMethods(JClassType type) {
206 List<JMethod> methods = new ArrayList<JMethod>(Arrays.asList(type.getInheritableMethods()));
207
208 for (Iterator<JMethod> iterator = methods.iterator(); iterator.hasNext();) {
209 JMethod jMethod = iterator.next();
210 if (!jMethod.getName().equals("onBrowserEvent")) {
211 iterator.remove();
212 }
213 }
214
215 return methods.toArray(new JMethod[methods.size()]);
216 }
217
218
219
220
221
222
223 private static List<JMethod> findGetterNames(JClassType owner) {
224 List<JMethod> ret = new ArrayList<JMethod>();
225 for (JMethod jMethod : owner.getInheritableMethods()) {
226 String getterName = jMethod.getName();
227 if (getterName.startsWith("get")) {
228 ret.add(jMethod);
229 }
230 }
231 return ret;
232 }
233
234
235
236
237
238
239 private static JParameter[] findRenderParameters(JClassType owner) {
240 JMethod[] methods = owner.getInheritableMethods();
241 JMethod renderMethod = null;
242
243 for (JMethod jMethod : methods) {
244 if (jMethod.getName().equals("render")) {
245 renderMethod = jMethod;
246 }
247 }
248
249 JParameter[] parameters = renderMethod.getParameters();
250 return Arrays.copyOfRange(parameters, 1, parameters.length);
251 }
252
253
254
255
256 private static JMethod[] findUiHandlerMethods(JClassType type) {
257 ArrayList<JMethod> result = new ArrayList<JMethod>();
258 JMethod[] allMethods = type.getInheritableMethods();
259
260 for (JMethod jMethod : allMethods) {
261 if (jMethod.getAnnotation(UiHandler.class) != null) {
262 result.add(jMethod);
263 }
264 }
265
266 return result.toArray(new JMethod[result.size()]);
267 }
268
269 private static String formatMethodError(JMethod eventMethod) {
270 return "\"" + eventMethod.getReadableDeclaration(true, true, true, true, true) + "\""
271 + " of " + eventMethod.getEnclosingType().getQualifiedSourceName();
272 }
273
274
275
276
277
278 private static String getterToFieldName(String name) {
279 String fieldName = name.substring(3);
280 return Introspector.decapitalize(fieldName);
281 }
282
283 private static String renderMethodParameters(JParameter[] renderParameters) {
284 StringBuilder builder = new StringBuilder();
285
286 for (int i = 0; i < renderParameters.length; i++) {
287 JParameter parameter = renderParameters[i];
288 builder.append("final ");
289 builder.append(parameter.getType().getQualifiedSourceName());
290 builder.append(" ");
291 builder.append(parameter.getName());
292 if (i < renderParameters.length - 1) {
293 builder.append(", ");
294 }
295 }
296
297 return builder.toString();
298 }
299
300 private final MortalLogger logger;
301
302
303
304
305
306 private final Map<String, String> elementParsers = new HashMap<String, String>();
307
308 private final List<String> initStatements = new ArrayList<String>();
309 private final List<String> statements = new ArrayList<String>();
310 private final HandlerEvaluator handlerEvaluator;
311 private final MessagesWriter messages;
312 private final DesignTimeUtils designTime;
313 private final Tokenator tokenator = new Tokenator();
314
315 private final String templatePath;
316 private final TypeOracle oracle;
317
318
319
320 private final JClassType baseClass;
321
322
323
324
325 private final String implClassName;
326
327 private final JClassType uiOwnerType;
328
329 private final JClassType uiRootType;
330
331 private final JClassType isRenderableClassType;
332
333 private final JClassType lazyDomElementClass;
334
335 private final OwnerClass ownerClass;
336
337 private final FieldManager fieldManager;
338
339 private final HtmlTemplatesWriter htmlTemplates;
340
341 private final ImplicitClientBundle bundleClass;
342
343 private final boolean useLazyWidgetBuilders;
344
345 private final boolean useSafeHtmlTemplates;
346
347 private int domId = 0;
348
349 private int fieldIndex;
350
351 private String gwtPrefix;
352
353 private int renderableStamper = 0;
354
355 private String rendered;
356
357
358
359 private final LinkedList<String> attachSectionElements = new LinkedList<String>();
360
361
362
363 private final Map<String, String> attachedVars = new HashMap<String, String>();
364
365 private int nextAttachVar = 0;
366
367
368
369
370 private final LinkedList<List<String>> detachStatementsStack = new LinkedList<List<String>>();
371 private final AttributeParsers attributeParsers;
372
373 private final UiBinderContext uiBinderCtx;
374
375 private final String binderUri;
376 private final boolean isRenderer;
377
378 private final ResourceOracle resourceOracle;
379
380 private final GssOptions gssOptions;
381
382 public UiBinderWriter(JClassType baseClass, String implClassName, String templatePath,
383 TypeOracle oracle, MortalLogger logger, FieldManager fieldManager,
384 MessagesWriter messagesWriter, DesignTimeUtils designTime, UiBinderContext uiBinderCtx,
385 boolean useSafeHtmlTemplates, boolean useLazyWidgetBuilders, String binderUri,
386 ResourceOracle resourceOracle, GssOptions gssOptions) throws UnableToCompleteException {
387 this.baseClass = baseClass;
388 this.implClassName = implClassName;
389 this.oracle = oracle;
390 this.logger = logger;
391 this.templatePath = templatePath;
392 this.fieldManager = fieldManager;
393 this.messages = messagesWriter;
394 this.designTime = designTime;
395 this.uiBinderCtx = uiBinderCtx;
396 this.useSafeHtmlTemplates = useSafeHtmlTemplates;
397 this.useLazyWidgetBuilders = useLazyWidgetBuilders;
398 this.binderUri = binderUri;
399 this.resourceOracle = resourceOracle;
400 this.gssOptions = gssOptions;
401
402 this.htmlTemplates = new HtmlTemplatesWriter(fieldManager, logger);
403
404
405 JClassType uibinderItself = oracle.findType(UiBinder.class.getCanonicalName());
406 if (uibinderItself.equals(baseClass)) {
407 die("You must use a subtype of UiBinder in GWT.create(). E.g.,\n"
408 + " interface Binder extends UiBinder<Widget, MyClass> {}\n"
409 + " GWT.create(Binder.class);");
410 }
411
412 JClassType[] uiBinderTypes = baseClass.getImplementedInterfaces();
413 if (uiBinderTypes.length == 0) {
414 throw new RuntimeException("No implemented interfaces for " + baseClass.getName());
415 }
416 JClassType uiBinderType = uiBinderTypes[0];
417
418 JClassType[] typeArgs = uiBinderType.isParameterized() == null
419 ? new JClassType[0] : uiBinderType.isParameterized().getTypeArgs();
420
421 String binderType = uiBinderType.getName();
422
423 JClassType uiRendererClass = getOracle().findType(UiRenderer.class.getName());
424 if (uiBinderType.isAssignableTo(uibinderItself)) {
425 if (typeArgs.length < 2) {
426 throw new RuntimeException("Root and owner type parameters are required for type %s"
427 + binderType);
428 }
429 uiRootType = typeArgs[0];
430 uiOwnerType = typeArgs[1];
431 isRenderer = false;
432 } else if (uiBinderType.isAssignableTo(uiRendererClass)) {
433 if (typeArgs.length >= 1) {
434 throw new RuntimeException("UiRenderer is not a parameterizable type in " + binderType);
435 }
436 if (!useSafeHtmlTemplates) {
437 die("Configuration property UiBinder.useSafeHtmlTemplates\n"
438 + " must be set to true to generate a UiRenderer");
439 }
440 if (!useLazyWidgetBuilders) {
441 die("Configuration property UiBinder.useLazyWidgetBuilders\n"
442 + " must be set to true to generate a UiRenderer");
443 }
444
445
446 uiOwnerType = uiBinderType;
447 uiRootType = null;
448 isRenderer = true;
449 } else {
450 die(baseClass.getName() + " must implement UiBinder or UiRenderer");
451
452 throw new UnableToCompleteException();
453 }
454
455 isRenderableClassType = oracle.findType(IsRenderable.class.getCanonicalName());
456 lazyDomElementClass = oracle.findType(LazyDomElement.class.getCanonicalName());
457
458 ownerClass = new OwnerClass(uiOwnerType, logger, uiBinderCtx);
459 bundleClass =
460 new ImplicitClientBundle(baseClass.getPackage().getName(), this.implClassName,
461 CLIENT_BUNDLE_FIELD, logger);
462 handlerEvaluator = new HandlerEvaluator(ownerClass, logger, oracle, useLazyWidgetBuilders);
463
464 attributeParsers = new AttributeParsers(oracle, fieldManager, logger);
465 }
466
467
468
469
470
471
472
473
474
475
476 public void addDetachStatement(String format, Object... args) {
477 detachStatementsStack.getFirst().add(String.format(format, args));
478 }
479
480
481
482
483
484 public void addInitStatement(String format, Object... params) {
485 initStatements.add(formatCode(format, params));
486 }
487
488
489
490
491
492 public void addStatement(String format, Object... args) {
493 String code = formatCode(format, args);
494
495 if (useLazyWidgetBuilders) {
496
497
498
499
500
501
502 int idx = code.indexOf(".");
503 String fieldName = code.substring(0, idx);
504 fieldManager.require(fieldName).addStatement(format, args);
505 } else {
506 statements.add(code);
507 }
508 }
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523 public void beginAttachedSection(String element) {
524 attachSectionElements.addFirst(element);
525 detachStatementsStack.addFirst(new ArrayList<String>());
526 }
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541 public String declareDomField(XMLElement source, String fieldName, String ancestorField)
542 throws UnableToCompleteException {
543 ensureAttached();
544 String name = declareDomIdHolder(fieldName);
545
546 if (useLazyWidgetBuilders) {
547
548 FieldWriter field = fieldManager.require(fieldName);
549
550
551
552
553
554
555 if (isOwnerFieldLazyDomElement(fieldName)) {
556 field.setInitializer(formatCode("new %s(%s)", field.getQualifiedSourceName(),
557 fieldManager.convertFieldToGetter(name)));
558 } else {
559
560 field.setInitializer(formatCode("new %s(%s).get().cast()",
561 LazyDomElement.class.getCanonicalName(), fieldManager.convertFieldToGetter(name)));
562
563
564 fieldManager.require(ancestorField).addAttachStatement(
565 fieldManager.convertFieldToGetter(fieldName) + ";");
566 }
567 } else {
568 setFieldInitializer(fieldName, "null");
569 addInitStatement("%s = com.google.gwt.dom.client.Document.get().getElementById(%s).cast();",
570 fieldName, name);
571 addInitStatement("%s.removeAttribute(\"id\");", fieldName);
572 }
573
574 return tokenForStringExpression(source, fieldManager.convertFieldToGetter(name));
575 }
576
577
578
579
580
581
582
583
584
585
586
587 public String declareDomIdHolder(String fieldName) throws UnableToCompleteException {
588 String domHolderName = "domId" + domId++;
589 FieldWriter domField =
590 fieldManager.registerField(FieldWriterType.DOM_ID_HOLDER,
591 oracle.findType(String.class.getName()), domHolderName);
592 if (isRenderer && fieldName != null) {
593 domField.setInitializer("buildInnerId(\"" + fieldName + "\", uiId)");
594 } else {
595 domField.setInitializer("com.google.gwt.dom.client.Document.get().createUniqueId()");
596 }
597
598 return domHolderName;
599 }
600
601
602
603
604
605
606
607
608 public String declareFieldIfNeeded(XMLElement elem) throws UnableToCompleteException {
609 String fieldName = getFieldName(elem);
610 if (fieldName != null) {
611
612
613
614
615
616 if (useLazyWidgetBuilders && isOwnerFieldLazyDomElement(fieldName)) {
617 fieldManager.registerFieldForLazyDomElement(findFieldType(elem),
618 ownerClass.getUiField(fieldName));
619 } else {
620 fieldManager.registerField(findFieldType(elem), fieldName);
621 }
622 }
623 return fieldName;
624 }
625
626
627
628
629
630
631
632
633 public String declareRenderableStamper() throws UnableToCompleteException {
634 String renderableStamperName = "renderableStamper" + renderableStamper++;
635 FieldWriter domField =
636 fieldManager.registerField(FieldWriterType.RENDERABLE_STAMPER,
637 oracle.findType(RenderableStamper.class.getName()), renderableStamperName);
638 domField.setInitializer(formatCode(
639 "new %s(com.google.gwt.dom.client.Document.get().createUniqueId())",
640 RenderableStamper.class.getName()));
641
642 return renderableStamperName;
643 }
644
645
646
647
648
649
650
651 public String declareTemplateCall(String html, String fieldName) throws IllegalArgumentException {
652 if (!useSafeHtmlTemplates) {
653 return '"' + html + '"';
654 }
655 FieldWriter w = fieldManager.lookup(fieldName);
656 HtmlTemplateMethodWriter templateMethod = htmlTemplates.addSafeHtmlTemplate(html, tokenator);
657 if (useLazyWidgetBuilders) {
658 w.setHtml(templateMethod.getIndirectTemplateCall());
659 } else {
660 w.setHtml(templateMethod.getDirectTemplateCall());
661 }
662 return w.getHtml();
663 }
664
665
666
667
668
669
670
671
672
673 public String detokenate(String betokened) {
674 return tokenator.detokenate(betokened);
675 }
676
677
678
679
680
681 public void die(String message) throws UnableToCompleteException {
682 logger.die(message);
683 }
684
685
686
687
688
689 public void die(String message, Object... params) throws UnableToCompleteException {
690 logger.die(message, params);
691 }
692
693
694
695
696
697 public void die(XMLElement context, String message, Object... params)
698 throws UnableToCompleteException {
699 logger.die(context, message, params);
700 }
701
702
703
704
705
706
707
708 public void endAttachedSection() {
709 String elementVar = attachSectionElements.removeFirst();
710 List<String> detachStatements = detachStatementsStack.removeFirst();
711 if (attachedVars.containsKey(elementVar)) {
712 String attachedVar = attachedVars.remove(elementVar);
713 addInitStatement("%s.detach();", attachedVar);
714 for (String statement : detachStatements) {
715 addInitStatement(statement);
716 }
717 }
718 }
719
720
721
722
723
724
725 public void ensureAttached() {
726 String attachSectionElement = attachSectionElements.getFirst();
727 if (!attachedVars.containsKey(attachSectionElement)) {
728 String attachedVar = "attachRecord" + nextAttachVar;
729 addInitStatement("UiBinderUtil.TempAttachment %s = UiBinderUtil.attachToDom(%s);",
730 attachedVar, attachSectionElement);
731 attachedVars.put(attachSectionElement, attachedVar);
732 nextAttachVar++;
733 }
734 }
735
736
737
738
739
740
741
742
743 public void ensureCurrentFieldAttached() {
744 ensureAttached();
745 }
746
747
748
749
750
751
752
753
754 public JClassType findFieldType(XMLElement elem) throws UnableToCompleteException {
755 String tagName = elem.getLocalName();
756
757
758 String uri = elem.getNamespaceUri();
759 if (SVGConstants.SVG_NAMESPACE_URI.equals(uri)) {
760 return findSvgDomElementTypeForTag(tagName);
761 }
762
763
764 if (!isImportedElement(elem)) {
765 return findDomElementTypeForTag(tagName);
766 }
767
768 String ns = elem.getNamespaceUri();
769 String packageName = ns.substring(PACKAGE_URI_SCHEME.length());
770 String className = tagName;
771
772 while (true) {
773 JClassType rtn = getOracle().findType(packageName + "." + className);
774 if (rtn != null) {
775 return rtn;
776 }
777
778
779
780 int index = className.indexOf(".");
781 if (index == -1) {
782 die(elem, "No class matching \"%s\" in %s", tagName, ns);
783 }
784 packageName = packageName + "." + className.substring(0, index);
785 className = className.substring(index + 1);
786 }
787 }
788
789
790
791
792
793
794 private JClassType findSvgDomElementTypeForTag(String tag) {
795 JClassType elementClass = oracle.findType(OMSVGElement.class.getName());
796 JClassType[] types = elementClass.getSubtypes();
797 for (JClassType type : types) {
798 TagName annotation = type.getAnnotation(TagName.class);
799 if (annotation != null) {
800 for (String annotationTag : annotation.value()) {
801 if (annotationTag.equals(tag)) {
802 return type;
803 }
804 }
805 }
806 }
807
808 return elementClass;
809 }
810
811
812
813
814
815
816 public void genPropertySet(String fieldName, String propName, String value) {
817 addStatement("%1$s.set%2$s(%3$s);", fieldName, capitalizePropName(propName), value);
818 }
819
820
821
822
823 public void genStringPropertySet(String fieldName, String propName, String value) {
824 genPropertySet(fieldName, propName, "\"" + value + "\"");
825 }
826
827
828
829
830 public JClassType getBaseClass() {
831 return baseClass;
832 }
833
834 public ImplicitClientBundle getBundleClass() {
835 return bundleClass;
836 }
837
838
839
840
841 public DesignTimeUtils getDesignTime() {
842 return designTime;
843 }
844
845 public FieldManager getFieldManager() {
846 return fieldManager;
847 }
848
849
850
851
852
853 public MortalLogger getLogger() {
854 return logger;
855 }
856
857
858
859
860 public MessagesWriter getMessages() {
861 return messages;
862 }
863
864
865
866
867 public TypeOracle getOracle() {
868 return oracle;
869 }
870
871 public OwnerClass getOwnerClass() {
872 return ownerClass;
873 }
874
875 public String getUiFieldAttributeName() {
876 return gwtPrefix + ":field";
877 }
878
879 public boolean isBinderElement(XMLElement elem) {
880 String uri = elem.getNamespaceUri();
881 return uri != null && binderUri.equals(uri);
882 }
883
884 public boolean isElementAssignableTo(XMLElement elem, Class<?> possibleSuperclass)
885 throws UnableToCompleteException {
886 JClassType classType = oracle.findType(possibleSuperclass.getCanonicalName());
887 return isElementAssignableTo(elem, classType);
888 }
889
890 public boolean isElementAssignableTo(XMLElement elem, JClassType possibleSupertype)
891 throws UnableToCompleteException {
892
893
894
895 JTypeParameter typeParameter = possibleSupertype.isTypeParameter();
896 if (typeParameter != null) {
897 JClassType[] bounds = typeParameter.getBounds();
898 for (JClassType bound : bounds) {
899 if (!isElementAssignableTo(elem, bound)) {
900 return false;
901 }
902 }
903 return true;
904 }
905
906
907
908
909
910
911 JParameterizedType parameterized = possibleSupertype.isParameterized();
912 if (parameterized != null) {
913 return isElementAssignableTo(elem, parameterized.getRawType());
914 }
915
916 JClassType fieldtype = findFieldType(elem);
917 if (fieldtype == null) {
918 return false;
919 }
920 return fieldtype.isAssignableTo(possibleSupertype);
921 }
922
923 public boolean isImportedElement(XMLElement elem) {
924 String uri = elem.getNamespaceUri();
925 return uri != null && uri.startsWith(PACKAGE_URI_SCHEME);
926 }
927
928
929
930
931 public boolean isOwnerFieldLazyDomElement(String fieldName) {
932 OwnerField ownerField = ownerClass.getUiField(fieldName);
933 if (ownerField == null) {
934 return false;
935 }
936
937 return lazyDomElementClass.isAssignableFrom(ownerField.getType().getRawType());
938 }
939
940 public boolean isRenderableElement(XMLElement elem) throws UnableToCompleteException {
941 return findFieldType(elem).isAssignableTo(isRenderableClassType);
942 }
943
944 public boolean isRenderer() {
945 return isRenderer;
946 }
947
948 public boolean isWidgetElement(XMLElement elem) throws UnableToCompleteException {
949 return isElementAssignableTo(elem, IsWidget.class);
950 }
951
952
953
954
955
956
957
958
959
960 public FieldWriter parseElementToField(XMLElement elem) throws UnableToCompleteException {
961 if (elementParsers.isEmpty()) {
962 registerParsers();
963 }
964
965
966 JClassType type = findFieldType(elem);
967
968
969 FieldWriter field = declareField(elem, type.getQualifiedSourceName());
970
971
972
973
974
975
976
977
978 fieldManager.push(elem, field);
979
980
981 for (ElementParser parser : getParsersForClass(type)) {
982 parser.parse(elem, field.getName(), type, this);
983 }
984 fieldManager.pop();
985
986 return field;
987 }
988
989
990
991
992
993
994
995 public void setFieldInitializer(String fieldName, String factoryMethod) {
996 fieldManager.lookup(fieldName).setInitializer(factoryMethod);
997 }
998
999
1000
1001
1002
1003
1004
1005
1006 public void setFieldInitializerAsConstructor(String fieldName, String... args) {
1007 JClassType assignableType = fieldManager.lookup(fieldName).getAssignableType();
1008 setFieldInitializer(fieldName, formatCode("new %s(%s)", assignableType.getQualifiedSourceName(),
1009 asCommaSeparatedList(args)));
1010 }
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021 public String tokenForSafeConstant(XMLElement source, String expression) {
1022 if (!useSafeHtmlTemplates) {
1023 return tokenForStringExpression(source, expression);
1024 }
1025
1026 expression = "SafeHtmlUtils.fromSafeConstant(" + expression + ")";
1027 htmlTemplates.noteSafeConstant(expression);
1028 return nextToken(source, expression);
1029 }
1030
1031
1032
1033
1034
1035
1036
1037 public String tokenForSafeHtmlExpression(XMLElement source, String expression) {
1038 if (!useSafeHtmlTemplates) {
1039 return tokenForStringExpression(source, expression + ".asString()");
1040 }
1041
1042 htmlTemplates.noteSafeConstant(expression);
1043 return nextToken(source, expression);
1044 }
1045
1046
1047
1048
1049
1050
1051
1052 public String tokenForSafeUriExpression(XMLElement source, String expression) {
1053 if (!useSafeHtmlTemplates) {
1054 return tokenForStringExpression(source, expression);
1055 }
1056
1057 htmlTemplates.noteUri(expression);
1058 return nextToken(source, expression);
1059 }
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071 public String tokenForStringExpression(XMLElement source, String expression) {
1072 return nextToken(source, "\" + " + expression + " + \"");
1073 }
1074
1075 public boolean useLazyWidgetBuilders() {
1076 return useLazyWidgetBuilders;
1077 }
1078
1079
1080
1081
1082 public boolean useSafeHtmlTemplates() {
1083 return useSafeHtmlTemplates;
1084 }
1085
1086
1087
1088
1089 public void warn(String message) {
1090 logger.warn(message);
1091 }
1092
1093
1094
1095
1096 public void warn(String message, Object... params) {
1097 logger.warn(message, params);
1098 }
1099
1100
1101
1102
1103 public void warn(XMLElement context, String message, Object... params) {
1104 logger.warn(context, message, params);
1105 }
1106
1107
1108
1109
1110
1111
1112
1113
1114 void parseDocument(Document doc, PrintWriter printWriter) throws UnableToCompleteException {
1115 Element documentElement = doc.getDocumentElement();
1116 gwtPrefix = documentElement.lookupPrefix(binderUri);
1117
1118 XMLElement elem =
1119 new XMLElementProviderImpl(attributeParsers, oracle, logger, designTime).get(documentElement);
1120 this.rendered = tokenator.detokenate(parseDocumentElement(elem));
1121 printWriter.print(rendered);
1122 }
1123
1124 private void addElementParser(String gwtClass, String parser) {
1125 elementParsers.put(gwtClass, parser);
1126 }
1127
1128 private void addWidgetParser(String className) {
1129 String gwtClass = "com.google.gwt.user.client.ui." + className;
1130 String parser = "com.google.gwt.uibinder.elementparsers." + className + "Parser";
1131 addElementParser(gwtClass, parser);
1132 }
1133
1134
1135
1136
1137
1138
1139 private FieldWriter declareField(XMLElement source, String typeName)
1140 throws UnableToCompleteException {
1141 JClassType type = oracle.findType(typeName);
1142 if (type == null) {
1143 die(source, "Unknown type %s", typeName);
1144 }
1145
1146 String fieldName = getFieldName(source);
1147 if (fieldName == null) {
1148
1149
1150
1151
1152 fieldName = "f_" + source.getLocalName() + ++fieldIndex;
1153 }
1154 fieldName = normalizeFieldName(fieldName);
1155 return fieldManager.registerField(type, fieldName);
1156 }
1157
1158 private void dieGettingEventTypeName(JMethod jMethod, Exception e)
1159 throws UnableToCompleteException {
1160 die("Could not obtain DomEvent.Type object for first parameter of %s (%s)",
1161 formatMethodError(jMethod), e.getMessage());
1162 }
1163
1164
1165
1166
1167
1168
1169
1170 private void ensureAttachmentCleanedUp() {
1171 if (!attachSectionElements.isEmpty()) {
1172 throw new IllegalStateException("Attachments not cleaned up: " + attachSectionElements);
1173 }
1174 if (!detachStatementsStack.isEmpty()) {
1175 throw new IllegalStateException("Detach not cleaned up: " + detachStatementsStack);
1176 }
1177 }
1178
1179
1180
1181
1182
1183 private void ensureInjectedCssFields() {
1184 for (ImplicitCssResource css : bundleClass.getCssMethods()) {
1185 String fieldName = css.getName();
1186 FieldWriter cssField = fieldManager.require(fieldName);
1187 cssField.addStatement("%s.ensureInjected();", fieldName);
1188 }
1189 }
1190
1191
1192
1193
1194
1195 private void evaluateUiFields() throws UnableToCompleteException {
1196 if (designTime.isDesignTime()) {
1197 return;
1198 }
1199 for (OwnerField ownerField : getOwnerClass().getUiFields()) {
1200 String fieldName = ownerField.getName();
1201 FieldWriter fieldWriter = fieldManager.lookup(fieldName);
1202
1203 if (fieldWriter == null) {
1204 die("Template %s has no %s attribute for %s.%s#%s", templatePath,
1205 getUiFieldAttributeName(), uiOwnerType.getPackage().getName(), uiOwnerType.getName(),
1206 fieldName);
1207 }
1208 }
1209 }
1210
1211
1212
1213
1214 private JClassType findDomElementTypeForTag(String tag) {
1215 JClassType elementClass = oracle.findType("com.google.gwt.dom.client.Element");
1216 JClassType[] types = elementClass.getSubtypes();
1217 for (JClassType type : types) {
1218 TagName annotation = type.getAnnotation(TagName.class);
1219 if (annotation != null) {
1220 for (String annotationTag : annotation.value()) {
1221 if (annotationTag.equals(tag)) {
1222 return type;
1223 }
1224 }
1225 }
1226 }
1227 return elementClass;
1228 }
1229
1230
1231
1232
1233 private String findEventTypeName(JMethod jMethod)
1234 throws UnableToCompleteException {
1235
1236 String eventTypeName = jMethod.getParameterTypes()[0].getQualifiedSourceName();
1237
1238 Class<?> domType;
1239
1240
1241 try {
1242 domType = Class.forName(eventTypeName);
1243 } catch (ClassNotFoundException e) {
1244 die("Could not find type %s in %s", eventTypeName, formatMethodError(jMethod));
1245 return null;
1246 }
1247
1248
1249 try {
1250 return ((Type<?>) domType.getMethod("getType", (Class[]) null).invoke(null,
1251 (Object[]) null)).getName();
1252 } catch (IllegalArgumentException e) {
1253 dieGettingEventTypeName(jMethod, e);
1254 } catch (SecurityException e) {
1255 dieGettingEventTypeName(jMethod, e);
1256 } catch (IllegalAccessException e) {
1257 dieGettingEventTypeName(jMethod, e);
1258 } catch (InvocationTargetException e) {
1259 dieGettingEventTypeName(jMethod, e);
1260 } catch (NoSuchMethodException e) {
1261 dieGettingEventTypeName(jMethod, e);
1262 }
1263
1264 return null;
1265 }
1266
1267
1268
1269
1270
1271 private String formatCode(String format, Object... params) {
1272 String r = String.format(Locale.US, format, params);
1273 return r;
1274 }
1275
1276
1277
1278
1279
1280
1281
1282 private String getFieldName(XMLElement elem) throws UnableToCompleteException {
1283 String fieldName = null;
1284 boolean hasOldSchoolId = false;
1285 if (elem.hasAttribute("id") && isWidgetElement(elem)) {
1286 hasOldSchoolId = true;
1287
1288 fieldName = elem.consumeRawAttribute("id");
1289 warn(elem, "Deprecated use of id=\"%1$s\" for field name. "
1290 + "Please switch to gwt:field=\"%1$s\" instead. " + "This will soon be a compile error!",
1291 fieldName);
1292 }
1293 if (elem.hasAttribute(getUiFieldAttributeName())) {
1294 if (hasOldSchoolId) {
1295 die(elem, "Cannot declare both id and field on the same element");
1296 }
1297 fieldName = elem.consumeRawAttribute(getUiFieldAttributeName());
1298 }
1299 return fieldName;
1300 }
1301
1302
1303 private String getAnnotatedParserForClass(JClassType uiClass) {
1304 String parserClassName = null;
1305 if (uiClass.isAnnotationPresent(ElementParserToUse.class)) {
1306 String uiClassName = uiClass.getQualifiedSourceName();
1307 parserClassName = uiClass.getAnnotation(ElementParserToUse.class).className();
1308 elementParsers.put(uiClassName, parserClassName);
1309 }
1310 return parserClassName;
1311 }
1312
1313
1314 private Class<? extends ElementParser> getParserForClass(JClassType uiClass) {
1315
1316 String uiClassName = uiClass.getQualifiedSourceName();
1317 String parserClassName = elementParsers.get(uiClassName);
1318 if (parserClassName == null) {
1319
1320 parserClassName = getAnnotatedParserForClass(uiClass);
1321 if (parserClassName == null) {
1322 return null;
1323 }
1324
1325 }
1326
1327
1328 try {
1329 return Class.forName(parserClassName).asSubclass(ElementParser.class);
1330 } catch (ClassNotFoundException e) {
1331 throw new RuntimeException("Unable to instantiate parser", e);
1332 } catch (ClassCastException e) {
1333 throw new RuntimeException(parserClassName + " must extend ElementParser");
1334 }
1335 }
1336
1337
1338
1339
1340
1341
1342 private Iterable<ElementParser> getParsersForClass(JClassType type) {
1343 List<ElementParser> parsers = new ArrayList<ElementParser>();
1344
1345
1346
1347
1348
1349
1350
1351
1352 parsers.add(new AttributeMessageParser());
1353 parsers.add(new UiChildParser(uiBinderCtx));
1354
1355 for (JClassType curType : getClassHierarchyBreadthFirst(type)) {
1356 try {
1357 Class<? extends ElementParser> cls = getParserForClass(curType);
1358 if (cls != null) {
1359 ElementParser parser = cls.newInstance();
1360 parsers.add(parser);
1361 }
1362 } catch (InstantiationException e) {
1363 throw new RuntimeException("Unable to instantiate " + curType.getName(), e);
1364 } catch (IllegalAccessException e) {
1365 throw new RuntimeException("Unable to instantiate " + curType.getName(), e);
1366 }
1367 }
1368
1369 parsers.add(new BeanParser(uiBinderCtx));
1370 parsers.add(new IsEmptyParser());
1371
1372 return parsers;
1373 }
1374
1375
1376
1377
1378
1379 private void maybeWriteFieldSetter(IndentedWriter niceWriter, OwnerField ownerField,
1380 JClassType templateClass, String templateField) throws UnableToCompleteException {
1381 JClassType fieldType = ownerField.getType().getRawType();
1382
1383 if (!ownerField.isProvided()) {
1384
1385
1386
1387
1388 if (!templateClass.isAssignableTo(fieldType)) {
1389 die("In @UiField %s, template field and owner field types don't match: %s is not assignable to %s",
1390 ownerField.getName(), templateClass.getQualifiedSourceName(),
1391 fieldType.getQualifiedSourceName());
1392 }
1393
1394
1395
1396 niceWriter.write("owner.%1$s = %2$s;", ownerField.getName(), templateField);
1397 } else {
1398
1399
1400
1401
1402 if (!fieldType.isAssignableTo(templateClass)) {
1403 die("In UiField(provided = true) %s, template field and field types don't match: "
1404 + "@UiField(provided=true)%s is not assignable to %s", ownerField.getName(),
1405 fieldType.getQualifiedSourceName(), templateClass.getQualifiedSourceName());
1406 }
1407 }
1408 }
1409
1410 private String nextToken(XMLElement source, String expression) {
1411 String nextToken = tokenator.nextToken(source, expression);
1412 return nextToken;
1413 }
1414
1415 private String normalizeFieldName(String fieldName) {
1416
1417
1418
1419 return fieldName.replace('.', '$');
1420 }
1421
1422
1423
1424
1425
1426 private String parseDocumentElement(XMLElement elem) throws UnableToCompleteException {
1427 fieldManager.registerFieldOfGeneratedType(oracle.findType(ClientBundle.class.getName()),
1428 bundleClass.getPackageName(), bundleClass.getClassName(), bundleClass.getFieldName());
1429
1430
1431
1432 FieldWriter rootField = new UiBinderParser(this, messages, fieldManager, oracle, bundleClass,
1433 binderUri, uiBinderCtx, resourceOracle, gssOptions).parse(elem);
1434
1435 fieldManager.validate();
1436
1437 StringWriter stringWriter = new StringWriter();
1438 IndentedWriter niceWriter = new IndentedWriter(new PrintWriter(stringWriter));
1439
1440 if (isRenderer) {
1441 ensureInjectedCssFields();
1442 writeRenderer(niceWriter, rootField);
1443 } else if (useLazyWidgetBuilders) {
1444 ensureInjectedCssFields();
1445 writeBinderForRenderableStrategy(niceWriter, rootField);
1446 } else {
1447 writeBinder(niceWriter, rootField);
1448 }
1449 ensureAttachmentCleanedUp();
1450 return stringWriter.toString();
1451 }
1452
1453 private void registerParsers() {
1454
1455
1456
1457 addElementParser("com.google.gwt.dom.client.Element",
1458 "com.google.gwt.uibinder.elementparsers.DomElementParser");
1459
1460
1461 addWidgetParser("UIObject");
1462 addWidgetParser("HasText");
1463 addWidgetParser("HasHTML");
1464 addWidgetParser("HasTreeItems");
1465 addWidgetParser("HasWidgets");
1466 addWidgetParser("HTMLPanel");
1467 addWidgetParser("FlowPanel");
1468 addWidgetParser("AbsolutePanel");
1469 addWidgetParser("DockPanel");
1470 addWidgetParser("StackPanel");
1471 addWidgetParser("DisclosurePanel");
1472 addWidgetParser("TabPanel");
1473 addWidgetParser("MenuItem");
1474 addWidgetParser("MenuBar");
1475 addWidgetParser("CellPanel");
1476 addWidgetParser("CustomButton");
1477 addWidgetParser("DialogBox");
1478 addWidgetParser("LayoutPanel");
1479 addWidgetParser("DockLayoutPanel");
1480 addWidgetParser("StackLayoutPanel");
1481 addWidgetParser("TabLayoutPanel");
1482 addWidgetParser("Image");
1483 addWidgetParser("ListBox");
1484 addWidgetParser("Grid");
1485 addWidgetParser("HasAlignment");
1486 addWidgetParser("DateLabel");
1487 addWidgetParser("NumberLabel");
1488 if (useLazyWidgetBuilders) {
1489 addWidgetParser("LazyPanel");
1490 addWidgetParser("RenderablePanel");
1491 }
1492 }
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505 private void validateEventMethod(JMethod eventMethod) throws UnableToCompleteException {
1506 JParameter[] parameters = eventMethod.getParameters();
1507 if (parameters.length < 3) {
1508 die("Too few parameters in %s",
1509 formatMethodError(eventMethod));
1510 }
1511
1512 String nativeEventName = NativeEvent.class.getCanonicalName();
1513 JClassType nativeEventType = oracle.findType(nativeEventName);
1514 if (!nativeEventType.equals(parameters[1].getType())) {
1515 die("Second parameter must be of type %s in %s", nativeEventName,
1516 formatMethodError(eventMethod));
1517 }
1518
1519 String elementName = com.google.gwt.dom.client.Element.class.getCanonicalName();
1520 JClassType elementType = oracle.findType(elementName);
1521 if (!elementType.equals(parameters[2].getType())) {
1522 die("Third parameter must be of type %s in %s", elementName,
1523 formatMethodError(eventMethod));
1524 }
1525
1526 if (parameters[0].getType().isClassOrInterface() == null) {
1527 die("First parameter must be a class or interface in %s",
1528 formatMethodError(eventMethod));
1529 }
1530
1531 JClassType eventReceiver = parameters[0].getType().isClassOrInterface();
1532
1533 validateEventReceiver(parameters, eventReceiver, eventMethod);
1534 }
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550 private void validateEventReceiver(JParameter[] onBrowserEventParameters,
1551 JClassType eventReceiver, JMethod sourceMethod)
1552 throws UnableToCompleteException {
1553
1554
1555 JType[] onBrowserEventParamTypes = new JType[onBrowserEventParameters.length - 2];
1556
1557
1558 onBrowserEventParamTypes[0] = oracle.findType(com.google.gwt.dom.client.Element.class
1559 .getCanonicalName());
1560
1561 for (int i = 3; i < onBrowserEventParameters.length; i++) {
1562 onBrowserEventParamTypes[i - 2] = onBrowserEventParameters[i].getType();
1563 }
1564
1565 for (JMethod jMethod : eventReceiver.getInheritableMethods()) {
1566 Class<UiHandler> annotationClass = UiHandler.class;
1567 UiHandler annotation = jMethod.getAnnotation(annotationClass);
1568
1569 if (annotation == null) {
1570 continue;
1571 }
1572
1573 String[] fields = annotation.value();
1574 if (fields == null) {
1575 die("@UiHandler returns null from its value in %s",
1576 formatMethodError(jMethod));
1577 }
1578 for (String fieldName : fields) {
1579 FieldWriter field = fieldManager.lookup(fieldName);
1580 if (field == null) {
1581 die("\"%s\" is not a known field name as listed in the @UiHandler annotation in %s",
1582 fieldName, formatMethodError(jMethod));
1583 }
1584 }
1585
1586
1587 JParameter[] eventHandlerParameters = jMethod.getParameters();
1588 JClassType domEventType = oracle.findType(DomEvent.class.getCanonicalName());
1589 JClassType firstParamType = eventHandlerParameters[0].getType().isClassOrInterface();
1590 if (firstParamType == null || !firstParamType.isAssignableTo(domEventType)) {
1591 die("First parameter must be assignable to com.google.gwt.dom.client.DomEvent in %s",
1592 formatMethodError(jMethod));
1593 }
1594
1595
1596 if (onBrowserEventParamTypes.length < eventHandlerParameters.length - 1) {
1597 die("Too many parameters in %s", formatMethodError(jMethod));
1598 }
1599 for (int i = 1; i < eventHandlerParameters.length; i++) {
1600 if (!eventHandlerParameters[i].getType().equals(onBrowserEventParamTypes[i - 1])) {
1601 die("Parameter %s in %s is not of the same type as parameter %s in %s",
1602 eventHandlerParameters[i].getName(), formatMethodError(jMethod),
1603 onBrowserEventParameters[i + 1].getName(),
1604 formatMethodError(sourceMethod));
1605 }
1606 }
1607 }
1608 }
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619 private void validateRendererGetters(JClassType owner) throws UnableToCompleteException {
1620 for (JMethod jMethod : owner.getInheritableMethods()) {
1621 String getterName = jMethod.getName();
1622 if (getterName.startsWith("get")) {
1623 String fieldName = getterToFieldName(getterName);
1624 FieldWriter field = fieldManager.lookup(fieldName);
1625 if (field == null || (!FieldWriterType.DEFAULT.equals(field.getFieldType())
1626 && !FieldWriterType.GENERATED_CSS.equals(field.getFieldType()))) {
1627 die("%s does not match a \"ui:field='%s'\" declaration in %s, "
1628 + "or '%s' refers to something other than a ui:style"
1629 + " or an HTML element in the template", getterName, fieldName,
1630 owner.getQualifiedSourceName(), fieldName);
1631 }
1632 if (FieldWriterType.DEFAULT.equals(field.getFieldType())
1633 && jMethod.getParameterTypes().length != 1) {
1634 die("Field getter %s must have exactly one parameter in %s", getterName,
1635 owner.getQualifiedSourceName());
1636 } else if (FieldWriterType.GENERATED_CSS.equals(field.getFieldType())
1637 && jMethod.getParameterTypes().length != 0) {
1638 die("Style getter %s must have no parameters in %s", getterName,
1639 owner.getQualifiedSourceName());
1640 } else if (jMethod.getParameterTypes().length == 1) {
1641 String elementClassName = com.google.gwt.dom.client.Element.class.getCanonicalName();
1642 JClassType elementType = oracle.findType(elementClassName);
1643 JClassType getterParamType =
1644 jMethod.getParameterTypes()[0].getErasedType().isClassOrInterface();
1645
1646 if (!elementType.isAssignableFrom(getterParamType)) {
1647 die("Getter %s must have exactly one parameter of type assignable to %s in %s",
1648 getterName, elementClassName, owner.getQualifiedSourceName());
1649 }
1650 }
1651 } else if (!getterName.equals("render") && !getterName.equals("onBrowserEvent")
1652 && !getterName.equals("isParentOrRenderer")) {
1653 die("Unexpected method \"%s\" found in %s", getterName, owner.getQualifiedSourceName());
1654 }
1655 }
1656 }
1657
1658
1659
1660
1661
1662
1663 private void validateRenderParameters(JClassType owner) throws UnableToCompleteException {
1664 JMethod[] methods = owner.getInheritableMethods();
1665 JMethod renderMethod = null;
1666
1667 for (JMethod jMethod : methods) {
1668 if (jMethod.getName().equals("render")) {
1669 if (renderMethod == null) {
1670 renderMethod = jMethod;
1671 } else {
1672 die("%s declares more than one method named render", owner.getQualifiedSourceName());
1673 }
1674 }
1675 }
1676
1677 if (renderMethod == null
1678 || renderMethod.getParameterTypes().length < 1
1679 || !renderMethod.getParameterTypes()[0].getErasedType().getQualifiedSourceName().equals(
1680 SafeHtmlBuilder.class.getCanonicalName())) {
1681 die("%s does not declare a render(SafeHtmlBuilder ...) method",
1682 owner.getQualifiedSourceName());
1683 }
1684 if (!JPrimitiveType.VOID.equals(renderMethod.getReturnType())) {
1685 die("%s#render(SafeHtmlBuilder ...) does not return void", owner.getQualifiedSourceName());
1686 }
1687 }
1688
1689
1690
1691
1692
1693
1694 private void writeAddedStatements(IndentedWriter niceWriter) {
1695 for (String s : statements) {
1696 niceWriter.write(s);
1697 }
1698 }
1699
1700
1701
1702
1703 private void writeBinder(IndentedWriter w, FieldWriter rootField) throws UnableToCompleteException {
1704 writePackage(w);
1705
1706 writeImports(w);
1707 w.newline();
1708
1709 writeClassOpen(w);
1710 writeStatics(w);
1711 w.newline();
1712
1713
1714 writeTemplatesInterface(w);
1715 w.newline();
1716
1717
1718 w.write("public %s createAndBindUi(final %s owner) {",
1719 uiRootType.getParameterizedQualifiedSourceName(),
1720 uiOwnerType.getParameterizedQualifiedSourceName());
1721 w.indent();
1722 w.newline();
1723
1724 writeGwtFields(w);
1725 w.newline();
1726
1727 designTime.writeAttributes(this);
1728 writeAddedStatements(w);
1729 w.newline();
1730
1731 writeInitStatements(w);
1732 w.newline();
1733
1734 writeHandlers(w);
1735 w.newline();
1736
1737 writeOwnerFieldSetters(w);
1738
1739 writeCssInjectors(w);
1740
1741 w.write("return %s;", rootField.getNextReference());
1742 w.outdent();
1743 w.write("}");
1744
1745
1746 w.outdent();
1747 w.write("}");
1748 }
1749
1750
1751
1752
1753 private void writeBinderForRenderableStrategy(IndentedWriter w, FieldWriter rootField)
1754 throws UnableToCompleteException {
1755 writePackage(w);
1756
1757 writeImports(w);
1758 w.newline();
1759
1760 writeClassOpen(w);
1761 writeStatics(w);
1762 w.newline();
1763
1764 writeTemplatesInterface(w);
1765
1766 w.newline();
1767
1768
1769 w.write("public %s createAndBindUi(final %s owner) {",
1770 uiRootType.getParameterizedQualifiedSourceName(),
1771 uiOwnerType.getParameterizedQualifiedSourceName());
1772 w.indent();
1773 w.newline();
1774
1775 designTime.writeAttributes(this);
1776 w.newline();
1777
1778 w.write("return new Widgets(owner).%s;", rootField.getNextReference());
1779 w.outdent();
1780 w.write("}");
1781
1782
1783 w.newline();
1784 w.write("/**");
1785 w.write(" * Encapsulates the access to all inner widgets");
1786 w.write(" */");
1787 w.write("class Widgets {");
1788 w.indent();
1789
1790 String ownerClassType = uiOwnerType.getParameterizedQualifiedSourceName();
1791 w.write("private final %s owner;", ownerClassType);
1792 w.newline();
1793
1794 writeHandlers(w);
1795 w.newline();
1796
1797 w.write("public Widgets(final %s owner) {", ownerClassType);
1798 w.indent();
1799 w.write("this.owner = owner;");
1800 fieldManager.initializeWidgetsInnerClass(w, getOwnerClass());
1801 w.outdent();
1802 w.write("}");
1803 w.newline();
1804
1805 htmlTemplates.writeTemplateCallers(w);
1806
1807 evaluateUiFields();
1808
1809 fieldManager.writeFieldDefinitions(w, getOracle(), getOwnerClass(), getDesignTime());
1810
1811 w.outdent();
1812 w.write("}");
1813
1814
1815 w.outdent();
1816 w.write("}");
1817 }
1818
1819 private void writeClassOpen(IndentedWriter w) {
1820 if (!isRenderer) {
1821 w.write("public class %s implements UiBinder<%s, %s>, %s {", implClassName,
1822 uiRootType.getParameterizedQualifiedSourceName(),
1823 uiOwnerType.getParameterizedQualifiedSourceName(),
1824 baseClass.getParameterizedQualifiedSourceName());
1825 } else {
1826 w.write("public class %s extends %s implements %s {", implClassName,
1827 AbstractUiRenderer.class.getName(),
1828 baseClass.getParameterizedQualifiedSourceName());
1829 }
1830 w.indent();
1831 }
1832
1833 private void writeCssInjectors(IndentedWriter w) {
1834 for (ImplicitCssResource css : bundleClass.getCssMethods()) {
1835 w.write("%s.%s().ensureInjected();", bundleClass.getFieldName(), css.getName());
1836 }
1837 w.newline();
1838 }
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848 private void writeGwtFields(IndentedWriter niceWriter) throws UnableToCompleteException {
1849
1850 Collection<OwnerField> ownerFields = getOwnerClass().getUiFields();
1851 for (OwnerField ownerField : ownerFields) {
1852 if (ownerField.isProvided()) {
1853 String fieldName = ownerField.getName();
1854 FieldWriter fieldWriter = fieldManager.lookup(fieldName);
1855
1856
1857 if (fieldWriter != null) {
1858 String initializer;
1859 if (designTime.isDesignTime()) {
1860 String typeName = ownerField.getType().getRawType().getQualifiedSourceName();
1861 initializer = designTime.getProvidedField(typeName, ownerField.getName());
1862 } else {
1863 initializer = formatCode("owner.%1$s", fieldName);
1864 }
1865 fieldManager.lookup(fieldName).setInitializer(initializer);
1866 }
1867 }
1868 }
1869
1870 fieldManager.writeGwtFieldsDeclaration(niceWriter);
1871 }
1872
1873 private void writeHandlers(IndentedWriter w) throws UnableToCompleteException {
1874 if (designTime.isDesignTime()) {
1875 return;
1876 }
1877 handlerEvaluator.run(w, fieldManager, "owner");
1878 }
1879
1880 private void writeImports(IndentedWriter w) {
1881 w.write("import com.google.gwt.core.client.GWT;");
1882 w.write("import com.google.gwt.dom.client.Element;");
1883 if (!(htmlTemplates.isEmpty())) {
1884 w.write("import com.google.gwt.safehtml.client.SafeHtmlTemplates;");
1885 w.write("import com.google.gwt.safehtml.shared.SafeHtml;");
1886 w.write("import com.google.gwt.safehtml.shared.SafeHtmlUtils;");
1887 w.write("import com.google.gwt.safehtml.shared.SafeHtmlBuilder;");
1888 w.write("import com.google.gwt.safehtml.shared.SafeUri;");
1889 w.write("import com.google.gwt.safehtml.shared.UriUtils;");
1890 w.write("import com.google.gwt.uibinder.client.UiBinderUtil;");
1891 }
1892
1893 if (!isRenderer) {
1894 w.write("import com.google.gwt.uibinder.client.UiBinder;");
1895 w.write("import com.google.gwt.uibinder.client.UiBinderUtil;");
1896 w.write("import %s.%s;", uiRootType.getPackage().getName(), uiRootType.getName());
1897 } else {
1898 w.write("import com.google.gwt.text.shared.AbstractSafeHtmlRenderer;");
1899 }
1900 }
1901
1902
1903
1904
1905
1906 private void writeInitStatements(IndentedWriter niceWriter) {
1907 for (String s : initStatements) {
1908 niceWriter.write(s);
1909 }
1910 }
1911
1912
1913
1914
1915 private void writeOwnerFieldSetters(IndentedWriter niceWriter) throws UnableToCompleteException {
1916 if (designTime.isDesignTime()) {
1917 return;
1918 }
1919 for (OwnerField ownerField : getOwnerClass().getUiFields()) {
1920 String fieldName = ownerField.getName();
1921 FieldWriter fieldWriter = fieldManager.lookup(fieldName);
1922
1923 if (fieldWriter != null) {
1924
1925 JClassType type = fieldWriter.getInstantiableType();
1926 if (type != null) {
1927 maybeWriteFieldSetter(niceWriter, ownerField, fieldWriter.getInstantiableType(),
1928 fieldName);
1929 } else {
1930
1931 if (!ownerField.isProvided()) {
1932 niceWriter.write("owner.%1$s = %1$s;", fieldName);
1933 }
1934 }
1935
1936 } else {
1937
1938 die("Template %s has no %s attribute for %s.%s#%s", templatePath,
1939 getUiFieldAttributeName(), uiOwnerType.getPackage().getName(), uiOwnerType.getName(),
1940 fieldName);
1941 }
1942 }
1943 }
1944
1945 private void writePackage(IndentedWriter w) {
1946 String packageName = baseClass.getPackage().getName();
1947 if (packageName.length() > 0) {
1948 w.write("package %1$s;", packageName);
1949 w.newline();
1950 }
1951 }
1952
1953
1954
1955
1956 private void writeRenderer(IndentedWriter w, FieldWriter rootField) throws UnableToCompleteException {
1957 validateRendererGetters(baseClass);
1958 validateRenderParameters(baseClass);
1959 JMethod[] eventMethods = findEventMethods(baseClass);
1960 for (JMethod jMethod : eventMethods) {
1961 validateEventMethod(jMethod);
1962 }
1963
1964 writePackage(w);
1965
1966 writeImports(w);
1967 w.newline();
1968
1969 writeClassOpen(w);
1970 writeStatics(w);
1971 w.newline();
1972
1973
1974 writeTemplatesInterface(w);
1975 w.newline();
1976 htmlTemplates.writeTemplateCallers(w);
1977
1978 w.newline();
1979
1980 JParameter[] renderParameters = findRenderParameters(baseClass);
1981 for (JParameter param : renderParameters) {
1982
1983 fieldManager.disableOptimization(param.getName());
1984 }
1985
1986
1987 w.write("public %s() {", implClassName);
1988 w.indent();
1989 w.write("build_fields();");
1990 w.outdent();
1991
1992 w.write("}");
1993 w.newline();
1994
1995
1996 w.write("private void build_fields() {");
1997 w.indent();
1998 fieldManager.initializeWidgetsInnerClass(w, getOwnerClass());
1999 w.outdent();
2000
2001 w.write("}");
2002 w.newline();
2003
2004 String renderParameterDeclarations = renderMethodParameters(renderParameters);
2005 w.write("public void render(final %s sb%s%s) {", SafeHtmlBuilder.class.getName(),
2006 renderParameterDeclarations.length() != 0 ? ", " : "", renderParameterDeclarations);
2007 w.indent();
2008 w.newline();
2009
2010 writeRenderParameterInitializers(w, renderParameters);
2011
2012 w.write("uiId = com.google.gwt.dom.client.Document.get().createUniqueId();");
2013 w.newline();
2014
2015 w.write("build_fields();");
2016 w.newline();
2017
2018 String safeHtml = rootField.getSafeHtml();
2019
2020
2021
2022 w.write(
2023 "sb.append(stampUiRendererAttribute(%s, RENDERED_ATTRIBUTE, uiId));",
2024 safeHtml);
2025 w.outdent();
2026
2027 w.write("}");
2028 w.newline();
2029
2030 fieldManager.writeFieldDefinitions(w, getOracle(), getOwnerClass(), getDesignTime());
2031
2032 writeRendererGetters(w, baseClass, rootField.getName());
2033
2034 writeRendererEventMethods(w, eventMethods, rootField.getName());
2035
2036
2037 w.outdent();
2038 w.write("}");
2039 }
2040
2041 private void writeRendererDispatcher(IndentedWriter w, String dispatcherName,
2042 JClassType targetType, String rootFieldName, JMethod[] uiHandlerMethods, JMethod sourceMethod)
2043 throws UnableToCompleteException {
2044
2045
2046 w.write("static class %s extends UiRendererDispatcher<%s> {", dispatcherName,
2047 targetType.getQualifiedSourceName());
2048 w.indent();
2049
2050 writeRendererDispatcherTableInit(w, rootFieldName, uiHandlerMethods,
2051 dispatcherName);
2052
2053 writeRendererDispatcherExtraParameters(w, sourceMethod);
2054
2055 writeRendererDispatcherFire(w, sourceMethod);
2056
2057 w.write("@SuppressWarnings(\"rawtypes\")");
2058 w.write("@Override");
2059
2060 w.write("public void fireEvent(com.google.gwt.event.shared.GwtEvent<?> %sEvent) {",
2061 SAFE_VAR_PREFIX);
2062 w.indent();
2063
2064 w.write("switch (getMethodIndex()) {");
2065 w.indent();
2066 for (int j = 0; j < uiHandlerMethods.length; j++) {
2067 JMethod uiMethod = uiHandlerMethods[j];
2068
2069
2070 w.write("case %s:", j);
2071 w.indent();
2072
2073
2074
2075 StringBuffer sb = new StringBuffer();
2076 JParameter[] sourceParameters = sourceMethod.getParameters();
2077
2078 JType[] uiHandlerParameterTypes = uiMethod.getParameterTypes();
2079 if (uiHandlerParameterTypes.length >= 2) {
2080 sb.append(", getRoot()");
2081 }
2082 for (int k = 2; k < uiHandlerParameterTypes.length; k++) {
2083 JParameter sourceParam = sourceParameters[k + 1];
2084 sb.append(", ");
2085 sb.append(sourceParam.getName());
2086 }
2087 w.write("getEventTarget().%s((%s) %sEvent%s);", uiMethod.getName(),
2088 uiHandlerParameterTypes[0].getQualifiedSourceName(), SAFE_VAR_PREFIX,
2089 sb.toString());
2090
2091 w.write("break;");
2092 w.newline();
2093 w.outdent();
2094 }
2095
2096 w.write("default:");
2097 w.indent();
2098
2099 w.write("break;");
2100 w.outdent();
2101 w.outdent();
2102 w.write("}");
2103
2104 w.outdent();
2105 w.write("}");
2106
2107 w.outdent();
2108 w.write("}");
2109 }
2110
2111 private void writeRendererDispatcherExtraParameters(IndentedWriter w, JMethod sourceMethod) {
2112 for (int i = 3; i < sourceMethod.getParameters().length; i++) {
2113 JParameter param = sourceMethod.getParameters()[i];
2114
2115
2116
2117 w.write("private %s %s;", param.getType().getParameterizedQualifiedSourceName(),
2118 param.getName());
2119 }
2120 }
2121
2122 private void writeRendererDispatcherFire(IndentedWriter w, JMethod sourceMethod) {
2123
2124 w.write("public void fire(");
2125 w.indent();
2126 JParameter[] sourceParameters = sourceMethod.getParameters();
2127 for (int i = 0; i < sourceParameters.length; i++) {
2128 JParameter param = sourceParameters[i];
2129 w.write(i == 0 ? "%s %s" : ", %s %s", param.getType().getQualifiedSourceName(), param.getName());
2130 }
2131 w.write(") {");
2132 w.indent();
2133
2134
2135 for (int i = 3; i < sourceParameters.length; i++) {
2136 JParameter sourceParam = sourceParameters[i];
2137 w.write("this.%s = %s;", sourceParam.getName(), sourceParam.getName());
2138 }
2139
2140
2141 w.write("fireEvent(%s, %s, %s);", sourceParameters[0].getName(), sourceParameters[1].getName(),
2142 sourceParameters[2].getName());
2143
2144 w.outdent();
2145 w.write("}");
2146 w.newline();
2147 }
2148
2149 private void writeRendererDispatcherTableInit(IndentedWriter w,
2150 String rootFieldName, JMethod[] uiHandlerMethods, String dispatcherName)
2151 throws UnableToCompleteException {
2152 ArrayList<String> keys = new ArrayList<String>();
2153 ArrayList<Integer> values = new ArrayList<Integer>();
2154
2155
2156 for (int i = 0; i < uiHandlerMethods.length; i++) {
2157 JMethod jMethod = uiHandlerMethods[i];
2158 String eventType = findEventTypeName(jMethod);
2159 String[] fieldNames = jMethod.getAnnotation(UiHandler.class).value();
2160 for (String fieldName : fieldNames) {
2161 if (rootFieldName.equals(fieldName)) {
2162 fieldName = AbstractUiRenderer.ROOT_FAKE_NAME;
2163 }
2164 keys.add(eventType + AbstractUiRenderer.UI_ID_SEPARATOR + fieldName);
2165 values.add(i);
2166 }
2167 }
2168
2169
2170 w.write("private static String[] %s_keys;", SAFE_VAR_PREFIX);
2171
2172 w.write("private static Integer[] %s_values;", SAFE_VAR_PREFIX);
2173
2174 w.write("static {");
2175 w.indent();
2176
2177 w.write("%s_keys = new String[] {", SAFE_VAR_PREFIX);
2178 w.indent();
2179 for (String key : keys) {
2180
2181 w.write("\"%s\",", key);
2182 }
2183 w.outdent();
2184 w.write("};");
2185 w.newline();
2186
2187
2188 w.write("%s_values = new Integer[] {", SAFE_VAR_PREFIX);
2189 w.indent();
2190 StringBuffer commaSeparatedValues = new StringBuffer();
2191 for (Integer value : values) {
2192 commaSeparatedValues.append(value);
2193 commaSeparatedValues.append(",");
2194 }
2195
2196 w.write("%s", commaSeparatedValues.toString());
2197 w.outdent();
2198 w.write("};");
2199 w.newline();
2200
2201 w.outdent();
2202 w.write("}");
2203 w.newline();
2204
2205
2206 w.write("public %s() {", dispatcherName);
2207 w.indent();
2208
2209 w.write("initDispatchTable(%s_keys, %s_values);", SAFE_VAR_PREFIX, SAFE_VAR_PREFIX);
2210
2211
2212
2213 HashSet<String> eventTypes = new HashSet<String>();
2214 for (JMethod uiMethod : uiHandlerMethods) {
2215 eventTypes.add(uiMethod.getParameterTypes()[0].getQualifiedSourceName());
2216 }
2217 for (String eventType : eventTypes) {
2218 w.write("%s.getType();", eventType);
2219 }
2220
2221
2222 w.outdent();
2223 w.write("}");
2224 w.newline();
2225 }
2226
2227 private void writeRendererEventMethods(IndentedWriter w, JMethod[] eventMethods,
2228 String rootField) throws UnableToCompleteException {
2229 for (JMethod jMethod : eventMethods) {
2230 JClassType eventTargetType = jMethod.getParameterTypes()[0].isClassOrInterface();
2231 String eventTargetSimpleName = eventTargetType.getSimpleSourceName();
2232 String dispatcherClassName = UI_RENDERER_DISPATCHER_PREFIX + eventTargetSimpleName;
2233 JMethod[] uiHandlerMethods = findUiHandlerMethods(eventTargetType);
2234
2235
2236 w.write("@Override");
2237 w.write("public %s {", jMethod.getReadableDeclaration(true, true, true, true, true));
2238
2239 if (uiHandlerMethods.length != 0) {
2240 w.indent();
2241
2242 w.write("if (singleton%s == null) {", dispatcherClassName);
2243 w.indent();
2244
2245 w.write("singleton%s = new %s();", dispatcherClassName, dispatcherClassName);
2246
2247 w.outdent();
2248 w.write("}");
2249
2250
2251 StringBuffer sb = new StringBuffer();
2252 JParameter[] parameters = jMethod.getParameters();
2253 for (int i = 0; i < parameters.length; i++) {
2254 JParameter callParam = parameters[i];
2255 if (i != 0) {
2256 sb.append(", ");
2257 }
2258 sb.append(callParam.getName());
2259 }
2260 w.write("singleton%s.fire(%s);", dispatcherClassName, sb.toString());
2261 w.outdent();
2262 }
2263
2264 w.write("}");
2265 w.newline();
2266
2267 if (uiHandlerMethods.length != 0) {
2268
2269 w.write("private static %s singleton%s;", dispatcherClassName, dispatcherClassName);
2270
2271 writeRendererDispatcher(w, dispatcherClassName, eventTargetType, rootField, uiHandlerMethods,
2272 jMethod);
2273 }
2274 }
2275 }
2276
2277 private void writeRendererGetters(IndentedWriter w, JClassType owner, String rootFieldName) {
2278 List<JMethod> getters = findGetterNames(owner);
2279
2280
2281 for (JMethod getter : getters) {
2282
2283 w.write("%s {", getter.getReadableDeclaration(false, false, false, false, true));
2284 w.indent();
2285 String getterFieldName = getterToFieldName(getter.getName());
2286
2287 FieldWriter fieldWriter = fieldManager.lookup(getterFieldName);
2288 if (FieldWriterType.GENERATED_CSS.equals(fieldWriter.getFieldType())) {
2289
2290 w.write("return (%s) %s;", getter.getReturnType().getErasedType().getQualifiedSourceName(),
2291 FieldManager.getFieldGetter(getterFieldName));
2292 } else {
2293
2294 String elementParameter = getter.getParameters()[0].getName();
2295 if (!getterFieldName.equals(rootFieldName)) {
2296
2297 w.write("return (%s) findInnerField(%s, \"%s\", RENDERED_ATTRIBUTE);",
2298 getter.getReturnType().getErasedType().getQualifiedSourceName(), elementParameter,
2299 getterFieldName);
2300 } else {
2301
2302 w.write("return (%s) findRootElement(%s, RENDERED_ATTRIBUTE);",
2303 getter.getReturnType().getErasedType().getQualifiedSourceName(), elementParameter);
2304 }
2305 }
2306 w.outdent();
2307 w.write("}");
2308 }
2309 }
2310
2311 private void writeRenderParameterInitializers(IndentedWriter w, JParameter[] renderParameters) {
2312 for (int i = 0; i < renderParameters.length; i++) {
2313 JParameter parameter = renderParameters[i];
2314 if (fieldManager.lookup(parameter.getName()) != null) {
2315 w.write("this.%s = %s;", parameter.getName(), parameter.getName());
2316 w.newline();
2317 }
2318 }
2319 }
2320
2321 private void writeStaticMessagesInstance(IndentedWriter niceWriter) {
2322 if (messages.hasMessages()) {
2323 niceWriter.write(messages.getDeclaration());
2324 }
2325 }
2326
2327 private void writeStatics(IndentedWriter w) {
2328 writeStaticMessagesInstance(w);
2329 designTime.addDeclarations(w);
2330 }
2331
2332
2333
2334
2335
2336 private void writeTemplatesInterface(IndentedWriter w) {
2337 if (!(htmlTemplates.isEmpty())) {
2338 assert useSafeHtmlTemplates : "SafeHtml is off, but templates were made.";
2339 htmlTemplates.writeInterface(w);
2340 w.newline();
2341 }
2342 }
2343 }