001    /*
002     * Copyright 2011 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 1.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl1.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.student.datadictionary.util;
017    
018    import java.io.File;
019    import java.io.FileNotFoundException;
020    import java.io.FileOutputStream;
021    import java.io.OutputStream;
022    import java.io.PrintStream;
023    import java.util.ArrayList;
024    import java.util.Collections;
025    import java.util.Comparator;
026    import java.util.Date;
027    import java.util.List;
028    import java.util.Map;
029    import java.util.Stack;
030    import org.kuali.rice.krad.datadictionary.AttributeDefinition;
031    import org.kuali.rice.krad.datadictionary.AttributeDefinitionBase;
032    import org.kuali.rice.krad.datadictionary.CollectionDefinition;
033    import org.kuali.rice.krad.datadictionary.ComplexAttributeDefinition;
034    import org.kuali.rice.krad.datadictionary.DataObjectEntry;
035    import org.kuali.rice.krad.datadictionary.validation.constraint.BaseConstraint;
036    import org.kuali.rice.krad.datadictionary.validation.constraint.CaseConstraint;
037    import org.kuali.rice.krad.datadictionary.validation.constraint.CommonLookupParam;
038    import org.kuali.rice.krad.datadictionary.validation.constraint.LookupConstraint;
039    import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint;
040    import org.kuali.rice.krad.datadictionary.validation.constraint.WhenConstraint;
041    import org.kuali.rice.krad.uif.control.Control;
042    import org.kuali.rice.krad.uif.control.TextControl;
043    
044    public class DictionaryFormatter {
045    
046        private DataObjectEntry doe;
047        private Map<String, DataObjectEntry> beansOfType;
048        private String beanId;
049        private String outputFileName;
050    
051        public DictionaryFormatter(DataObjectEntry doe, Map<String, DataObjectEntry> beansOfType, String beanId, String outputFileName) {
052            this.doe = doe;
053            this.beansOfType = beansOfType;
054            this.beanId = beanId;
055            this.outputFileName = outputFileName;
056        }
057    
058        public void formatForHtml() {
059            File file = new File(this.outputFileName);
060            OutputStream outputStream;
061            try {
062                outputStream = new FileOutputStream(file, false);
063            } catch (FileNotFoundException ex) {
064                throw new IllegalArgumentException(this.outputFileName, ex);
065            }
066            PrintStream out = new PrintStream(outputStream);
067            writeHeader(out, beanId);
068            writeBody(out);
069            writeFooter(out);
070            out.close();
071        }
072    
073        public static void writeHeader(PrintStream out, String title) {
074            out.println("<html>");
075            out.println("<head>");
076            writeTag(out, "title", title);
077            out.println("</head>");
078            out.println("<body bgcolor=\"#ffffff\" topmargin=0 marginheight=0>");
079        }
080    
081        public static void writeFooter(PrintStream out) {
082            out.println("</body>");
083            out.println("</html>");
084        }
085    
086        private String initUpper(String str) {
087            if (str == null) {
088                return null;
089            }
090            if (str.length() == 0) {
091                return str;
092            }
093            if (str.length() == 1) {
094                return str.toUpperCase();
095            }
096            return str.substring(0, 1).toUpperCase() + str.substring(1);
097        }
098    
099        private void writeBody(PrintStream out) {
100            out.println("<a href=\"index.html\">home</a>");
101            out.println("<a href=\"../contractdocs/" + initUpper(doe.getName()) + ".html\">contract doc</a>");
102            out.println("<br>");
103            out.println("(!) This page was automatically generated on " + new Date());
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.getStyleClasses() != null) {
878                    if (textControl.getStyleClasses().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    }