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