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