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