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

Jean-Noël Rouvignac
04.34.2016 6ac6c922ba0db2c398652655dd857c6d556fe3bd
Restore accidentally deleted ConfigGuideGeneration

Code has been adapted to use the new config framework.
The folowing code has been commented out because these methods no longer
exist:
// Switch off class name validation in client.
// ClassPropertyDefinition.setAllowClassValidation(false);
// Switch off attribute type name validation in client.
// AttributeTypePropertyDefinition.setCheckSchema(false);
2 files added
1634 ■■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/admin/doc/ConfigGuideGeneration.java 1606 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/admin/doc/package-info.java 28 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/admin/doc/ConfigGuideGeneration.java
New file
@@ -0,0 +1,1606 @@
/*
 * The contents of this file are subject to the terms of the Common Development and
 * Distribution License (the License). You may not use this file except in compliance with the
 * License.
 *
 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
 * specific language governing permission and limitations under the License.
 *
 * When distributing Covered Software, include this CDDL Header Notice in each file and include
 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2007-2010 Sun Microsystems, Inc.
 * Portions Copyright 2011-2016 ForgeRock AS.
 */
package org.opends.server.admin.doc;
import java.io.File;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.Properties;
import java.util.TreeMap;
import java.util.TreeSet;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.config.ACIPropertyDefinition;
import org.forgerock.opendj.config.AbsoluteInheritedDefaultBehaviorProvider;
import org.forgerock.opendj.config.AbstractManagedObjectDefinition;
import org.forgerock.opendj.config.AdministratorAction.Type;
import org.forgerock.opendj.config.AggregationPropertyDefinition;
import org.forgerock.opendj.config.AliasDefaultBehaviorProvider;
import org.forgerock.opendj.config.AttributeTypePropertyDefinition;
import org.forgerock.opendj.config.BooleanPropertyDefinition;
import org.forgerock.opendj.config.ClassPropertyDefinition;
import org.forgerock.opendj.config.ConfigurationFramework;
import org.forgerock.opendj.config.DNPropertyDefinition;
import org.forgerock.opendj.config.DefaultBehaviorProvider;
import org.forgerock.opendj.config.DefinedDefaultBehaviorProvider;
import org.forgerock.opendj.config.DurationPropertyDefinition;
import org.forgerock.opendj.config.EnumPropertyDefinition;
import org.forgerock.opendj.config.IPAddressMaskPropertyDefinition;
import org.forgerock.opendj.config.IPAddressPropertyDefinition;
import org.forgerock.opendj.config.IntegerPropertyDefinition;
import org.forgerock.opendj.config.LDAPProfile;
import org.forgerock.opendj.config.ManagedObjectOption;
import org.forgerock.opendj.config.PropertyDefinition;
import org.forgerock.opendj.config.PropertyDefinitionVisitor;
import org.forgerock.opendj.config.PropertyOption;
import org.forgerock.opendj.config.RelationDefinition;
import org.forgerock.opendj.config.RelationOption;
import org.forgerock.opendj.config.RelativeInheritedDefaultBehaviorProvider;
import org.forgerock.opendj.config.SizePropertyDefinition;
import org.forgerock.opendj.config.StringPropertyDefinition;
import org.forgerock.opendj.config.Tag;
import org.forgerock.opendj.config.TopCfgDefn;
import org.forgerock.opendj.config.UndefinedDefaultBehaviorProvider;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.server.config.meta.RootCfgDefn;
import org.opends.server.util.EmbeddedUtils;
import org.opends.server.util.DynamicConstants;
/**
 * 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 static final String ACI_SYNTAX_REL_URL =
    "/doc/admin-guide/#about-acis";
  private static final String DURATION_SYNTAX_REL_URL =
    "duration-syntax.html";
  private final String CSS_FILE = "opendj-config.css";
  private final String MAIN_FILE = "index.html";
  private final String INHERITANCE_TREE_FILE =
    "ManagedObjectInheritanceTree.html";
  private final String RELATION_TREE_FILE = "ManagedObjectRelationTree.html";
  private final String MO_LIST_FILE = "ManagedObjectList.html";
  private final String PROPERTIES_INDEX_FILE = "PropertiesIndex.html";
  private final String WELCOME_FILE = "welcome.html";
  private final String MAINTOP_FILE = "maintop.html";
  private final String INDEX_FILE = "index.html";
  private final String FAVICON = "http://forgerock.org/favicon.ico";
  private static final String CONFIG_GUIDE_DIR = "opendj_config_guide";
  private final String MAIN_FRAME = "mainFrame";
  /**
   * Entry point for documentation generation.
   *
   * Properties:
   * GenerationDir - The directory where the doc is generated
   *              (default is /var/tmp/[CONFIG_GUIDE_DIR>])
   * LdapMapping - Presence means that the LDAP mapping section is to be
   *               generated (default is no)
   * OpenDJWiki - The URL of the OpenDJ Wiki
   *              (default is
   *              "http://wikis.forgerock.org/confluence/display/OPENDJ")
   * OpenDJHome - The URL of the OpenDJ project Home page
   *              (default is "http://opendj.forgerock.org")
   *
   * @param args none.
   */
  public static void main(String[] args) {
    Properties properties = System.getProperties();
    generationDir = properties.getProperty("GenerationDir");
    if (generationDir == null) {
      // Default dir is prefixed by the system-dependent default temporary dir
      generationDir = System.getProperty("java.io.tmpdir") + File.separator +
        CONFIG_GUIDE_DIR;
    }
    // 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);
    if (properties.getProperty("LdapMapping") != null) {
      ldapMapping = true;
    }
    OpenDJWiki = properties.getProperty("OpenDJWiki");
    if (OpenDJWiki == null) {
      // Default is current wiki
      OpenDJWiki = "http://wikis.forgerock.org/confluence/display/OPENDJ";
    }
    OpenDJHome = properties.getProperty("OpenDJHome");
    if (OpenDJHome == null) {
      // Default is current OpenDJ project home
      OpenDJHome = "http://opendj.forgerock.org";
    }
    aciSyntaxPage = OpenDJHome + ACI_SYNTAX_REL_URL;
    durationSyntaxPage = DURATION_SYNTAX_REL_URL;
    ConfigGuideGeneration myGen = new ConfigGuideGeneration();
    myGen.generate();
  }
  private void generate() {
    init();
    genManagedObjectRelationTree(catTopRelList);
    genManagedObjectInheritanceTree(catTopMoList);
    genAllManagedObject(topMoList);
    genManagedObjectList(moList);
    genPropertiesIndex();
    genIndexPage();
    genMainTopPage();
    genWelcomePage();
   }
  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 {
      ConfigurationFramework.getInstance().initialize();
    } catch (ConfigException 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;
      }
      if (topObject.hasOption(ManagedObjectOption.HIDDEN))
      {
        continue;
      }
      topMoList.put(topObject.getName(), topObject);
    }
    // Build a list of top relations by category (core, database, ...)
    for (RelationDefinition rel : topRelList.values()) {
      AbstractManagedObjectDefinition<?, ?> mo = rel.getChildDefinition();
      Collection<Tag> tags = mo.getAllTags();
      for (Tag tag : tags) {
        TreeMap<String, RelationDefinition> catMap =
          catTopRelList.get(tag.getName());
        if (catMap == null) {
          catMap = new TreeMap<>();
          catTopRelList.put(tag.getName(), catMap);
        }
        catMap.put(mo.getName(), rel);
      }
    }
    // Build a list of top managed objects by category (core, database, ...)
    for (AbstractManagedObjectDefinition<?, ?> topObject : topMoList.values()) {
      Collection<Tag> tags = topObject.getAllTags();
      for (Tag tag : tags) {
        TreeMap<String, AbstractManagedObjectDefinition> catMap =
          catTopMoList.get(tag.getName());
        if (catMap == null) {
          catMap = new TreeMap<>();
          catTopMoList.put(tag.getName(), catMap);
        }
        catMap.put(topObject.getName(), topObject);
      }
    }
  }
  /**
   * Generate the inheritance tree of all the managed objects.
   */
  @SuppressWarnings("unchecked")
  private void genManagedObjectInheritanceTree(
    TreeMap<String, TreeMap<String, AbstractManagedObjectDefinition>> list) {
    htmlHeader(DynamicConstants.PRODUCT_NAME + " " +
            "Configuration Reference - Inheritance View");
    tabMenu(INHERITANCE_TREE_FILE);
    viewHelp("This view represents the inheritance relationships between " +
      "configuration components.");
    jumpSection();
    for (String catName : list.keySet()) {
      heading3(getFriendlyName(catName));
      // Get the list of the category
      TreeMap<String, AbstractManagedObjectDefinition> catList = list.get(catName);
      for (AbstractManagedObjectDefinition mo : catList.values()) {
        RelationDefinition relDefn = relList.get(mo.getName());
        if (relDefn != null && relDefn.hasOption(RelationOption.HIDDEN)) {
          continue;
        }
        paragraph(
          getLink(mo.getUserFriendlyName().toString(),
          mo.getName() + ".html", MAIN_FRAME));
        if (mo.hasChildren()) {
          genMoInheritanceTree(makeMOTreeMap(mo.getChildren()));
        }
      }
    }
    htmlFooter();
    generateFile(INHERITANCE_TREE_FILE);
  }
  @SuppressWarnings("unchecked")
  private void genMoInheritanceTree(
    TreeMap<String, AbstractManagedObjectDefinition> catList) {
    beginList();
    for (AbstractManagedObjectDefinition mo : catList.values()) {
      link(mo.getUserFriendlyName().toString(), mo.getName() + ".html",
        MAIN_FRAME);
      if (mo.hasChildren()) {
        genMoInheritanceTree(makeMOTreeMap(mo.getChildren()));
      }
    }
    endList();
  }
   private void jumpSection() {
     htmlBuff.append("<p class=\"category-index\"><strong>Jump To:</strong><br>\n");
     String[] catNames = catTopMoList.keySet().toArray(new String[0]);
    for (int ii=0; ii < catNames.length; ii++) {
      if (ii != 0) {
        htmlBuff.append(", ");
      }
      String catFriendlyName = getFriendlyName(catNames[ii]);
      htmlBuff.append(getLink(catFriendlyName, "#" + catFriendlyName));
    }
    htmlBuff.append("</p>\n");
   }
  /**
   * Generate the relation tree of all the managed objects.
   */
  private void genManagedObjectRelationTree(
    TreeMap <String, TreeMap<String, RelationDefinition>> list) {
    htmlHeader(DynamicConstants.PRODUCT_NAME +
            " Configuration Reference - Structure View");
    tabMenu(RELATION_TREE_FILE);
    viewHelp("This view represents the structural relationships between " +
      "components and indicates how certain components can exist only within " +
      "container components.");
    jumpSection();
    for (String catName : list.keySet()) {
      heading3(getFriendlyName(catName));
      // Get the list of the category
      TreeMap<String, RelationDefinition> catList = list.get(catName);
      genMORelationTree(catList);
    }
    htmlFooter();
    generateFile(RELATION_TREE_FILE);
  }
  @SuppressWarnings("unchecked")
  private void genMORelationTree(TreeMap<String, RelationDefinition> list) {
    for (RelationDefinition rel : list.values()) {
      AbstractManagedObjectDefinition childMo = rel.getChildDefinition();
      AbstractManagedObjectDefinition parentMo = rel.getParentDefinition();
      // Does not generate several entry for the same relation
      if (relList.put(childMo.getName(), rel) != null) {
       continue;
      }
      if (rel.hasOption(RelationOption.HIDDEN)) {
        continue;
      }
      String linkStr = getLink(childMo.getUserFriendlyName().toString(),
        childMo.getName() + ".html", MAIN_FRAME);
      String fromStr = "";
      if (!parentMo.getName().equals("")) {
        fromStr = " (from " +
          getLink(parentMo.getUserFriendlyName().toString(),
          parentMo.getName() + ".html", MAIN_FRAME) + ")";
      }
      if (!inList) {
        paragraph(linkStr + fromStr);
      } else {
        bullet(linkStr + fromStr);
      }
      genMORelationSubTree(makeRelTreeMap(childMo.getAllRelationDefinitions()));
      if (childMo.hasChildren()) {
        for (Iterator<AbstractManagedObjectDefinition> it =
          childMo.getChildren().iterator(); it.hasNext();) {
          AbstractManagedObjectDefinition mo = it.next();
          if (mo.hasOption(ManagedObjectOption.HIDDEN))
          {
            continue;
          }
          genMORelationSubTree(makeRelTreeMap(mo.getAllRelationDefinitions()));
        }
      }
    }
  }
  private void genMORelationSubTree(TreeMap<String, RelationDefinition> list) {
    if (!list.values().isEmpty()) {
      beginList();
      genMORelationTree(list);
      endList();
    }
  }
  /**
   * Generate all the managed objects HTML pages and their children.
   */
  @SuppressWarnings("unchecked")
  private void genAllManagedObject(
    TreeMap<String, AbstractManagedObjectDefinition> list) {
    for (AbstractManagedObjectDefinition mo : list.values()) {
      RelationDefinition relDefn = relList.get(mo.getName());
      if (relDefn != null && relDefn.hasOption(RelationOption.HIDDEN)) {
        continue;
      }
      moList.put(mo.getName(), mo);
      genManagedObject(mo);
      if (mo.hasChildren()) {
        genAllManagedObject(makeMOTreeMap(mo.getChildren()));
      }
    }
  }
  private void genManagedObject(AbstractManagedObjectDefinition mo) {
    //------------------------------------------------------------------------
    // Header
    //------------------------------------------------------------------------
    homeLink();
    String title = mo.getUserFriendlyName().toString();
    htmlHeader(DynamicConstants.PRODUCT_NAME + " - " + title);
    // title
    heading2(title);
    // Abstract notice
    if (mo.hasChildren()) {
      paragraph(
        "Note: this is an abstract component, that cannot be instantiated.",
        TextStyle.ITALIC);
    }
    // description
    paragraph(mo.getSynopsis());
    paragraph(mo.getDescription());
    // sub-components
    if (mo.hasChildren()) {
      heading3("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("These " + mo.getUserFriendlyPluralName() +
        " inherit from the properties described below.");
    }
    // Parent
    if (!mo.getParent().isTop()) {
      heading3("Parent Component");
      paragraph("The " + mo.getUserFriendlyName() +
        " component inherits from the " +
        getLink(mo.getParent().getUserFriendlyName().toString(),
        mo.getParent().getName() + ".html"));
    }
    // Relations
    generateRelationsSection(mo);
    // Page links in case of LDAP mapping
    if (ldapMapping) {
      newline();
      horizontalLine();
      newline();
      paragraph("This page describes the " + mo.getUserFriendlyName() + ":");
      beginList();
      link("Properties", "#Properties");
      link("LDAP Mapping", "#LDAP Mapping");
      endList();
      newline();
    }
    //------------------------------------------------------------------------
    // Properties
    //------------------------------------------------------------------------
    heading3("Properties");
    paragraph("A description of each property follows.");
    newline();
    TreeMap<String, PropertyDefinition> basicProps = new TreeMap<>();
    TreeMap<String, PropertyDefinition> advancedProps = new TreeMap<>();
    // Properties actually defined in this managed object
    @SuppressWarnings("unchecked")
    Collection<PropertyDefinition> props = mo.getAllPropertyDefinitions();
    for ( PropertyDefinition prop : props) {
      if (prop.hasOption(PropertyOption.ADVANCED)) {
        advancedProps.put(prop.getName(), prop);
      } else {
        basicProps.put(prop.getName(), prop);
      }
    }
    propertiesLinkTable(basicProps, advancedProps);
    // basic properties
    if (!basicProps.isEmpty()) {
      heading4("Basic Properties");
      for ( PropertyDefinition prop : basicProps.values()) {
        generateProperty(mo, prop);
        newline();
      }
      newline();
    }
    // advanced properties
    if (!advancedProps.isEmpty()) {
      heading4("Advanced Properties");
      for ( PropertyDefinition prop : advancedProps.values()) {
        generateProperty(mo, prop);
        newline();
      }
      newline();
    }
    if (ldapMapping) {
      genLdapMapping(mo);
    }
    htmlFooter();
    generateFile(mo.getName() + ".html");
  }
  private TreeMap<String, PropertyDefinition>
    getPropertyList(AbstractManagedObjectDefinition mo) {
    @SuppressWarnings("unchecked")
    Collection<PropertyDefinition> props = mo.getAllPropertyDefinitions();
    return makePropTreeMap(props);
  }
  private void homeLink() {
    htmlBuff.append("<div style=\"font-size:11px;margin-top:-10px;")
      .append("margin-bottom:-10px; text-align:right\"><a href=\"")
      .append(MAIN_FILE)
      .append("\" target=\"_top\">Configuration Reference Home</a></div>");
  }
  private void generateRelationsSection(AbstractManagedObjectDefinition mo) {
    // Composition relations
    @SuppressWarnings("unchecked")
    Collection<RelationDefinition> compRels = mo.getRelationDefinitions();
    @SuppressWarnings("unchecked")
    Collection<RelationDefinition> reverseCompRels =
      mo.getReverseRelationDefinitions();
    // Aggregation properties
    @SuppressWarnings("unchecked")
    Collection<AggregationPropertyDefinition> aggregProps =
      mo.getAggregationPropertyDefinitions();
    @SuppressWarnings("unchecked")
    Collection<AggregationPropertyDefinition> reverseAggregProps =
      mo.getReverseAggregationPropertyDefinitions();
    // Check if something to print in composition relations
    // (even if the list not empty, it may contain only hidden relations)
    boolean isCompRelsEmpty = true;
    if (!compRels.isEmpty()) {
      for (RelationDefinition rel : compRels) {
        if (rel.hasOption(RelationOption.HIDDEN)) {
          continue;
        }
        isCompRelsEmpty = false;
      }
    }
    boolean isReverseCompRelsEmpty = true;
    if (!reverseCompRels.isEmpty()) {
      for (RelationDefinition rel : reverseCompRels) {
        if (rel.hasOption(RelationOption.HIDDEN)) {
          continue;
        }
        // check if it is not root
        if (rel.getParentDefinition().getName().equals("")) {
          continue;
        }
        isReverseCompRelsEmpty = false;
      }
    }
    // Check if something to print in reverse aggregation relations
    // (even if the list not empty, it may contain only relations from
    // hidden component)
    boolean isReverseAggregPropsEmpty = true;
    if (!reverseAggregProps.isEmpty()) {
      for (AggregationPropertyDefinition agg : reverseAggregProps) {
        AbstractManagedObjectDefinition fromMo =
          agg.getManagedObjectDefinition();
        @SuppressWarnings("unchecked")
        Collection<RelationDefinition> rels =
          fromMo.getAllReverseRelationDefinitions();
        for (RelationDefinition rel : rels) {
          if (rel.hasOption(RelationOption.HIDDEN)) {
            continue;
          }
          isReverseAggregPropsEmpty = false;
        }
      }
    }
    //
    // Relations FROM this component
    //
    if (!isCompRelsEmpty || !aggregProps.isEmpty()) {
        heading3("Relations From this Component");
    }
    if (!isCompRelsEmpty) {
      paragraph(
        "The following components have a direct COMPOSITION relation FROM " +
        mo.getUserFriendlyPluralName() + " :");
      for ( RelationDefinition rel : compRels) {
        if (rel.hasOption(RelationOption.HIDDEN)) {
          continue;
        }
        beginList();
        AbstractManagedObjectDefinition childRel = rel.getChildDefinition();
        link(childRel.getUserFriendlyName().toString(), childRel.getName() +
          ".html");
        endList();
      }
    }
    if (!aggregProps.isEmpty()) {
      paragraph(
        "The following components have a direct AGGREGATION relation FROM " +
        mo.getUserFriendlyPluralName() + " :");
      TreeMap<String, AbstractManagedObjectDefinition> componentList = new TreeMap<>();
      for ( AggregationPropertyDefinition agg : aggregProps) {
        RelationDefinition rel = agg.getRelationDefinition();
        AbstractManagedObjectDefinition childRel = rel.getChildDefinition();
        componentList.put(childRel.getName(), childRel);
      }
      for (AbstractManagedObjectDefinition component : componentList.values()) {
        beginList();
        link(component.getUserFriendlyName().toString(), component.getName() + ".html");
        endList();
      }
    }
    //
    // Relations TO this component
    //
    if (!isReverseCompRelsEmpty || !isReverseAggregPropsEmpty) {
        heading3("Relations To this Component");
    }
    if (!mo.getReverseRelationDefinitions().isEmpty()
        && !isReverseCompRelsEmpty)
    {
      paragraph(
        "The following components have a direct COMPOSITION relation TO " +
        mo.getUserFriendlyPluralName() + " :");
      for ( RelationDefinition rel : reverseCompRels) {
        beginList();
        AbstractManagedObjectDefinition childRel = rel.getParentDefinition();
        link(childRel.getUserFriendlyName().toString(), childRel.getName() + ".html");
        endList();
      }
    }
    if (!isReverseAggregPropsEmpty) {
      paragraph(
        "The following components have a direct AGGREGATION relation TO " +
        mo.getUserFriendlyPluralName() + " :");
      TreeMap<String, AbstractManagedObjectDefinition> componentList = new TreeMap<>();
      for ( AggregationPropertyDefinition agg : reverseAggregProps) {
        AbstractManagedObjectDefinition fromMo =
          agg.getManagedObjectDefinition();
        componentList.put(fromMo.getName(), fromMo);
      }
      for (AbstractManagedObjectDefinition component : componentList.values()) {
        beginList();
        link(component.getUserFriendlyName().toString(), component.getName() +
          ".html");
        endList();
      }
    }
  }
  private void generateProperty(
    AbstractManagedObjectDefinition mo, PropertyDefinition prop) {
    // Property name
    paragraph(getAnchor(prop.getName()) + prop.getName(), TextStyle.STANDARD,
      "propertyname");
    // Property table
    startTable();
    tableRow("Description",
      ((prop.getSynopsis() != null) ? prop.getSynopsis()+ " " : "") +
      ((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");
    } else {
      tableRow("Required", "No");
    }
    String action = "None";
    if (prop.getAdministratorAction() != null) {
      LocalizableMessage synopsis = prop.getAdministratorAction().getSynopsis();
      Type actionType = prop.getAdministratorAction().getType();
      String actionStr = "";
      if (actionType == Type.COMPONENT_RESTART) {
        actionStr = "The " + mo.getUserFriendlyName() +
          " must be disabled and re-enabled for changes to this setting " +
          "to take effect";
      } else if (actionType == Type.SERVER_RESTART) {
        actionStr = "Restart the server";
      } else if (actionType == Type.NONE) {
        actionStr = "None";
      }
      String dot = actionStr.equals("") ? "" : ". ";
      action = actionStr +
        ((synopsis != null) ? dot + synopsis : "");
    }
    tableRow("Admin Action Required", action);
    if (prop.hasOption(PropertyOption.ADVANCED)) {
      tableRow("Advanced Property", "Yes");
    } else {
      tableRow("Advanced Property", "No");
    }
    if (prop.hasOption(PropertyOption.READ_ONLY)) {
      tableRow("Read-only", "Yes");
    } else {
      tableRow("Read-only", "No");
    }
    endTable();
  }
  private void propertiesLinkTable(TreeMap<String,
    PropertyDefinition> basicProps,
    TreeMap<String, PropertyDefinition> advancedProps) {
    htmlBuff.append("<table border=\"0\" cellspacing=\"0\" class=\"jump-table\">\n")
        .append("  <tr>\n")
        .append("    <th>Basic Properties:</th>\n")
        .append("    <th>Advanced Properties:</th>\n")
        .append("  </tr>\n");
    PropertyDefinition[] basicPropsArray =
      basicProps.values().toArray(new PropertyDefinition[0]);
    PropertyDefinition[] advancedPropsArray =
      advancedProps.values().toArray(new PropertyDefinition[0]);
    for (int ii=0;
        ii < basicPropsArray.length || ii < advancedPropsArray.length;
        ii++) {
      String basicPropName =
        ii < basicPropsArray.length ? basicPropsArray[ii].getName() : null;
      String advancedPropName =
        ii < advancedPropsArray.length ?
          advancedPropsArray[ii].getName() : null;
      String basicHtmlCell = "";
      if (basicPropName != null) {
        basicHtmlCell = "  <td>&darr;&nbsp;<a href=\"#" + basicPropName + "\">"
          + basicPropName + "</a></td>\n";
      } else if (basicPropsArray.length == 0 && ii == 0) {
        basicHtmlCell = "  <td>&nbsp;None</td>\n";
      } else if (ii >= basicPropsArray.length) {
        // Case of nb of basic props < nb of advanced props
        basicHtmlCell = "  <td></td>\n";
      }
      String advancedHtmlCell = "";
      if (advancedPropName != null) {
        advancedHtmlCell = "  <td>&darr;&nbsp;<a href=\"#" + advancedPropName +
          "\">" + advancedPropName + "</a></td>\n";
      } else if (advancedPropsArray.length == 0 && ii == 0) {
        advancedHtmlCell = "  <td>&nbsp;None</td>\n";
      }
      htmlBuff.append("<tr>\n");
      htmlBuff.append(basicHtmlCell).append(advancedHtmlCell);
      htmlBuff.append("</tr>\n");
    }
    htmlBuff.append("</table>\n");
  }
  private void genLdapMapping(AbstractManagedObjectDefinition mo) {
    //------------------------------------------------------------------------
    // LDAP mapping
    //------------------------------------------------------------------------
    heading3("LDAP Mapping");
    paragraph(
      "Each 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 : getPropertyList(mo).values()) {
      tableRow(prop.getName(), ldapProfile.getAttributeName(mo, prop));
    }
    endTable();
  }
  /** Generate a list of managed objects. */
  private void genManagedObjectList(
    TreeMap<String, AbstractManagedObjectDefinition> list) {
    htmlHeader(DynamicConstants.PRODUCT_NAME
            + " Configuration Reference - Components View");
    tabMenu(MO_LIST_FILE);
    viewHelp("This view provides a list of all configuration components, " +
      "in alphabetical order.");
    newline();
    StringBuffer moPointers = new StringBuffer();
    String lettersPointers = "";
    String firstChar = ".";
    for (AbstractManagedObjectDefinition mo : list.values()) {
      if (!mo.getName().startsWith(firstChar)) {
        firstChar = mo.getName().substring(0, 1);
        String letter = firstChar.toUpperCase();
        moPointers.append(getAnchor(letter)).append(getHeading2(letter));
        lettersPointers += getLink(letter, "#" + letter) + " ";
      }
      moPointers.append("<p> ")
        .append(getLink(mo.getUserFriendlyName().toString(), mo.getName() + ".html", MAIN_FRAME))
        .append("</p>\n");
    }
    paragraph(lettersPointers);
    htmlBuff.append(moPointers);
    htmlFooter();
    generateFile(MO_LIST_FILE);
  }
  /** Generate an index of properties. */
  private void genPropertiesIndex() {
    // Build a sorted list of (property name + its managed object name)
    TreeSet<String> propMoList = new TreeSet<>();
    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)).append(getHeading2(letter));
        lettersPointers += getLink(letter, "#" + letter) + " ";
      }
      String propLink = getLink(propName,
        mo.getName() + ".html" + "#" + propName, MAIN_FRAME);
      String moLink =
        getLink(mo.getUserFriendlyName().toString(), mo.getName() + ".html",
        MAIN_FRAME, "#666");
      paragraph(propLink + "  [ " + moLink + " ]");
    }
    String indexBody = htmlBuff.toString();
    htmlBuff = new StringBuffer();
    htmlHeader(DynamicConstants.PRODUCT_NAME +
            " Configuration Reference - Properties View");
    tabMenu(PROPERTIES_INDEX_FILE);
    viewHelp("This view provides a list of all configuration properties, " +
      "in alphabetical order, and indicates the configuration component to " +
      "which each property applies.");
    newline();
    paragraph(lettersPointers);
    htmlBuff.append(indexBody);
    htmlFooter();
    generateFile(PROPERTIES_INDEX_FILE);
  }
    private void genWelcomePage() {
    htmlHeader(DynamicConstants.PRODUCT_NAME +
            " Configuration Reference - Welcome");
    heading2("About This Reference");
    paragraph("This reference " +
      "describes the " + DynamicConstants.PRODUCT_NAME +
      " configuration properties that can be manipulated " +
      "with the dsconfig command.");
    paragraph("Configuration components are grouped according to the area of " +
      "the server in which they are used, as follows:");
    beginList();
    for (String catName : catTopMoList.keySet()) {
      bullet(getFriendlyName(catName));
    }
    endList();
    paragraph(
      "For ease of reference, the configuration is described on multiple " +
      "tabs. These tabs provide alternative views of the configuration " +
      "components:");
    beginList();
    bullet("The <strong>Inheritance</strong> view represents the inheritance " +
      "relationships between configuration components. A sub-component " +
      "inherits all of the properties of its parent component.");
    bullet("The <strong>Structure</strong> view represents the structural " +
      "relationships between components and indicates how certain components " +
      "can exist only within container components. When a container " +
      "component is deleted, all of the components within it are also " +
      "deleted.");
    bullet(
      "The <strong>Components</strong> view provides an alphabetical list " +
      "of all configuration components.");
    bullet(
      "The <strong>Properties</strong> view provides an alphabetical list " +
      "of all configuration properties, and indicates the configuration " +
      "component to which each property applies.");
    endList();
    newline();
    paragraph("When you set up " +
            DynamicConstants.PRODUCT_NAME +
            ", certain components are created in the " +
      "configuration by default. These components are configured with " +
      "specific values, which are not necessarily the same as the " +
      "\"default values\" of new components that you create using dsconfig. " +
      "The \"default values\" listed in this document refer to the values " +
      "of the new components that you create using dsconfig.");
    htmlFooter();
    generateFile(WELCOME_FILE);
  }
  private void genMainTopPage() {
    htmlHeader(DynamicConstants.PRODUCT_NAME +
            " Configuration Reference - Main Top");
    // "Home" might be depend on where this is published.
    /*
    htmlBuff.append("<div class=\"breadcrumb\"><span class=\"pageactions\">" +
      "<a href=\"" + OpenDJHome + "\" target=\"_parent\">" +
      "<span style=\"font-size: 12px;\">&laquo;&nbsp;&nbsp;</span>" +
      "Back to " +
      DynamicConstants.PRODUCT_NAME + " Home</a></span>&nbsp;&nbsp;</div>\n");
    */
    htmlBuff.append("<div class=\"breadcrumb\"><span class=\"pageactions\">" +
            "&nbsp;&nbsp;</span>&nbsp;&nbsp;</div>\n");
    htmlBuff.append("<table class=\"titletable\" cellspacing=\"0\" " +
      "width=\"100%\">\n");
    htmlBuff.append("<tbody><tr>\n");
    htmlBuff.append("  <td><h2>"+
            DynamicConstants.PRODUCT_NAME +
            " Configuration Reference</h2></td>\n");
    /*
    htmlBuff.append("  <td valign=\"bottom\" width=\"10%\">" +
      "<a href=\"" + OpenDJHome + "\" target=\"_parent\">" +
      "<img src=\"opendj_logo_sm.png\" alt=\"OpenDJ Logo\" align=\"bottom\" " +
      "border=\"0\" height=\"33\" width=\"114\"></a></td>\n");
    */
    htmlBuff.append("  <td valign=\"bottom\" width=\"10%\">" +
            "<img src=\"opendj_logo_sm.png\" alt=\"OpenDJ Logo\" align=\"bottom\" " +
            "border=\"0\" height=\"33\" width=\"114\"></td>\n");
    htmlBuff.append("</tr>\n");
    htmlBuff.append("</tbody></table>\n");
    htmlFooter();
    generateFile(MAINTOP_FILE);
  }
  private void genIndexPage() {
    htmlBuff.append(getHtmlHeader(
            DynamicConstants.PRODUCT_NAME + " Configuration Reference"));
    htmlBuff.append("<frameset rows=\"80,*\" framespacing=\"1\" " +
      "frameborder=\"yes\" border=\"1\" bordercolor=\"#333333\">\n");
    htmlBuff.append("  <frame src=\"" + MAINTOP_FILE + "\" name=\"topFrame\" " +
      "id=\"topFrame\" border=\"1\" title=\"topFrame\" scrolling=\"no\">\n");
    htmlBuff.append("  <frameset cols=\"375,*\" frameborder=\"yes\" " +
      "border=\"1\" " +
      "framespacing=\"1\">\n");
    htmlBuff.append("     <frame src=\"" + INHERITANCE_TREE_FILE + "\" " +
      "name=\"leftFrame\" id=\"leftFrame\" title=\"leftFrame\" " +
      "scrolling=\"auto\">\n");
    htmlBuff.append("     <frame src=\"" + WELCOME_FILE +
      "\" name=\"mainFrame\" " +
      "id=\"mainFrame\" title=\"mainFrame\" scrolling=\"auto\">\n");
    htmlBuff.append("   </frameset>\n");
    htmlBuff.append("</frameset>\n");
    htmlBuff.append("<noframes><body>\n");
    htmlBuff.append("</body>\n");
    htmlBuff.append("</noframes>\n");
    htmlBuff.append("</html>\n");
    generateFile(INDEX_FILE);
  }
  private String getBaseDN(
    AbstractManagedObjectDefinition mo, LDAPProfile ldapProfile) {
    RelationDefinition rel = relList.get(mo.getName());
    if (rel != null) {
      String baseDn = ldapProfile.getRelationRDNSequence(rel);
      if (!baseDn.equals("")) {
        return baseDn;
      } else {
        // Check the parent relation
        return getBaseDN(rel.getParentDefinition(), ldapProfile);
      }
    } 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) {
        // Rather than return a link that is coupled to a site location,
        // assume that readers can find ACI syntax in the documentation.
        // ACI syntax being difficult to understand and to explain,
        // it is better not to have to maintain a separate page, either.
        return "An ACI syntax"; // getLink("An ACI Syntax", aciSyntaxPage);
      }
      @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 = "";
        durationStr += getLink("A duration Syntax", durationSyntaxPage) +
          ". ";
        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 = Double.valueOf(prop.getBaseUnit().
          fromMilliSeconds(prop.getLowerLimit())).longValue();
        durationStr += "Lower limit is " + lowerLimitStr +
          " " + prop.getBaseUnit().getLongName() + ". ";
        if (prop.getUpperLimit() != null) {
          long upperLimitStr = Double.valueOf(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);
          }
          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) {
        String retStr = "A String";
        if (prop.getPatternSynopsis() != null) {
          retStr = prop.getPatternSynopsis().toString();
        }
        return retStr;
      }
      @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<>();
    for (AbstractManagedObjectDefinition mo : coll) {
      if (mo.hasOption(ManagedObjectOption.HIDDEN))
      {
        continue;
      }
      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<>();
    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<>();
    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>").append(str).append("</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 = Integer.valueOf(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)).append("<body>\n");
  }
  private final String Now = new Date().toString();
  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" +
      "<link rel=\"shortcut icon\" href=\"" + FAVICON + "\">\n" +
      "<meta name=\"date generated\" content=\"" + Now + "\">\n" +
      "</head>\n";
  }
  /** Add a Tab Menu, the active tab is the one given as parameter. */
  private void tabMenu(String activeTab) {
    htmlBuff.append(
      "<div class=\"tabmenu\"> " +
      "<span><a " +
      (activeTab.equals(INHERITANCE_TREE_FILE) ? "class=\"activetab\" " : "") +
      "href=\"" + INHERITANCE_TREE_FILE + "\"" +
      " title=\"Inheritance View of Components\">Inheritance</a></span> " +
      "<span><a " +
      (activeTab.equals(RELATION_TREE_FILE) ? "class=\"activetab\" " : "") +
      "href=\"" + RELATION_TREE_FILE + "\"" +
      " title=\"Relational View of Components\">Structure</a></span> " +
      "<span><a " +
      (activeTab.equals(MO_LIST_FILE) ? "class=\"activetab\" " : "") +
      "href=\"" + MO_LIST_FILE + "\"" +
      " title=\"Alphabetical Index of Components\">Components</a></span> " +
      "<span><a " +
      (activeTab.equals(PROPERTIES_INDEX_FILE) ? "class=\"activetab\" " : "") +
      "href=\"" + PROPERTIES_INDEX_FILE + "\"" +
      " title=\"Alphabetical Index of Properties\" >Properties</a></span>" +
      "</div>" +
      "\n"
      );
  }
  private String getLink(String str, String link) {
    return getLink(str, link, null, null);
  }
  private String getLink(String str, String link, String target) {
    return getLink(str, link, target, null);
  }
  private String getLink(String str, String link, String target, String color) {
    return "<a " +
      (color != null ? "style=\"color:" + color + "\" " : "") +
      "href=\"" + link + "\"" +
      (target == null ? "" : " target=\"" + target + "\"") +
      ">"
      + str + "</a>";
  }
  private void link(String str, String link) {
    link(str, link, null, null);
  }
  private void link(String str, String link, String target) {
    link(str, link, target, null);
  }
  private void link(String str, String link, String target, String color) {
    String htmlStr = "";
    if (!inList && getIndentPixels() > 0) {
      htmlStr += "<div style=\"margin-left: " + getIndentPixels() + "px;\">";
    } else if (inList) {
      htmlStr += "<li>";
    }
    htmlStr += getLink(str, link, target, color);
    if (!inList && getIndentPixels() > 0) {
      htmlStr += "</div>";
    } else if (inList) {
      htmlStr += "</li>";
    }
    if (!inList) {
      htmlStr += "<br>";
    }
    htmlBuff.append(htmlStr).append("\n");
  }
  private void newline() {
    htmlBuff.append(
      getNewLine());
  }
  private String getNewLine() {
    return "<br>\n";
  }
  private void paragraph(LocalizableMessage description) {
    if (description != null) {
      paragraph(description.toString());
    }
  }
  private void paragraph(String description) {
    paragraph(description, TextStyle.STANDARD, null);
  }
  private void paragraph(String description, TextStyle style) {
    paragraph(description, style, null);
  }
  private void paragraph(String description, TextStyle style, String pClass) {
    String indentStr = "";
    String styleStr = "";
    String classStr = "";
    if (getIndentPixels() > 0) {
      indentStr = "style=\"margin-left: " + getIndentPixels() + "px;\"";
    }
    if (style == TextStyle.BOLD) {
      styleStr = "style=\"font-weight: bold;\"";
    } else if (style == TextStyle.ITALIC) {
      styleStr = "style=\"font-style: italic;\"";
    }
    if (pClass != null) {
      classStr = "class=" + pClass;
    }
    htmlBuff.append("<p ").append(indentStr).append(" ").append(styleStr).append(" ").append(classStr).append(">")
        .append(description)
        .append("</p>\n");
  }
  private int getIndentPixels() {
    return ind * 40;
  }
  private void startTable() {
    htmlBuff.append("<table ")
            .append("style=\"width: 100%; text-align: left;\"")
            .append("border=\"1\"")
            .append("cellpadding=\"1\"")
            .append("cellspacing=\"0\"")
            .append(">\n");
    htmlBuff.append("<tbody>\n");
  }
  /**
   * Generate a "friendly" name from a string :
   * '-' and '_' replaced by space
   * first letter of a word in uppercase
   */
  private String getFriendlyName(String str) {
    String retStr = "";
    String[] words = str.split("\\p{Punct}");
    for (int ii = 0; ii < words.length; ii++) {
      if (ii>0) {
        retStr += " ";
      }
      String word = words[ii];
       String firstChar = word.substring(0, 1).toUpperCase();
       retStr += firstChar + word.substring(1, word.length());
    }
    return retStr;
  }
  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=\"")
              .append("vertical-align: top; ")
              .append(ii == 0 ? "width: 20%;" : "")
              .append("\">")
              .append(string)
              .append("<br></td>");
    }
    htmlBuff.append(
      "</tr>\n");
  }
  /**
   * Text style.
   */
  private enum TextStyle {
    STANDARD, BOLD, ITALIC, UNDERLINE, FIXED_WIDTH
  }
  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").append("</html>\n");
  }
  private void viewHelp(String helpStr) {
    htmlBuff.append("<p class=\"view-help\" >")
            .append(helpStr)
            .append("</p>")
            .append("\n");
  }
  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 final TreeMap<String, RelationDefinition> topRelList = new TreeMap<>();
  private final TreeMap<String, RelationDefinition> relList = new TreeMap<>();
  private final TreeMap<String, TreeMap<String, RelationDefinition>> catTopRelList = new TreeMap<>();
  /** Managed object list. */
  private final TreeMap<String, AbstractManagedObjectDefinition> moList = new TreeMap<>();
  private final TreeMap<String, AbstractManagedObjectDefinition> topMoList = new TreeMap<>();
  private final TreeMap<String, TreeMap<String, AbstractManagedObjectDefinition>>
    catTopMoList = new TreeMap<>();
  private final int ind = 0;
  private StringBuffer htmlBuff = new StringBuffer();
  private static String generationDir;
  private static boolean ldapMapping;
  private static String OpenDJWiki;
  private static String OpenDJHome;
  private static String aciSyntaxPage;
  private static String durationSyntaxPage;
  private boolean inList;
  private int listLevel;
}
opendj-server-legacy/src/main/java/org/opends/server/admin/doc/package-info.java
New file
@@ -0,0 +1,28 @@
/*
 * The contents of this file are subject to the terms of the Common Development and
 * Distribution License (the License). You may not use this file except in compliance with the
 * License.
 *
 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
 * specific language governing permission and limitations under the License.
 *
 * When distributing Covered Software, include this CDDL Header Notice in each file and include
 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2007-2008 Sun Microsystems, Inc.
 * Portions Copyright 2016 ForgeRock AS.
 */
/**
 * Administration documentation classes.
 * <p>
 * This package contains classes used to generate administration documentation.
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.PRIVATE)
package org.opends.server.admin.doc;