001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2007-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.admin.doc;
028    
029    import java.io.File;
030    import java.io.PrintWriter;
031    import java.util.Collection;
032    import java.util.Iterator;
033    import java.util.Properties;
034    import java.util.TreeMap;
035    import java.util.TreeSet;
036    import org.opends.messages.Message;
037    import org.opends.server.admin.ACIPropertyDefinition;
038    import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
039    import org.opends.server.admin.AbstractManagedObjectDefinition;
040    import org.opends.server.admin.AdministratorAction.Type;
041    import org.opends.server.admin.AggregationPropertyDefinition;
042    import org.opends.server.admin.AliasDefaultBehaviorProvider;
043    import org.opends.server.admin.AttributeTypePropertyDefinition;
044    import org.opends.server.admin.BooleanPropertyDefinition;
045    import org.opends.server.admin.ClassLoaderProvider;
046    import org.opends.server.admin.ClassPropertyDefinition;
047    import org.opends.server.admin.DNPropertyDefinition;
048    import org.opends.server.admin.DefaultBehaviorProvider;
049    import org.opends.server.admin.DefinedDefaultBehaviorProvider;
050    import org.opends.server.admin.DurationPropertyDefinition;
051    import org.opends.server.admin.EnumPropertyDefinition;
052    import org.opends.server.admin.IPAddressMaskPropertyDefinition;
053    import org.opends.server.admin.IPAddressPropertyDefinition;
054    import org.opends.server.admin.IntegerPropertyDefinition;
055    import org.opends.server.admin.LDAPProfile;
056    import org.opends.server.admin.PropertyDefinition;
057    import org.opends.server.admin.PropertyDefinitionVisitor;
058    import org.opends.server.admin.PropertyOption;
059    import org.opends.server.admin.RelationDefinition;
060    import org.opends.server.admin.RelationOption;
061    import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
062    import org.opends.server.admin.SizePropertyDefinition;
063    import org.opends.server.admin.StringPropertyDefinition;
064    import org.opends.server.admin.Tag;
065    import org.opends.server.admin.TopCfgDefn;
066    import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
067    import org.opends.server.admin.std.meta.RootCfgDefn;
068    import org.opends.server.types.InitializationException;
069    import org.opends.server.util.EmbeddedUtils;
070    
071    /**
072     *  This class allow Configuration Guide documentation generation (html format).
073     * It is based on the Admin Framework Introspection API
074     *
075     */
076    public class ConfigGuideGeneration {
077    
078      // Note : still to be done :
079      // I18n support. Today all the strings are hardcoded in this file
080    
081      private final static String ACI_SYNTAX_REL_URL = "/page/ACISyntax";
082      private final static String DURATION_SYNTAX_REL_URL =
083        "/page/DefinitionDuration";
084      private final String CSS_FILE = "opends-config.css";
085    
086      private final String MAIN_FILE = "index.html";
087      private final String INHERITANCE_TREE_FILE =
088        "ManagedObjectInheritanceTree.html";
089      private final String RELATION_TREE_FILE = "ManagedObjectRelationTree.html";
090      private final String MO_LIST_FILE = "ManagedObjectList.html";
091      private final String PROPERTIES_INDEX_FILE = "PropertiesIndex.html";
092      private final String WELCOME_FILE = "welcome.html";
093      private final String MAINTOP_FILE = "maintop.html";
094      private final String INDEX_FILE = "index.html";
095    
096      private static final String CONFIG_GUIDE_DIR = "opends_config_guide";
097      private final String MAIN_FRAME = "mainFrame";
098    
099      /**
100       * Entry point for documentation generation.
101       *
102       * Properties:
103       * GenerationDir - The directory where the doc is generated
104       *              (default is /var/tmp/[CONFIG_GUIDE_DIR>])
105       * LdapMapping - Presence means that the LDAP mapping section is to be
106       *               generated (default is no)
107       * OpendsWiki - The URL of the OpenDS Wiki
108       *              (default is "https://www.opends.org/wiki")
109       * OpendsHome - The URL of the OpenDS project Home page
110       *              (default is "http://www.opends.org")
111       *
112       * @param args none.
113       */
114      public static void main(String[] args) {
115        Properties properties = System.getProperties();
116        generationDir = properties.getProperty("GenerationDir");
117        if (generationDir == null) {
118          // Default dir is prefixed by the system-dependent default temporary dir
119          generationDir = System.getProperty("java.io.tmpdir") + File.separator +
120            CONFIG_GUIDE_DIR;
121        }
122        // Create new dir if necessary
123        try {
124          (new File(generationDir)).mkdir();
125        } catch (Exception e) {
126          e.printStackTrace();
127          System.exit(1);
128        }
129        System.out.println("Generation directory is : " + generationDir);
130    
131        if (properties.getProperty("LdapMapping") != null) {
132          ldapMapping = true;
133        }
134    
135        opendsWiki = properties.getProperty("OpendsWiki");
136        if (opendsWiki == null) {
137          // Default is current wiki
138          opendsWiki = "https://www.opends.org/wiki";
139        }
140        aciSyntaxPage = opendsWiki + ACI_SYNTAX_REL_URL;
141        durationSyntaxPage = opendsWiki + DURATION_SYNTAX_REL_URL;
142    
143        opendsHome = properties.getProperty("OpendsHome");
144        if (opendsHome == null) {
145          // Default is current OpenDS project home
146          opendsHome = "http://www.opends.org";
147        }
148    
149        ConfigGuideGeneration myGen = new ConfigGuideGeneration();
150        myGen.generate();
151      }
152    
153      private void generate() {
154        init();
155    
156        // Generate the relation tree of all the managed objects
157        genManagedObjectRelationTree(catTopRelList);
158    
159        // Generate the inheritance tree of all the managed objects
160        genManagedObjectInheritanceTree(catTopMoList);
161    
162        // Generate all the managed objects and their children
163        genAllManagedObject(topMoList);
164    
165        // Generate a list of managed objects
166        genManagedObjectList(moList);
167    
168        // Generate an index of properties
169        genPropertiesIndex();
170    
171        // Generate the Index page
172        genIndexPage();
173    
174        // Generate the Main Top page
175        genMainTopPage();
176    
177        // Generate the Welcome page
178        genWelcomePage();
179       }
180    
181      private void init() {
182    
183        // Build a list of top relations
184        RootCfgDefn rootCfg = RootCfgDefn.getInstance();
185        for (RelationDefinition rel : rootCfg.getAllRelationDefinitions()) {
186          topRelList.put(rel.getChildDefinition().getName(), rel);
187        }
188    
189        // Enable the client-side class loader to explicitly load classes
190        // which are not directly reachable from the root configuration
191        EmbeddedUtils.initializeForClientUse();
192        // Bootstrap definition classes.
193        try {
194          ClassLoaderProvider.getInstance().enable();
195        } catch (InitializationException e) {
196          System.err.println("ERROR : Cannot enable the client-side class loader.");
197          e.printStackTrace();
198          System.exit(1);
199        }
200        // Switch off class name validation in client.
201        ClassPropertyDefinition.setAllowClassValidation(false);
202        // Switch off attribute type name validation in client.
203        AttributeTypePropertyDefinition.setCheckSchema(false);
204    
205        // Build a sorted list of top managed objects
206        TopCfgDefn topCfg = TopCfgDefn.getInstance();
207        Collection<AbstractManagedObjectDefinition<?, ?>> topObjects =
208          topCfg.getChildren();
209        for (AbstractManagedObjectDefinition topObject : topObjects) {
210          if (topObject.getName().equals("")) {
211            // root
212            continue;
213          }
214          topMoList.put(topObject.getName(), topObject);
215        }
216    
217    
218        // Build a list of top relations by category (core, database, ...)
219        for (RelationDefinition rel : topRelList.values()) {
220          AbstractManagedObjectDefinition<?, ?> mo = rel.getChildDefinition();
221          Collection<Tag> tags = mo.getAllTags();
222          for (Tag tag : tags) {
223            TreeMap<String, RelationDefinition> catMap =
224              catTopRelList.get(tag.getName());
225            if (catMap == null) {
226              catMap = new TreeMap<String, RelationDefinition>();
227              catTopRelList.put(tag.getName(), catMap);
228            }
229            catMap.put(mo.getName(), rel);
230          }
231        }
232    
233        // Build a list of top managed objects by category (core, database, ...)
234        for (AbstractManagedObjectDefinition<?, ?> topObject : topMoList.values()) {
235          Collection<Tag> tags = topObject.getAllTags();
236          for (Tag tag : tags) {
237            TreeMap<String, AbstractManagedObjectDefinition> catMap =
238              catTopMoList.get(tag.getName());
239            if (catMap == null) {
240              catMap = new TreeMap<String, AbstractManagedObjectDefinition>();
241              catTopMoList.put(tag.getName(), catMap);
242            }
243            catMap.put(topObject.getName(), topObject);
244          }
245        }
246    
247      }
248    
249      /**
250       * Generate the inheritance tree of all the managed objects.
251       */
252      @SuppressWarnings("unchecked")
253      private void genManagedObjectInheritanceTree(
254        TreeMap<String, TreeMap<String, AbstractManagedObjectDefinition>> list) {
255    
256        htmlHeader("OpenDS Configuration Reference - Inheritance View");
257        tabMenu(INHERITANCE_TREE_FILE);
258        viewHelp("This view represents the inheritance relationships between " +
259          "configuration components.");
260        jumpSection();
261    
262        for (String catName : list.keySet()) {
263          heading3(getFriendlyName(catName));
264          // Get the list of the category
265          TreeMap<String, AbstractManagedObjectDefinition> catList =
266            list.get(catName);
267          for (AbstractManagedObjectDefinition mo : catList.values()) {
268            if ((relList.get(mo.getName()) != null) &&
269              (relList.get(mo.getName()).hasOption(RelationOption.HIDDEN))) {
270              continue;
271            }
272            paragraph(
273              getLink(mo.getUserFriendlyName().toString(),
274              mo.getName() + ".html", MAIN_FRAME));
275            if (mo.hasChildren()) {
276              genMoInheritanceTree(makeMOTreeMap(mo.getChildren()));
277            }
278          }
279        }
280    
281        htmlFooter();
282        generateFile(INHERITANCE_TREE_FILE);
283      }
284    
285      @SuppressWarnings("unchecked")
286      private void genMoInheritanceTree(
287        TreeMap<String, AbstractManagedObjectDefinition> catList) {
288    
289        beginList();
290        for (AbstractManagedObjectDefinition mo : catList.values()) {
291          link(mo.getUserFriendlyName().toString(), mo.getName() + ".html",
292            MAIN_FRAME);
293          if (mo.hasChildren()) {
294            genMoInheritanceTree(makeMOTreeMap(mo.getChildren()));
295          }
296        }
297        endList();
298      }
299    
300       private void jumpSection() {
301         htmlBuff.append("<p class=\"category-index\">" +
302           "<strong>Jump To:</strong><br>\n");
303    
304         String[] catNames = catTopMoList.keySet().toArray(new String[0]);
305        for (int ii=0; ii < catNames.length; ii++) {
306          if (ii != 0) {
307            htmlBuff.append(", ");
308          }
309          String catFriendlyName = getFriendlyName(catNames[ii]);
310          htmlBuff.append(getLink(catFriendlyName, "#" + catFriendlyName));
311        }
312        htmlBuff.append("</p>\n");
313       }
314    
315    
316      /**
317       * Generate the relation tree of all the managed objects.
318       */
319      private void genManagedObjectRelationTree(
320        TreeMap <String, TreeMap<String, RelationDefinition>> list) {
321    
322        htmlHeader("OpenDS Configuration Reference - Structure View");
323        tabMenu(RELATION_TREE_FILE);
324        viewHelp("This view represents the structural relationships between " +
325          "components and indicates how certain components can exist only within " +
326          "container components.");
327        jumpSection();
328    
329        for (String catName : list.keySet()) {
330          heading3(getFriendlyName(catName));
331          // Get the list of the category
332          TreeMap<String, RelationDefinition> catList = list.get(catName);
333          genMORelationTree(catList);
334        }
335    
336        htmlFooter();
337        generateFile(RELATION_TREE_FILE);
338      }
339    
340    
341      @SuppressWarnings("unchecked")
342      private void genMORelationTree(TreeMap<String, RelationDefinition> list) {
343        for (RelationDefinition rel : list.values()) {
344          AbstractManagedObjectDefinition childMo = rel.getChildDefinition();
345          AbstractManagedObjectDefinition parentMo = rel.getParentDefinition();
346          relList.put(childMo.getName(), rel);
347          if (rel.hasOption(RelationOption.HIDDEN)) {
348            continue;
349          }
350          String linkStr = getLink(childMo.getUserFriendlyName().toString(),
351            childMo.getName() + ".html", MAIN_FRAME);
352          String fromStr = "";
353          if (!parentMo.getName().equals("")) {
354            fromStr = " (from " +
355              getLink(parentMo.getUserFriendlyName().toString(),
356              parentMo.getName() + ".html", MAIN_FRAME) + ")";
357          }
358          if (!inList) {
359            paragraph(linkStr + fromStr);
360          } else {
361            bullet(linkStr + fromStr);
362          }
363          genMORelationSubTree(makeRelTreeMap(childMo.getAllRelationDefinitions()));
364          if (childMo.hasChildren()) {
365            for (Iterator<AbstractManagedObjectDefinition> it =
366              childMo.getChildren().iterator(); it.hasNext();) {
367    
368              AbstractManagedObjectDefinition mo = it.next();
369              genMORelationSubTree(makeRelTreeMap(mo.getAllRelationDefinitions()));
370            }
371          }
372        }
373      }
374    
375    
376      private void genMORelationSubTree(TreeMap<String, RelationDefinition> list) {
377        if (!list.values().isEmpty()) {
378          beginList();
379          genMORelationTree(list);
380          endList();
381        }
382      }
383    
384    
385      /**
386       * Generate all the managed objects HTML pages.
387       */
388      @SuppressWarnings("unchecked")
389      private void genAllManagedObject(
390        TreeMap<String, AbstractManagedObjectDefinition> list) {
391    
392        for (AbstractManagedObjectDefinition mo : list.values()) {
393          if ((relList.get(mo.getName()) != null) &&
394            (relList.get(mo.getName()).hasOption(RelationOption.HIDDEN))) {
395            continue;
396          }
397          moList.put(mo.getName(), mo);
398          genManagedObject(mo);
399          if (mo.hasChildren()) {
400            genAllManagedObject(makeMOTreeMap(mo.getChildren()));
401          }
402        }
403      }
404    
405      private void genManagedObject(AbstractManagedObjectDefinition mo) {
406        //------------------------------------------------------------------------
407        // Header
408        //------------------------------------------------------------------------
409    
410        homeLink();
411        String title = mo.getUserFriendlyName().toString();
412        htmlHeader("OpenDS - " + title);
413    
414        // title
415        heading2(title);
416    
417        // Abstract notice
418        if (mo.hasChildren()) {
419          paragraph(
420            "Note: this is an abstract component, that cannot be instantiated.",
421            TextStyle.ITALIC);
422        }
423    
424        // description
425        paragraph(mo.getSynopsis());
426        paragraph(mo.getDescription());
427    
428        // sub-components
429        if (mo.hasChildren()) {
430          heading3("Direct Subcomponents");
431          paragraph("The following " + mo.getUserFriendlyPluralName() +
432            " are available in the server :");
433          beginList();
434          @SuppressWarnings("unchecked")
435          TreeMap<String, AbstractManagedObjectDefinition> children =
436            makeMOTreeMap(mo.getChildren());
437          for ( AbstractManagedObjectDefinition child : children.values()) {
438            link(child.getUserFriendlyName().toString(), child.getName() + ".html");
439          }
440          endList();
441    
442          paragraph("These " + mo.getUserFriendlyPluralName() +
443            " inherit from the properties described below.");
444        }
445    
446        // Parent
447        if (!mo.getParent().isTop()) {
448          heading3("Parent Component");
449          paragraph("The " + mo.getUserFriendlyName() +
450            " component inherits from the " +
451            getLink(mo.getParent().getUserFriendlyName().toString(),
452            mo.getParent().getName() + ".html"));
453        }
454    
455        // Relations
456        generateRelationsSection(mo);
457    
458        // Page links in case of LDAP mapping
459        if (ldapMapping) {
460          newline();
461          horizontalLine();
462          newline();
463          paragraph("This page describes the " + mo.getUserFriendlyName() + ":");
464          beginList();
465          link("Properties", "#Properties");
466          link("LDAP Mapping", "#LDAP Mapping");
467          endList();
468          newline();
469        }
470    
471    
472        //------------------------------------------------------------------------
473        // Properties
474        //------------------------------------------------------------------------
475    
476        heading3("Properties");
477    
478        paragraph("A description of each property follows.");
479        newline();
480    
481        TreeMap<String, PropertyDefinition> basicProps =
482          new TreeMap<String, PropertyDefinition>();
483        TreeMap<String, PropertyDefinition> advancedProps =
484          new TreeMap<String, PropertyDefinition>();
485        // Properties actually defined in this managed object
486        @SuppressWarnings("unchecked")
487        Collection<PropertyDefinition> props = mo.getAllPropertyDefinitions();
488        for ( PropertyDefinition prop : props) {
489          if (prop.hasOption(PropertyOption.ADVANCED)) {
490            advancedProps.put(prop.getName(), prop);
491          } else {
492            basicProps.put(prop.getName(), prop);
493          }
494        }
495    
496        propertiesLinkTable(basicProps, advancedProps);
497    
498        // basic properties
499        if (basicProps.size() > 0) {
500          heading4("Basic Properties");
501          for ( PropertyDefinition prop : basicProps.values()) {
502            generateProperty(mo, prop);
503            newline();
504          }
505          newline();
506        }
507    
508        // advanced properties
509        if (advancedProps.size() > 0) {
510          heading4("Advanced Properties");
511          for ( PropertyDefinition prop : advancedProps.values()) {
512            generateProperty(mo, prop);
513            newline();
514          }
515          newline();
516        }
517    
518        if (ldapMapping) {
519          genLdapMapping(mo);
520        }
521    
522        htmlFooter();
523    
524        generateFile(mo.getName() + ".html");
525      }
526    
527    
528      private TreeMap<String, PropertyDefinition>
529        getPropertyList(AbstractManagedObjectDefinition mo) {
530    
531        @SuppressWarnings("unchecked")
532        Collection<PropertyDefinition> props = mo.getAllPropertyDefinitions();
533        return makePropTreeMap(props);
534      }
535    
536      private void homeLink() {
537        htmlBuff.append("<div style=\"font-size:11px;margin-top:-10px;" +
538          "margin-bottom:-10px; text-align:right\"><a href=\"" +
539          MAIN_FILE +
540          "\" target=\"_top\">Configuration Reference Home</a></div>");
541      }
542    
543    
544      private void generateRelationsSection(AbstractManagedObjectDefinition mo) {
545        // Composition relations
546        @SuppressWarnings("unchecked")
547        Collection<RelationDefinition> compRels = mo.getRelationDefinitions();
548        @SuppressWarnings("unchecked")
549        Collection<RelationDefinition> reverseCompRels =
550          mo.getReverseRelationDefinitions();
551        // Aggregation properties
552        @SuppressWarnings("unchecked")
553        Collection<AggregationPropertyDefinition> aggregProps =
554          mo.getAggregationPropertyDefinitions();
555        @SuppressWarnings("unchecked")
556        Collection<AggregationPropertyDefinition> reverseAggregProps =
557          mo.getReverseAggregationPropertyDefinitions();
558    
559    
560        // Check if something to print in composition relations
561        // (even if the list not empty, it may contain only hidden relations)
562        boolean isCompRelsEmpty = true;
563        if (!compRels.isEmpty()) {
564          for (RelationDefinition rel : compRels) {
565            if (rel.hasOption(RelationOption.HIDDEN)) {
566              continue;
567            }
568            isCompRelsEmpty = false;
569          }
570        }
571        boolean isReverseCompRelsEmpty = true;
572        if (!reverseCompRels.isEmpty()) {
573          for (RelationDefinition rel : reverseCompRels) {
574            if (rel.hasOption(RelationOption.HIDDEN)) {
575              continue;
576            }
577            // check if it is not root
578            if (rel.getParentDefinition().getName().equals("")) {
579              continue;
580            }
581            isReverseCompRelsEmpty = false;
582          }
583        }
584    
585        //
586        // Relations FROM this component
587        //
588    
589        if (!isCompRelsEmpty || !aggregProps.isEmpty()) {
590            heading3("Relations From this Component");
591        }
592    
593        if (!isCompRelsEmpty) {
594          paragraph(
595            "The following components have a direct COMPOSITION relation FROM " +
596            mo.getUserFriendlyPluralName() + " :");
597          for ( RelationDefinition rel : compRels) {
598            if (rel.hasOption(RelationOption.HIDDEN)) {
599              continue;
600            }
601            beginList();
602            AbstractManagedObjectDefinition childRel = rel.getChildDefinition();
603            link(childRel.getUserFriendlyName().toString(), childRel.getName() +
604              ".html");
605            endList();
606          }
607        }
608        if (!aggregProps.isEmpty()) {
609          paragraph(
610            "The following components have a direct AGGREGATION relation FROM " +
611            mo.getUserFriendlyPluralName() + " :");
612          TreeMap<String, AbstractManagedObjectDefinition> componentList =
613            new TreeMap<String, AbstractManagedObjectDefinition>();
614          for ( AggregationPropertyDefinition agg : aggregProps) {
615            RelationDefinition rel = agg.getRelationDefinition();
616            AbstractManagedObjectDefinition childRel = rel.getChildDefinition();
617            componentList.put(childRel.getName(), childRel);
618          }
619          for (AbstractManagedObjectDefinition component : componentList.values()) {
620            beginList();
621            link(component.getUserFriendlyName().toString(), component.getName() +
622              ".html");
623            endList();
624          }
625        }
626    
627    
628        //
629        // Relations TO this component
630        //
631    
632        if (!isReverseCompRelsEmpty || !reverseAggregProps.isEmpty()) {
633            heading3("Relations To this Component");
634        }
635    
636        if (!mo.getReverseRelationDefinitions().isEmpty()) {
637          if (!isReverseCompRelsEmpty) {
638            paragraph(
639              "The following components have a direct COMPOSITION relation TO " +
640              mo.getUserFriendlyPluralName() + " :");
641            for ( RelationDefinition rel : reverseCompRels) {
642              beginList();
643              AbstractManagedObjectDefinition childRel = rel.getParentDefinition();
644              link(childRel.getUserFriendlyName().toString(), childRel.getName() +
645                ".html");
646              endList();
647            }
648          }
649        }
650        if (!reverseAggregProps.isEmpty()) {
651          paragraph(
652            "The following components have a direct AGGREGATION relation TO " +
653            mo.getUserFriendlyPluralName() + " :");
654          TreeMap<String, AbstractManagedObjectDefinition> componentList =
655            new TreeMap<String, AbstractManagedObjectDefinition>();
656          for ( AggregationPropertyDefinition agg : reverseAggregProps) {
657            AbstractManagedObjectDefinition fromMo =
658              agg.getManagedObjectDefinition();
659            componentList.put(fromMo.getName(), fromMo);
660          }
661          for (AbstractManagedObjectDefinition component : componentList.values()) {
662            beginList();
663            link(component.getUserFriendlyName().toString(), component.getName() +
664              ".html");
665            endList();
666    
667          }
668        }
669    
670      }
671    
672      private void generateProperty(
673        AbstractManagedObjectDefinition mo, PropertyDefinition prop) {
674    
675        // Property name
676        paragraph(getAnchor(prop.getName()) + prop.getName(), TextStyle.STANDARD,
677          "propertyname");
678    
679        // Property table
680        startTable();
681        tableRow("Description",
682          ((prop.getSynopsis() != null) ? prop.getSynopsis().toString()+ " " : "") +
683          ((prop.getDescription() != null) ?
684            prop.getDescription().toString() : ""));
685    
686        // Default value
687        String defValueStr = getDefaultBehaviorString(prop);
688        tableRow("Default Value", defValueStr);
689    
690        tableRow("Allowed Values", getSyntaxStr(prop));
691    
692        tableRow("Multi-valued",
693          (prop.hasOption(PropertyOption.MULTI_VALUED) ? "Yes" : "No"));
694    
695        if (prop.hasOption(PropertyOption.MANDATORY)) {
696          tableRow("Required", "Yes");
697        } else {
698          tableRow("Required", "No");
699        }
700    
701        String action = "None";
702        if (prop.getAdministratorAction() != null) {
703          Message synopsis = prop.getAdministratorAction().getSynopsis();
704          Type actionType = prop.getAdministratorAction().getType();
705          String actionStr = "";
706          if (actionType == actionType.COMPONENT_RESTART) {
707            actionStr = "The " + mo.getUserFriendlyName() +
708              " must be disabled and re-enabled for changes to this setting " +
709              "to take effect";
710          } else if (actionType == actionType.SERVER_RESTART) {
711            actionStr = "Restart the server";
712          } else if (actionType == actionType.NONE) {
713            actionStr = "None";
714          }
715          String dot = (actionStr.equals("") ? "" : ". ");
716          action = actionStr +
717            ((synopsis != null) ? dot + synopsis : "");
718        }
719        tableRow("Admin Action Required", action);
720    
721        if (prop.hasOption(PropertyOption.ADVANCED)) {
722          tableRow("Advanced Property", "Yes");
723        } else {
724          tableRow("Advanced Property", "No");
725        }
726    
727        endTable();
728    
729      }
730    
731    
732      private void propertiesLinkTable(TreeMap<String,
733        PropertyDefinition> basicProps,
734        TreeMap<String, PropertyDefinition> advancedProps) {
735        htmlBuff.append(
736          "<table border=\"0\" cellspacing=\"0\" class=\"jump-table\">\n" +
737          "  <tr>\n" +
738          "    <th>Basic Properties:</th>\n" +
739          "    <th>Advanced Properties:</th>\n" +
740          "  </tr>\n");
741    
742        PropertyDefinition[] basicPropsArray =
743          basicProps.values().toArray(new PropertyDefinition[0]);
744        PropertyDefinition[] advancedPropsArray =
745          advancedProps.values().toArray(new PropertyDefinition[0]);
746    
747        for (int ii=0;
748            (ii < basicPropsArray.length) || (ii < advancedPropsArray.length);
749            ii++) {
750          String basicPropName =
751            ii < basicPropsArray.length ? basicPropsArray[ii].getName() : null;
752          String advancedPropName =
753            ii < advancedPropsArray.length ?
754              advancedPropsArray[ii].getName() : null;
755    
756          String basicHtmlCell = "";
757          if (basicPropName != null) {
758            basicHtmlCell = "  <td>&darr;&nbsp;<a href=\"#" + basicPropName + "\">"
759              + basicPropName + "</a></td>\n";
760          } else if ((basicPropsArray.length == 0) && (ii == 0)) {
761            basicHtmlCell = "  <td>&nbsp;None</td>\n";
762          } else if (ii >= basicPropsArray.length) {
763            // Case of nb of basic props < nb of advanced props
764            basicHtmlCell = "  <td></td>\n";
765          }
766    
767          String advancedHtmlCell = "";
768          if (advancedPropName != null) {
769            advancedHtmlCell = "  <td>&darr;&nbsp;<a href=\"#" + advancedPropName +
770              "\">" + advancedPropName + "</a></td>\n";
771          } else if ((advancedPropsArray.length == 0) && (ii == 0)) {
772            advancedHtmlCell = "  <td>&nbsp;None</td>\n";
773          }
774    
775          htmlBuff.append("<tr>\n");
776          htmlBuff.append(basicHtmlCell + advancedHtmlCell);
777          htmlBuff.append("</tr>\n");
778        }
779        htmlBuff.append("</table>\n");
780      }
781    
782    
783      private void genLdapMapping(AbstractManagedObjectDefinition mo) {
784        //------------------------------------------------------------------------
785        // LDAP mapping
786        //------------------------------------------------------------------------
787    
788        heading3("LDAP Mapping");
789        paragraph(
790          "Each configuration property can be mapped to a specific " +
791          "LDAP attribute under the \"cn=config\" entry. " +
792          "The mappings that follow are provided for information only. " +
793          "In general, you should avoid changing the server configuration " +
794          "by manipulating the LDAP attributes directly.");
795    
796        // Managed object table
797        startTable();
798    
799        LDAPProfile ldapProfile = LDAPProfile.getInstance();
800        tableRow("Base DN", getBaseDN(mo, ldapProfile));
801    
802        tableRow("objectclass name", ldapProfile.getObjectClass(mo));
803        if (mo.getParent().getName() != null) {
804          String superior = "";
805          if (mo.getParent().getName().equals("top")) {
806            superior = "top";
807          } else {
808            if (moList.get(mo.getParent().getName()) != null) {
809              superior =
810                ldapProfile.getObjectClass(moList.get(mo.getParent().getName()));
811            } else {
812              System.err.println(
813                "Error: managed object " + mo.getName() + " not found.");
814            }
815          }
816          tableRow("objectclass superior", superior);
817        } else {
818          System.err.println(
819            "Error: objectclass superior not found for " + mo.getName());
820        }
821        endTable();
822    
823        newline();
824        // Properties table
825        startTable();
826        tableRow("Property", "LDAP attribute");
827        for ( PropertyDefinition prop : getPropertyList(mo).values()) {
828          tableRow(prop.getName(), ldapProfile.getAttributeName(mo, prop));
829        }
830    
831        endTable();
832    
833      }
834    
835      private void genManagedObjectList(
836        TreeMap<String, AbstractManagedObjectDefinition> list) {
837    
838        htmlHeader("OpenDS Configuration Reference - Components View");
839        tabMenu(MO_LIST_FILE);
840        viewHelp("This view provides a list of all configuration components, " +
841          "in alphabetical order.");
842    
843        newline();
844        StringBuffer moPointers = new StringBuffer();
845        String lettersPointers = "";
846        String firstChar = ".";
847        for (AbstractManagedObjectDefinition mo : list.values()) {
848          if (!mo.getName().startsWith(firstChar)) {
849            firstChar = mo.getName().substring(0, 1);
850            String letter = firstChar.toUpperCase();
851            moPointers.append(getAnchor(letter) + getHeading2(letter));
852            lettersPointers += getLink(letter, "#" + letter) + " ";
853          }
854          moPointers.append(
855            "<p> " +
856            getLink(mo.getUserFriendlyName().toString(), mo.getName() + ".html",
857            MAIN_FRAME) +
858            "</p>\n");
859        }
860        paragraph(lettersPointers);
861        htmlBuff.append(moPointers);
862        htmlFooter();
863        generateFile(MO_LIST_FILE);
864      }
865    
866      private void genPropertiesIndex() {
867    
868        // Build a sorted list of (property name + its managed object name)
869        TreeSet<String> propMoList = new TreeSet<String>();
870        for (AbstractManagedObjectDefinition<?, ?> mo : moList.values()) {
871          for (PropertyDefinition<?> prop : mo.getPropertyDefinitions()) {
872            propMoList.add(
873              prop.getName() + "," + prop.getManagedObjectDefinition().getName());
874          }
875        }
876    
877        String lettersPointers = "";
878        String firstChar = ".";
879        for (String propMoStr : propMoList) {
880          String[] propMoArray = propMoStr.split(",");
881          String propName = propMoArray[0];
882          AbstractManagedObjectDefinition mo = moList.get(propMoArray[1]);
883          if (!propName.startsWith(firstChar)) {
884            firstChar = propName.substring(0, 1);
885            String letter = firstChar.toUpperCase();
886            htmlBuff.append(getAnchor(letter) + getHeading2(letter));
887            lettersPointers += getLink(letter, "#" + letter) + " ";
888          }
889          String propLink = getLink(propName,
890            mo.getName() + ".html" + "#" + propName, MAIN_FRAME);
891          String moLink =
892            getLink(mo.getUserFriendlyName().toString(), mo.getName() + ".html",
893            MAIN_FRAME, "#666");
894          paragraph(propLink + "  [ " + moLink + " ]");
895        }
896    
897        String indexBody = htmlBuff.toString();
898        htmlBuff = new StringBuffer();
899        htmlHeader("OpenDS Configuration Reference - Properties View");
900        tabMenu(PROPERTIES_INDEX_FILE);
901        viewHelp("This view provides a list of all configuration properties, " +
902          "in alphabetical order, and indicates the configuration component to " +
903          "which each property applies.");
904    
905        newline();
906        paragraph(lettersPointers);
907        htmlBuff.append(indexBody);
908        htmlFooter();
909        generateFile(PROPERTIES_INDEX_FILE);
910      }
911    
912        private void genWelcomePage() {
913        htmlHeader("OpenDS Configuration Reference - Welcome");
914        heading2("About This Reference");
915        paragraph("This reference " +
916          "describes the OpenDS configuration properties that can be manipulated " +
917          "with the dsconfig command.");
918        paragraph("Configuration components are grouped according to the area of " +
919          "the server in which they are used, as follows:");
920    
921        beginList();
922        for (String catName : catTopMoList.keySet()) {
923          bullet(getFriendlyName(catName));
924        }
925        endList();
926    
927        paragraph(
928          "For ease of reference, the configuration is described on multiple " +
929          "tabs. These tabs provide alternative views of the configuration " +
930          "components:");
931        beginList();
932        bullet("The <strong>Inheritance</strong> view represents the inheritance " +
933          "relationships between configuration components. A sub-component " +
934          "inherits all of the properties of its parent component.");
935        bullet("The <strong>Structure</strong> view represents the structural " +
936          "relationships between components and indicates how certain components " +
937          "can exist only within container components. When a container " +
938          "component is deleted, all of the components within it are also " +
939          "deleted.");
940        bullet(
941          "The <strong>Components</strong> view provides an alphabetical list " +
942          "of all configuration components.");
943        bullet(
944          "The <strong>Properties</strong> view provides an alphabetical list " +
945          "of all configuration properties, and indicates the configuration " +
946          "component to which each property applies.");
947        endList();
948    
949        newline();
950        paragraph("When you set up OpenDS, certain components are created in the " +
951          "configuration by default. These components are configured with " +
952          "specific values, which are not necessarily the same as the " +
953          "\"default values\" of new components that you create using dsconfig. " +
954          "The \"default values\" listed in this document refer to the values " +
955          "of the new components that you create using dsconfig.");
956    
957        htmlFooter();
958        generateFile(WELCOME_FILE);
959    
960      }
961    
962      private void genMainTopPage() {
963        htmlHeader("OpenDS Configuration Reference - Main Top");
964        htmlBuff.append("<div class=\"breadcrumb\"><span class=\"pageactions\">" +
965          "<a href=\"" + opendsWiki + "\" target=\"_parent\">" +
966          "<span style=\"font-size: 12px;\">&laquo;&nbsp;&nbsp;</span>" +
967          "Back to OpenDS Wiki</a></span>&nbsp;&nbsp;</div>\n");
968        htmlBuff.append("<table class=\"titletable\" cellspacing=\"0\" " +
969          "width=\"100%\">\n");
970        htmlBuff.append("<tbody><tr>\n");
971        htmlBuff.append("  <td><h2>OpenDS Configuration Reference</h2></td>\n");
972        htmlBuff.append("  <td valign=\"bottom\" width=\"10%\">" +
973          "<a href=\"" + opendsHome + "\" target=\"_parent\">" +
974          "<img src=\"opends_logo_sm.png\" alt=\"OpenDS Logo\" align=\"bottom\" " +
975          "border=\"0\" height=\"33\" width=\"104\"></a></td>\n");
976        htmlBuff.append("</tr>\n");
977        htmlBuff.append("</tbody></table>\n");
978    
979        htmlFooter();
980        generateFile(MAINTOP_FILE);
981    
982      }
983    
984      private void genIndexPage() {
985        htmlBuff.append(getHtmlHeader("OpenDS Configuration Reference"));
986    
987        htmlBuff.append("<frameset rows=\"80,*\" framespacing=\"1\" " +
988          "frameborder=\"yes\" border=\"1\" bordercolor=\"#333333\">\n");
989        htmlBuff.append("  <frame src=\"" + MAINTOP_FILE + "\" name=\"topFrame\" " +
990          "id=\"topFrame\" border=\"1\" title=\"topFrame\" scrolling=\"no\">\n");
991        htmlBuff.append("  <frameset cols=\"375,*\" frameborder=\"yes\" " +
992          "border=\"1\" " +
993          "framespacing=\"1\">\n");
994        htmlBuff.append("     <frame src=\"" + INHERITANCE_TREE_FILE + "\" " +
995          "name=\"leftFrame\" id=\"leftFrame\" title=\"leftFrame\" " +
996          "scrolling=\"auto\">\n");
997        htmlBuff.append("     <frame src=\"" + WELCOME_FILE +
998          "\" name=\"mainFrame\" " +
999          "id=\"mainFrame\" title=\"mainFrame\" scrolling=\"auto\">\n");
1000        htmlBuff.append("   </frameset>\n");
1001        htmlBuff.append("</frameset>\n");
1002        htmlBuff.append("<noframes><body>\n");
1003        htmlBuff.append("</body>\n");
1004        htmlBuff.append("</noframes>\n");
1005        htmlBuff.append("</html>\n");
1006    
1007        generateFile(INDEX_FILE);
1008      }
1009    
1010      private String getBaseDN(
1011        AbstractManagedObjectDefinition mo, LDAPProfile ldapProfile) {
1012    
1013        RelationDefinition rel = relList.get(mo.getName());
1014        if (rel != null) {
1015          String baseDn = ldapProfile.getRelationRDNSequence(rel);
1016          if (!baseDn.equals("")) {
1017            return baseDn;
1018          } else {
1019            // Check the parent relation
1020            return getBaseDN(rel.getParentDefinition(), ldapProfile);
1021          }
1022        } else if (moList.get(mo.getParent().getName()) != null) {
1023          // check its superior
1024          return getBaseDN(moList.get(mo.getParent().getName()), ldapProfile);
1025        } else {
1026          System.err.println("Error: Base DN not found for " + mo.getName());
1027        }
1028        return null;
1029      }
1030    
1031      @SuppressWarnings("unchecked")
1032      private String getSyntaxStr(PropertyDefinition prop) {
1033        // Create a visitor for performing syntax specific processing.
1034        PropertyDefinitionVisitor<String, Void> visitor =
1035          new PropertyDefinitionVisitor<String, Void>() {
1036    
1037          @Override
1038          public String visitACI(ACIPropertyDefinition prop, Void p) {
1039            return getLink("An ACI Syntax", aciSyntaxPage);
1040          }
1041    
1042          @Override
1043          public String visitAggregation(
1044            AggregationPropertyDefinition prop, Void p) {
1045    
1046            RelationDefinition rel = prop.getRelationDefinition();
1047            String linkStr = getLink(rel.getUserFriendlyName().toString(),
1048              rel.getName() + ".html");
1049          return "The DN of any " +  linkStr + ". " +
1050            ((prop.getSourceConstraintSynopsis() != null) ?
1051              prop.getSourceConstraintSynopsis().toString() : "");
1052          }
1053    
1054          @Override
1055          public String visitAttributeType(
1056            AttributeTypePropertyDefinition prop, Void p) {
1057            return "The name of an attribute type defined in the server schema.";
1058          }
1059    
1060          @Override
1061          public String visitBoolean(BooleanPropertyDefinition prop, Void p) {
1062            return "true" + getNewLine() + "false";
1063          }
1064    
1065          @Override
1066          public String visitClass(ClassPropertyDefinition prop, Void p) {
1067            String classStr =
1068              "A java class that implements or extends the class(es) :";
1069            for (String clazz : prop.getInstanceOfInterface()) {
1070              classStr += getNewLine() + clazz;
1071            }
1072            return classStr;
1073          }
1074    
1075          @Override
1076          public String visitDN(DNPropertyDefinition prop, Void p) {
1077            String retStr = "A valid DN.";
1078            if (prop.getBaseDN() != null) {
1079              retStr += prop.getBaseDN().toString();
1080            }
1081            return retStr;
1082          }
1083    
1084          @Override
1085          public String visitDuration(DurationPropertyDefinition prop, Void p) {
1086            String durationStr = "";
1087    
1088            durationStr += getLink("A duration Syntax", durationSyntaxPage) +
1089              ". ";
1090            if (prop.isAllowUnlimited()) {
1091              durationStr += "A value of \"-1\" or \"unlimited\" for no limit. ";
1092            }
1093            if (prop.getMaximumUnit() != null) {
1094              durationStr += "Maximum unit is \"" +
1095                prop.getMaximumUnit().getLongName() + "\". ";
1096            }
1097            long lowerLimitStr = new Double(prop.getBaseUnit().
1098              fromMilliSeconds(prop.getLowerLimit())).longValue();
1099            durationStr += "Lower limit is " + lowerLimitStr +
1100              " " + prop.getBaseUnit().getLongName() + ". ";
1101            if (prop.getUpperLimit() != null) {
1102              long upperLimitStr = new Double(prop.getBaseUnit().
1103                fromMilliSeconds(prop.getUpperLimit())).longValue();
1104              durationStr += "Upper limit is " + upperLimitStr +
1105                " " + prop.getBaseUnit().getLongName() + ". ";
1106            }
1107    
1108            return durationStr;
1109          }
1110    
1111          @Override
1112          public String visitEnum(EnumPropertyDefinition prop, Void p) {
1113            String enumStr = "";
1114            Class en = prop.getEnumClass();
1115            for (Object cst : en.getEnumConstants()) {
1116              enumStr += cst.toString();
1117              if (prop.getValueSynopsis((Enum) cst) != null) {
1118                enumStr += " - " + prop.getValueSynopsis((Enum) cst).toString();
1119              }
1120              enumStr += getNewLine() + getNewLine();
1121            }
1122            return enumStr;
1123          }
1124    
1125          @Override
1126          public String visitInteger(IntegerPropertyDefinition prop, Void p) {
1127            String intStr = "An integer value.";
1128            intStr += " Lower value is " + prop.getLowerLimit() + ".";
1129            if (prop.getUpperLimit() != null) {
1130              intStr += " Upper value is " + prop.getUpperLimit() + " .";
1131            }
1132            if (prop.isAllowUnlimited()) {
1133              intStr += " A value of \"-1\" or \"unlimited\" for no limit.";
1134            }
1135            if (prop.getUnitSynopsis() != null) {
1136              intStr += " Unit is " + prop.getUnitSynopsis() + ".";
1137            }
1138            return intStr;
1139          }
1140    
1141          @Override
1142          public String visitIPAddress(IPAddressPropertyDefinition prop, Void p) {
1143            return "An IP address";
1144          }
1145    
1146          @Override
1147          public String visitIPAddressMask(
1148            IPAddressMaskPropertyDefinition prop, Void p) {
1149    
1150            return "An IP address mask";
1151          }
1152    
1153          @Override
1154          public String visitSize(SizePropertyDefinition prop, Void p) {
1155            String sizeStr = "A positive integer representing a size.";
1156            if (prop.getLowerLimit() != 0) {
1157              sizeStr += " Lower value is " + prop.getLowerLimit() + ".";
1158            }
1159            if (prop.getUpperLimit() != null) {
1160              sizeStr += " Upper value is " + prop.getUpperLimit() + " .";
1161            }
1162            if (prop.isAllowUnlimited()) {
1163              sizeStr += " A value of \"-1\" or \"unlimited\" for no limit.";
1164            }
1165            return sizeStr;
1166          }
1167    
1168          @Override
1169          public String visitString(StringPropertyDefinition prop, Void p) {
1170            String retStr = "A String";
1171            if (prop.getPatternSynopsis() != null) {
1172              retStr = prop.getPatternSynopsis().toString();
1173            }
1174            return retStr;
1175          }
1176    
1177          @Override
1178          public String visitUnknown(PropertyDefinition prop, Void p) {
1179            return "Unknown";
1180          }
1181        };
1182    
1183        // Invoke the visitor against the property definition.
1184        return (String) prop.accept(visitor, null);
1185    
1186      }
1187    
1188      @SuppressWarnings("unchecked")
1189      private String getDefaultBehaviorString(PropertyDefinition prop) {
1190        DefaultBehaviorProvider defaultBehav = prop.getDefaultBehaviorProvider();
1191        String defValueStr = "";
1192        if (defaultBehav instanceof UndefinedDefaultBehaviorProvider) {
1193          defValueStr = "None";
1194        } else if (defaultBehav instanceof DefinedDefaultBehaviorProvider) {
1195          DefinedDefaultBehaviorProvider defBehav =
1196            (DefinedDefaultBehaviorProvider) defaultBehav;
1197          for (Iterator<String> it = defBehav.getDefaultValues().iterator();
1198          it.hasNext();) {
1199    
1200            String str = it.next();
1201            defValueStr += str + (it.hasNext() ? "\n" : "");
1202          }
1203        } else if (defaultBehav instanceof AliasDefaultBehaviorProvider) {
1204          AliasDefaultBehaviorProvider aliasBehav = (
1205            AliasDefaultBehaviorProvider) defaultBehav;
1206          defValueStr = aliasBehav.getSynopsis().toString();
1207        } else if
1208          (defaultBehav instanceof RelativeInheritedDefaultBehaviorProvider) {
1209          RelativeInheritedDefaultBehaviorProvider relativBehav =
1210            (RelativeInheritedDefaultBehaviorProvider) defaultBehav;
1211          defValueStr = getDefaultBehaviorString(
1212            relativBehav.getManagedObjectDefinition().
1213            getPropertyDefinition(relativBehav.getPropertyName()));
1214        } else if
1215          (defaultBehav instanceof AbsoluteInheritedDefaultBehaviorProvider) {
1216          AbsoluteInheritedDefaultBehaviorProvider absoluteBehav =
1217            (AbsoluteInheritedDefaultBehaviorProvider) defaultBehav;
1218          defValueStr = getDefaultBehaviorString(
1219            absoluteBehav.getManagedObjectDefinition().
1220            getPropertyDefinition(absoluteBehav.getPropertyName()));
1221        }
1222        return defValueStr;
1223      }
1224    
1225      private TreeMap<String, AbstractManagedObjectDefinition> makeMOTreeMap(
1226        Collection<AbstractManagedObjectDefinition> coll) {
1227    
1228        if (coll == null) {
1229          return null;
1230        }
1231        TreeMap<String, AbstractManagedObjectDefinition> map =
1232          new TreeMap<String, AbstractManagedObjectDefinition>();
1233        for (AbstractManagedObjectDefinition mo : coll) {
1234          map.put(mo.getName(), mo);
1235        }
1236        return map;
1237      }
1238    
1239      private TreeMap<String, RelationDefinition> makeRelTreeMap(
1240        Collection<RelationDefinition> coll) {
1241    
1242        if (coll == null) {
1243          return null;
1244        }
1245        TreeMap<String, RelationDefinition> map =
1246          new TreeMap<String, RelationDefinition>();
1247        for (RelationDefinition rel : coll) {
1248          map.put(rel.getChildDefinition().getName(), rel);
1249        }
1250        return map;
1251      }
1252    
1253      private TreeMap<String, PropertyDefinition> makePropTreeMap(
1254        Collection<PropertyDefinition> coll) {
1255    
1256        if (coll == null) {
1257          return null;
1258        }
1259        TreeMap<String, PropertyDefinition> map =
1260          new TreeMap<String, PropertyDefinition>();
1261        for (PropertyDefinition prop : coll) {
1262          map.put(prop.getName(), prop);
1263        }
1264        return map;
1265      }
1266    
1267      private void horizontalLine() {
1268        htmlBuff.append("<hr style=\"width: 100%; height: 2px;\">");
1269      }
1270    
1271      private void endTable() {
1272        htmlBuff.append("</tbody>\n");
1273        htmlBuff.append("</table>\n");
1274      }
1275    
1276      private void bullet(String str) {
1277        htmlBuff.append(
1278          "<li>" +
1279          str +
1280          "</li>\n");
1281      }
1282    
1283      private void heading2(String string) {
1284        heading(string, 2);
1285      }
1286    
1287      private void heading3(String string) {
1288        heading(string, 3);
1289      }
1290    
1291      private void heading4(String string) {
1292        heading(string, 4);
1293      }
1294    
1295      private void heading(String str, int level) {
1296        htmlBuff.append(getHeading(str, level));
1297      }
1298    
1299      private String getHeading(String str, int level) {
1300        String strLevel = (new Integer(level)).toString();
1301        return "<h" + strLevel + ">" +
1302          "<a name=\"" + str + "\"></a>" +
1303          str +
1304          "</h" + strLevel + ">\n";
1305      }
1306    
1307      private String getHeading2(String str) {
1308        return getHeading(str, 2);
1309      }
1310    
1311      private String getAnchor(String str) {
1312        return "<a name=\"" + str + "\"></a>";
1313      }
1314    
1315      private void htmlHeader(String pageTitle) {
1316        htmlBuff.append(getHtmlHeader(pageTitle) +
1317          "<body>\n");
1318    
1319      }
1320    
1321      private String getHtmlHeader(String pageTitle) {
1322        return ("<html>\n" +
1323          "<head>\n" +
1324          "<meta http-equiv=\"content-type\"\n" +
1325          "content=\"text/html; charset=ISO-8859-1\">\n" +
1326          "<title>" + pageTitle + "</title>\n" +
1327          "<link rel=\"stylesheet\" type=\"text/css\"\n" +
1328          "href=\"" + CSS_FILE + "\">\n" +
1329          "</head>\n");
1330      }
1331    
1332      // Add a Tab Menu, the active tab is the one given as parameter
1333      private void tabMenu(String activeTab) {
1334        htmlBuff.append(
1335          "<div class=\"tabmenu\"> " +
1336    
1337          "<span><a " +
1338          (activeTab.equals(INHERITANCE_TREE_FILE) ? "class=\"activetab\" " : "") +
1339          "href=\"" + INHERITANCE_TREE_FILE + "\"" +
1340          " title=\"Inheritance View of Components\">Inheritance</a></span> " +
1341    
1342          "<span><a " +
1343          (activeTab.equals(RELATION_TREE_FILE) ? "class=\"activetab\" " : "") +
1344          "href=\"" + RELATION_TREE_FILE + "\"" +
1345          " title=\"Relational View of Components\">Structure</a></span> " +
1346    
1347          "<span><a " +
1348          (activeTab.equals(MO_LIST_FILE) ? "class=\"activetab\" " : "") +
1349          "href=\"" + MO_LIST_FILE + "\"" +
1350          " title=\"Alphabetical Index of Components\">Components</a></span> " +
1351    
1352          "<span><a " +
1353          (activeTab.equals(PROPERTIES_INDEX_FILE) ? "class=\"activetab\" " : "") +
1354          "href=\"" + PROPERTIES_INDEX_FILE + "\"" +
1355          " title=\"Alphabetical Index of Properties\" >Properties</a></span>" +
1356    
1357          "</div>" +
1358          "\n"
1359          );
1360      }
1361    
1362      private String getLink(String str, String link) {
1363        return getLink(str, link, null, null);
1364      }
1365    
1366      private String getLink(String str, String link, String target) {
1367        return getLink(str, link, target, null);
1368      }
1369    
1370      private String getLink(String str, String link, String target, String color) {
1371        return "<a " +
1372          (color != null ? "style=\"color:" + color + "\" " : "") +
1373          "href=\"" + link + "\"" +
1374          (target == null ? "" : " target=\"" + target + "\"") +
1375          ">"
1376          + str + "</a>";
1377      }
1378    
1379      private void link(String str, String link) {
1380        link(str, link, null, null);
1381      }
1382    
1383      private void link(String str, String link, String target) {
1384        link(str, link, target, null);
1385      }
1386    
1387      private void link(String str, String link, String target, String color) {
1388        String htmlStr = "";
1389        if (!inList && getIndentPixels() > 0) {
1390          htmlStr += "<div style=\"margin-left: " + getIndentPixels() + "px;\">";
1391        } else if (inList) {
1392          htmlStr += "<li>";
1393        }
1394        htmlStr += getLink(str, link, target, color);
1395        if (!inList && getIndentPixels() > 0) {
1396          htmlStr += "</div>";
1397        } else if (inList) {
1398          htmlStr += "</li>";
1399        }
1400        if (!inList) {
1401          htmlStr += "<br>";
1402        }
1403        htmlBuff.append(htmlStr + "\n");
1404      }
1405    
1406      private void newline() {
1407        htmlBuff.append(
1408          getNewLine());
1409      }
1410    
1411      private String getNewLine() {
1412        return "<br>\n";
1413      }
1414    
1415      private void paragraph(Message description) {
1416        if (description != null) {
1417          paragraph(description.toString());
1418        }
1419      }
1420    
1421      private void paragraph(Message description, TextStyle style) {
1422        if (description != null) {
1423          paragraph(description.toString(), style, null);
1424        }
1425      }
1426    
1427      private void paragraph(String description) {
1428        paragraph(description, TextStyle.STANDARD, null);
1429      }
1430    
1431      private void paragraph(String description, TextStyle style) {
1432        paragraph(description, style, null);
1433      }
1434    
1435      private void paragraph(String description, TextStyle style, String pClass) {
1436        String indentStr = "";
1437        String styleStr = "";
1438        String classStr = "";
1439        if (getIndentPixels() > 0) {
1440          indentStr = "style=\"margin-left: " + getIndentPixels() + "px;\"";
1441        }
1442        if (style == style.BOLD) {
1443          styleStr = "style=\"font-weight: bold;\"";
1444        } else if (style == style.ITALIC) {
1445          styleStr = "style=\"font-style: italic;\"";
1446        }
1447        if (pClass != null) {
1448          classStr = "class=" + pClass;
1449        }
1450    
1451        htmlBuff.append(
1452          "<p " +
1453          indentStr + " " +
1454          styleStr + " " +
1455          classStr +
1456          ">" +
1457          description +
1458          "</p>\n");
1459      }
1460    
1461      private int getIndentPixels() {
1462        return (ind * 40);
1463      }
1464    
1465      private void startTable() {
1466        htmlBuff.append(
1467          "<table " +
1468          "style=\"width: 100%; text-align: left;\"" +
1469          "border=\"1\"" +
1470          "cellpadding=\"1\"" +
1471          "cellspacing=\"0\"" +
1472          ">\n");
1473    
1474        htmlBuff.append("<tbody>\n");
1475      }
1476    
1477      /*
1478       * Generate a "friendly" name from a string :
1479       * '-' and '_' replaced by space
1480       * first letter of a word in uppercase
1481       */
1482      private String getFriendlyName(String str) {
1483        String retStr = "";
1484        String[] words = str.split("\\p{Punct}");
1485        for (int ii = 0; ii < words.length; ii++) {
1486          if (ii>0) {
1487            retStr += " ";
1488          }
1489          String word = words[ii];
1490           String firstChar = word.substring(0, 1).toUpperCase();
1491           retStr += firstChar + word.substring(1, word.length());
1492        }
1493        return retStr;
1494      }
1495    
1496      private void tableRow(String... strings) {
1497        htmlBuff.append(
1498          "<tr>\n");
1499        for (int ii = 0; ii < strings.length; ii++) {
1500          String string = strings[ii];
1501          htmlBuff.append(
1502            "<td style=\"" +
1503            "vertical-align: top; " +
1504            ((ii == 0) ? "width: 20%;" : "") +
1505            "\">" +
1506            string +
1507            "<br></td>");
1508        }
1509        htmlBuff.append(
1510          "</tr>\n");
1511      }
1512    
1513      /**
1514       * Text style.
1515       */
1516      private enum TextStyle {
1517    
1518        STANDARD, BOLD, ITALIC, UNDERLINE, FIXED_WIDTH
1519      }
1520    
1521      /**
1522       * List style.
1523       */
1524      private enum ListStyle {
1525    
1526        STANDARD, BULLET, NUMBER
1527      }
1528    
1529      private void indent() {
1530        ind++;
1531      }
1532    
1533      private void outdent() {
1534        ind--;
1535      }
1536    
1537      private void beginList() {
1538        inList = true;
1539        listLevel++;
1540        htmlBuff.append(
1541          "<ul>\n");
1542      }
1543    
1544      private void endList() {
1545        listLevel--;
1546        if (listLevel == 0) {
1547          inList = false;
1548        }
1549        htmlBuff.append(
1550          "</ul>\n");
1551      }
1552    
1553      private void htmlFooter() {
1554        htmlBuff.append(
1555          "</body>\n" +
1556          "</html>\n");
1557      }
1558    
1559      private static void usage() {
1560        System.err.println(
1561          "Usage : Provide the argument : output generation directory.");
1562        System.exit(1);
1563      }
1564    
1565      private void viewHelp(String helpStr) {
1566        htmlBuff.append(
1567          "<p class=\"view-help\" >" +
1568          helpStr +
1569          "</p>" +
1570          "\n"
1571          );
1572      }
1573    
1574      private void generateFile(String fileName) {
1575        // Write the html buffer in a file
1576        try {
1577          PrintWriter file = new java.io.PrintWriter(
1578            new java.io.FileWriter(generationDir + File.separator + fileName));
1579          file.write(htmlBuff.toString());
1580          file.close();
1581        } catch (Exception e) {
1582          e.printStackTrace();
1583          System.exit(1);
1584        }
1585        // re-init html buffer
1586        htmlBuff = new StringBuffer();
1587      }
1588    
1589      // Relation List from RootConfiguration
1590      private TreeMap<String, RelationDefinition> topRelList =
1591        new TreeMap<String, RelationDefinition>();
1592      private TreeMap<String, RelationDefinition> relList =
1593        new TreeMap<String, RelationDefinition>();
1594      private TreeMap<String, TreeMap<String, RelationDefinition>> catTopRelList =
1595        new TreeMap<String, TreeMap<String, RelationDefinition>>();
1596      // managed object list
1597      private TreeMap<String, AbstractManagedObjectDefinition> moList =
1598        new TreeMap<String, AbstractManagedObjectDefinition>();
1599      private TreeMap<String, AbstractManagedObjectDefinition> topMoList =
1600        new TreeMap<String, AbstractManagedObjectDefinition>();
1601      private TreeMap<String, TreeMap<String, AbstractManagedObjectDefinition>>
1602        catTopMoList =
1603        new TreeMap<String, TreeMap<String, AbstractManagedObjectDefinition>>();
1604      private int ind = 0;
1605      private StringBuffer htmlBuff = new StringBuffer();
1606      private static String generationDir;
1607      private static boolean ldapMapping = false;
1608      private static String opendsWiki;
1609      private static String opendsHome;
1610      private static String aciSyntaxPage;
1611      private static String durationSyntaxPage;
1612      private boolean inList = false;
1613      private int listLevel = 0;
1614    }