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