From f16a87f10d5c028aa2614dedc833b9c780fbe5f0 Mon Sep 17 00:00:00 2001
From: jcambon <jcambon@localhost>
Date: Thu, 17 Jan 2008 09:59:46 +0000
Subject: [PATCH] This is a new feature : automatically generate HTML configuration guide (issue #2004)

---
 opendj-sdk/opends/build.xml                                                         |   24 +
 opendj-sdk/opends/resource/config/opends-config.css                                 |   92 ++++
 opendj-sdk/opends/src/server/org/opends/server/admin/doc/ConfigGuideGeneration.java | 1037 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1,150 insertions(+), 3 deletions(-)

diff --git a/opendj-sdk/opends/build.xml b/opendj-sdk/opends/build.xml
index 8226932..8e4f4bb 100644
--- a/opendj-sdk/opends/build.xml
+++ b/opendj-sdk/opends/build.xml
@@ -42,6 +42,8 @@
   <property name="ext.dir"          location="ext"                     />
   <property name="package.dir"      location="${build.dir}/package"    />
   <property name="javadoc.dir"      location="${build.dir}/javadoc"    />
+  <property name="docgen.dir"       location="${build.dir}/docgen"    />
+  <property name="configguide.dir"  location="${docgen.dir}/configuration_guide"    />
   <property name="resource.dir"     location="resource"                />
   <property name="scripts.dir"      location="${resource.dir}/bin"     />
   <property name="config.dir"       location="${resource.dir}/config"  />
@@ -157,7 +159,7 @@
 
   <!-- The build target that should be used for nightly builds. -->
   <target name="nightly"
-       depends="checkstyle,dsml,srczip,javadoc,coverage,testallwithcoverage"
+       depends="checkstyle,dsml,srczip,javadoc,docgen,coverage,testallwithcoverage"
        description="Perform all processing needed for nightly builds.">
   </target>
 
@@ -174,7 +176,7 @@
 
   <!-- The build target that should be used to build everything. -->
   <target name="all"
-       depends="checkstyle,clean,checkprecommit,dsml,srczip,javadoc,testallwithcoverage"
+       depends="checkstyle,clean,checkprecommit,dsml,srczip,javadoc,docgen,testallwithcoverage"
        description="Build using all defined targets.">
   </target>
 
@@ -758,7 +760,23 @@
   </target>
 
 
-
+  <!-- Generate the Configuration guide. -->
+  <target name="docgen" depends="compile,copymessages"
+       description="Generate the Configuration guide (html).">
+    <mkdir dir="${configguide.dir}" />
+    <!-- copy the stylesheet file -->
+    <copy todir="${configguide.dir}"
+          file="${config.dir}/opends-config.css" />
+    <java fork="true" classname="org.opends.server.admin.doc.ConfigGuideGeneration" failonerror="true">
+      <classpath>
+        <fileset dir="${lib.dir}">
+          <include name="*.jar" />
+        </fileset>
+        <dirset dir="${classes.dir}" />
+      </classpath>
+      <arg value="${configguide.dir}"/>
+    </java>         
+  </target>
 
   <!-- Populate the Directory Server package, but don't zip it up. -->
   <target name="prepackage" depends="cleancompile"
diff --git a/opendj-sdk/opends/resource/config/opends-config.css b/opendj-sdk/opends/resource/config/opends-config.css
new file mode 100644
index 0000000..3a50069
--- /dev/null
+++ b/opendj-sdk/opends/resource/config/opends-config.css
@@ -0,0 +1,92 @@
+
+body                     { font-family: Arial, Helvetica, sans-serif; 
+                           font-size: 12px;
+                           color: #333; background-color: #fff; margin:20px }
+
+/* +++ 210 Page titles, headings, and paragraphs +++ */
+.pagename                { font-size: 1.7em; font-weight: normal;color:#5F6466; 
+                           margin: 0.5em 0.5em 0.5em 0; }
+h1                       { font-size: 2.0em; font-weight: normal;
+	                         margin-top: 0em; margin-bottom: 0em; }
+h2                       { font-size: 1.7em; font-weight: normal; 
+                           margin: 2.1em 0em 1em 0em;
+						   
+	                        border-bottom: 1px #D9D9D9 solid; padding-bottom:5px }
+h3                       { font-size: 1.4em; font-weight: bold;
+                           margin: 1.2em 0em .8em 0em; }
+h4                       { font-size: 1.2em; font-weight: bold;
+                           margin: 1.2em 0em .8em 0em; }
+h5                       { font-size: 1.0em; font-weight: bold;
+	                         margin: 1.2em 0em .8em 0em; }
+h6                       { font-size: 0.8em; font-weight: bold;
+	                         margin: 1.2em 0em .8em 0em; }
+p                        { margin: .75em 0 1em 0; }
+strong, b                { font-weight: bold;	}
+
+a                        { color: #006699; }
+
+
+ol                    { margin: 0.8em 0 0.8em 0.8em }
+ul                       { margin: 0.8em 0.2em } 
+li                       { margin: 0.6em 0.1em}
+li > p                   { margin-top: 0.2em }
+ol                       { list-style-type:decimal }
+ol ol                    { list-style-type:lower-alpha }
+ol ol ol                 { list-style-type:lower-roman }
+ol ol ol ol              { list-style-type:decimal }
+ol ol ol ol ol           { list-style-type:lower-alpha }
+ol ol ol ol ol ol        { list-style-type:lower-roman }
+ul ol                    { list-style-type:decimal }
+ul ol ol                 { list-style-type:lower-alpha }
+ul ol ol ol              { list-style-type:lower-roman }
+ul ol ol ol ol           { list-style-type:decimal }
+ul ol ol ol ol ol        { list-style-type:lower-alpha }
+ul ol ol ol ol ol ol     { list-style-type:lower-roman }
+ul                       { list-style:square }
+
+dl {margin-top:10px}
+dl dt                    { font-weight: bold;margin-bottom:3px }
+dl dd                    { margin-left: 10px; }
+
+
+.tabmenu                 { margin: 0; margin-left: -10px;padding: 0.25em 0 0.25em 1em;
+                           border-bottom: 1px solid #8f989f;margin-bottom:20px }
+.tabmenu span            { margin: 0; padding: 0; overflow: hidden; }
+.tabmenu span a          { color: #1A1A1A; background: #eee;
+                           margin: 0 0 0 -4px; padding: 4px 9px 3px;
+                           text-decoration: none; cursor: pointer;
+                           border: 1px solid gray; border-bottom:none;background-image:url(tab_deselected.jpg); }
+.tabmenu span a:hover      {text-decoration:underline; }
+
+.tabmenu .activetab      { color: #1A1A1A; 
+                           font-weight: normal;
+                           background-image:url(tab_selected.gif);background-color: #fff; cursor: default; border-bottom: 1px solid #fff; }
+
+
+
+
+table               { border:none;border-bottom:dotted 1px #ccc;margin: 0px 0px 20px;font-size: 12px;}
+table tr th       { border:none;border-top: dotted 1px #ccc;padding:5px 10px;background-color:#f5f5f5;font-weight:bold;text-align:left;vertical-align:bottom; 
+	                       empty-cells: show;font-size: 12px; }
+table tr td       { border:none;border-top: dotted 1px #ccc;padding:4px 10px;vertical-align:top;
+	                       empty-cells: show;font-size: 12px; }
+
+.breadcrumb { background-color:#f5f5f5; color:#f5994f; position:absolute;top:0;left:0;width:100%; padding:6px 15px;font-weight:normal;border-bottom:dotted 1px #ccc; }
+/*.breadcrumb a {color: #f5994f} */
+.pageactions a           { background-color: #f5f5f5;
+							font-size:10px;
+                        	 border: 1px solid  #999;
+							background-image:url(pageaction.gif);
+						   text-decoration:none;
+                           line-height: normal; padding: 0.25em 0.5em;
+						   color:#333;
+                         }
+
+
+/*.titletable {border-top:none;border-bottom: 1px #D9D9D9 solid;margin-bottom:20px } */
+.titletable {border:none;margin-bottom:5px;margin-top:10px }
+.titletable h2 {border:none; margin-bottom: .4em; margin-top:.8em }
+.titletable td { vertical-align:middle; border-top:none;padding-left:0px } 
+.propertyname {font-size: 14px; font-weight: bold;padding:3px 0px 3px 10px;margin: 0px;border-top: dotted 1px #ccc;background-color:#f5f5f5; }
+
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/doc/ConfigGuideGeneration.java b/opendj-sdk/opends/src/server/org/opends/server/admin/doc/ConfigGuideGeneration.java
new file mode 100644
index 0000000..a02642a
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/doc/ConfigGuideGeneration.java
@@ -0,0 +1,1037 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007-2008 Sun Microsystems, Inc.
+ */
+package org.opends.server.admin.doc;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import org.opends.messages.Message;
+import org.opends.server.admin.ACIPropertyDefinition;
+import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
+import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.AdministratorAction.Type;
+import org.opends.server.admin.AggregationPropertyDefinition;
+import org.opends.server.admin.AliasDefaultBehaviorProvider;
+import org.opends.server.admin.AttributeTypePropertyDefinition;
+import org.opends.server.admin.BooleanPropertyDefinition;
+import org.opends.server.admin.ClassLoaderProvider;
+import org.opends.server.admin.ClassPropertyDefinition;
+import org.opends.server.admin.DNPropertyDefinition;
+import org.opends.server.admin.DefaultBehaviorProvider;
+import org.opends.server.admin.DefinedDefaultBehaviorProvider;
+import org.opends.server.admin.DurationPropertyDefinition;
+import org.opends.server.admin.EnumPropertyDefinition;
+import org.opends.server.admin.IPAddressMaskPropertyDefinition;
+import org.opends.server.admin.IPAddressPropertyDefinition;
+import org.opends.server.admin.IntegerPropertyDefinition;
+import org.opends.server.admin.LDAPProfile;
+import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyDefinitionVisitor;
+import org.opends.server.admin.PropertyOption;
+import org.opends.server.admin.RelationDefinition;
+import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
+import org.opends.server.admin.SizePropertyDefinition;
+import org.opends.server.admin.StringPropertyDefinition;
+import org.opends.server.admin.TopCfgDefn;
+import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
+import org.opends.server.admin.std.meta.RootCfgDefn;
+import org.opends.server.types.InitializationException;
+import org.opends.server.util.EmbeddedUtils;
+
+/**
+ *  This class allow Configuration Guide documentation generation (html format).
+ * It is based on the Admin Framework Introspection API
+ *
+ */
+public class ConfigGuideGeneration {
+
+  // Note : still to be done :
+  // I18n support. Today all the strings are hardcoded in this file
+  
+  private final String OPENDS_WIKI = "https://www.opends.org/wiki";
+  private final String ACI_SYNTAX_PAGE = OPENDS_WIKI + "/page/ACISyntax";
+  private final String CSS_FILE = "opends-config.css";
+
+  /**
+   * Entry point for documentation generation.
+   *
+   * @param args The output generation directory (optional)
+   */
+  public static void main(String[] args) {
+    if (args.length == 0) {
+      // Default dir is prefixed by the system-dependent default temporary dir
+      generationDir = System.getProperty("java.io.tmpdir") + File.separator +
+        "opends_config_guide";
+    } else if ((args.length != 1) || !(new File(args[0])).isDirectory()) {
+      usage();
+    } else {
+      generationDir = args[0];
+    }
+    // Create new dir if necessary
+    try {
+      (new File(generationDir)).mkdir();
+    } catch (Exception e) {
+      e.printStackTrace();
+      System.exit(1);
+    }
+    System.out.println("Generation directory is : " + generationDir);
+    ConfigGuideGeneration myGen = new ConfigGuideGeneration();
+    myGen.generate();
+  }
+
+  private void generate() {
+    init();
+
+    // Generate the inheritance tree of all the managed objects
+    genManagedObjectInheritanceTree(topMoList);
+
+    // Generate the relation tree of all the managed objects
+    genManagedObjectRelationTree(topRelList);
+
+    // Generate all the managed objects and their children
+    genAllManagedObject(topMoList);
+
+    // Generate a list of managed objects
+    genManagedObjectList(moList);
+
+    // Generate an index of properties
+    genPropertiesIndex();
+  }
+
+  private void init() {
+
+    // Build a list of top relations
+    RootCfgDefn rootCfg = RootCfgDefn.getInstance();
+    for (RelationDefinition rel : rootCfg.getAllRelationDefinitions()) {
+      topRelList.put(rel.getChildDefinition().getName(), rel);
+    }
+
+    // Enable the client-side class loader to explicitly load classes
+    // which are not directly reachable from the root configuration
+    EmbeddedUtils.initializeForClientUse();
+    // Bootstrap definition classes.
+    try {
+      ClassLoaderProvider.getInstance().enable();
+    } catch (InitializationException e) {
+      System.err.println("ERROR : Cannot enable the client-side class loader.");
+      e.printStackTrace();
+      System.exit(1);
+    }
+    // Switch off class name validation in client.
+    ClassPropertyDefinition.setAllowClassValidation(false);
+    // Switch off attribute type name validation in client.
+    AttributeTypePropertyDefinition.setCheckSchema(false);
+
+    // Build a sorted list of top managed objects
+    TopCfgDefn topCfg = TopCfgDefn.getInstance();
+    Collection<AbstractManagedObjectDefinition<?, ?>> topObjects =
+      topCfg.getChildren();
+    for (AbstractManagedObjectDefinition topObject : topObjects) {
+      if (topObject.getName().equals("")) {
+        // root
+        continue;
+      }
+      topMoList.put(topObject.getName(), topObject);
+    }
+
+  }
+
+  /**
+   * Generate the inheritance tree of all the managed objects.
+   */
+  private void genManagedObjectInheritanceTree(
+    TreeMap<String, AbstractManagedObjectDefinition> list) {
+
+    htmlHeader("OpenDS - Configuring Specific Server Components - " +
+      "Inheritance tree");
+    heading2("Configuring Specific Server Components - Inheritance tree");
+    genMoInheritanceTree(list);
+    generateFile("ManagedObjectInheritanceTree.html");
+  }
+
+  @SuppressWarnings("unchecked")
+  private void genMoInheritanceTree(
+    TreeMap<String, AbstractManagedObjectDefinition> list) {
+
+    beginList();
+    for (AbstractManagedObjectDefinition mo : list.values()) {
+      if (listLevel == 1) {
+        paragraph(
+          getLink(mo.getUserFriendlyPluralName().toString(),
+          mo.getName() + ".html"));
+      } else {
+        link(mo.getUserFriendlyName().toString(), mo.getName() + ".html");
+      }
+      if (mo.hasChildren()) {
+        genMoInheritanceTree(makeMOTreeMap(mo.getChildren()));
+      }
+    }
+    endList();
+    if (listLevel == 1) {
+      newline();
+    }
+  }
+
+  /**
+   * Generate the relation tree of all the managed objects.
+   */
+  private void genManagedObjectRelationTree(
+    TreeMap<String, RelationDefinition> list) {
+
+    htmlHeader("OpenDS - Configuring Specific Server Components - " +
+      "Containment tree");
+    heading2("Configuring Specific Server Components - Containment tree");
+    paragraph(
+      "This tree represents the composition relation between components. " +
+      "This means that a child component is deleted " +
+      "when its parent is deleted.");
+    genMORelationTree(list);
+    generateFile("ManagedObjectRelationTree.html");
+  }
+
+  @SuppressWarnings("unchecked")
+  private void genMORelationTree(TreeMap<String, RelationDefinition> subList) {
+    if (!subList.values().isEmpty()) {
+      beginList();
+      for (RelationDefinition rel : subList.values()) {
+        AbstractManagedObjectDefinition childMo = rel.getChildDefinition();
+        AbstractManagedObjectDefinition parentMo = rel.getParentDefinition();
+        relList.put(childMo.getName(), rel);
+        String linkStr = getLink(childMo.getUserFriendlyName().toString(),
+          childMo.getName() + ".html");
+        String fromStr = "";
+        if (!parentMo.getName().equals("")) {
+          fromStr = " (from " +
+            getLink(parentMo.getUserFriendlyName().toString(),
+            parentMo.getName() + ".html") + ")";
+        }
+        bullet(linkStr + fromStr);
+        genMORelationTree(makeRelTreeMap(childMo.getAllRelationDefinitions()));
+        if (childMo.hasChildren()) {
+          for (Iterator<AbstractManagedObjectDefinition> it =
+            childMo.getChildren().iterator(); it.hasNext();) {
+
+            AbstractManagedObjectDefinition mo = it.next();
+            genMORelationTree(makeRelTreeMap(mo.getAllRelationDefinitions()));
+          }
+        }
+        if (listLevel == 1) {
+          newline();
+        }
+      }
+      endList();
+    }
+  }
+
+  /**
+   * Generate all the managed objects HTML pages.
+   */
+  @SuppressWarnings("unchecked")
+  private void genAllManagedObject(
+    TreeMap<String, AbstractManagedObjectDefinition> list) {
+
+    for (AbstractManagedObjectDefinition mo : list.values()) {
+      moList.put(mo.getName(), mo);
+      genManagedObject(mo);
+      if (mo.hasChildren()) {
+        genAllManagedObject(makeMOTreeMap(mo.getChildren()));
+      }
+    }
+  }
+
+  private void genManagedObject(AbstractManagedObjectDefinition mo) {
+    //------------------------------------------------------------------------
+    // Header
+    //------------------------------------------------------------------------
+
+    String title = "The " + mo.getUserFriendlyName() + " Configuration";
+    htmlHeader("OpenDS - " + title);
+
+    // title
+    heading2(title);
+
+    // description
+    paragraph(mo.getSynopsis());
+    paragraph(mo.getDescription());
+
+    newline();
+    horizontalLine();
+
+    // sub-components
+    if (mo.hasChildren()) {
+      heading4("Direct Subcomponents");
+      paragraph("The following " + mo.getUserFriendlyPluralName() +
+        " are available in the server :");
+      beginList();
+      @SuppressWarnings("unchecked")
+      TreeMap<String, AbstractManagedObjectDefinition> children =
+        makeMOTreeMap(mo.getChildren());
+      for ( AbstractManagedObjectDefinition child : children.values()) {
+        link(child.getUserFriendlyName().toString(), child.getName() + ".html");
+      }
+      endList();
+
+      paragraph("All the " + mo.getUserFriendlyPluralName() +
+        " inherit from the properties described below.");
+    }
+
+    // Parent
+    if (!mo.getParent().isTop()) {
+      heading4("Parent Component");
+      paragraph("The " + mo.getUserFriendlyName() + " component inherits from "
+        + getLink(mo.getParent().getUserFriendlyName().toString(),
+        mo.getParent().getName() + ".html"));
+    }
+
+    // Relations
+    if (!mo.getRelationDefinitions().isEmpty()) {
+      heading4("Relations From this Component");
+      paragraph(
+        "The following components have a direct composition relation FROM " +
+        mo.getUserFriendlyPluralName() + " :");
+      @SuppressWarnings("unchecked")
+      Collection<RelationDefinition> rels = mo.getRelationDefinitions();
+      for ( RelationDefinition rel : rels) {
+        beginList();
+        AbstractManagedObjectDefinition childRel = rel.getChildDefinition();
+        link(childRel.getUserFriendlyName().toString(), childRel.getName() +
+          ".html");
+        endList();
+      }
+    }
+    if (!mo.getReverseRelationDefinitions().isEmpty()) {
+      boolean isRoot = false;
+      @SuppressWarnings("unchecked")
+      Collection<RelationDefinition> rels = mo.getReverseRelationDefinitions();
+      for ( RelationDefinition rel : rels) {
+        // only check if it is not root
+        if (rel.getParentDefinition().getName().equals("")) {
+          isRoot = true;
+        }
+      }
+      if (!isRoot) {
+        heading4("Relations To this Component");
+        paragraph(
+          "The following components have a direct composition relation TO " +
+          mo.getUserFriendlyPluralName() + " :");
+        for ( RelationDefinition rel : rels) {
+          beginList();
+          AbstractManagedObjectDefinition childRel = rel.getParentDefinition();
+          link(childRel.getUserFriendlyName().toString(), childRel.getName() +
+            ".html");
+          endList();
+        }
+      }
+    }
+
+    newline();
+    horizontalLine();
+    newline();
+
+    // Page links
+    paragraph("This page describes the " + mo.getUserFriendlyName() + ":");
+    beginList();
+    link("Properties", "#Properties");
+    link("LDAP Mapping", "#LDAP Mapping");
+    endList();
+    newline();
+
+
+    //------------------------------------------------------------------------
+    // Properties
+    //------------------------------------------------------------------------
+
+    heading3("Properties");
+
+    paragraph(mo.getUserFriendlyPluralName() +
+      " contain the following properties:");
+    newline();
+
+    // Properties actually defined in this managed object
+    @SuppressWarnings("unchecked")
+    Collection<PropertyDefinition> props = mo.getAllPropertyDefinitions();
+    TreeMap<String, PropertyDefinition> propList = makePropTreeMap(props);
+    for ( PropertyDefinition prop : propList.values()) {
+      generateProperty(mo, prop);
+      newline();
+    }
+
+    newline();
+
+    //------------------------------------------------------------------------
+    // LDAP mapping
+    //------------------------------------------------------------------------
+
+    heading3("LDAP Mapping");
+    paragraph(
+      "Each dscfg configuration property can be mapped to a specific " +
+      "LDAP attribute under the \"cn=config\" entry. " +
+      "The mappings that follow are provided for information only. " +
+      "In general, you should avoid changing the server configuration " +
+      "by manipulating the LDAP attributes directly.");
+
+    // Managed object table
+    startTable();
+
+    LDAPProfile ldapProfile = LDAPProfile.getInstance();
+    tableRow("Base DN", getBaseDN(mo, ldapProfile));
+
+    tableRow("objectclass name", ldapProfile.getObjectClass(mo));
+    if (mo.getParent().getName() != null) {
+      String superior = "";
+      if (mo.getParent().getName().equals("top")) {
+        superior = "top";
+      } else {
+        if (moList.get(mo.getParent().getName()) != null) {
+          superior =
+            ldapProfile.getObjectClass(moList.get(mo.getParent().getName()));
+        } else {
+          System.err.println(
+            "Error: managed object " + mo.getName() + " not found.");
+        }
+      }
+      tableRow("objectclass superior", superior);
+    } else {
+      System.err.println(
+        "Error: objectclass superior not found for " + mo.getName());
+    }
+    endTable();
+
+    newline();
+    // Properties table
+    startTable();
+    tableRow("Property", "LDAP attribute");
+    for ( PropertyDefinition prop : propList.values()) {
+      tableRow(prop.getName(), ldapProfile.getAttributeName(mo, prop));
+    }
+
+    endTable();
+
+    htmlFooter();
+
+    generateFile(mo.getName() + ".html");
+  }
+
+  private void generateProperty(
+    AbstractManagedObjectDefinition mo, PropertyDefinition prop) {
+
+    // Property name
+    paragraph(getAnchor(prop.getName()) + prop.getName(), TextStyle.BOLD);
+
+    // Property table
+    startTable();
+    tableRow("Description",
+      ((prop.getSynopsis() != null) ? prop.getSynopsis().toString() : "") +
+      ((prop.getDescription() != null) ?
+        prop.getDescription().toString() : ""));
+
+    // Default value
+    String defValueStr = getDefaultBehaviorString(prop);
+    tableRow("Default Value", defValueStr);
+
+    tableRow("Allowed Values", getSyntaxStr(prop));
+
+    tableRow("Multi-valued",
+      (prop.hasOption(PropertyOption.MULTI_VALUED) ? "Yes" : "No"));
+
+    if (prop.hasOption(PropertyOption.MANDATORY)) {
+      tableRow("Required", "Yes");
+    }
+
+    if (prop.getAdministratorAction() != null) {
+      Message synopsis = prop.getAdministratorAction().getSynopsis();
+      Type actionType = prop.getAdministratorAction().getType();
+      String actionStr = "";
+      if (actionType == actionType.COMPONENT_RESTART) {
+        actionStr = "The " + mo.getUserFriendlyName() +
+          " must be disabled and re-enabled for changes to this setting " +
+          "to take effect";
+      } else if (actionType == actionType.SERVER_RESTART) {
+        actionStr = "Restart the server";
+      } else if (actionType == actionType.NONE) {
+        actionStr = "None";
+      }
+      String action = actionStr +
+        ((synopsis != null) ? ". " + synopsis : "");
+      tableRow("Admin Action Required", action);
+    }
+
+
+    if (prop.hasOption(PropertyOption.ADVANCED)) {
+      tableRow("Advanced Property", "Yes");
+    }
+
+    endTable();
+
+  }
+
+  private void genManagedObjectList(
+    TreeMap<String, AbstractManagedObjectDefinition> list) {
+
+    htmlHeader("Component List");
+    for (AbstractManagedObjectDefinition mo : list.values()) {
+      link(mo.getUserFriendlyName().toString(), mo.getName() + ".html");
+    }
+    htmlFooter();
+    generateFile("ManagedObjectList.html");
+  }
+
+  private void genPropertiesIndex() {
+
+    // Build a sorted list of (property name + its managed object name)
+    TreeSet<String> propMoList = new TreeSet<String>();
+    for (AbstractManagedObjectDefinition<?, ?> mo : moList.values()) {
+      for (PropertyDefinition<?> prop : mo.getPropertyDefinitions()) {
+        propMoList.add(
+          prop.getName() + "," + prop.getManagedObjectDefinition().getName());
+      }
+    }
+
+    String lettersPointers = "";
+    String firstChar = ".";
+    for (String propMoStr : propMoList) {
+      String[] propMoArray = propMoStr.split(",");
+      String propName = propMoArray[0];
+      AbstractManagedObjectDefinition mo = moList.get(propMoArray[1]);
+      if (!propName.startsWith(firstChar)) {
+        firstChar = propName.substring(0, 1);
+        String letter = firstChar.toUpperCase();
+        htmlBuff.append(getAnchor(letter) + getHeading2(letter));
+        lettersPointers += getLink(letter, "#" + letter) + " ";
+      }
+      String propLink = getLink(propName,
+        mo.getName() + ".html" + "#" + propName);
+      String moLink =
+        getLink(mo.getUserFriendlyName().toString(), mo.getName() + ".html");
+      paragraph(propLink + " (" + moLink + ")");
+    }
+
+    String indexBody = htmlBuff.toString();
+    htmlBuff = new StringBuffer();
+    htmlHeader("Properties Index");
+    paragraph(lettersPointers);
+    htmlBuff.append(indexBody);
+    htmlFooter();
+    generateFile("PropertiesIndex.html");
+  }
+
+  private String getBaseDN(
+    AbstractManagedObjectDefinition mo, LDAPProfile ldapProfile) {
+
+    if (relList.get(mo.getName()) != null) {
+      return ldapProfile.getRelationRDNSequence(relList.get(mo.getName()));
+    } else if (moList.get(mo.getParent().getName()) != null) {
+      // check its superior
+      return getBaseDN(moList.get(mo.getParent().getName()), ldapProfile);
+    } else {
+      System.err.println("Error: Base DN not found for " + mo.getName());
+    }
+    return null;
+  }
+
+  @SuppressWarnings("unchecked")
+  private String getSyntaxStr(PropertyDefinition prop) {
+    // Create a visitor for performing syntax specific processing.
+    PropertyDefinitionVisitor<String, Void> visitor =
+      new PropertyDefinitionVisitor<String, Void>() {
+
+      @Override
+      public String visitACI(ACIPropertyDefinition prop, Void p) {
+        return getLink("An ACI Syntax", ACI_SYNTAX_PAGE);
+      }
+
+      @Override
+      public String visitAggregation(
+        AggregationPropertyDefinition prop, Void p) {
+
+        RelationDefinition rel = prop.getRelationDefinition();
+        String linkStr = getLink(rel.getUserFriendlyName().toString(),
+          rel.getName() + ".html");
+      return "The DN of any " +  linkStr + ". " +
+        ((prop.getSourceConstraintSynopsis() != null) ?
+          prop.getSourceConstraintSynopsis().toString() : "");
+      }
+
+      @Override
+      public String visitAttributeType(
+        AttributeTypePropertyDefinition prop, Void p) {
+
+        return "The name of an attribute type defined in the server schema.";
+      }
+
+      @Override
+      public String visitBoolean(BooleanPropertyDefinition prop, Void p) {
+        return "true" + getNewLine() + "false";
+      }
+
+      @Override
+      public String visitClass(ClassPropertyDefinition prop, Void p) {
+        String classStr =
+          "A java class that implements or extends the class(es) :";
+        for (String clazz : prop.getInstanceOfInterface()) {
+          classStr += getNewLine() + clazz;
+        }
+        return classStr;
+      }
+
+      @Override
+      public String visitDN(DNPropertyDefinition prop, Void p) {
+        String retStr = "A valid DN.";
+        if (prop.getBaseDN() != null) {
+          retStr += prop.getBaseDN().toString();
+        }
+        return retStr;
+      }
+
+      @Override
+      public String visitDuration(DurationPropertyDefinition prop, Void p) {
+        String durationStr = "";
+
+        if (prop.isAllowUnlimited()) {
+          durationStr += "A value of \"-1\" or \"unlimited\" for no limit. ";
+        }
+        if (prop.getMaximumUnit() != null) {
+          durationStr += "Maximum unit is \"" +
+            prop.getMaximumUnit().getLongName() + "\". ";
+        }
+        long lowerLimitStr = new Double(prop.getBaseUnit().
+          fromMilliSeconds(prop.getLowerLimit())).longValue();
+        durationStr += "Lower limit is " + lowerLimitStr +
+          " " + prop.getBaseUnit().getLongName() + ". ";
+        if (prop.getUpperLimit() != null) {
+          long upperLimitStr = new Double(prop.getBaseUnit().
+            fromMilliSeconds(prop.getUpperLimit())).longValue();
+          durationStr += "Upper limit is " + upperLimitStr +
+            " " + prop.getBaseUnit().getLongName() + ". ";
+        }
+
+        return durationStr;
+      }
+
+      @Override
+      public String visitEnum(EnumPropertyDefinition prop, Void p) {
+        String enumStr = "";
+        Class en = prop.getEnumClass();
+        for (Object cst : en.getEnumConstants()) {
+          enumStr += cst.toString();
+          if (prop.getValueSynopsis((Enum) cst) != null) {
+            enumStr += " - " + prop.getValueSynopsis((Enum) cst).toString();
+          }
+          enumStr += getNewLine() + getNewLine();
+        }
+        return enumStr;
+      }
+
+      @Override
+      public String visitInteger(IntegerPropertyDefinition prop, Void p) {
+        String intStr = "An integer value.";
+        intStr += " Lower value is " + prop.getLowerLimit() + ".";
+        if (prop.getUpperLimit() != null) {
+          intStr += " Upper value is " + prop.getUpperLimit() + " .";
+        }
+        if (prop.isAllowUnlimited()) {
+          intStr += " A value of \"-1\" or \"unlimited\" for no limit.";
+        }
+        if (prop.getUnitSynopsis() != null) {
+          intStr += " Unit is " + prop.getUnitSynopsis() + ".";
+        }
+        return intStr;
+      }
+
+      @Override
+      public String visitIPAddress(IPAddressPropertyDefinition prop, Void p) {
+        return "An IP address";
+      }
+
+      @Override
+      public String visitIPAddressMask(
+        IPAddressMaskPropertyDefinition prop, Void p) {
+
+        return "An IP address mask";
+      }
+
+      @Override
+      public String visitSize(SizePropertyDefinition prop, Void p) {
+        String sizeStr = "A positive integer representing a size.";
+        if (prop.getLowerLimit() != 0) {
+          sizeStr += " Lower value is " + prop.getLowerLimit() + ".";
+        }
+        if (prop.getUpperLimit() != null) {
+          sizeStr += " Upper value is " + prop.getUpperLimit() + " .";
+        }
+        if (prop.isAllowUnlimited()) {
+          sizeStr += " A value of \"-1\" or \"unlimited\" for no limit.";
+        }
+        return sizeStr;
+      }
+
+      @Override
+      public String visitString(StringPropertyDefinition prop, Void p) {
+        return "A String";
+      }
+
+      @Override
+      public String visitUnknown(PropertyDefinition prop, Void p) {
+        return "Unknown";
+      }
+    };
+
+    // Invoke the visitor against the property definition.
+    return (String) prop.accept(visitor, null);
+
+  }
+
+  @SuppressWarnings("unchecked")
+  private String getDefaultBehaviorString(PropertyDefinition prop) {
+    DefaultBehaviorProvider defaultBehav = prop.getDefaultBehaviorProvider();
+    String defValueStr = "";
+    if (defaultBehav instanceof UndefinedDefaultBehaviorProvider) {
+      defValueStr = "None";
+    } else if (defaultBehav instanceof DefinedDefaultBehaviorProvider) {
+      DefinedDefaultBehaviorProvider defBehav =
+        (DefinedDefaultBehaviorProvider) defaultBehav;
+      for (Iterator<String> it = defBehav.getDefaultValues().iterator();
+      it.hasNext();) {
+
+        String str = it.next();
+        defValueStr += str + (it.hasNext() ? "\n" : "");
+      }
+    } else if (defaultBehav instanceof AliasDefaultBehaviorProvider) {
+      AliasDefaultBehaviorProvider aliasBehav = (
+        AliasDefaultBehaviorProvider) defaultBehav;
+      defValueStr = aliasBehav.getSynopsis().toString();
+    } else if
+      (defaultBehav instanceof RelativeInheritedDefaultBehaviorProvider) {
+      RelativeInheritedDefaultBehaviorProvider relativBehav =
+        (RelativeInheritedDefaultBehaviorProvider) defaultBehav;
+      defValueStr = getDefaultBehaviorString(
+        relativBehav.getManagedObjectDefinition().
+        getPropertyDefinition(relativBehav.getPropertyName()));
+    } else if
+      (defaultBehav instanceof AbsoluteInheritedDefaultBehaviorProvider) {
+      AbsoluteInheritedDefaultBehaviorProvider absoluteBehav =
+        (AbsoluteInheritedDefaultBehaviorProvider) defaultBehav;
+      defValueStr = getDefaultBehaviorString(
+        absoluteBehav.getManagedObjectDefinition().
+        getPropertyDefinition(absoluteBehav.getPropertyName()));
+    }
+    return defValueStr;
+  }
+
+  private TreeMap<String, AbstractManagedObjectDefinition> makeMOTreeMap(
+    Collection<AbstractManagedObjectDefinition> coll) {
+
+    if (coll == null) {
+      return null;
+    }
+    TreeMap<String, AbstractManagedObjectDefinition> map =
+      new TreeMap<String, AbstractManagedObjectDefinition>();
+    for (AbstractManagedObjectDefinition mo : coll) {
+      map.put(mo.getName(), mo);
+    }
+    return map;
+  }
+
+  private TreeMap<String, RelationDefinition> makeRelTreeMap(
+    Collection<RelationDefinition> coll) {
+
+    if (coll == null) {
+      return null;
+    }
+    TreeMap<String, RelationDefinition> map =
+      new TreeMap<String, RelationDefinition>();
+    for (RelationDefinition rel : coll) {
+      map.put(rel.getChildDefinition().getName(), rel);
+    }
+    return map;
+  }
+
+  private TreeMap<String, PropertyDefinition> makePropTreeMap(
+    Collection<PropertyDefinition> coll) {
+
+    if (coll == null) {
+      return null;
+    }
+    TreeMap<String, PropertyDefinition> map =
+      new TreeMap<String, PropertyDefinition>();
+    for (PropertyDefinition prop : coll) {
+      map.put(prop.getName(), prop);
+    }
+    return map;
+  }
+
+  private void horizontalLine() {
+    htmlBuff.append("<hr style=\"width: 100%; height: 2px;\">");
+  }
+
+  private void endTable() {
+    htmlBuff.append("</tbody>\n");
+    htmlBuff.append("</table>\n");
+  }
+
+  private void bullet(String str) {
+    htmlBuff.append(
+      "<li>" +
+      str +
+      "</li>\n");
+  }
+
+  private void heading2(String string) {
+    heading(string, 2);
+  }
+
+  private void heading3(String string) {
+    heading(string, 3);
+  }
+
+  private void heading4(String string) {
+    heading(string, 4);
+  }
+
+  private void heading(String str, int level) {
+    htmlBuff.append(getHeading(str, level));
+  }
+
+  private String getHeading(String str, int level) {
+    String strLevel = (new Integer(level)).toString();
+    return "<h" + strLevel + ">" +
+      "<a name=\"" + str + "\"></a>" +
+      str +
+      "</h" + strLevel + ">\n";
+  }
+
+  private String getHeading2(String str) {
+    return getHeading(str, 2);
+  }
+
+  private String getAnchor(String str) {
+    return "<a name=\"" + str + "\"></a>";
+  }
+
+  private void htmlHeader(String pageTitle) {
+    htmlBuff.append(getHtmlHeader(pageTitle) +
+      "<body style=\"color: rgb(0, 0, 0); " +
+      "background-color: rgb(255, 255, 255);\">\n");
+
+  }
+
+//  private void htmlHeaderForFrames(String pageTitle) {
+//    htmlBuff.append(getHtmlHeader(pageTitle));
+//  }
+  private String getHtmlHeader(String pageTitle) {
+    return ("<html>\n" +
+      "<head>\n" +
+      "<meta http-equiv=\"content-type\"\n" +
+      "content=\"text/html; charset=ISO-8859-1\">\n" +
+      "<title>" + pageTitle + "</title>\n" +
+      "<link rel=\"stylesheet\" type=\"text/css\"\n" +
+      "href=\"" + CSS_FILE + "\">\n" +
+      "</head>\n");
+  }
+
+  private String getLink(String str, String link) {
+    return "<a href=\"" + link + "\">" + str + "</a>";
+  }
+
+  private void link(String str, String link) {
+    String htmlStr = "";
+    if (!inList && getIndentPixels() > 0) {
+      htmlStr += "<div style=\"margin-left: " + getIndentPixels() + "px;\">";
+    } else if (inList) {
+      htmlStr += "<li>";
+    }
+    htmlStr += getLink(str, link);
+    if (!inList && getIndentPixels() > 0) {
+      htmlStr += "</div>";
+    } else if (inList) {
+      htmlStr += "</li>";
+    }
+    if (!inList) {
+      htmlStr += "<br>";
+    }
+    htmlBuff.append(htmlStr + "\n");
+  }
+
+  private void newline() {
+    htmlBuff.append(
+      getNewLine());
+  }
+
+  private String getNewLine() {
+    return "<br>\n";
+  }
+
+  private void paragraph(Message description) {
+    if (description != null) {
+      paragraph(description.toString());
+    }
+  }
+
+  private void paragraph(Message description, TextStyle style) {
+    if (description != null) {
+      paragraph(description.toString(), style);
+    }
+  }
+
+  private void paragraph(String description) {
+    paragraph(description, TextStyle.STANDARD);
+  }
+
+  private void paragraph(String description, TextStyle style) {
+    String firstTag;
+    if (getIndentPixels() > 0) {
+      firstTag = "<p style=\"margin-left: " + getIndentPixels() + "px;\">";
+    } else if (style == style.BOLD) {
+      firstTag = "<p style=\"font-weight: bold;\">";
+    } else {
+      firstTag = "<p>";
+    }
+    htmlBuff.append(
+      firstTag +
+      description +
+      "</p>\n");
+  }
+
+  private int getIndentPixels() {
+    return (ind * 40);
+  }
+
+  private void startTable() {
+    htmlBuff.append(
+      "<table " +
+      "style=\"width: 100%; text-align: left;\"" +
+      "border=\"1\"" +
+      "cellpadding=\"1\"" +
+      "cellspacing=\"0\"" +
+      ">\n");
+
+    htmlBuff.append("<tbody>\n");
+  }
+
+  private void tableRow(String... strings) {
+    htmlBuff.append(
+      "<tr>\n");
+    for (int ii = 0; ii < strings.length; ii++) {
+      String string = strings[ii];
+      htmlBuff.append(
+        "<td style=\"" +
+        "vertical-align: top; " +
+        ((ii == 0) ? "width: 20%;" : "") +
+        "\">" +
+        string +
+        "<br></td>");
+    }
+    htmlBuff.append(
+      "</tr>\n");
+  }
+
+  /**
+   * Text style.
+   */
+  private enum TextStyle {
+
+    STANDARD, BOLD, ITALIC, UNDERLINE, FIXED_WIDTH
+  }
+
+  /**
+   * List style.
+   */
+  private enum ListStyle {
+
+    STANDARD, BULLET, NUMBER
+  }
+
+  private void indent() {
+    ind++;
+  }
+
+  private void outdent() {
+    ind--;
+  }
+
+  private void beginList() {
+    inList = true;
+    listLevel++;
+    htmlBuff.append(
+      "<ul>\n");
+  }
+
+  private void endList() {
+    listLevel--;
+    if (listLevel == 0) {
+      inList = false;
+    }
+    htmlBuff.append(
+      "</ul>\n");
+  }
+
+  private void htmlFooter() {
+    htmlBuff.append(
+      "</body>\n" +
+      "</html>\n");
+  }
+
+  private static void usage() {
+    System.err.println(
+      "Usage : Provide the argument : output generation directory.");
+    System.exit(1);
+  }
+
+  private void generateFile(String fileName) {
+    // Write the html buffer in a file
+    try {
+      PrintWriter file = new java.io.PrintWriter(
+        new java.io.FileWriter(generationDir + File.separator + fileName));
+      file.write(htmlBuff.toString());
+      file.close();
+    } catch (Exception e) {
+      e.printStackTrace();
+      System.exit(1);
+    }
+    // re-init html buffer
+    htmlBuff = new StringBuffer();
+  }
+
+  // Relation List from RootConfiguration
+  private TreeMap<String, RelationDefinition> topRelList =
+    new TreeMap<String, RelationDefinition>();
+  private TreeMap<String, RelationDefinition> relList =
+    new TreeMap<String, RelationDefinition>();
+  // managed object list
+  private TreeMap<String, AbstractManagedObjectDefinition> moList =
+    new TreeMap<String, AbstractManagedObjectDefinition>();
+  private TreeMap<String, AbstractManagedObjectDefinition> topMoList =
+    new TreeMap<String, AbstractManagedObjectDefinition>();
+  private int ind = 0;
+  private StringBuffer htmlBuff = new StringBuffer();
+  private static String generationDir;
+  private boolean inList = false;
+  private int listLevel = 0;
+}

--
Gitblit v1.10.0