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