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