mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

jcambon
17.59.2008 f16a87f10d5c028aa2614dedc833b9c780fbe5f0
This is a new feature : automatically generate HTML configuration guide (issue #2004)

This new doc generation is included in the build.xml :
- a new target has been added : "docgen"
- this target is called at the same level as the javadoc target (i.e. from "all" and "nightly" targets)
- the doc is generated at : build/docgen/configuration_guide
(note that Brian is still working on the mockup, so the headers of the pages will have a nicer look)
2 files added
1 files modified
1153 ■■■■■ changed files
opendj-sdk/opends/build.xml 24 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/resource/config/opends-config.css 92 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/doc/ConfigGuideGeneration.java 1037 ●●●●● patch | view | raw | blame | history
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"
opendj-sdk/opends/resource/config/opends-config.css
New file
@@ -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; }
opendj-sdk/opends/src/server/org/opends/server/admin/doc/ConfigGuideGeneration.java
New file
@@ -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;
}