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