1 package org.kuali.student.lum.dto.test;
2
3 import org.kuali.student.r1.common.dictionary.dto.*;
4 import org.kuali.student.r1.common.dictionary.service.impl.Dictionary2BeanComparer;
5
6 import java.util.*;
7
8 public class DictionaryDiscrepencyTester
9 {
10
11 private boolean printOnlyDiscrepencies = false;
12 private StringBuilder builder = new StringBuilder (5000);
13 private ObjectStructureDefinition os;
14 private String rowSeperator = "\n";
15 private String colSeperator = "|";
16 private String name;
17 private String className;
18 private boolean processSubstructures = false;
19 private int level;
20 private Map<String, ObjectStructureDefinition> subStructuresToProcess =
21 new LinkedHashMap ();
22 private Set<ObjectStructureDefinition> subStructuresAlreadyProcessed;
23
24 public DictionaryDiscrepencyTester (String name,
25 String className,
26 ObjectStructureDefinition os,
27 Set<ObjectStructureDefinition> subStructuresAlreadyProcessed,
28 int level,
29 boolean processSubstructures, boolean printOnlyDiscrepencies)
30 {
31 this.name = name;
32 this.className = className;
33 this.os = os;
34 this.subStructuresAlreadyProcessed = subStructuresAlreadyProcessed;
35 this.level = level;
36 this.processSubstructures = processSubstructures;
37 this.printOnlyDiscrepencies = printOnlyDiscrepencies;
38 }
39 public static final String UNBOUNDED = "unbounded";
40
41 public String getRowSeperator ()
42 {
43 return rowSeperator;
44 }
45
46 public void setRowSeperator (String rowSeperator)
47 {
48 this.rowSeperator = rowSeperator;
49 }
50
51 public String getColSeparator ()
52 {
53 return colSeperator;
54 }
55
56 public void setColSeparator (String separator)
57 {
58 this.colSeperator = separator;
59 }
60
61 private String pad (String str, int size)
62 {
63 StringBuilder padStr = new StringBuilder (size);
64 padStr.append (str);
65 while (padStr.length () < size)
66 {
67 padStr.append (' ');
68 }
69 return padStr.toString ();
70 }
71
72 public List<String> formatForWiki ()
73 {
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154 List<String> discrepancies = null;
155 if (className == null)
156 {
157 discrepancies = new ArrayList (1);
158 discrepancies.add (
159 "There is no corresponding java class for this dictionary object structure");
160 }
161 else
162 {
163 discrepancies = new Dictionary2BeanComparer (className, os).compare ();
164 }
165
166
167
168
169
170
171
172
173
174
175
176
177 Set<ObjectStructureDefinition> subStructuresAlreadyProcessedBeforeProcessingSubStructures =
178 new HashSet ();
179 subStructuresAlreadyProcessedBeforeProcessingSubStructures.addAll (
180 subStructuresAlreadyProcessed);
181 for (String subName : this.subStructuresToProcess.keySet ())
182 {
183 ObjectStructureDefinition subOs = this.subStructuresToProcess.get (subName);
184 if ( ! subStructuresAlreadyProcessedBeforeProcessingSubStructures.contains (
185 subOs))
186 {
187 this.subStructuresAlreadyProcessed.add (subOs);
188
189 Class<?> subClazz = getClass (subOs.getName ());
190 DictionaryDiscrepencyTester formatter =
191 new DictionaryDiscrepencyTester (subName, subOs.getName (),
192 subOs,
193 subStructuresAlreadyProcessed,
194 level + 1,
195 this.processSubstructures, this.printOnlyDiscrepencies);
196
197
198 }
199 }
200
201 return discrepancies;
202 }
203
204
205
206 private List<FieldDefinition> getSortedFields ()
207 {
208 List<FieldDefinition> fields = os.getAttributes ();
209 Collections.sort (fields, new FieldDefinitionNameComparator ());
210 return fields;
211 }
212
213 private static class FieldDefinitionNameComparator implements Comparator <FieldDefinition>
214 {
215 @Override
216 public int compare (FieldDefinition o1, FieldDefinition o2)
217 {
218 return o1.getName ().toLowerCase ().compareTo (o2.getName ().toLowerCase ());
219 }
220
221 }
222
223 private Class getClass (String className)
224 {
225 try
226 {
227 return Class.forName (className);
228 }
229 catch (ClassNotFoundException ex)
230 {
231 return null;
232
233 }
234 }
235
236 private String formatAsString (List<String> discrepancies)
237 {
238 int i = 0;
239 StringBuilder builder = new StringBuilder ();
240 for (String discrep : discrepancies)
241 {
242 i ++;
243 builder.append (i + ". " + discrep + "\n");
244 }
245 return builder.toString ();
246 }
247
248 private String calcDataType (FieldDefinition fd)
249 {
250 if (fd.getDataType ().equals (DataType.COMPLEX))
251 {
252 if (fd.getDataObjectStructure () == null)
253 {
254 throw new IllegalArgumentException (
255 fd.getName () + " is complex but does not have a sub-structure defined");
256 }
257 Class subClazz = this.getClass (fd.getDataObjectStructure ().getName ());
258 String subStrucName = calcComplexSubStructureName (fd);
259
260
261 if (this.processSubstructures || subClazz == null)
262 {
263 if ( ! this.subStructuresAlreadyProcessed.contains (
264 fd.getDataObjectStructure ()))
265 {
266
267 this.subStructuresToProcess.put (subStrucName, fd.getDataObjectStructure ());
268 }
269 }
270 return "[" + calcNotSoSimpleName (subStrucName) + "|#" + subStrucName + "]";
271 }
272 return fd.getDataType ().toString ();
273 }
274
275 private String calcDefaultValue (FieldDefinition fd)
276 {
277 if (fd.getDefaultValue () != null)
278 {
279 return fd.getDefaultValue ().toString ();
280 }
281 return " ";
282 }
283
284
285 private String calcDynamicOrHidden (FieldDefinition fd)
286 {
287 if (fd.isHide ())
288 {
289 if (fd.isDynamic ())
290 {
291 return "dynamic and hidden";
292 }
293 return "hidden";
294 }
295 if (fd.isDynamic ())
296 {
297 return "dynamic";
298 }
299 return " ";
300 }
301
302 private String calcComplexSubStructureName (FieldDefinition fd)
303 {
304 if (this.processSubstructures)
305 {
306 return name + "." + fd.getName () + "." + calcSimpleName (
307 fd.getDataObjectStructure ().getName ());
308 }
309 return calcSimpleName (fd.getDataObjectStructure ().getName ());
310 }
311
312 private String calcSimpleName (String name)
313 {
314 if (name.lastIndexOf (".") != -1)
315 {
316 name = name.substring (name.lastIndexOf (".") + 1);
317 }
318 return name;
319 }
320
321 private String calcNotSoSimpleName (String name)
322 {
323 if (name.lastIndexOf (".") == -1)
324 {
325 return name;
326 }
327 String simpleName = calcSimpleName (name);
328 String fieldName = calcSimpleName (name.substring (0, name.length ()
329 - simpleName.length ()
330 - 1));
331 return fieldName + "." + simpleName;
332 }
333
334 private String calcRequired (FieldDefinition fd)
335 {
336 if (fd.getMaxOccurs () != null)
337 {
338 if ( ! fd.getMaxOccurs ().equals (UNBOUNDED))
339 {
340 if (Integer.parseInt (fd.getMaxOccurs ()) == 0)
341 {
342 return "Not allowed";
343 }
344 }
345 }
346
347 if (fd.getMinOccurs () != null)
348 {
349 if (fd.getMinOccurs () >= 1)
350 {
351 return "required";
352 }
353 }
354
355 return " ";
356
357 }
358 private static final String LINK_TO_DEFINITIONS =
359 "KULSTG:Formatted View of Base Dictionary#Valid Character Definitions";
360
361 private String calcValidChars (FieldDefinition fd)
362 {
363 if (fd.getValidChars () == null)
364 {
365 return " ";
366 }
367 return calcValidChars (fd.getValidChars ());
368 }
369
370 private String calcValidChars (ValidCharsConstraint cons)
371 {
372 String labelKey = cons.getLabelKey();
373 if (labelKey == null)
374 {
375 labelKey = "validation.validChars";
376 }
377 String validChars = escapeWiki (cons.getValue ());
378 String descr = "[" + labelKey + "|" + LINK_TO_DEFINITIONS + "]" + "\\\\\n"
379 + validChars;
380 return descr;
381 }
382
383 private String escapeWiki (String str)
384 {
385 StringBuilder bldr = new StringBuilder (str.length ());
386 for (int i = 0; i < str.length (); i ++)
387 {
388 char c = str.charAt (i);
389 switch (c)
390 {
391 case '{':
392 case '}':
393 case '[':
394 case ']':
395 case '|':
396 bldr.append ('\\');
397 }
398 bldr.append (c);
399 }
400 return bldr.toString ();
401 }
402
403 private String calcLookup (FieldDefinition fd)
404 {
405 if (fd.getLookupDefinition () == null)
406 {
407 return " ";
408 }
409 return calcLookup (fd.getLookupDefinition ());
410 }
411
412 private String calcLookup (LookupConstraint lc)
413 {
414 StringBuilder bldr = new StringBuilder ();
415 bldr.append (lc.getId ());
416
417
418
419 String and = "";
420 bldr.append ("\\\\");
421 bldr.append ("\n");
422 bldr.append ("Implemented using search: ");
423 String searchPage = calcWikiSearchPage (lc.getSearchTypeId ());
424 bldr.append ("[" + lc.getSearchTypeId () + "|" + searchPage + "#"
425 + lc.getSearchTypeId () + "]");
426 List<CommonLookupParam> configuredParameters = filterConfiguredParams (
427 lc.getParams ());
428 if (configuredParameters.size () > 0)
429 {
430 bldr.append ("\\\\");
431 bldr.append ("\n");
432 bldr.append (" where ");
433 and = "";
434 for (CommonLookupParam param : configuredParameters)
435 {
436 bldr.append (and);
437 and = " and ";
438 bldr.append (param.getName ());
439 bldr.append ("=");
440 if (param.getDefaultValueString () != null)
441 {
442 bldr.append (param.getDefaultValueString ());
443 continue;
444 }
445 if (param.getDefaultValueList () != null)
446 {
447 String comma = "";
448 for (String defValue : param.getDefaultValueList ())
449 {
450 bldr.append (comma);
451 comma = ", ";
452 bldr.append (defValue);
453 }
454 }
455 }
456 }
457 return bldr.toString ();
458 }
459
460 private String calcValidCharsMinMax (FieldDefinition fd)
461 {
462 String validChars = calcValidChars (fd);
463 String minMax = calcMinMax (fd);
464 String and = " and ";
465 if (validChars.trim ().equals (""))
466 {
467 return minMax;
468 }
469 if (minMax.trim ().equals (""))
470 {
471 return validChars;
472 }
473 return validChars + "\\\\\n" + minMax;
474 }
475
476 private String calcMinMax (FieldDefinition fd)
477 {
478 if (fd.getExclusiveMin () == null)
479 {
480 if (fd.getInclusiveMax () == null)
481 {
482 return " ";
483 }
484 return "Must be <= " + fd.getInclusiveMax ();
485 }
486 if (fd.getInclusiveMax () == null)
487 {
488 return "Must be > " + fd.getExclusiveMin ();
489 }
490 return "Must be > " + fd.getExclusiveMin () + " and < "
491 + fd.getInclusiveMax ();
492 }
493 private static final String PAGE_PREFIX = "Formatted View of ";
494 private static final String PAGE_SUFFIX = " Searches";
495
496 private String calcWikiSearchPage (String searchType)
497 {
498 return PAGE_PREFIX + calcWikigPageAbbrev (searchType) + PAGE_SUFFIX;
499 }
500
501 private String calcWikigPageAbbrev (String searchType)
502 {
503 if (searchType == null)
504 {
505 return null;
506 }
507 if (searchType.equals ("enumeration.management.search"))
508 {
509 return "EM";
510 }
511 if (searchType.startsWith ("lu."))
512 {
513 return "LU";
514 }
515 if (searchType.startsWith ("cluset."))
516 {
517 return "LU";
518 }
519 if (searchType.startsWith ("lo."))
520 {
521 return "LO";
522 }
523 if (searchType.startsWith ("lrc."))
524 {
525 return "LRC";
526 }
527 if (searchType.startsWith ("comment."))
528 {
529 return "Comment";
530 }
531 if (searchType.startsWith ("org."))
532 {
533 return "Organization";
534 }
535 if (searchType.startsWith ("atp."))
536 {
537 return "ATP";
538 }
539 if (searchType.startsWith ("subjectCode."))
540 {
541 return "SC";
542 }
543 throw new IllegalArgumentException ("Unknown type of search: " + searchType);
544 }
545
546 private List<CommonLookupParam> filterConfiguredParams (
547 List<CommonLookupParam> params)
548 {
549 List list = new ArrayList ();
550 if (params == null)
551 {
552 return list;
553 }
554 if (params.size () == 0)
555 {
556 return list;
557 }
558 for (CommonLookupParam param : params)
559 {
560 if (param.getDefaultValueString () != null)
561 {
562 list.add (param);
563 continue;
564 }
565 if (param.getDefaultValueList () != null)
566 {
567 list.add (param);
568 }
569 }
570 return list;
571 }
572
573 private String calcRepeating (FieldDefinition fd)
574 {
575 if (fd.getMaxOccurs () == null)
576 {
577 return "???";
578 }
579 if (fd.getMaxOccurs ().equals (UNBOUNDED))
580 {
581 if (fd.getMinOccurs () != null && fd.getMinOccurs () > 1)
582 {
583 return "repeating: minimum " + fd.getMinOccurs () + " times";
584 }
585 return "repeating: unlimited";
586 }
587 if (Integer.parseInt (fd.getMaxOccurs ()) == 0)
588 {
589 return "NOT USED";
590 }
591 if (Integer.parseInt (fd.getMaxOccurs ()) == 1)
592 {
593 return " ";
594
595 }
596
597 if (fd.getMinOccurs () != null)
598 {
599 if (fd.getMinOccurs () > 1)
600 {
601 return "repeating: " + fd.getMinOccurs () + " to " + fd.getMaxOccurs ()
602 + " times";
603 }
604 }
605 return "repeating: maximum " + fd.getMaxOccurs () + " times";
606 }
607
608 private String calcLength (FieldDefinition fd)
609 {
610 if (fd.getMaxLength () != null)
611 {
612 if (fd.getMinLength () != null && fd.getMinLength () != 0)
613 {
614 if (Integer.parseInt (fd.getMaxLength ()) == fd.getMinLength ())
615 {
616 return ("(must be " + fd.getMaxLength () + ")");
617 }
618 return "(" + fd.getMinLength () + " to " + fd.getMaxLength () + ")";
619 }
620 return "(up to " + fd.getMaxLength () + ")";
621 }
622 if (fd.getMinLength () != null)
623 {
624 return "(over " + fd.getMinLength () + ")";
625 }
626 return " ";
627 }
628
629 private String calcCrossField (FieldDefinition fd)
630 {
631 StringBuilder b = new StringBuilder ();
632 String semicolon = "";
633 String cfr = calcCrossFieldRequire (fd);
634 if (cfr != null)
635 {
636 b.append (semicolon);
637 semicolon = "; ";
638 b.append (cfr);
639 }
640 String cfw = calcCrossFieldWhen (fd);
641 if (cfw != null)
642 {
643 b.append (semicolon);
644 semicolon = "; ";
645 b.append (cfw);
646 }
647 if (b.length () == 0)
648 {
649 return " ";
650 }
651 return b.toString ();
652 }
653
654 private String calcCrossFieldRequire (FieldDefinition fd)
655 {
656 if (fd.getRequireConstraint () == null)
657 {
658 return null;
659 }
660 if (fd.getRequireConstraint ().size () == 0)
661 {
662 return null;
663 }
664 StringBuilder b = new StringBuilder ();
665 String comma = "";
666 b.append ("if not empty then ");
667 for (RequiredConstraint rc : fd.getRequireConstraint ())
668 {
669 b.append (comma);
670 comma = ", ";
671 b.append (rc.getFieldPath ());
672 }
673 if (fd.getRequireConstraint ().size () == 1)
674 {
675 b.append (" is");
676 }
677 else
678 {
679 b.append (" are");
680 }
681 b.append (" also required");
682 return b.toString ();
683 }
684
685 private String calcCrossFieldWhen (FieldDefinition fd)
686 {
687 if (fd.getCaseConstraint () == null)
688 {
689 return null;
690 }
691 StringBuilder b = new StringBuilder ();
692 CaseConstraint cc = fd.getCaseConstraint ();
693 for (WhenConstraint wc : cc.getWhenConstraint ())
694 {
695 b.append ("\\\\");
696 b.append ("\n");
697 b.append ("when ");
698 b.append (cc.getFieldPath ());
699 b.append (" ");
700 if ( ! cc.isCaseSensitive ())
701 {
702 b.append ("ignoring case ");
703 }
704 b.append (cc.getOperator ());
705 b.append (" ");
706
707 b.append ("\\\\");
708 b.append ("\n");
709 String comma = "";
710 for (Object value : wc.getValues ())
711 {
712 b.append (comma);
713 comma = " or ";
714 b.append (asString (value));
715 }
716 b.append ("\\\\");
717 b.append ("\n");
718 b.append ("then override constraint:"
719 + calcOverride (fd, wc.getConstraint ()));
720 }
721 return b.toString ();
722 }
723
724 private String calcOverride (FieldDefinition fd, Constraint cons)
725 {
726 StringBuilder b = new StringBuilder ();
727 b.append (calcOverride ("serviceSide", fd.isServerSide (),
728 cons.isServerSide ()));
729 b.append (calcOverride ("exclusiveMin", fd.getExclusiveMin (),
730 cons.getExclusiveMin ()));
731 b.append (calcOverride ("inclusiveMax", fd.getInclusiveMax (),
732 cons.getInclusiveMax ()));
733 String minOccursMessage = calcOverride ("minOccurs", fd.getMinOccurs (),
734 cons.getMinOccurs ());
735 if ( ! minOccursMessage.trim ().equals (""))
736 {
737 if (cons.getMinOccurs () != null && cons.getMinOccurs () == 1)
738 {
739 minOccursMessage = " REQUIRED";
740 }
741 }
742 b.append (minOccursMessage);
743 b.append (calcOverride ("validchars", fd.getValidChars (),
744 cons.getValidChars ()));
745 b.append (calcOverride ("lookup", fd.getLookupDefinition (),
746 cons.getLookupDefinition ()));
747
748 return b.toString ();
749 }
750
751 private String calcOverride (String attribute, LookupConstraint val1,
752 LookupConstraint val2)
753 {
754 if (val1 == val2)
755 {
756 return "";
757 }
758 if (val1 == null && val2 != null)
759 {
760 return " add lookup " + this.calcLookup (val2);
761 }
762 if (val1 != null && val2 == null)
763 {
764 return " remove lookup constraint";
765 }
766 return " change lookup to " + calcLookup (val2);
767 }
768
769 private String calcOverride (String attribute, ValidCharsConstraint val1,
770 ValidCharsConstraint val2)
771 {
772 if (val1 == val2)
773 {
774 return "";
775 }
776 if (val1 == null && val2 != null)
777 {
778 return " add validchars " + calcValidChars (val2);
779 }
780 if (val1 != null && val2 == null)
781 {
782 return " remove validchars constraint";
783 }
784 return " change validchars to " + calcValidChars (val2);
785 }
786
787 private String calcOverride (String attribute, boolean val1, boolean val2)
788 {
789 if (val1 == val2)
790 {
791 return "";
792 }
793 return " " + attribute + "=" + val2;
794 }
795
796 private String calcOverride (String attribute, String val1, String val2)
797 {
798 if (val1 == null && val2 == null)
799 {
800 return "";
801 }
802 if (val1 == val2)
803 {
804 return "";
805 }
806 if (val1 == null)
807 {
808 return " " + attribute + "=" + val2;
809 }
810 if (val1.equals (val2))
811 {
812 return "";
813 }
814 return " " + attribute + "=" + val2;
815 }
816
817 private String calcOverride (String attribute, Object val1, Object val2)
818 {
819 if (val1 == null && val2 == null)
820 {
821 return "";
822 }
823 if (val1 == val2)
824 {
825 return "";
826 }
827 if (val1 == null)
828 {
829 return " " + attribute + "=" + val2;
830 }
831 if (val1.equals (val2))
832 {
833 return "";
834 }
835 return " " + attribute + "=" + asString (val2);
836 }
837
838 private String asString (Object value)
839 {
840 if (value == null)
841 {
842 return "null";
843 }
844 if (value instanceof String)
845 {
846 return (String) value;
847 }
848 return value.toString ();
849 }
850 }