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