View Javadoc

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