1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.student.contract.model.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.util.Collections;
23 import java.util.Date;
24 import java.util.Enumeration;
25 import java.util.List;
26 import java.util.Stack;
27
28 import org.kuali.student.contract.model.MessageStructure;
29 import org.kuali.student.contract.model.ServiceContractModel;
30 import org.kuali.student.contract.model.XmlType;
31 import org.kuali.student.contract.writer.XmlWriter;
32
33
34
35
36
37 public class XmlKradBaseDictionaryCreator {
38
39 private ServiceContractModel model;
40 private ModelFinder finder;
41 private String directory;
42 private String className;
43 private XmlType xmlType;
44 private XmlWriter gwriter;
45 private XmlWriter bwriter;
46 private List<MessageStructure> messageStructures;
47
48 public XmlKradBaseDictionaryCreator(String directory,
49 ServiceContractModel model, String className) {
50 this.directory = directory;
51 this.model = model;
52 this.finder = new ModelFinder(this.model);
53 this.className = className;
54 this.xmlType = this.finder.findXmlType(className);
55 if (xmlType == null) {
56 throw new IllegalArgumentException(className);
57 }
58 this.messageStructures = this.finder.findMessageStructures(className);
59 if (this.messageStructures.isEmpty()) {
60 throw new IllegalStateException(className);
61 }
62 }
63
64 public void write() {
65 this.initXmlWriters();
66 this.writeSpringHeaderOpen(gwriter);
67 this.writeWarning(gwriter);
68 this.writeGeneratedImports(gwriter);
69 this.writeGeneratedObjectStructure(gwriter);
70 this.writeSpringHeaderClose(gwriter);
71
72 this.writeSpringHeaderOpen(bwriter);
73 this.writeNote (bwriter);
74 this.writeBaseImports(bwriter);
75 this.writeBaseObjectStructure(bwriter);
76 this.writeSpringHeaderClose(bwriter);
77 }
78
79 private void initXmlWriters() {
80 String generatedFileName = "/ks-" + initUpper(className) + "-dictionary-generated.xml";
81 String baseFileName = "/ks-" + initUpper(className) + "-dictionary.xml";
82
83 File dir = new File(this.directory);
84
85
86 if (!dir.exists()) {
87 if (!dir.mkdirs()) {
88 throw new IllegalStateException("Could not create directory "
89 + this.directory);
90 }
91 }
92
93 try {
94 PrintStream out = new PrintStream(new FileOutputStream(this.directory + "/" + generatedFileName, false));
95 this.gwriter = new XmlWriter(out, 0);
96 } catch (FileNotFoundException ex) {
97 throw new IllegalStateException(ex);
98 }
99 try {
100 PrintStream out = new PrintStream(new FileOutputStream(this.directory + "/" + baseFileName, false));
101 this.bwriter = new XmlWriter(out, 0);
102 } catch (FileNotFoundException ex) {
103 throw new IllegalStateException(ex);
104 }
105 }
106
107 private static String initLower(String str) {
108 if (str == null) {
109 return null;
110 }
111 if (str.length() == 0) {
112 return str;
113 }
114 if (str.length() == 1) {
115 return str.toLowerCase();
116 }
117 return str.substring(0, 1).toLowerCase() + str.substring(1);
118 }
119
120 private static String initUpper(String str) {
121 if (str == null) {
122 return null;
123 }
124 if (str.length() == 0) {
125 return str;
126 }
127 if (str.length() == 1) {
128 return str.toUpperCase();
129 }
130 return str.substring(0, 1).toUpperCase() + str.substring(1);
131 }
132
133 private void writeSpringHeaderClose(XmlWriter out) {
134 out.decrementIndent();
135 out.indentPrintln("</beans>");
136 }
137
138 private void writeSpringHeaderOpen(XmlWriter out) {
139 out.indentPrintln("<!--");
140 out.indentPrintln(" Copyright 2011 The Kuali Foundation");
141 out.println("");
142 out.indentPrintln(" Licensed under the Educational Community License, Version 2.0 (the \"License\");");
143 out.indentPrintln(" you may not use this file except in compliance with the License.");
144 out.indentPrintln(" You may obtain a copy of the License at");
145 out.indentPrintln("");
146 out.indentPrintln(" http://www.opensource.org/licenses/ecl2.php");
147 out.println("");
148 out.indentPrintln(" Unless required by applicable law or agreed to in writing, software");
149 out.indentPrintln(" distributed under the License is distributed on an \"AS IS\" BASIS,");
150 out.indentPrintln(" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.");
151 out.indentPrintln(" See the License for the specific language governing permissions and");
152 out.indentPrintln(" limitations under the License.");
153 out.indentPrintln("-->");
154 out.indentPrintln("<beans xmlns=\"http://www.springframework.org/schema/beans\"");
155 out.indentPrintln("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
156 out.indentPrintln("xsi:schemaLocation=\""
157 + "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd" + "\">");
158 out.println("");
159 out.incrementIndent();
160 }
161
162 private void writeWarning(XmlWriter out) {
163 out.println("");
164 out.indentPrintln("<!-- ********************************************************");
165 out.indentPrintln(" WARNING ");
166 out.indentPrintln(" DO NOT UPDATE THIS FILE MANUALLY");
167 out.indentPrintln(" This dictionary file was automatically generated on " + new Date());
168 out.indentPrintln(" The DictionaryGeneratorMojo reads the service contract ");
169 out.indentPrintln(" and creates these ks-XXXX-dictionary-generated.xml files.");
170 out.indentPrintln(" ");
171 out.indentPrintln(" If this file is out of sync with the contract re-run the mojo.");
172 out.indentPrintln(" ");
173 out.indentPrintln(" To add in additional constraints or change these default values (perhaps");
174 out.indentPrintln(" because the generator is not perfect) please update the corresponding ");
175 out.indentPrintln(" ks-XXXX-dictionary.xml instead of this one.");
176 out.indentPrintln("************************************************************* -->");
177 }
178
179 private void writeNote(XmlWriter out) {
180 out.println("");
181 out.indentPrintln("<!-- ********************************************************");
182 out.indentPrintln(" NOTE");
183 out.indentPrintln(" THIS FILE WAS INTENDED TO BE MODIFIED");
184 out.println ("");
185 out.indentPrintln(" While this file was originally generated on " + new Date());
186 out.indentPrintln(" it was intended to be subsequently modified by hand.");
187 out.indentPrintln(" It imports a corresponding ks-XXXX-dictionary-generated.xml file");
188 out.indentPrintln(" that was also automatically generated by the DictionaryCreatorMojo.");
189 out.indentPrintln(" This file gives you the ability to layer in addiditional definitions");
190 out.indentPrintln(" that are not/cannot be generated simply by reading the service contract");
191 out.println ("");
192 out.indentPrintln(" The goal is to be able to easily re-generate the ks-XXXX-dictionary-generated.xml file");
193 out.indentPrintln(" without affecting the manually entered additions that are coded here.");
194 out.indentPrintln("************************************************************* -->");
195 }
196
197 private void writeGeneratedImports(XmlWriter out) {
198 out.indentPrintln("<import resource=\"classpath:ks-base-dictionary.xml\"/>");
199
200
201
202 }
203
204 private void writeBaseImports(XmlWriter out) {
205 out.indentPrintln("<import resource=\"classpath:ks-" + initUpper(className) + "-dictionary-generated.xml\"/>");
206 }
207
208 private String stripListOffEnd(String name) {
209 if (name.endsWith("List")) {
210 return name.substring(0, name.length() - "List".length());
211 }
212 return name;
213 }
214
215 private void writeSubStructures(XmlType type, Stack<String> stack) {
216 boolean first = true;
217 for (MessageStructure ms : finder.findMessageStructures(type.getName())) {
218 XmlType st = finder.findXmlType(this.stripListOffEnd(ms.getType()));
219 if (st == null) {
220 throw new NullPointerException(ms.getType() + " does not exist in list of types with parents " + calcParents(stack));
221 }
222 if (!st.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
223 continue;
224 }
225 if (first) {
226 first = false;
227 gwriter.indentPrintln("<ul>");
228 }
229 if (!stack.contains(st.getName())) {
230 stack.push(st.getName());
231 this.writeSubStructures(st, stack);
232 stack.pop();
233 }
234 }
235 if (!first) {
236 gwriter.indentPrintln("</ul>");
237 }
238 }
239
240 private String calcParents(Stack<String> stack) {
241 StringBuilder sb = new StringBuilder();
242 String dot = "";
243 Enumeration<String> en = stack.elements();
244 while (en.hasMoreElements()) {
245 sb.append(dot);
246 dot = ".";
247 sb.append(en.nextElement());
248 }
249 return sb.toString();
250 }
251
252 private void writeGeneratedObjectStructure(XmlWriter out) {
253
254 out.println("");
255 out.indentPrintln("<!-- " + className + "-->");
256 out.indentPrintln("<bean id=\"" + initUpper(className) + "-generated\" abstract=\"true\" parent=\"DataObjectEntry\">");
257 out.incrementIndent();
258 writeProperty("name", initLower(className));
259 writeProperty("objectClass", xmlType.getJavaPackage() + "." + initUpper(className));
260 writeProperty("objectLabel", calcObjectLabel());
261 writeProperty("objectDescription", xmlType.getDesc());
262 String titleAttribute = calcTitleAttribute();
263 if (titleAttribute != null) {
264 writeProperty("titleAttribute", titleAttribute);
265 }
266 out.indentPrintln("<property name=\"primaryKeys\">");
267 List<String> pks = calcPrimaryKeys();
268 if (pks != null && !pks.isEmpty()) {
269 out.incrementIndent();
270 out.indentPrintln("<list>");
271 out.incrementIndent();
272 for (String pk : pks) {
273 addValue(pk);
274 }
275 out.decrementIndent();
276 out.indentPrintln("</list>");
277 out.decrementIndent();
278 }
279 out.indentPrintln("</property>");
280 out.indentPrintln("<property name=\"attributes\">");
281 out.incrementIndent();
282 out.indentPrintln("<list>");
283 out.incrementIndent();
284 this.writeGeneratedAttributeRefBeans(className, null, new Stack<String>(), this.messageStructures, out);
285 out.decrementIndent();
286 out.indentPrintln("</list>");
287 out.decrementIndent();
288 out.indentPrintln("</property>");
289 out.decrementIndent();
290 out.indentPrintln("</bean>");
291
292
293 this.writeGeneratedAttributeDefinitions(className, null, new Stack<String>(), this.messageStructures, out);
294
295 }
296
297 private void addValue(String value) {
298 gwriter.indentPrintln("<value>" + value + "</value>");
299 }
300
301 private String calcObjectLabel() {
302 String label = this.className;
303 if (label.endsWith("Info")) {
304 label = label.substring(0, label.length() - "Info".length());
305 }
306 label = initUpper(label);
307 return splitCamelCase(label);
308 }
309
310
311 private static String splitCamelCase(String s) {
312 return s.replaceAll(
313 String.format("%s|%s|%s",
314 "(?<=[A-Z])(?=[A-Z][a-z])",
315 "(?<=[^A-Z])(?=[A-Z])",
316 "(?<=[A-Za-z])(?=[^A-Za-z])"),
317 " ");
318 }
319
320 private void writeGeneratedAttributeRefBeans(String className, String parentFieldName,
321 Stack<String> parents, List<MessageStructure> fields, XmlWriter out) {
322 if (parents.contains(className)) {
323 return;
324 }
325 for (MessageStructure ms : fields) {
326 String fieldName = calcFieldName(className, parentFieldName, ms);
327 out.indentPrintln("<ref bean=\"" + fieldName + "\"/>");
328
329 String childTypeName = this.stripListOffEnd(ms.getType());
330 XmlType childType = this.finder.findXmlType(childTypeName);
331 if (childType == null) {
332 throw new IllegalStateException(childTypeName);
333 }
334 if (childType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
335 parents.push(className);
336 List<MessageStructure> childFields = this.finder.findMessageStructures(childTypeName);
337 if (childFields.isEmpty()) {
338 throw new IllegalStateException(childTypeName);
339 }
340 writeGeneratedAttributeRefBeans(childTypeName, fieldName, parents, childFields, out);
341 parents.pop();
342 }
343 }
344 }
345
346 private void writeGeneratedAttributeDefinitions(String className, String parentFieldName,
347 Stack<String> parents, List<MessageStructure> fields, XmlWriter out) {
348 if (parents.contains(className)) {
349 return;
350 }
351 for (MessageStructure ms : fields) {
352 String fieldName = calcFieldName(className, parentFieldName, ms);
353 String childTypeName = this.stripListOffEnd(ms.getType());
354 XmlType childType = this.finder.findXmlType(childTypeName);
355 if (childType == null) {
356 throw new IllegalStateException(childTypeName);
357 }
358 writeGeneratedAttributeDefinition(className, parentFieldName, ms, out);
359
360
361 if (childType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
362 parents.push(className);
363 List<MessageStructure> childFields = this.finder.findMessageStructures(childTypeName);
364 if (childFields.isEmpty()) {
365 throw new IllegalStateException(childTypeName);
366 }
367 writeGeneratedAttributeDefinitions(childTypeName, fieldName, parents, childFields, out);
368 parents.pop();
369 }
370 }
371 }
372
373 private void writeGeneratedAttributeDefinition(String className, String parentFieldName, MessageStructure ms, XmlWriter out) {
374
375
376 String fieldName = this.calcFieldName(className, parentFieldName, ms);
377 String baseKualiType = this.calcBaseKualiType(ms);
378 out.println("");
379 out.indentPrintln("<bean id=\"" + fieldName + "-generated\" abstract=\"true\" parent=\"" + baseKualiType + "\">");
380 out.incrementIndent();
381 writeProperty("name", initLower(ms.getShortName()));
382 if (ms.isOverriden()) {
383 writeProperty("shortLabel", ms.getName());
384 }
385 out.decrementIndent();
386
387
388
389
390 out.indentPrintln("</bean>");
391 }
392
393 private void writeBaseObjectStructure(XmlWriter out) {
394
395 out.println("");
396 out.indentPrintln("<!-- " + className + "-->");
397 out.indentPrintln("<bean id=\"" + initUpper(className) + "-parent\" abstract=\"true\" parent=\"" + initUpper(className) + "-generated\">");
398 out.writeComment("insert any overrides to the generated object definitions here");
399 out.indentPrintln("</bean>");
400
401
402 out.indentPrintln("<bean id=\"" + initUpper(className) + "\" parent=\"" + initUpper(className) + "-parent\"/>");
403 out.println("");
404
405
406 this.writeBaseAttributeDefinitions(className, null, new Stack<String>(), this.messageStructures, out);
407
408 }
409
410 private void writeBaseAttributeDefinitions(String className, String parentFieldName,
411 Stack<String> parents, List<MessageStructure> fields, XmlWriter out) {
412 if (parents.contains(className)) {
413 return;
414 }
415 for (MessageStructure ms : fields) {
416 String fieldName = calcFieldName(className, parentFieldName, ms);
417 String childTypeName = this.stripListOffEnd(ms.getType());
418 XmlType childType = this.finder.findXmlType(childTypeName);
419 if (childType == null) {
420 throw new IllegalStateException(childTypeName);
421 }
422 writeBaseAttributeDefinition(className, parentFieldName, ms, out);
423
424
425 if (childType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
426 parents.push(className);
427 List<MessageStructure> childFields = this.finder.findMessageStructures(childTypeName);
428 if (childFields.isEmpty()) {
429 throw new IllegalStateException(childTypeName);
430 }
431 writeBaseAttributeDefinitions(childTypeName, fieldName, parents, childFields, out);
432 parents.pop();
433 }
434 }
435 }
436
437 private void writeBaseAttributeDefinition(String className, String parentFieldName, MessageStructure ms, XmlWriter out) {
438
439
440 String fieldName = this.calcFieldName(className, parentFieldName, ms);
441
442 out.println("");
443 out.indentPrintln("<bean id=\"" + fieldName + "-parent\" abstract=\"true\" parent=\"" + fieldName + "-generated\">");
444 out.writeComment("insert any overrides to the generated attribute definitions here");
445 out.indentPrintln("</bean>");
446
447
448 out.indentPrintln("<bean id=\"" + fieldName + "\" parent=\"" + fieldName + "-parent\"/>");
449 }
450
451 private String calcBaseKualiType(MessageStructure ms) {
452
453 String name = ms.getShortName();
454 if (name.equals("id")) {
455 return "BaseKuali.id";
456 }
457 if (name.equals("key")) {
458 return "BaseKuali.key";
459 }
460 if (name.equals("descr")) {
461 return "BaseKuali.descr";
462 }
463 if (name.equals("name")) {
464 return "BaseKuali.name";
465 }
466 if (name.equals("typeKey")) {
467 return "BaseKuali.typeKey";
468 }
469 if (name.equals("stateKey")) {
470 return "BaseKuali.stateKey";
471 }
472
473 if (name.equals("type")) {
474 return "BaseKuali.typeKey";
475 }
476 if (name.equals("state")) {
477 return "BaseKuali.stateKey";
478 }
479 if (name.equals("effectiveDate")) {
480 return "BaseKuali.effectiveDate";
481 }
482 if (name.equals("expirationDate")) {
483 return "BaseKuali.expirationDate";
484 }
485 if (name.endsWith("orgId")) {
486 return "BaseKuali.orgId";
487 }
488 if (name.endsWith("personId")) {
489 return "BaseKuali.personId";
490 }
491 if (name.endsWith("principalId")) {
492 return "BaseKuali.principalId";
493 }
494 if (name.endsWith("cluId")) {
495 return "BaseKuali.cluId";
496 }
497 if (name.endsWith("luiId")) {
498 return "BaseKuali.luiId";
499 }
500 if (name.endsWith("Code")) {
501 return "BaseKuali.code";
502 }
503
504
505 String type = this.stripListOffEnd(ms.getType());
506 if (type.equalsIgnoreCase("String")) {
507 return "BaseKuali.string";
508 }
509 if (type.equalsIgnoreCase("DateTime")) {
510 return "BaseKuali.dateTime";
511 }
512 if (type.equalsIgnoreCase("Date")) {
513 return "BaseKuali.date";
514 }
515 if (type.equalsIgnoreCase("Boolean")) {
516 return "BaseKuali.boolean";
517 }
518 if (type.equalsIgnoreCase("Integer")) {
519 return "BaseKuali.integer";
520 }
521
522
523
524 if (type.equalsIgnoreCase(XmlType.COMPLEX)) {
525 return "BaseKuali.complex";
526 }
527
528 return "BaseKuali.string";
529
530 }
531
532 private String calcFieldName(String className, String parentFieldName, MessageStructure ms) {
533 if (parentFieldName == null) {
534 return initUpper(className) + "." + initLower(ms.getShortName());
535 }
536 return parentFieldName + "." + initLower(ms.getShortName());
537 }
538
539 private String calcTitleAttribute() {
540 MessageStructure ms = null;
541 ms = this.findMessageStructure("name");
542 if (ms != null) {
543 return initLower(ms.getShortName());
544 }
545 ms = this.findMessageStructure("title");
546 if (ms != null) {
547 return initLower(ms.getShortName());
548 }
549 ms = this.findMessageStructureEndsWith("name");
550 if (ms != null) {
551 return initLower(ms.getShortName());
552 }
553 ms = this.findMessageStructureEndsWith("title");
554 if (ms != null) {
555 return initLower(ms.getShortName());
556 }
557 ms = this.findMessageStructure("key");
558 if (ms != null) {
559 return initLower(ms.getShortName());
560 }
561
562 System.out.println("XmlKradBaseDictionaryCreator: could not find a title attribute for " + this.className);
563
564
565
566
567 return null;
568 }
569
570 private MessageStructure findMessageStructureEndsWith(String shortNameEndsWith) {
571 shortNameEndsWith = shortNameEndsWith.toLowerCase();
572 for (MessageStructure ms : this.messageStructures) {
573 if (ms.getShortName().toLowerCase().endsWith(shortNameEndsWith)) {
574 return ms;
575 }
576 }
577 return null;
578 }
579
580 private MessageStructure findMessageStructure(String shortName) {
581 for (MessageStructure ms : this.messageStructures) {
582 if (ms.getShortName().equalsIgnoreCase(shortName)) {
583 return ms;
584 }
585 }
586 return null;
587 }
588
589 private MessageStructure getMessageStructure(String shortName) {
590 MessageStructure ms = this.findMessageStructure(shortName);
591 if (ms == null) {
592 throw new IllegalArgumentException(shortName);
593 }
594 return ms;
595 }
596
597 private List<String> calcPrimaryKeys() {
598 MessageStructure ms = null;
599 ms = this.findMessageStructure("id");
600 if (ms != null) {
601 return Collections.singletonList(initLower(ms.getShortName()));
602 }
603 ms = this.findMessageStructure("key");
604 if (ms != null) {
605 return Collections.singletonList(initLower(ms.getShortName()));
606 }
607 return Collections.EMPTY_LIST;
608 }
609
610 private void writeProperty(String propertyName, String propertyValue) {
611 gwriter.indentPrintln("<property name=\"" + propertyName + "\" value=\"" + propertyValue + "\"/>");
612 }
613 }