1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.student.datadictionary.util;
17
18 import java.io.File;
19 import java.io.FileNotFoundException;
20 import java.io.FileOutputStream;
21 import java.io.PrintStream;
22 import java.text.BreakIterator;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.Date;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Stack;
30 import org.apache.commons.lang.StringEscapeUtils;
31
32 import org.kuali.student.contract.model.MessageStructure;
33 import org.kuali.student.contract.model.ServiceContractModel;
34 import org.kuali.student.contract.model.XmlType;
35 import org.kuali.student.contract.model.util.ModelFinder;
36 import org.kuali.student.contract.writer.XmlWriter;
37
38
39
40
41
42 public class KradDictionaryCreator {
43
44 private ServiceContractModel model;
45 private ModelFinder finder;
46 private String directory;
47 private String className;
48 private XmlType xmlType;
49 private XmlWriter gwriter;
50 private XmlWriter mwriter;
51 private List<MessageStructure> messageStructures;
52 private boolean writeManual;
53 private boolean writeGenerated;
54
55 public KradDictionaryCreator(String directory,
56 ServiceContractModel model, String className, boolean writeManual, boolean writeGenerated) {
57 this.directory = directory;
58 this.model = model;
59 this.finder = new ModelFinder(this.model);
60 this.className = className;
61 this.xmlType = this.finder.findXmlType(className);
62 if (xmlType == null) {
63 throw new IllegalArgumentException(className);
64 }
65 this.messageStructures = this.finder.findMessageStructures(className);
66 this.writeManual = writeManual;
67 this.writeGenerated = writeGenerated;
68
69
70
71 }
72
73 public void write() {
74 this.initXmlWriters();
75 if (writeGenerated) {
76 this.writeSpringHeaderOpen(gwriter);
77 this.writeWarning(gwriter);
78 this.writeGeneratedImports(gwriter);
79 this.writeGeneratedObjectStructure(gwriter);
80 this.writeSpringHeaderClose(gwriter);
81 }
82 if (this.writeManual) {
83 this.writeSpringHeaderOpen(mwriter);
84 this.writeNote(mwriter);
85 this.writeManualImports(mwriter);
86 this.writeManualObjectStructure(mwriter);
87 this.writeSpringHeaderClose(mwriter);
88 }
89 }
90
91 private void initXmlWriters() {
92 String generatedFileName = "/ks-" + initUpper(className) + "-dictionary-generated.xml";
93 String manualFileName = "/ks-" + initUpper(className) + "-dictionary.xml";
94
95 File dir = new File(this.directory);
96
97
98 if (!dir.exists()) {
99 if (!dir.mkdirs()) {
100 throw new IllegalStateException("Could not create directory "
101 + this.directory);
102 }
103 }
104
105 if (writeGenerated) {
106 try {
107 PrintStream out = new PrintStream(new FileOutputStream(this.directory + "/" + generatedFileName, false));
108 this.gwriter = new XmlWriter(out, 0);
109 } catch (FileNotFoundException ex) {
110 throw new IllegalStateException(ex);
111 }
112 }
113 if (this.writeManual) {
114 try {
115 PrintStream out = new PrintStream(new FileOutputStream(this.directory + "/" + manualFileName, false));
116 this.mwriter = new XmlWriter(out, 0);
117 } catch (FileNotFoundException ex) {
118 throw new IllegalStateException(ex);
119 }
120 }
121 }
122
123 private static String initLower(String str) {
124 if (str == null) {
125 return null;
126 }
127 if (str.length() == 0) {
128 return str;
129 }
130 if (str.length() == 1) {
131 return str.toLowerCase();
132 }
133 return str.substring(0, 1).toLowerCase() + str.substring(1);
134 }
135
136 private static String initUpper(String str) {
137 if (str == null) {
138 return null;
139 }
140 if (str.length() == 0) {
141 return str;
142 }
143 if (str.length() == 1) {
144 return str.toUpperCase();
145 }
146 return str.substring(0, 1).toUpperCase() + str.substring(1);
147 }
148
149 private void writeSpringHeaderClose(XmlWriter out) {
150 out.decrementIndent();
151 out.indentPrintln("</beans>");
152 }
153
154 private void writeSpringHeaderOpen(XmlWriter out) {
155 out.indentPrintln("<!--");
156 out.indentPrintln(" Copyright 2011 The Kuali Foundation");
157 out.println("");
158 out.indentPrintln(" Licensed under the Educational Community License, Version 2.0 (the \"License\");");
159 out.indentPrintln(" you may not use this file except in compliance with the License.");
160 out.indentPrintln(" You may obtain a copy of the License at");
161 out.indentPrintln("");
162 out.indentPrintln(" http://www.opensource.org/licenses/ecl2.php");
163 out.println("");
164 out.indentPrintln(" Unless required by applicable law or agreed to in writing, software");
165 out.indentPrintln(" distributed under the License is distributed on an \"AS IS\" BASIS,");
166 out.indentPrintln(" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.");
167 out.indentPrintln(" See the License for the specific language governing permissions and");
168 out.indentPrintln(" limitations under the License.");
169 out.indentPrintln("-->");
170 out.indentPrintln("<beans xmlns=\"http://www.springframework.org/schema/beans\"");
171 out.indentPrintln("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
172 out.indentPrintln("xsi:schemaLocation=\""
173 + "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd" + "\">");
174 out.println("");
175 out.incrementIndent();
176 }
177
178 private void writeWarning(XmlWriter out) {
179 out.println("");
180 out.indentPrintln("<!-- ********************************************************");
181 out.incrementIndent();
182 out.indentPrintln(" WARNING ");
183 out.indentPrintln(" DO NOT UPDATE THIS FILE MANUALLY");
184 out.indentPrintln("This dictionary file was automatically generated on " + new Date());
185 out.indentPrintln("The DictionaryGeneratorMojo reads the service contract ");
186 out.indentPrintln("and creates these ks-XXXX-dictionary-generated.xml files.");
187 out.println("");
188 out.indentPrintln("If this file is out of sync with the contract re-run the mojo.");
189 out.println("");
190 out.indentPrintln("To add additional constraints or change these default values (perhaps");
191 out.indentPrintln("because the generator is not perfect) please update the corresponding ");
192 out.indentPrintln("ks-XXXX-dictionary.xml instead of this one.");
193 out.decrementIndent();
194 out.indentPrintln("************************************************************* -->");
195 }
196
197 private void writeNote(XmlWriter out) {
198 out.println("");
199 out.indentPrintln("<!-- ********************************************************");
200 out.incrementIndent();
201 out.indentPrintln(" NOTE");
202 out.indentPrintln(" THIS FILE WAS INTENDED TO BE MODIFIED");
203 out.println("");
204 out.indentPrintln("While this file was originally generated on " + new Date() + ", it");
205 out.indentPrintln("was intended to be subsequently modified by hand.");
206 out.indentPrintln("It imports a corresponding ks-XXXX-dictionary-generated.xml file, ");
207 out.indentPrintln("that was also automatically generated by the ContractDocMojo.");
208 out.indentPrintln("This file gives you the ability to layer on addiditional definitions and constrints");
209 out.indentPrintln("that are not/cannot be generated simply by reading the service contract.");
210 out.println("");
211 out.indentPrintln("The goal of this file is to be able to re-generate the corresponding");
212 out.indentPrintln("ks-XXXX-dictionary-generated.xml file without affecting these manually entered additions");
213 out.indentPrintln("that are encoded here.");
214 out.decrementIndent();
215 out.indentPrintln("************************************************************* -->");
216 }
217
218 private void writeGeneratedImports(XmlWriter out) {
219
220 out.writeCommentBox("The following file is required for this file to load:\n ks-base-dictionary.xml\nplus any of its dependencies");
221 out.indentPrintln("<import resource=\"classpath:ks-base-dictionary.xml\"/>");
222
223
224
225 }
226
227 private void writeManualImports(XmlWriter out) {
228 out.writeComment("The following file gets generated during the build and gets put into the target/classes directory");
229 out.indentPrintln("<import resource=\"classpath:ks-" + initUpper(className) + "-dictionary-generated.xml\"/>");
230 List<String> imports = this.getComplexSubObjectsThatAreLists();
231 if (!imports.isEmpty()) {
232 out.writeComment("TODO: remove these once the jira about lists of complex objects gets fixed");
233 for (String impName : imports) {
234 out.indentPrintln("<import resource=\"classpath:ks-" + initUpper(impName) + "-dictionary.xml\"/>");
235 }
236 }
237 }
238
239 private List<String> getComplexSubObjectsThatAreLists() {
240 List<String> list = new ArrayList();
241 for (MessageStructure ms : this.messageStructures) {
242 switch (this.calculateCategory(ms)) {
243 case LIST_OF_COMPLEX:
244 list.add(this.stripListOffEnd(ms.getType()));
245 }
246 }
247 return list;
248 }
249
250 private String stripListOffEnd(String name) {
251 if (name.endsWith("List")) {
252 return name.substring(0, name.length() - "List".length());
253 }
254 return name;
255 }
256
257 private String calcDataObjectClass(XmlType xmlType) {
258
259
260 if (xmlType.getJavaPackage() == null || xmlType.getJavaPackage().isEmpty()) {
261 return xmlType.getName();
262 }
263 return xmlType.getJavaPackage() + "." + initUpper(xmlType.getName());
264 }
265
266 private void writeGeneratedObjectStructure(XmlWriter out) {
267
268 out.println("");
269 out.indentPrintln("<!-- " + className + "-->");
270 out.indentPrintln("<bean id=\"" + initUpper(className) + "-generated\" abstract=\"true\" parent=\"DataObjectEntry\">");
271 out.incrementIndent();
272 writeProperty("name", initLower(className), out);
273 writeProperty("dataObjectClass", calcDataObjectClass(xmlType), out);
274 writeProperty("objectLabel", calcObjectLabel(), out);
275 writePropertyValue("objectDescription", xmlType.getDesc(), out);
276 String titleAttribute = calcTitleAttribute();
277 if (titleAttribute != null) {
278 writeProperty("titleAttribute", titleAttribute, out);
279 }
280 out.indentPrintln("<property name=\"primaryKeys\">");
281 List<String> pks = calcPrimaryKeys();
282 if (pks != null && !pks.isEmpty()) {
283 out.incrementIndent();
284 out.indentPrintln("<list>");
285 out.incrementIndent();
286 for (String pk : pks) {
287 addValue(pk);
288 }
289 out.decrementIndent();
290 out.indentPrintln("</list>");
291 out.decrementIndent();
292 }
293 out.indentPrintln("</property>");
294
295 this.writeAllGeneratedAttributeRefBeans(className, null, new Stack<String>(), this.messageStructures, out);
296
297 out.indentPrintln("</bean>");
298
299
300 this.writeGeneratedAttributeDefinitions(className, null, new Stack<String>(), this.messageStructures, out);
301 }
302
303 private void writeAllGeneratedAttributeRefBeans(String currentClassName,
304 String parentName,
305 Stack<String> parents,
306 List<MessageStructure> fields,
307 XmlWriter out) {
308 if (parents.contains(currentClassName)) {
309 return;
310 }
311 out.println("");
312 out.indentPrintln("<property name=\"attributes\">");
313 out.incrementIndent();
314 out.indentPrintln("<list>");
315 out.incrementIndent();
316 this.writeGeneratedAttributeRefBeans(currentClassName, parentName, parents, fields, out, Category.PRIMITIVE);
317 out.decrementIndent();
318 out.indentPrintln("</list>");
319 out.decrementIndent();
320 out.indentPrintln("</property>");
321
322 out.println("");
323 out.indentPrintln("<property name=\"complexAttributes\">");
324 out.incrementIndent();
325 out.indentPrintln("<list>");
326 out.incrementIndent();
327 this.writeGeneratedAttributeRefBeans(currentClassName, parentName, parents, fields, out, Category.COMPLEX);
328 out.decrementIndent();
329 out.indentPrintln("</list>");
330 out.decrementIndent();
331 out.indentPrintln("</property>");
332
333 out.println("");
334 out.indentPrintln("<property name=\"collections\">");
335 out.incrementIndent();
336 out.indentPrintln("<list>");
337 out.incrementIndent();
338 this.writeGeneratedAttributeRefBeans(currentClassName, parentName, parents, fields, out, Category.LIST_OF_COMPLEX);
339 out.decrementIndent();
340 out.indentPrintln("</list>");
341 out.decrementIndent();
342 out.indentPrintln("</property>");
343 out.decrementIndent();
344 }
345
346 private void addValue(String value) {
347 gwriter.indentPrintln("<value>" + value + "</value>");
348 }
349
350 private String calcObjectLabel() {
351 String label = this.className;
352 if (label.endsWith("Info")) {
353 label = label.substring(0, label.length() - "Info".length());
354 }
355 label = initUpper(label);
356 return splitCamelCase(label);
357 }
358
359
360 private static String splitCamelCase(String s) {
361 if (s == null) {
362 return null;
363 }
364 return s.replaceAll(
365 String.format("%s|%s|%s",
366 "(?<=[A-Z])(?=[A-Z][a-z])",
367 "(?<=[^A-Z])(?=[A-Z])",
368 "(?<=[A-Za-z])(?=[^A-Za-z])"),
369 " ");
370 }
371
372 private enum Category {
373
374 PRIMITIVE, COMPLEX, LIST_OF_COMPLEX, LIST_OF_PRIMITIVE, DYNAMIC_ATTRIBUTE
375 };
376
377 private Category calculateCategory(MessageStructure ms) {
378 if (ms.getShortName().equals("attributes")) {
379 return Category.DYNAMIC_ATTRIBUTE;
380 }
381 String childXmlTypeName = this.stripListOffEnd(ms.getType());
382 XmlType childXmlType = this.finder.findXmlType(childXmlTypeName);
383 if (childXmlType == null) {
384 throw new IllegalStateException(childXmlTypeName);
385 }
386 if (ms.getType().endsWith("List")) {
387 if (childXmlType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
388 return Category.LIST_OF_COMPLEX;
389 }
390 return Category.LIST_OF_PRIMITIVE;
391 }
392 if (childXmlType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
393 return Category.COMPLEX;
394 }
395 return Category.PRIMITIVE;
396 }
397
398 private void writeGeneratedAttributeRefBeans(String currentClass,
399 String parentName,
400 Stack<String> parents,
401 List<MessageStructure> fields,
402 XmlWriter out,
403 Category filter) {
404 if (parents.contains(currentClass)) {
405 return;
406 }
407 for (MessageStructure ms : fields) {
408 Category category = this.calculateCategory(ms);
409 if (!category.equals(filter)) {
410 continue;
411 }
412 String childXmlTypeName = this.stripListOffEnd(ms.getType());
413 XmlType childXmlType = this.finder.findXmlType(childXmlTypeName);
414 if (childXmlType == null) {
415 throw new IllegalStateException(childXmlTypeName);
416 }
417 String pathName = calcPathName(parentName, ms);
418 String beanName = calcBeanName(pathName);
419
420
421
422
423 out.indentPrintln("<ref bean=\"" + beanName + "\"/>");
424
425
426
427
428
429
430
431
432
433
434 }
435 }
436
437 private void writeGeneratedAttributeDefinitions(String currentClassName,
438 String parentName,
439 Stack<String> parents,
440 List<MessageStructure> fields,
441 XmlWriter out) {
442 if (parents.contains(currentClassName)) {
443 return;
444 }
445 for (MessageStructure ms : fields) {
446 Category category = this.calculateCategory(ms);
447 switch (category) {
448 case DYNAMIC_ATTRIBUTE:
449 continue;
450 }
451 String pathName = calcPathName(parentName, ms);
452 String beanName = calcBeanName(pathName);
453 String childXmlTypeName = this.stripListOffEnd(ms.getType());
454 XmlType childXmlType = this.finder.findXmlType(childXmlTypeName);
455 if (childXmlType == null) {
456 throw new IllegalStateException(childXmlTypeName);
457 }
458 writeGeneratedAttributeDefinition(currentClassName, parentName, parents, ms, out);
459
460
461 switch (category) {
462 case COMPLEX:
463
464 parents.push(currentClassName);
465 List<MessageStructure> childFields = this.finder.findMessageStructures(childXmlTypeName);
466 writeGeneratedAttributeDefinitions(childXmlTypeName, pathName, parents, childFields, out);
467 parents.pop();
468 }
469 }
470 }
471
472 private boolean shouldWriteDetails(MessageStructure ms) {
473 if (predefinedFieldMap.get(ms.getShortName().toLowerCase()) == null) {
474 return true;
475 }
476 if (ms.isOverriden()) {
477 return true;
478 }
479
480 return false;
481 }
482
483 private void writeGeneratedAttributeDefinition(String currentClassName, String parentName, Stack<String> parents, MessageStructure ms, XmlWriter out) {
484
485
486 String pathName = calcPathName(parentName, ms);
487 String beanName = calcBeanName(pathName);
488 String baseKualiParentBean = this.calcBaseKualiParentBean(ms);
489 out.println("");
490 out.indentPrintln("<bean id=\"" + beanName + "-generated\" abstract=\"true\" parent=\"" + baseKualiParentBean + "\">");
491 out.incrementIndent();
492 writeProperty("name", calcSimpleName(ms), out);
493 switch (this.calculateCategory(ms)) {
494 case PRIMITIVE:
495 if (this.shouldWriteDetails(ms)) {
496 writeProperty("shortLabel", calcShortLabel(ms), out);
497 writePropertyValue("summary", calcSummary(ms), out);
498 writeProperty("label", calcLabel(ms), out);
499 writePropertyValue("description", calcDescription(ms), out);
500 if (this.calcReadOnly(ms)) {
501 this.writeReadOnlyAttributeSecurity(out);
502 }
503 writeProperty("required", calcRequired(ms), out);
504 }
505 break;
506 case LIST_OF_PRIMITIVE:
507
508
509 writeProperty("shortLabel", calcShortLabel(ms), out);
510 writePropertyValue("summary", calcSummary(ms), out);
511 writeProperty("label", calcLabel(ms), out);
512 writeProperty("elementLabel", calcElementLabel(ms), out);
513 writePropertyValue("description", calcDescription(ms), out);
514 writeProperty("minOccurs", calcMinOccurs(ms), out);
515 writeProperty("dataObjectClass", calcDataObjectClass(ms), out);
516 break;
517 case LIST_OF_COMPLEX:
518 writeProperty("shortLabel", calcShortLabel(ms), out);
519 writePropertyValue("summary", calcSummary(ms), out);
520 writeProperty("label", calcLabel(ms), out);
521 writeProperty("elementLabel", calcElementLabel(ms), out);
522 writePropertyValue("description", calcDescription(ms), out);
523 writeProperty("minOccurs", calcMinOccurs(ms), out);
524 writeProperty("dataObjectClass", calcDataObjectClass(ms), out);
525 break;
526 case COMPLEX:
527 writeProperty("shortLabel", calcShortLabel(ms), out);
528 writePropertyValue("summary", calcSummary(ms), out);
529 writeProperty("label", calcLabel(ms), out);
530 writePropertyValue("description", calcDescription(ms), out);
531 writeProperty("required", calcRequired(ms), out);
532 writePropertyStart("dataObjectEntry", out);
533 out.indentPrintln("<bean parent=\"DataObjectEntry\">");
534 out.incrementIndent();
535 writeProperty("name", calcSimpleName(ms), out);
536 writeProperty("dataObjectClass", calcDataObjectClass(ms), out);
537 writeProperty("objectLabel", calcLabel(ms), out);
538 writePropertyValue("objectDescription", calcDescription(ms), out);
539
540 String childXmlTypeName = this.stripListOffEnd(ms.getType());
541 List<MessageStructure> childFields = this.finder.findMessageStructures(childXmlTypeName);
542 writeAllGeneratedAttributeRefBeans(childXmlTypeName, pathName, parents, childFields, out);
543 out.indentPrintln("</bean>");
544 writePropertyEnd(out);
545 break;
546 default:
547 throw new IllegalStateException("unknown/unhandled type " + ms.getId());
548 }
549 out.decrementIndent();
550
551
552
553
554 out.indentPrintln("</bean>");
555 }
556
557 private String calcDataObjectClass(MessageStructure ms) {
558 XmlType msType = this.finder.findXmlType(this.stripListOffEnd(ms.getType()));
559 return this.calcDataObjectClass(msType);
560 }
561
562 private String calcBeanName(String pathName) {
563 return initUpper(className) + "." + pathName;
564 }
565
566 private String calcPathName(String parentName, MessageStructure ms) {
567 String name = this.calcSimpleName(ms);
568 if (parentName == null) {
569 return name;
570 }
571 return parentName + "." + name;
572 }
573
574 private String calcSimpleName(MessageStructure ms) {
575 String name = initLower(ms.getShortName());
576 return name;
577 }
578
579 private boolean calcReadOnly(MessageStructure ms) {
580 if (ms.getReadOnly() == null) {
581 return false;
582 }
583 return true;
584 }
585
586 private void writeReadOnlyAttributeSecurity(XmlWriter out) {
587 out.indentPrintln("<!-- commented out until KRAD bug gets fixed that requires mask to also be entered");
588 out.indentPrintln("<property name=\"attributeSecurity\">");
589 out.indentPrintln("<ref bean=\"BaseKuali.readOnlyAttributeSecurity\"/>");
590 out.indentPrintln("</property>");
591 out.indentPrintln("-->");
592 }
593
594 private String calcElementLabel(MessageStructure ms) {
595 String label = this.calcShortLabel(ms);
596 if (label.endsWith("s")) {
597 label = label.substring(0, label.length() - 1);
598 }
599 return label;
600 }
601
602 private String calcShortLabel(MessageStructure ms) {
603 return this.splitCamelCase(initUpper(ms.getShortName()));
604 }
605
606 private String calcLabel(MessageStructure ms) {
607 return ms.getName();
608 }
609
610 private String calcSummary(MessageStructure ms) {
611 BreakIterator bi = BreakIterator.getSentenceInstance();
612 String description = ms.getDescription();
613 if (description == null) {
614 return "???";
615 }
616 bi.setText(ms.getDescription());
617
618 if (bi.next() == BreakIterator.DONE) {
619 return ms.getDescription();
620 }
621 String firstSentence = description.substring(0, bi.current());
622 return firstSentence;
623 }
624
625 private String calcDescription(MessageStructure ms) {
626 return ms.getDescription();
627 }
628
629 private String calcMinOccurs(MessageStructure ms) {
630 String required = this.calcRequired(ms);
631 if ("false".equals(required)) {
632 return "0";
633 }
634 return "1";
635 }
636
637 private String calcRequired(MessageStructure ms) {
638 if (ms.getRequired() == null) {
639 return "false";
640 }
641 if (ms.getRequired().equalsIgnoreCase("Required")) {
642 return "true";
643 }
644
645 return "false";
646 }
647
648 private void writeManualObjectStructure(XmlWriter out) {
649
650 out.println("");
651 out.indentPrintln("<!-- " + className + "-->");
652
653 out.indentPrintln("<bean id=\"" + initUpper(className) + "\" parent=\"" + initUpper(className) + "-parent\"/>");
654 out.indentPrintln("<bean id=\"" + initUpper(className) + "-parent\" abstract=\"true\" parent=\"" + initUpper(className) + "-generated\">");
655 out.writeComment("insert any overrides to the generated object definitions here");
656 out.indentPrintln("</bean>");
657
658
659 this.writeManualAttributeDefinitions(className, null, new Stack<String>(), this.messageStructures, out);
660
661 }
662
663 private void writeManualAttributeDefinitions(String currentClass, String parentName,
664 Stack<String> parents, List<MessageStructure> fields, XmlWriter out) {
665 if (parents.contains(currentClass)) {
666 return;
667 }
668 for (MessageStructure ms : fields) {
669 Category cat = this.calculateCategory(ms);
670
671 switch (cat) {
672 case DYNAMIC_ATTRIBUTE:
673 continue;
674 }
675
676 String pathName = calcPathName(parentName, ms);
677 String beanName = calcBeanName(pathName);
678 String childXmlTypeName = this.stripListOffEnd(ms.getType());
679 XmlType childXmlType = this.finder.findXmlType(childXmlTypeName);
680 if (childXmlType == null) {
681 throw new IllegalStateException(childXmlTypeName);
682 }
683 writeManualAttributeDefinition(currentClass, parentName, ms, out);
684
685
686 switch (cat) {
687 case COMPLEX:
688 parents.push(currentClass);
689 List<MessageStructure> childFields = this.finder.findMessageStructures(childXmlTypeName);
690
691
692
693 writeManualAttributeDefinitions(childXmlTypeName, pathName, parents, childFields, out);
694 parents.pop();
695 }
696 }
697 }
698
699 private void writeManualAttributeDefinition(String currentClass, String parentName, MessageStructure ms, XmlWriter out) {
700
701
702 String pathName = calcPathName(parentName, ms);
703 String beanName = calcBeanName(pathName);
704
705
706 out.println("");
707 out.indentPrintln("<bean id=\"" + beanName + "\" parent=\"" + beanName + "-parent\"/>");
708 out.indentPrintln("<bean id=\"" + beanName + "-parent\" abstract=\"true\" parent=\"" + beanName + "-generated\">");
709 out.writeComment("insert any overrides to the generated attribute definitions here");
710 out.indentPrintln("</bean>");
711 }
712
713
714
715 private static Map<String, String> predefinedFieldMap = null;
716
717 {
718 Map<String, String> map = new HashMap<String, String>();
719 map.put("id", "BaseKuali.id");
720 map.put("key", "BaseKuali.key");
721 map.put("name", "BaseKuali.name");
722 map.put("descr", "BaseKuali.descr");
723 map.put("plain", "BaseKuali.descr.plain");
724 map.put("formatted", "BaseKuali.descr.formatted");
725 map.put("desc", "BaseKuali.desc");
726 map.put("typeKey", "BaseKuali.typeKey");
727 map.put("stateKey", "BaseKuali.stateKey");
728 map.put("type", "BaseKuali.type");
729 map.put("state", "BaseKuali.state");
730 map.put("effectiveDate", "BaseKuali.effectiveDate");
731 map.put("expirationDate", "BaseKuali.expirationDate");
732 map.put("meta", "BaseKuali.meta");
733 map.put("createTime", "BaseKuali.meta.createTime");
734 map.put("updateTime", "BaseKuali.meta.updateTime");
735 map.put("createId", "BaseKuali.meta.createId");
736 map.put("updateId", "BaseKuali.meta.updateId");
737 map.put("versionInd", "BaseKuali.meta.versionInd");
738
739 predefinedFieldMap = new HashMap(map.size());
740 for (String key : map.keySet()) {
741 predefinedFieldMap.put(key.toLowerCase(), map.get(key));
742 }
743 }
744
745
746
747
748 private static Map<String, String> endsWithMap = null;
749
750 {
751 Map<String, String> map = new HashMap<String, String>();
752 map.put("startDate", "BaseKuali.startDate");
753 map.put("endDate", "BaseKuali.endDate");
754 map.put("start", "BaseKuali.start");
755 map.put("end", "BaseKuali.end");
756 map.put("OrgId", "BaseKuali.orgId");
757 map.put("OrgIds", "BaseKuali.orgId");
758 map.put("PersonId", "BaseKuali.personId");
759 map.put("PersonIds", "BaseKuali.personId");
760 map.put("PrincipalId", "BaseKuali.principalId");
761 map.put("PrincipalIds", "BaseKuali.principalId");
762 map.put("CluId", "BaseKuali.cluId");
763 map.put("CluIds", "BaseKuali.cluId");
764 map.put("LuiId", "BaseKuali.luiId");
765 map.put("LuiIds", "BaseKuali.luiId");
766 map.put("AtpId", "BaseKuali.atpId");
767 map.put("AtpIds", "BaseKuali.atpId");
768 map.put("TermId", "BaseKuali.termId");
769 map.put("TermIds", "BaseKuali.termId");
770 map.put("HolidayCalendarId", "BaseKuali.holidayCalendarId");
771 map.put("HolidayCalendarIds", "BaseKuali.holidayCalendarId");
772 map.put("Code", "BaseKuali.code");
773
774 endsWithMap = new HashMap(map.size());
775 for (String key : map.keySet()) {
776 endsWithMap.put(key.toLowerCase(), map.get(key));
777 }
778 }
779
780
781
782
783
784 private static Map<String, String> typeMap = null;
785
786 {
787 Map<String, String> map = new HashMap<String, String>();
788 map.put("String", "BaseKuali.string");
789 map.put("DateTime", "BaseKuali.dateTime");
790 map.put("Date", "BaseKuali.date");
791 map.put("Boolean", "BaseKuali.boolean");
792 map.put("Integer", "BaseKuali.integer");
793 map.put("Long", "BaseKuali.long");
794 map.put("Float", "BaseKuali.float");
795 map.put("Double", "BaseKuali.double");
796
797 typeMap = new HashMap(map.size());
798 for (String key : map.keySet()) {
799 typeMap.put(key.toLowerCase(), map.get(key));
800 }
801 }
802
803 private String calcBaseKualiParentBean(MessageStructure ms) {
804 switch (this.calculateCategory(ms)) {
805 case COMPLEX:
806 return "ComplexAttributeDefinition";
807 case LIST_OF_COMPLEX:
808 return "CollectionDefinition";
809 case LIST_OF_PRIMITIVE:
810
811 System.out.println("Treating list of primitives same as collection defintion: " + ms.getId());
812 return "CollectionDefinition";
813 case PRIMITIVE:
814 break;
815 default:
816 throw new IllegalStateException("unknown/uhandled category " + ms.getId());
817 }
818 String name = ms.getShortName();
819 String baseKualiType = predefinedFieldMap.get(name.toLowerCase());
820 if (baseKualiType != null) {
821 return baseKualiType;
822 }
823
824
825 for (String key : endsWithMap.keySet()) {
826 if (name.toLowerCase().endsWith(key)) {
827 return endsWithMap.get(key);
828 }
829 }
830
831
832 String type = this.stripListOffEnd(ms.getType());
833 baseKualiType = typeMap.get(type.toLowerCase());
834 if (baseKualiType != null) {
835 return baseKualiType;
836 }
837 throw new IllegalStateException("All primitives are supposed to be handled by a predefined type " + ms.getId());
838 }
839
840 private String calcTitleAttribute() {
841 MessageStructure ms = null;
842 ms = this.findMessageStructure("name");
843 if (ms != null) {
844 return initLower(ms.getShortName());
845 }
846 ms = this.findMessageStructure("title");
847 if (ms != null) {
848 return initLower(ms.getShortName());
849 }
850 ms = this.findMessageStructureEndsWith("name");
851 if (ms != null) {
852 return initLower(ms.getShortName());
853 }
854 ms = this.findMessageStructureEndsWith("title");
855 if (ms != null) {
856 return initLower(ms.getShortName());
857 }
858 ms = this.findMessageStructure("key");
859 if (ms != null) {
860 return initLower(ms.getShortName());
861 }
862
863 System.out.println("XmlKradBaseDictionaryCreator: could not find a title attribute for " + this.className);
864
865
866
867
868 return null;
869 }
870
871 private MessageStructure findMessageStructureEndsWith(String shortNameEndsWith) {
872 shortNameEndsWith = shortNameEndsWith.toLowerCase();
873 for (MessageStructure ms : this.messageStructures) {
874 if (ms.getShortName().toLowerCase().endsWith(shortNameEndsWith)) {
875 return ms;
876 }
877 }
878 return null;
879 }
880
881 private MessageStructure findMessageStructure(String shortName) {
882 for (MessageStructure ms : this.messageStructures) {
883 if (ms.getShortName().equalsIgnoreCase(shortName)) {
884 return ms;
885 }
886 }
887 return null;
888 }
889
890 private MessageStructure getMessageStructure(String shortName) {
891 MessageStructure ms = this.findMessageStructure(shortName);
892 if (ms == null) {
893 throw new IllegalArgumentException(shortName);
894 }
895 return ms;
896 }
897
898 private List<String> calcPrimaryKeys() {
899 MessageStructure ms = null;
900 ms = this.findMessageStructure("id");
901 if (ms != null) {
902 return Collections.singletonList(initLower(ms.getShortName()));
903 }
904 ms = this.findMessageStructure("key");
905 if (ms != null) {
906 return Collections.singletonList(initLower(ms.getShortName()));
907 }
908
909 if (this.messageStructures.size() > 0) {
910 ms = this.messageStructures.get(0);
911 return Collections.singletonList(ms.getShortName());
912 }
913 return Collections.EMPTY_LIST;
914 }
915
916 private void writePropertyStart(String propertyName, XmlWriter out) {
917 out.indentPrintln("<property name=\"" + propertyName + "\">");
918 out.incrementIndent();
919 }
920
921 private void writePropertyEnd(XmlWriter out) {
922 out.decrementIndent();
923 out.indentPrintln("</property>");
924 }
925
926 private void writeProperty(String propertyName, String propertyValue, XmlWriter out) {
927 out.indentPrintln("<property name=\"" + propertyName + "\" value=\"" + replaceDoubleQuotes(propertyValue) + "\"/>");
928 }
929
930 private void writePropertyValue(String propertyName, String propertyValue, XmlWriter out) {
931 writePropertyStart(propertyName, out);
932 out.indentPrintln("<value>");
933
934 out.println(escapeHtml(propertyValue));
935 out.indentPrintln("</value>");
936 writePropertyEnd(out);
937 }
938
939 private String escapeHtml(String str) {
940 if (str == null) {
941 return null;
942 }
943 return StringEscapeUtils.escapeHtml(str);
944 }
945
946 private String replaceDoubleQuotes(String str) {
947 if (str == null) {
948 return null;
949 }
950 return str.replace("\"", "'");
951 }
952 }