View Javadoc

1   /**
2    * Copyright 2004-2013 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.opensource.org/licenses/ecl2.php
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.OutputStream;
22  import java.io.PrintStream;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.Comparator;
26  import java.util.Date;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Stack;
30  
31  import org.kuali.rice.krad.datadictionary.AttributeDefinition;
32  import org.kuali.rice.krad.datadictionary.AttributeDefinitionBase;
33  import org.kuali.rice.krad.datadictionary.CollectionDefinition;
34  import org.kuali.rice.krad.datadictionary.ComplexAttributeDefinition;
35  import org.kuali.rice.krad.datadictionary.DataObjectEntry;
36  import org.kuali.rice.krad.datadictionary.validation.constraint.BaseConstraint;
37  import org.kuali.rice.krad.datadictionary.validation.constraint.CaseConstraint;
38  import org.kuali.rice.krad.datadictionary.validation.constraint.CommonLookupParam;
39  import org.kuali.rice.krad.datadictionary.validation.constraint.LookupConstraint;
40  import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint;
41  import org.kuali.rice.krad.datadictionary.validation.constraint.WhenConstraint;
42  import org.kuali.rice.krad.uif.control.Control;
43  import org.kuali.rice.krad.uif.control.TextControl;
44  import org.kuali.student.contract.model.impl.ServiceContractModelPescXsdLoader;
45  import org.kuali.student.contract.model.util.VersionLinesUtility;
46  import org.slf4j.Logger;
47  import org.slf4j.LoggerFactory;
48  
49  public class DictionaryFormatter {
50  
51      private static Logger log = LoggerFactory.getLogger(DictionaryFormatter.class);
52      
53      private DataObjectEntry doe;
54      private Map<String, DataObjectEntry> beansOfType;
55      private String beanId;
56      private String outputFileName;
57  
58      public DictionaryFormatter(DataObjectEntry doe, Map<String, DataObjectEntry> beansOfType, String beanId, String outputFileName) {
59          this.doe = doe;
60          this.beansOfType = beansOfType;
61          this.beanId = beanId;
62          this.outputFileName = outputFileName;
63      }
64  
65      public void formatForHtml(String projectVersion, String formattedDate) {
66          File file = new File(this.outputFileName);
67          OutputStream outputStream;
68          try {
69              outputStream = new FileOutputStream(file, false);
70          } catch (FileNotFoundException ex) {
71              throw new IllegalArgumentException(this.outputFileName, ex);
72          }
73          PrintStream out = new PrintStream(outputStream);
74          writeHeader(out, beanId);
75          writeBody(out, projectVersion, formattedDate);
76          writeFooter(out);
77          out.close();
78      }
79  
80      public static void writeHeader(PrintStream out, String title) {
81          out.println("<html>");
82          out.println("<head>");
83          writeTag(out, "title", title);
84          out.println ("<style>li.invalid { background: red; }</style>");
85          out.println("</head>");
86          out.println("<body bgcolor=\"#ffffff\" topmargin=0 marginheight=0>");
87      }
88  
89      public static void writeFooter(PrintStream out) {
90          out.println("</body>");
91          out.println("</html>");
92      }
93  
94      private String initUpper(String str) {
95          if (str == null) {
96              return null;
97          }
98          if (str.length() == 0) {
99              return str;
100         }
101         if (str.length() == 1) {
102             return str.toUpperCase();
103         }
104         return str.substring(0, 1).toUpperCase() + str.substring(1);
105     }
106 
107     private void writeBody(PrintStream out, String projectVersion, String formattedDate) {
108     	
109     	VersionLinesUtility.writeVersionTag(out, "<a href=\"index.html\">home</a>", "<a href=\"../contractdocs/" + initUpper(doe.getName()) + ".html\">contract doc</a>", projectVersion, formattedDate);
110 //  builder.append ("======= start dump of object structure definition ========");
111         out.println("<h1>" + this.beanId + "</h1>");
112 
113         out.println("<br>");
114         out.println("<table border=1>");
115 
116         out.println("<tr>");
117         out.println("<th bgcolor=lightblue>");
118         out.println("Name");
119         out.println("</th>");
120         out.println("<td>");
121         out.println(doe.getName());
122         out.println("</td>");
123         out.println("</tr>");
124 
125         out.println("<tr>");
126         out.println("<th bgcolor=lightblue>");
127         out.println("Label");
128         out.println("</th>");
129         out.println("<td>");
130         out.println(doe.getObjectLabel());
131         out.println("</td>");
132         out.println("</tr>");
133 
134         out.println("<tr>");
135         out.println("<th bgcolor=lightblue>");
136         out.println("JSTL Key");
137         out.println("</th>");
138         out.println("<td>");
139         out.println(doe.getJstlKey());
140         out.println("</td>");
141         out.println("</tr>");
142 
143         out.println("<tr>");
144         out.println("<th bgcolor=lightblue>");
145         out.println("Java Class");
146         out.println("</th>");
147         out.println("<td>");
148         out.println(doe.getFullClassName());
149         out.println("</td>");
150         out.println("</tr>");
151         out.println("<tr>");
152 
153         if (!doe.getDataObjectClass().getName().equals(doe.getFullClassName())) {
154             out.println("<tr>");
155             out.println("<th bgcolor=lightblue>");
156             out.println("Object Class");
157             out.println("</th>");
158             out.println("<td>");
159             out.println(doe.getDataObjectClass().getName());
160             out.println("</td>");
161             out.println("</tr>");
162             out.println("<tr>");
163         }
164 
165         if (!doe.getEntryClass().getName().equals(doe.getFullClassName())) {
166             out.println("<tr>");
167             out.println("<th bgcolor=lightblue>");
168             out.println("Entry Class");
169             out.println("</th>");
170             out.println("<td>");
171             out.println(doe.getEntryClass().getName());
172             out.println("</td>");
173             out.println("</tr>");
174             out.println("<tr>");
175         }
176 
177         out.println("<tr>");
178         out.println("<th bgcolor=lightblue>");
179         out.println("Description");
180         out.println("</th>");
181         out.println("<td>");
182         out.println(doe.getObjectDescription());
183         out.println("</td>");
184         out.println("</tr>");
185 
186         out.println("<tr>");
187         out.println("<th bgcolor=lightblue>");
188         out.println("Primary Key(s)");
189         out.println("</th>");
190         out.println("<td>");
191         StringBuilder bldr = new StringBuilder();
192         String comma = "";
193         if (doe.getPrimaryKeys() != null) {
194             for (String pk : doe.getPrimaryKeys()) {
195                 bldr.append(comma);
196                 comma = ", ";
197                 bldr.append(pk);
198             }
199         }
200         out.println(bldr.toString());
201         out.println("</td>");
202         out.println("</tr>");
203 
204         out.println("<tr>");
205         out.println("<th bgcolor=lightblue>");
206         out.println("Field to use as the title (or name)");
207         out.println("</th>");
208         out.println("<td>");
209         out.println(doe.getTitleAttribute());
210         out.println("</td>");
211         out.println("</tr>");
212 
213         out.println("</table>");
214 //        out.println("<br>");
215 
216         // fields
217         out.println("<h1>Field Definitions</h1>");
218         // check for discrepancies first
219         List<String> discrepancies = new Dictionary2BeanComparer(doe.getFullClassName(), doe).compare();
220         if (discrepancies.isEmpty()) {
221             out.println("No discrepancies were found between the dictionary definition and the java object -- ");
222             out.println("WARNING: take this with a grain of salt - the comparison does not dig into complex sub-objects nor collections so...");
223         } else {
224             out.println("<b>" + discrepancies.size() + " discrepancie(s) were found between the dictionary definition and the java object" + "</b>");
225             out.println("<ol>");
226             for (String discrepancy : discrepancies) {
227                 out.println("<li>" + discrepancy);
228             }
229             out.println("</ol>");
230         }
231         // field table
232         out.println("<table border=1>");
233         out.println("<tr bgcolor=lightblue>");
234         out.println("<th>");
235         out.println("Field");
236         out.println("</th>");
237         out.println("<th>");
238         out.println("Required?");
239         out.println("</th>");
240         out.println("<th>");
241         out.println("DataType");
242         out.println("</th>");
243         out.println("<th>");
244         out.println("Length");
245         out.println("</th>");
246         out.println("<th>");
247         out.println("Short Label");
248         out.println("</th>");
249         out.println("<th>");
250         out.println("Summary");
251         out.println("</th>");
252         out.println("<th>");
253         out.println("Label");
254         out.println("</th>");
255         out.println("<th>");
256         out.println("Description");
257         out.println("</th>");
258         out.println("<th>");
259         out.println("Read Only, Dynamic, or Hidden");
260         out.println("</th>");
261         out.println("<th>");
262         out.println("Default");
263         out.println("</th>");
264         out.println("<th>");
265         out.println("Repeats?");
266         out.println("</th>");
267         out.println("<th>");
268         out.println("Valid Characters");
269         out.println("</th>");
270         out.println("<th>");
271         out.println("Lookup");
272         out.println("</th>");
273         out.println("<th>");
274         out.println("Cross Field");
275         out.println("</th>");
276         out.println("<th>");
277         out.println("Default Control");
278         out.println("</th>");
279         out.println("</tr>");
280         this.writeAttributes(out, doe, new Stack<String>(), new Stack<DataObjectEntry>());
281         out.println("</table>");
282         return;
283     }
284 
285     private void writeAttributes(PrintStream out, DataObjectEntry ode, Stack<String> parentNames, Stack<DataObjectEntry> parents) {
286         // stop recursion
287         if (parents.contains(ode)) {
288             return;
289         }
290 //        for (AttributeDefinition ad : getSortedFields()) {
291         if (ode.getAttributes() != null) {
292             for (AttributeDefinition ad : ode.getAttributes()) {
293                 out.println("<tr>");
294                 out.println("<td>");
295                 out.println(nbsp(calcName(ad.getName(), parentNames)));
296                 out.println("</td>");
297                 out.println("<td>");
298                 out.println(nbsp(calcRequired(ad)));
299                 out.println("</td>");
300                 out.println("<td>");
301                 out.println(nbsp(calcDataType(ad)));
302                 out.println("</td>");
303                 out.println("<td>");
304                 out.println(nbsp(calcLength(ad)));
305                 out.println("</td>");
306                 out.println("<td>");
307                 out.println(nbsp(calcShortLabel(ad)));
308                 out.println("</td>");
309                 out.println("<td>");
310                 out.println(nbsp(calcSummary(ad)));
311                 out.println("</td>");
312                 out.println("<td>");
313                 out.println(nbsp(calcLabel(ad)));
314                 out.println("</td>");
315                 out.println("<td>");
316                 out.println(nbsp(calcDescription(ad)));
317                 out.println("</td>");
318                 out.println("<td>");
319                 out.println(nbsp(calcDynamicHiddenReadOnly(ad)));
320                 out.println("</td>");
321                 out.println("<td>");
322                 out.println(nbsp(calcDefaultValue(ad)));
323                 out.println("</td>");
324                 out.println("<td>");
325                 out.println(nbsp(null));
326                 out.println("</td>");
327                 out.println("<td>");
328                 out.println(nbsp(calcForceUpperValidCharsMinMax(ad)));
329                 out.println("</td>");
330                 out.println("<td>");
331                 out.println(nbsp(calcLookup(ad)));
332                 out.println("</td>");
333                 out.println("<td>");
334                 out.println(nbsp(calcCrossField(ad)));
335                 out.println("</td>");
336                 out.println("<td>");
337                 out.println(nbsp(calcControl(ad)));
338                 out.println("</td>");
339                 out.println("</tr>");
340             }
341         }
342         if (ode.getComplexAttributes() != null) {
343             for (ComplexAttributeDefinition cad : ode.getComplexAttributes()) {
344                 out.println("<tr>");
345                 out.println("<td>");
346                 out.println(nbsp(calcName(cad.getName(), parentNames)));
347                 out.println("</td>");
348                 out.println("<td>");
349                 out.println(nbsp(calcRequired(cad)));
350                 out.println("</td>");
351                 out.println("<td>");
352                 out.println("Complex");
353                 out.println("</td>");
354                 out.println("<td>");
355                 out.println(nbsp(null));
356                 out.println("</td>");
357                 out.println("<td>");
358                 out.println(nbsp(calcShortLabel(cad)));
359                 out.println("</td>");
360                 out.println("<td>");
361                 out.println(nbsp(calcSummary(cad)));
362                 out.println("</td>");
363                 out.println("<td>");
364                 out.println(nbsp(calcLabel(cad)));
365                 out.println("</td>");
366                 out.println("<td>");
367                 out.println(nbsp(calcDescription(cad)));
368                 out.println("</td>");
369                 out.println("<td>");
370                 out.println(nbsp(null));
371                 out.println("</td>");
372                 out.println("<td>");
373                 out.println(nbsp(null));
374                 out.println("</td>");
375                 out.println("<td>");
376                 out.println(nbsp(null));
377                 out.println("</td>");
378                 out.println("<td>");
379                 out.println(nbsp(null));
380                 out.println("</td>");
381                 out.println("<td>");
382                 out.println(nbsp(null));
383                 out.println("</td>");
384                 out.println("<td>");
385                 out.println(nbsp(null));
386                 out.println("</td>");
387                 out.println("<td>");
388                 out.println(nbsp(null));
389                 out.println("</td>");
390                 out.println("</tr>");
391                 parentNames.push(cad.getName());
392                 parents.push(ode);
393                 this.writeAttributes(out, (DataObjectEntry) cad.getDataObjectEntry(), parentNames, parents);
394                 parentNames.pop();
395                 parents.pop();
396             }
397         }
398         if (ode.getCollections() != null) {
399             for (CollectionDefinition cd : ode.getCollections()) {
400                 out.println("<tr>");
401                 out.println("<td>");
402                 out.println(nbsp(calcName(cd.getName(), parentNames)));
403                 out.println("</td>");
404                 out.println("<td>");
405                 out.println(nbsp(calcRequired(cd)));
406                 out.println("</td>");
407                 out.println("<td>");
408                 out.println("Complex");
409                 out.println("</td>");
410                 out.println("<td>");
411                 out.println(nbsp(null));
412                 out.println("</td>");
413                 out.println("<td>");
414                 out.println(nbsp(calcShortLabel(cd)));
415                 out.println("</td>");
416                 out.println("<td>");
417                 out.println(nbsp(calcSummary(cd)));
418                 out.println("</td>");
419                 out.println("<td>");
420                 out.println(nbsp(calcLabel(cd)));
421                 out.println("</td>");
422                 out.println("<td>");
423                 out.println(nbsp(calcDescription(cd)));
424                 out.println("</td>");
425                 out.println("<td>");
426                 out.println(nbsp(null));
427                 out.println("</td>");
428                 out.println("<td>");
429                 out.println(nbsp(null));
430                 out.println("</td>");
431                 out.println("<td>");
432                 out.println(nbsp("Repeating"));
433                 out.println("</td>");
434                 out.println("<td>");
435                 out.println(nbsp(null));
436                 out.println("</td>");
437                 out.println("<td>");
438                 out.println(nbsp(null));
439                 out.println("</td>");
440                 out.println("<td>");
441                 out.println(nbsp(null));
442                 out.println("</td>");
443                 out.println("<td>");
444                 out.println(nbsp(null));
445                 out.println("</td>");
446                 out.println("</tr>");
447                 DataObjectEntry childDoe = this.getDataOjbectEntry(cd.getDataObjectClass());
448                 if (childDoe == null) {
449                     // TODO: uncomment this but right now there are xml files that don't have one defined and it seems to work so...
450 //                    throw new NullPointerException ("Could not find a data object entry, " + cd.getDataObjectClass() + " for field " + calcName(cd.getName(), parents));
451                     log.warn("Could not find a data object entry, " + cd.getDataObjectClass() + " for field " + calcName(cd.getName(), parentNames));
452                 } else {
453                     parentNames.push(cd.getName());
454                     parents.push(ode);
455                     this.writeAttributes(out, (DataObjectEntry) childDoe, parentNames, parents);
456                     parentNames.pop();
457                     parents.pop();
458                 }
459             }
460         }
461     }
462 
463     private DataObjectEntry getDataOjbectEntry(String className) {
464         for (DataObjectEntry doe : this.beansOfType.values()) {
465             if (doe.getDataObjectClass().getName().equals(className)) {
466                 return doe;
467             }
468         }
469         return null;
470     }
471 
472     private String calcName(String name, Stack<String> parents) {
473         StringBuilder sb = new StringBuilder();
474         for (String parent : parents) {
475             sb.append(parent);
476             sb.append(".");
477         }
478         sb.append(name);
479         return sb.toString();
480     }
481 
482     private String calcShortLabel(CollectionDefinition cd) {
483         return cd.getShortLabel();
484     }
485 
486     private String calcShortLabel(AttributeDefinitionBase ad) {
487         return ad.getShortLabel();
488     }
489 
490     private String calcLabel(CollectionDefinition cd) {
491         return cd.getLabel();
492     }
493 
494     private String calcLabel(AttributeDefinitionBase ad) {
495         return ad.getLabel();
496     }
497 
498     private String calcSummary(CollectionDefinition ad) {
499         return ad.getSummary();
500     }
501 
502     private String calcSummary(AttributeDefinitionBase ad) {
503         return ad.getSummary();
504     }
505 
506     private String calcDescription(CollectionDefinition cd) {
507         return cd.getDescription();
508     }
509 
510     private String calcDescription(AttributeDefinitionBase ad) {
511         return ad.getDescription();
512     }
513 
514     private List<AttributeDefinition> getSortedFields() {
515         List<AttributeDefinition> fields = doe.getAttributes();
516         Collections.sort(fields, new AttributeDefinitionNameComparator());
517         return fields;
518     }
519 
520     private static class AttributeDefinitionNameComparator implements Comparator<AttributeDefinition> {
521 
522         @Override
523         public int compare(AttributeDefinition o1, AttributeDefinition o2) {
524             return o1.getName().toLowerCase().compareTo(o2.getName().toLowerCase());
525         }
526     }
527 
528     private String formatAsString(List<String> discrepancies) {
529         int i = 0;
530         StringBuilder builder = new StringBuilder();
531         for (String discrep : discrepancies) {
532             i++;
533             builder.append(i + ". " + discrep + "\n");
534         }
535         return builder.toString();
536     }
537 
538     private String calcDataType(AttributeDefinition ad) {
539 //        if (ad.getDataType().equals(DataType.COMPLEX)) {
540 //            if (ad.getDataObjectStructure() == null) {
541 //                throw new IllegalArgumentException(
542 //                        ad.getName() + " is complex but does not have a sub-structure defined");
543 //            }
544 //            Class subClazz = this.getClass(ad.getDataObjectStructure().getName());
545 //            String subStrucName = calcComplexSubStructureName(ad);
546 //            // process if explicity asking for substructures OR the field is a freestanding field
547 //            // so it won't be processed by just processing all of the DTO's and their sub-objects
548 //            if (this.processSubstructures || subClazz == null) {
549 //                if (!this.subStructuresAlreadyProcessed.contains(
550 //                        ad.getDataObjectStructure())) {
551 ////     System.out.println ("Adding " + subStrucName + " to set to be processed");
552 //                    this.subStructuresToProcess.put(subStrucName, ad.getDataObjectStructure());
553 //                }
554 //            }
555 //            return "[" + calcNotSoSimpleName(subStrucName) + "|#" + subStrucName + "]";
556 //        }
557         return ad.getDataType().toString();
558     }
559 
560     private String calcDefaultValue(AttributeDefinition ad) {
561 //        if (ad.getDefaultValue() != null) {
562 //            return ad.getDefaultValue().toString();
563 //        }
564         return " ";
565     }
566 
567     private String calcDynamicHiddenReadOnly(AttributeDefinition ad) {
568         StringBuilder sb = new StringBuilder();
569         String comma = "";
570         comma = this.appendIfNotNull(sb, this.calcDynamic(ad), comma);
571         comma = this.appendIfNotNull(sb, this.calcHidden(ad), comma);
572         comma = this.appendIfNotNull(sb, this.calcReadOnly(ad), comma);
573         return sb.toString();
574     }
575 
576     private String appendIfNotNull(StringBuilder sb, String value, String comma) {
577         if (value == null) {
578             return comma;
579         }
580         sb.append(comma);
581         sb.append(value);
582         return ", ";
583     }
584 
585     private String calcDynamic(AttributeDefinition ad) {
586         // TODO: implement once KRAD team implements
587 //        if (ad.isDynamic()) {
588 //            return "dynamic";
589 //        }
590         return null;
591     }
592 
593     private String calcHidden(AttributeDefinition ad) {
594         if (ad.getAttributeSecurity() == null) {
595             return null;
596         }
597         if (ad.getAttributeSecurity().isHide()) {
598             return "Hidden";
599         }
600         return null;
601 
602     }
603 
604     private String calcReadOnly(AttributeDefinition ad) {
605         if (ad.getAttributeSecurity() == null) {
606             return null;
607         }
608         if (ad.getAttributeSecurity().isReadOnly()) {
609             return "Read only";
610         }
611         return null;
612 
613     }
614 
615     private String calcComplexSubStructureName(AttributeDefinition ad) {
616 //        if (this.processSubstructures) {
617 //            return name + "." + ad.getName() + "." + calcSimpleName(
618 //                    ad.getDataObjectStructure().getName());
619 //        }
620 //        return calcSimpleName(ad.getDataObjectStructure().getName());
621         return " ";
622     }
623 
624     private String calcSimpleName(String simpleName) {
625         if (simpleName.lastIndexOf(".") != -1) {
626             simpleName = simpleName.substring(simpleName.lastIndexOf(".") + 1);
627         }
628         return simpleName;
629     }
630 
631     private String calcNotSoSimpleName(String name) {
632         if (name.lastIndexOf(".") == -1) {
633             return name;
634         }
635         String simpleName = calcSimpleName(name);
636         String fieldName = calcSimpleName(name.substring(0, name.length()
637                 - simpleName.length()
638                 - 1));
639         return fieldName + "." + simpleName;
640     }
641 
642     private String calcRequired(CollectionDefinition cd) {
643         if (cd.getMinOccurs() != null) {
644             if (cd.getMinOccurs() >= 1) {
645                 return "required";
646             }
647         }
648         // TODO: Deal with collections
649 //        if (ad.getMaximumNumberOfElements() != null) {
650 //            if (ad.getMaximumNumberOfElements().intValue() == 0) {
651 //                return "Not allowed";
652 //            }
653 //        }
654 //
655 //        if (ad.getMinimumNumberOfElements() != null) {
656 //            if (ad.getMinimumNumberOfElements().intValue() >= 1) {
657 //                return "required";
658 //            }
659 //        }
660         return " ";
661 //  return "optional";
662     }
663 
664     private String calcRequired(AttributeDefinitionBase ad) {
665         if (ad.isRequired() != null) {
666             if (ad.isRequired()) {
667                 return "required";
668             }
669         }
670         // TODO: Deal with collections
671 //        if (ad.getMaximumNumberOfElements() != null) {
672 //            if (ad.getMaximumNumberOfElements().intValue() == 0) {
673 //                return "Not allowed";
674 //            }
675 //        }
676 //
677 //        if (ad.getMinimumNumberOfElements() != null) {
678 //            if (ad.getMinimumNumberOfElements().intValue() >= 1) {
679 //                return "required";
680 //            }
681 //        }
682         return " ";
683 //  return "optional";
684     }
685 
686     private String calcForceUpperCase(AttributeDefinition ad) {
687         if (ad.getForceUppercase() != null && ad.getForceUppercase()) {
688             return "FORCE UPPER CASE";
689         }
690         return " ";
691     }
692 
693     private String calcValidChars(AttributeDefinition ad) {
694         if (ad.getValidCharactersConstraint() == null) {
695             return " ";
696         }
697         return calcValidChars(ad.getValidCharactersConstraint());
698     }
699 
700     private String calcValidChars(ValidCharactersConstraint cons) {
701         String messageKey = cons.getMessageKey();
702         if (messageKey == null) {
703             messageKey = "validation.validChars";
704         }
705         String validChars = escapeXML(cons.getValue());
706         String descr = messageKey + "<br>" + validChars;
707         return descr;
708     }
709 
710     private String calcLookup(AttributeDefinition ad) {
711         if (ad.getLookupDefinition() == null) {
712             return " ";
713         }
714         return calcLookup(ad.getLookupDefinition());
715     }
716 
717     private String calcLookup(LookupConstraint lc) {
718         StringBuilder bldr = new StringBuilder();
719         bldr.append(lc.getId());
720 //  this is the search description not the lookup description
721 //  builder.append (" - ");
722 //  builder.append (lc.getDesc ());
723         String and = "";
724         bldr.append("<br>");
725         bldr.append("\n");
726         bldr.append("Implemented using search: ");
727         String searchPage = calcWikiSearchPage(lc.getSearchTypeId());
728         bldr.append("[" + lc.getSearchTypeId() + "|" + searchPage + "#"
729                 + lc.getSearchTypeId() + "]");
730         List<CommonLookupParam> configuredParameters = filterConfiguredParams(
731                 lc.getParams());
732         if (configuredParameters.size() > 0) {
733             bldr.append("<br>");
734             bldr.append("\n");
735             bldr.append(" where ");
736             and = "";
737             for (CommonLookupParam param : configuredParameters) {
738                 bldr.append(and);
739                 and = " and ";
740                 bldr.append(param.getName());
741                 bldr.append("=");
742                 if (param.getDefaultValueString() != null) {
743                     bldr.append(param.getDefaultValueString());
744                     continue;
745                 }
746                 if (param.getDefaultValueList() != null) {
747                     String comma = "";
748                     for (String defValue : param.getDefaultValueList()) {
749                         bldr.append(comma);
750                         comma = ", ";
751                         bldr.append(defValue);
752                     }
753                 }
754             }
755         }
756         return bldr.toString();
757     }
758 
759     private String calcForceUpperValidCharsMinMax(AttributeDefinition ad) {
760         StringBuilder bldr = new StringBuilder();
761         String brk = "";
762         String forceUpper = calcForceUpperCase(ad);
763         if (!forceUpper.trim().isEmpty()) {
764             bldr.append(forceUpper);
765             brk = "<BR>";
766         }
767 
768         String validChars = calcValidChars(ad);
769         if (!validChars.trim().isEmpty()) {
770             bldr.append(brk);
771             brk = "<BR>";
772             bldr.append(validChars);
773         }
774 
775         String minMax = calcMinMax(ad);
776         if (!minMax.trim().isEmpty()) {
777             bldr.append(brk);
778             brk = "<BR>";
779             bldr.append(minMax);
780         }
781 
782         return bldr.toString();
783     }
784 
785     private String calcMinMax(AttributeDefinition ad) {
786         if (ad.getExclusiveMin() == null) {
787             if (ad.getInclusiveMax() == null) {
788                 return " ";
789             }
790             return "Must be <= " + ad.getInclusiveMax();
791         }
792         if (ad.getInclusiveMax() == null) {
793             return "Must be > " + ad.getExclusiveMin();
794         }
795         return "Must be > " + ad.getExclusiveMin() + " and < "
796                 + ad.getInclusiveMax();
797     }
798     private static final String PAGE_PREFIX = "Formatted View of ";
799     private static final String PAGE_SUFFIX = " Searches";
800 
801     private String calcWikiSearchPage(String searchType) {
802         return PAGE_PREFIX + calcWikigPageAbbrev(searchType) + PAGE_SUFFIX;
803     }
804 
805     private String calcWikigPageAbbrev(String searchType) {
806         if (searchType == null) {
807             return null;
808         }
809         if (searchType.equals("enumeration.management.search")) {
810             return "EM";
811         }
812         if (searchType.startsWith("lu.")) {
813             return "LU";
814         }
815         if (searchType.startsWith("cluset.")) {
816             return "LU";
817         }
818         if (searchType.startsWith("lo.")) {
819             return "LO";
820         }
821         if (searchType.startsWith("lrc.")) {
822             return "LRC";
823         }
824         if (searchType.startsWith("comment.")) {
825             return "Comment";
826         }
827         if (searchType.startsWith("org.")) {
828             return "Organization";
829         }
830         if (searchType.startsWith("atp.")) {
831             return "ATP";
832         }
833         throw new IllegalArgumentException("Unknown type of search: " + searchType);
834     }
835 
836     private List<CommonLookupParam> filterConfiguredParams(
837             List<CommonLookupParam> params) {
838         List list = new ArrayList();
839         if (params == null) {
840             return list;
841         }
842         if (params.size() == 0) {
843             return list;
844         }
845         for (CommonLookupParam param : params) {
846             if (param.getDefaultValueString() != null) {
847                 list.add(param);
848                 continue;
849             }
850             if (param.getDefaultValueList() != null) {
851                 list.add(param);
852             }
853         }
854         return list;
855     }
856 
857     private String calcLength(AttributeDefinition ad) {
858         if (ad.getMaxLength() != null) {
859             if (ad.getMinLength() != null && ad.getMinLength() != 0) {
860                 if (ad.getMaxLength() == ad.getMinLength()) {
861                     return ("must be " + ad.getMaxLength());
862                 }
863                 return ad.getMinLength() + " to " + ad.getMaxLength();
864             }
865             return "up to " + ad.getMaxLength();
866         }
867         if (ad.getMinLength() != null) {
868             return "at least " + ad.getMinLength();
869         }
870         return " ";
871     }
872 
873     private String calcControl(AttributeDefinition ad) {
874         Control control = ad.getControlField();
875         if (control == null) {
876             return " ";
877         }
878         if (control instanceof TextControl) {
879             TextControl textControl = (TextControl) control;
880             if (textControl.getDatePicker() != null) {
881                 return "DateControl";
882             }
883             if (!textControl.getStyleClassesAsString().isEmpty()) {
884                 if (textControl.getStyleClassesAsString().contains("amount")) {
885                     return "CurrencyControl";
886                 }
887             }
888         }
889         return control.getClass().getSimpleName();
890     }
891 
892     private String calcCrossField(AttributeDefinition ad) {
893         StringBuilder b = new StringBuilder();
894         String semicolon = "";
895         String cfr = calcCrossFieldRequire(ad);
896         if (cfr != null) {
897             b.append(semicolon);
898             semicolon = "; ";
899             b.append(cfr);
900         }
901         String cfw = calcCrossFieldWhen(ad);
902         if (cfw != null) {
903             b.append(semicolon);
904             semicolon = "; ";
905             b.append(cfw);
906         }
907         if (b.length() == 0) {
908             return " ";
909         }
910         return b.toString();
911     }
912 
913     private String calcCrossFieldRequire(AttributeDefinitionBase ad) {
914 //        if (ad.getRequireConstraint() == null) {
915 //            return null;
916 //        }
917 //        if (ad.getRequireConstraint().size() == 0) {
918 //            return null;
919 //        }
920         StringBuilder b = new StringBuilder();
921 //        String comma = "";
922 //        b.append("if not empty then ");
923 //        for (RequiredConstraint rc : ad.getRequireConstraint()) {
924 //            b.append(comma);
925 //            comma = ", ";
926 //            b.append(rc.getPropertyName());
927 //        }
928 //        if (ad.getRequireConstraint().size() == 1) {
929 //            b.append(" is");
930 //        } else {
931 //            b.append(" are");
932 //        }
933 //        b.append(" also required");
934         return b.toString();
935     }
936 
937     private String calcCrossFieldWhen(AttributeDefinition ad) {
938         if (ad.getCaseConstraint() == null) {
939             return null;
940         }
941         StringBuilder b = new StringBuilder();
942         CaseConstraint cc = ad.getCaseConstraint();
943         for (WhenConstraint wc : cc.getWhenConstraint()) {
944             b.append("\\\\");
945             b.append("\n");
946             b.append("when ");
947             b.append(cc.getPropertyName());
948             b.append(" ");
949             if (!cc.isCaseSensitive()) {
950                 b.append("ignoring case ");
951             }
952             b.append(cc.getOperator());
953             b.append(" ");
954 
955             b.append("\\\\");
956             b.append("\n");
957             String comma = "";
958             for (Object value : wc.getValues()) {
959                 b.append(comma);
960                 comma = " or ";
961                 b.append(asString(value));
962             }
963             b.append("\\\\");
964             b.append("\n");
965             b.append("then override constraint:"
966                     + calcOverride(ad, (BaseConstraint) wc.getConstraint()));
967         }
968         return b.toString();
969     }
970 
971     private String calcOverride(AttributeDefinition ad, BaseConstraint cons) {
972         StringBuilder b = new StringBuilder();
973 //        b.append(calcOverride("serviceSide", ad.(),
974 //                cons.getApplyClientSide()));
975 //        b.append(calcOverride("exclusiveMin", ad.getExclusiveMin(),
976 //                cons.getExclusiveMin()));
977 //        b.append(calcOverride("inclusiveMax", ad.getInclusiveMax(),
978 //                cons.getInclusiveMax()));
979 //        String minOccursMessage = calcOverride("minOccurs", ad.getMinimumNumberOfElements(),
980 //                cons.getMinimumNumberOfElements());
981 //        if (!minOccursMessage.trim().equals("")) {
982 //            if (cons.getMinimumNumberOfElements() != null && cons.getMinimumNumberOfElements() == 1) {
983 //                minOccursMessage = " REQUIRED";
984 //            }
985 //        }
986 //        b.append(minOccursMessage);
987 //        b.append(calcOverride("validchars", ad.getValidCharactersConstraint(),
988 //                cons.getValidCharactersConstraint()));
989 //        b.append(calcOverride("lookup", ad.getLookupDefinition(),
990 //                cons.getLookupDefinition()));
991         //TODO: other more complex constraints
992         return b.toString();
993     }
994 
995     private String calcOverride(String attribute, LookupConstraint val1,
996             LookupConstraint val2) {
997         if (val1 == val2) {
998             return "";
999         }
1000         if (val1 == null && val2 != null) {
1001             return " add lookup " + this.calcLookup(val2);
1002         }
1003         if (val1 != null && val2 == null) {
1004             return " remove lookup constraint";
1005         }
1006         return " change lookup to " + calcLookup(val2);
1007     }
1008 
1009     private String calcOverride(String attribute, ValidCharactersConstraint val1,
1010             ValidCharactersConstraint val2) {
1011         if (val1 == val2) {
1012             return "";
1013         }
1014         if (val1 == null && val2 != null) {
1015             return " add validchars " + calcValidChars(val2);
1016         }
1017         if (val1 != null && val2 == null) {
1018             return " remove validchars constraint";
1019         }
1020         return " change validchars to " + calcValidChars(val2);
1021     }
1022 
1023     private String calcOverride(String attribute, boolean val1, boolean val2) {
1024         if (val1 == val2) {
1025             return "";
1026         }
1027         return " " + attribute + "=" + val2;
1028     }
1029 
1030     private String calcOverride(String attribute, String val1, String val2) {
1031         if (val1 == null && val2 == null) {
1032             return "";
1033         }
1034         if (val1 == val2) {
1035             return "";
1036         }
1037         if (val1 == null) {
1038             return " " + attribute + "=" + val2;
1039         }
1040         if (val1.equals(val2)) {
1041             return "";
1042         }
1043         return " " + attribute + "=" + val2;
1044     }
1045 
1046     private String calcOverride(String attribute, Object val1, Object val2) {
1047         if (val1 == null && val2 == null) {
1048             return "";
1049         }
1050         if (val1 == val2) {
1051             return "";
1052         }
1053         if (val1 == null) {
1054             return " " + attribute + "=" + val2;
1055         }
1056         if (val1.equals(val2)) {
1057             return "";
1058         }
1059         return " " + attribute + "=" + asString(val2);
1060     }
1061 
1062     private String asString(Object value) {
1063         if (value == null) {
1064             return "null";
1065         }
1066         if (value instanceof String) {
1067             return (String) value;
1068         }
1069         return value.toString();
1070     }
1071 
1072     private String nbsp(String str) {
1073         if (str == null) {
1074             return "&nbsp;";
1075         }
1076         if (str.trim().isEmpty()) {
1077             return "&nbsp;";
1078         }
1079         return str;
1080     }
1081 
1082     public static void writeTag(PrintStream out, String tag, String value) {
1083         writeTag(out, tag, null, value);
1084     }
1085 
1086     public static void writeTag(PrintStream out, String tag, String modifiers, String value) {
1087         if (value == null) {
1088             return;
1089         }
1090         if (value.equals("")) {
1091             return;
1092         }
1093         out.print("<" + tag);
1094         if (modifiers != null && !modifiers.isEmpty()) {
1095             out.print(" " + modifiers);
1096         }
1097         out.print(">");
1098         out.print(escapeXML(value));
1099         out.print("</" + tag + ">");
1100         out.println("");
1101     }
1102 
1103     public static String escapeXML(String s) {
1104         StringBuffer sb = new StringBuffer();
1105         int n = s.length();
1106         for (int i = 0; i < n; i++) {
1107             // http://www.hdfgroup.org/HDF5/XML/xml_escape_chars.htm
1108             char c = s.charAt(i);
1109             switch (c) {
1110                 case '"':
1111                     sb.append("&quot;");
1112                     break;
1113                 case '\'':
1114                     sb.append("&apos;");
1115                     break;
1116                 case '&':
1117                     sb.append("&amp;");
1118                     break;
1119                 case '<':
1120                     sb.append("&lt;");
1121                     break;
1122                 case '>':
1123                     sb.append("&gt;");
1124                     break;
1125                 //case ' ': sb.append("&nbsp;");break;\
1126                 default:
1127                     sb.append(c);
1128                     break;
1129             }
1130         }
1131         return sb.toString();
1132     }
1133 
1134     private void writeLink(PrintStream out, String url, String value) {
1135         out.print("<a href=\"" + url + "\">" + value + "</a>");
1136     }
1137 }