1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.demo.uif.components;
17
18 import java.io.File;
19 import java.lang.reflect.Method;
20 import java.net.URL;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.Comparator;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30
31 import org.apache.commons.io.FileUtils;
32 import org.apache.commons.io.LineIterator;
33 import org.apache.commons.lang.StringUtils;
34 import org.kuali.rice.core.api.util.ConcreteKeyValue;
35 import org.kuali.rice.core.api.util.KeyValue;
36 import org.kuali.rice.krad.datadictionary.parse.BeanTag;
37 import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
38 import org.kuali.rice.krad.datadictionary.parse.BeanTags;
39 import org.kuali.rice.krad.messages.MessageService;
40 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
41 import org.kuali.rice.krad.uif.component.Component;
42 import org.kuali.rice.krad.uif.container.Group;
43 import org.kuali.rice.krad.uif.container.TabGroup;
44 import org.kuali.rice.krad.uif.control.MultiValueControl;
45 import org.kuali.rice.krad.uif.element.Header;
46 import org.kuali.rice.krad.uif.element.Message;
47 import org.kuali.rice.krad.uif.field.InputField;
48 import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle;
49 import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleRestriction;
50 import org.kuali.rice.krad.uif.util.ComponentFactory;
51 import org.kuali.rice.krad.uif.view.FormView;
52 import org.kuali.rice.krad.uif.widget.SyntaxHighlighter;
53
54
55
56
57
58
59 public class ComponentLibraryView extends FormView {
60 private static final long serialVersionUID = 3981186175467661843L;
61
62 private String rootJavadocAddress;
63 private String rootDocBookAddress;
64 private String docBookAnchor;
65 private String componentName;
66 private String javaFullClassPath;
67 private String xmlFilePath;
68 private String description;
69 private String usage;
70 private String largeExampleFieldId;
71 private SyntaxHighlighter htmlCodeViewer;
72
73 public static enum ExampleSize {
74 SMALL, LARGE, XLARGE, WINDOW;
75 }
76
77 private ExampleSize exampleSize;
78
79 private Group detailsGroup;
80
81 private ComponentExhibit exhibit;
82 private List<Group> demoGroups;
83
84
85
86
87
88
89
90 @Override
91 public void performInitialization(Object model) {
92 super.performInitialization(model);
93
94 MessageService messageService = KRADServiceLocatorWeb.getMessageService();
95
96
97 this.getPage().setHeaderText(this.getComponentName());
98
99 TabGroup tabGroup = ComponentFactory.getTabGroup();
100 List<Component> tabItems = new ArrayList<Component>();
101
102
103 Group usageGroup = ComponentFactory.getVerticalBoxGroup();
104
105
106 Header usageHeader = (Header) ComponentFactory.getNewComponentInstance("Uif-SubSectionHeader");
107 usageHeader.setHeaderLevel("H3");
108 usageHeader.setHeaderText(messageService.getMessageText("KR-SAP", null, "componentLibrary.usage"));
109 usageHeader.setRender(false);
110 usageGroup.setHeader(usageHeader);
111
112
113 List<Component> usageItems = new ArrayList<Component>();
114 Message usageMessage = ComponentFactory.getMessage();
115 usageMessage.setMessageText(usage);
116 usageItems.add(usageMessage);
117 usageItems.add(htmlCodeViewer);
118 usageGroup.setItems(usageItems);
119
120 tabItems.add(usageGroup);
121
122
123 if (javaFullClassPath != null) {
124 processDocumentationTab(tabItems);
125 }
126
127
128 tabGroup.setItems(tabItems);
129
130 tabGroup.addStyleClass("demo-componentDetailsTabs");
131
132
133 List<Component> detailsItems = new ArrayList<Component>();
134 detailsItems.addAll(detailsGroup.getItems());
135 detailsItems.add(tabGroup);
136 detailsGroup.setItems(detailsItems);
137
138
139 List<String> sourceCode = new ArrayList<String>();
140
141
142 processXmlSource(sourceCode);
143
144
145 exhibit.setDemoSourceCode(sourceCode);
146 exhibit.setDemoGroups(this.getDemoGroups());
147
148 if (this.getExampleSize() != null &&
149 (this.getExampleSize().equals(ExampleSize.LARGE) || this.getExampleSize().equals(ExampleSize.XLARGE))) {
150 exhibit.getTabGroup().addStyleClass("demo-noTabs");
151 Group headerRightGroup = ViewLifecycle.getView().getPage().getHeader().getRightGroup();
152 for (Component item : headerRightGroup.getItems()) {
153 if (item instanceof InputField && ((InputField) item).getControl() instanceof MultiValueControl && item
154 .getId().equals(this.getLargeExampleFieldId())) {
155
156 List<KeyValue> values = new ArrayList<KeyValue>();
157 for (Group demoGroup : demoGroups) {
158 values.add(new ConcreteKeyValue(demoGroup.getId(), demoGroup.getHeader().getHeaderText()));
159 }
160
161
162 ((MultiValueControl) ((InputField) item).getControl()).setOptions(values);
163 item.setRender(true);
164 }
165 }
166 }
167
168 if(this.getExampleSize() != null && this.getExampleSize().equals(ExampleSize.XLARGE)){
169 this.addStyleClass("demo-xLargeLibraryView");
170 }
171
172
173 List<Component> pageItems = new ArrayList<Component>();
174 pageItems.addAll(this.getPage().getItems());
175 pageItems.add(exhibit);
176 pageItems.add(detailsGroup);
177 this.getPage().setItems(pageItems);
178 }
179
180
181
182
183
184
185
186 private void processDocumentationTab(List<Component> tabItems) {
187 MessageService messageService = KRADServiceLocatorWeb.getMessageService();
188
189 try {
190 Class<?> componentClass = Class.forName(javaFullClassPath);
191 Method methodsArray[] = componentClass.getMethods();
192
193
194 String classMessage = messageService.getMessageText("KR-SAP", null, javaFullClassPath);
195
196 if (classMessage == null) {
197 classMessage = "NO DOCUMENTATION AVAILABLE/FOUND... we are working on it!";
198 }
199
200
201 classMessage = classMessage.replaceAll("\\{[@#]link (.*?)\\}", "<i>$1</i>");
202 classMessage = classMessage.replaceAll("\\{[@#]code (.*?)\\}", "<i>$1</i>");
203
204
205 String schemaTable =
206 "<table class='demo-schemaIdDocTable'><tr><th>Schema Name</th>" + "<th>Uif Bean Id</th></tr>";
207 if (componentClass.isAnnotationPresent(BeanTag.class)) {
208 BeanTag beanTag = componentClass.getAnnotation(BeanTag.class);
209 schemaTable = schemaTable +
210 "<tr><td>" + beanTag.name() + "</td><td>" + beanTag.parent() + "</td></tr>";
211 schemaTable = schemaTable + "</table>";
212 } else if (componentClass.isAnnotationPresent(BeanTags.class)) {
213 BeanTags beanTags = componentClass.getAnnotation(BeanTags.class);
214 BeanTag[] beanTagArray = beanTags.value();
215 for (BeanTag beanTag : beanTagArray) {
216 schemaTable = schemaTable +
217 "<tr><td>" + beanTag.name() + "</td><td>" + beanTag.parent() + "</td></tr>";
218 }
219 schemaTable = schemaTable + "</table>";
220 } else {
221 schemaTable = "";
222 }
223
224 String componentName = StringUtils.defaultIfBlank(StringUtils.defaultString(getComponentName()) + " ", "");
225
226 String javadocTitle = messageService.getMessageText("KR-SAP", null, "componentLibrary.javaDoc");
227 String kradGuideTitle = messageService.getMessageText("KR-SAP", null, "componentLibrary.kradGuide");
228 String devDocumentationTitle = messageService.getMessageText("KR-SAP", null,
229 "componentLibrary.devDocumentation");
230 String beanDefsTitle = messageService.getMessageText("KR-SAP", null, "componentLibrary.beanDefs");
231
232
233 String docLinkDiv = "<div class='demo-docLinks'> "
234 + "<label>Additional Resources:</label><a class='demo-documentationLink'"
235 + " href='"
236 + getRootJavadocAddress()
237 + javaFullClassPath.replace('.', '/')
238 + ".html' target='_blank'>"
239 + javadocTitle
240 + "</a>"
241 + "<a class='demo-documentationLink'"
242 + " href='"
243 + getRootDocBookAddress()
244 + getDocBookAnchor()
245 + "' target='_blank'>"
246 + kradGuideTitle
247 + "</a>"
248 + "</div>";
249
250
251 String documentationMessageContent =
252 "<H3 class=\"uif-documentationHeader\">" + componentName + devDocumentationTitle + "</H3>" +
253 docLinkDiv + classMessage + "<H3>" + beanDefsTitle + "</H3>" + schemaTable;
254
255 List<String> propertyDescriptions = new ArrayList<String>();
256 Map<String, List<String>> inheritedProperties = new HashMap<String, List<String>>();
257
258 List<Method> methods = Arrays.asList(methodsArray);
259
260
261 Collections.sort(methods, new Comparator<Method>() {
262 @Override
263 public int compare(Method method1, Method method2) {
264 String name1 = getPropName(method1);
265 String name2 = getPropName(method2);
266 return name1.compareTo(
267 name2);
268 }
269 });
270
271
272 for (Method method : methods) {
273 BeanTagAttribute attribute = method.getAnnotation(BeanTagAttribute.class);
274 if (attribute != null) {
275
276 String name = getPropName(method);
277 String methodClass = method.getDeclaringClass().getName();
278 String returnType = method.getReturnType().getName();
279 returnType = returnType.replaceAll("<.*?>", "");
280 String returnTypeShort = returnType.substring(returnType.lastIndexOf(".") + 1);
281
282
283 String key = methodClass + "|" + name + "|" + returnTypeShort;
284 String propertyMessage = messageService.getMessageText("KR-SAP", null, key);
285
286 if (propertyMessage == null) {
287 propertyMessage = "NO DOCUMENTATION AVAILABLE... we are working on it!";
288 }
289
290
291 propertyMessage = propertyMessage.replaceAll("\\{[@#]link (.*?)\\}", "<i>$1</i>");
292 propertyMessage = propertyMessage.replaceAll("\\{[@#]code (.*?)\\}", "<i>$1</i>");
293
294
295 if (returnType.startsWith("org.kuali")) {
296 returnTypeShort = "<a href='"
297 + getRootJavadocAddress()
298 + returnType.replace('.', '/')
299 + ".html' target='_blank'>"
300 + returnTypeShort
301 + "</a>";
302 }
303
304
305 propertyMessage = "<div class='demo-propertyItem'>"
306 + "<h4 class='demo-propertyName'>"
307 + name
308 + "</h4>"
309 + "<div class='demo-propertyType'>"
310 + returnTypeShort
311 + "</div>"
312 + "<div class='demo-propertyDesc'>"
313 + propertyMessage
314 + "</div></div>";
315
316 if (!methodClass.equals(javaFullClassPath)) {
317
318 List<String> classProperties = inheritedProperties.get(methodClass);
319 if (classProperties == null) {
320 classProperties = new ArrayList<String>();
321 }
322 classProperties.add(propertyMessage);
323 inheritedProperties.put(methodClass, classProperties);
324 } else {
325 propertyDescriptions.add(propertyMessage);
326 }
327 }
328 }
329
330 documentationMessageContent =
331 documentationMessageContent + "<H3>Properties</H3><div class='demo-propertiesContent'>";
332 for (String desc : propertyDescriptions) {
333 documentationMessageContent = documentationMessageContent + desc;
334 }
335 documentationMessageContent = documentationMessageContent + "</div>";
336
337 Group documentationGroup = ComponentFactory.getVerticalBoxGroup();
338
339
340 Header documentationHeader = (Header) ComponentFactory.getNewComponentInstance("Uif-SubSectionHeader");
341 documentationHeader.setHeaderLevel("H3");
342 documentationHeader.setHeaderText(messageService.getMessageText("KR-SAP", null,
343 "componentLibrary.documentation"));
344 documentationHeader.setRender(false);
345 documentationGroup.setHeader(documentationHeader);
346
347 List<Component> propertiesItems = new ArrayList<Component>();
348 Message propertiesMessage = ComponentFactory.getMessage();
349 propertiesMessage.setParseComponents(false);
350 propertiesMessage.setMessageText(documentationMessageContent);
351 propertiesItems.add(propertiesMessage);
352
353
354 if (!inheritedProperties.isEmpty()) {
355
356
357 for (String className : inheritedProperties.keySet()) {
358 String messageContent = "";
359 List<String> inheritedPropertyDescriptions = inheritedProperties.get(className);
360
361 for (String desc : inheritedPropertyDescriptions) {
362 messageContent = messageContent + desc;
363 }
364
365 Group iPropertiesGroup = ComponentFactory.getVerticalBoxGroup();
366
367
368 Header iPropHeader = (Header) ComponentFactory.getNewComponentInstance("Uif-SubSectionHeader");
369 iPropHeader.setHeaderLevel("H3");
370 iPropHeader.setHeaderText(messageService.getMessageText("KR-SAP", null,
371 "componentLibrary.inheritedFrom") + " " + className);
372
373 iPropertiesGroup.setHeader(iPropHeader);
374 iPropertiesGroup.getDisclosure().setRender(true);
375 iPropertiesGroup.getDisclosure().setDefaultOpen(false);
376
377 List<Component> iPropertiesItems = new ArrayList<Component>();
378 Message iPropertiesMessage = ComponentFactory.getMessage();
379 iPropertiesMessage.setParseComponents(false);
380 iPropertiesMessage.setMessageText(messageContent);
381 iPropertiesItems.add(iPropertiesMessage);
382 iPropertiesGroup.setItems(iPropertiesItems);
383
384 propertiesItems.add(iPropertiesGroup);
385 }
386 }
387
388 documentationGroup.setItems(propertiesItems);
389
390 tabItems.add(documentationGroup);
391 } catch (Exception e) {
392 throw new RuntimeException("Error loading class: " + javaFullClassPath, e);
393 }
394 }
395
396
397
398
399
400
401
402 private String getPropName(Method method) {
403 String name = method.getName();
404
405 if (name.startsWith("get")) {
406 name = name.replaceFirst("get", "");
407 } else {
408 name = name.replaceFirst("is", "");
409 }
410
411 name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
412
413 return name;
414 }
415
416
417
418
419
420
421 private void processXmlSource(List<String> sourceCode) {
422 Map<String, String> idSourceMap = new HashMap<String, String>();
423 if (xmlFilePath != null) {
424 try {
425
426 URL fileUrl = ComponentLibraryView.class.getClassLoader().getResource(xmlFilePath);
427 File file = new File(fileUrl.toURI());
428 Pattern examplePattern = Pattern.compile("ex:(.*?)(\\s|(-->))");
429
430 boolean readingSource = false;
431 String currentSource = "";
432 String currentId = "";
433
434 LineIterator lineIt = FileUtils.lineIterator(file);
435 while (lineIt.hasNext()) {
436 String line = lineIt.next();
437 if (line.contains("ex:") && !readingSource) {
438
439 readingSource = true;
440
441 Matcher matcher = examplePattern.matcher(line);
442 if (matcher.find()) {
443 currentId = matcher.group(1);
444 }
445
446 currentSource = idSourceMap.get(currentId) != null ? idSourceMap.get(currentId) : "";
447
448 if (!currentSource.isEmpty()) {
449 currentSource = currentSource + "\n";
450 }
451 } else if (line.contains("ex:") && readingSource) {
452
453 readingSource = false;
454 idSourceMap.put(currentId, currentSource);
455 } else if (readingSource) {
456
457 currentSource = currentSource + line + "\n";
458 }
459
460 }
461 } catch (Exception e) {
462 throw new RuntimeException(
463 "file not found or error while reading: " + xmlFilePath + " for source reading", e);
464 }
465 }
466
467 for (Group demoGroup : demoGroups) {
468
469 String groupId = demoGroup.getId();
470 String source = idSourceMap.get(groupId);
471 if (source != null) {
472
473 sourceCode.add(translateSource(source));
474 }
475 }
476 }
477
478
479
480
481
482
483
484
485 private String translateSource(String source) {
486
487 source = source.replace("<", "<");
488 source = source.replace(">", ">");
489 source = source.replaceAll("[ \\t]", " ");
490
491 Pattern linePattern = Pattern.compile("(( )*).*?(\\n)+");
492 Matcher matcher = linePattern.matcher(source);
493 int toRemove = -1;
494
495
496 while (matcher.find()) {
497 String spaces = matcher.group(1);
498
499 int count = StringUtils.countMatches(spaces, " ");
500 if (toRemove == -1 || count < toRemove) {
501 toRemove = count;
502 }
503 }
504
505 matcher.reset();
506 String newSource = "";
507
508
509 while (matcher.find()) {
510 String line = matcher.group();
511 newSource = newSource + line.replaceFirst("( ){" + toRemove + "}", "");
512 }
513
514
515 newSource = newSource.replaceAll("\\n$", "");
516
517 newSource = newSource.replace("\n", "
");
518
519 return newSource;
520 }
521
522
523
524
525 public ComponentLibraryView() {
526 demoGroups = new ArrayList<Group>();
527 }
528
529
530
531
532
533
534 public String getComponentName() {
535 return componentName;
536 }
537
538
539
540
541
542
543 public void setComponentName(String componentName) {
544 this.componentName = componentName;
545 }
546
547
548
549
550
551
552
553 public String getJavaFullClassPath() {
554 return javaFullClassPath;
555 }
556
557
558
559
560
561
562 public void setJavaFullClassPath(String javaFullClassPath) {
563 this.javaFullClassPath = javaFullClassPath;
564 }
565
566
567
568
569
570
571 public String getXmlFilePath() {
572 return xmlFilePath;
573 }
574
575
576
577
578
579
580 public void setXmlFilePath(String xmlFilePath) {
581 this.xmlFilePath = xmlFilePath;
582 }
583
584
585
586
587
588
589 public String getDescription() {
590 return description;
591 }
592
593
594
595
596
597
598 public void setDescription(String description) {
599 this.description = description;
600 }
601
602
603
604
605
606
607 public String getUsage() {
608 return usage;
609 }
610
611
612
613
614
615
616 public void setUsage(String usage) {
617 this.usage = usage;
618 }
619
620
621
622
623
624
625 @ViewLifecycleRestriction
626 public Group getDetailsGroup() {
627 return detailsGroup;
628 }
629
630
631
632
633
634
635 public void setDetailsGroup(Group detailsGroup) {
636 this.detailsGroup = detailsGroup;
637 }
638
639
640
641
642
643
644 @ViewLifecycleRestriction
645 public ComponentExhibit getExhibit() {
646 return exhibit;
647 }
648
649
650
651
652
653
654 public void setExhibit(ComponentExhibit exhibit) {
655 this.exhibit = exhibit;
656 }
657
658
659
660
661
662
663
664 @ViewLifecycleRestriction
665 public List<Group> getDemoGroups() {
666 return demoGroups;
667 }
668
669
670
671
672
673
674 public void setDemoGroups(List<Group> demoGroups) {
675 this.demoGroups = demoGroups;
676 }
677
678
679
680
681
682
683 public String getRootJavadocAddress() {
684 return rootJavadocAddress;
685 }
686
687
688
689
690
691
692 public void setRootJavadocAddress(String rootJavadocAddress) {
693 this.rootJavadocAddress = rootJavadocAddress;
694 }
695
696
697
698
699
700
701 public String getRootDocBookAddress() {
702 return rootDocBookAddress;
703 }
704
705
706
707
708
709
710 public void setRootDocBookAddress(String rootDocBookAddress) {
711 this.rootDocBookAddress = rootDocBookAddress;
712 }
713
714
715
716
717
718
719 public String getDocBookAnchor() {
720 if (docBookAnchor == null) {
721 return "";
722 } else {
723 return "#" + docBookAnchor;
724 }
725 }
726
727
728
729
730
731
732 public void setDocBookAnchor(String docBookAnchor) {
733 this.docBookAnchor = docBookAnchor;
734 }
735
736 public ExampleSize getExampleSize() {
737 return exampleSize;
738 }
739
740 public void setExampleSize(ExampleSize exampleSize) {
741 this.exampleSize = exampleSize;
742 }
743
744 public String getLargeExampleFieldId() {
745 return largeExampleFieldId;
746 }
747
748 public void setLargeExampleFieldId(String largeExampleFieldId) {
749 this.largeExampleFieldId = largeExampleFieldId;
750 }
751
752
753
754
755
756
757 @ViewLifecycleRestriction
758 public SyntaxHighlighter getHtmlCodeViewer() {
759 return htmlCodeViewer;
760 }
761
762
763
764
765 public void setHtmlCodeViewer(SyntaxHighlighter htmlCodeViewer) {
766 this.htmlCodeViewer = htmlCodeViewer;
767 }
768 }