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