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