From 8990a259a41f2f90606233139c4937fc1c8182cc Mon Sep 17 00:00:00 2001
From: Mark Craig <mark.craig@forgerock.com>
Date: Wed, 18 Feb 2015 10:12:37 +0000
Subject: [PATCH] CR-6114 OPENDJ-1822 Separate generated content from formatting

---
 opendj-cli/src/main/java/com/forgerock/opendj/cli/SubCommandUsageHandler.java      |   16 
 opendj-cli/src/main/resources/templates/refEntry.ftl                               |  135 ++++++
 opendj-config/src/main/java/org/forgerock/opendj/config/dsconfig/DSConfig.java     |  363 +++++++---------
 opendj-cli/src/main/resources/templates/dscfgAppendProps.ftl                       |   35 +
 opendj-cli/src/main/resources/templates/dscfgVariableList.ftl                      |   51 ++
 opendj-cli/src/main/java/com/forgerock/opendj/cli/DocGenerationHelper.java         |  137 ++++++
 opendj-cli/src/main/resources/templates/dscfgListSubtypes.ftl                      |   56 ++
 opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties              |    7 
 opendj-cli/src/main/resources/templates/refSect2.ftl                               |   76 +++
 opendj-cli/src/main/java/com/forgerock/opendj/cli/SubCommandArgumentParser.java    |  165 ++-----
 opendj-config/src/main/resources/com/forgerock/opendj/dsconfig/dsconfig.properties |   69 +++
 opendj-cli/pom.xml                                                                 |    7 
 opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java              |   90 ++--
 opendj-cli/src/main/resources/templates/dscfgVarListEntry.ftl                      |   32 +
 14 files changed, 859 insertions(+), 380 deletions(-)

diff --git a/opendj-cli/pom.xml b/opendj-cli/pom.xml
index 9e766a1..fb39c3a 100644
--- a/opendj-cli/pom.xml
+++ b/opendj-cli/pom.xml
@@ -21,7 +21,7 @@
   !
   ! CDDL HEADER END
   !
-  !      Copyright 2014 ForgeRock AS
+  !      Copyright 2014-2015 ForgeRock AS.
   !    
 -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
@@ -60,6 +60,11 @@
       <version>${forgerockBuildToolsVersion}</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.freemarker</groupId>
+      <artifactId>freemarker</artifactId>
+      <version>2.3.21</version>
+    </dependency>
   </dependencies>
   <properties>
     <opendj.osgi.import>
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java
index d5739e1..77b5003 100644
--- a/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java
@@ -28,18 +28,23 @@
 
 import static com.forgerock.opendj.cli.ArgumentConstants.*;
 import static com.forgerock.opendj.cli.CliMessages.*;
+import static com.forgerock.opendj.cli.DocGenerationHelper.*;
 import static com.forgerock.opendj.cli.Utils.*;
 import static com.forgerock.opendj.util.StaticUtils.*;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.OutputStream;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
+import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 import java.util.SortedSet;
@@ -658,74 +663,63 @@
         final StringBuilder buffer = new StringBuilder();
         usageOrVersionDisplayed = true;
         if (System.getProperty("org.forgerock.opendj.gendoc") != null) {
-            toRefSect2(buffer);
+            toRefEntry(buffer);
         } else {
             getUsage(buffer);
         }
         return buffer.toString();
     }
 
-    private void toRefSect2(StringBuilder sb) {
+    /**
+     * Appends a generated DocBook XML RefEntry (man page) to the StringBuilder.
+     *
+     * @param sb    Append the RefEntry element to this.
+     */
+    private void toRefEntry(StringBuilder sb) {
         final String scriptName = getScriptName();
         if (scriptName == null) {
             throw new RuntimeException("The script name should have been set via the environment property '"
                     + PROPERTY_SCRIPT_NAME + "'.");
         }
 
-        sb.append("<refsect2 xml:id=\"").append(scriptName).append("\">").append(EOL);
-        sb.append(" <title>").append(scriptName).append("</title>").append(EOL);
-        sb.append(" <para>").append(getToolDescription()).append("</para>").append(EOL);
+        // Model for a FreeMarker template.
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("locale", Locale.getDefault().getLanguage());
+        map.put("year", new SimpleDateFormat("yyyy").format(new Date()));
+        map.put("name", scriptName);
+        map.put("descTitle", REF_TITLE_DESCRIPTION.get());
+        map.put("optsTitle", REF_TITLE_OPTIONS.get());
+        map.put("optsIntro", REF_INTRO_OPTIONS.get(scriptName));
+        String args = null;
+        if (allowsTrailingArguments) {
+            if (trailingArgsDisplayName != null) {
+                args = trailingArgsDisplayName;
+            } else {
+                args = INFO_ARGPARSER_USAGE_TRAILINGARGS.get().toString();
+            }
+        }
+        map.put("args", args);
+        map.put("description", getToolDescription());
 
         // If there is a supplement to the description for this utility,
-        // then it is formatted for use in generated reference documentation.
-        // In other words, it is already DocBook XML, so append it as is.
-        final LocalizableMessage toolDocDescriptionSupplement = getDocToolDescriptionSupplement();
-        if (!LocalizableMessage.EMPTY.equals(toolDocDescriptionSupplement)) {
-            sb.append(toolDocDescriptionSupplement.toString()).append(EOL);
-        }
-
+        // then it is already DocBook XML, so use it as is.
+        map.put("info", getDocToolDescriptionSupplement());
         if (!argumentList.isEmpty()) {
-            sb.append(" <variablelist>").append(EOL);
+            List<Map<String, Object>> options = new LinkedList<Map<String, Object>>();
             for (Argument a : argumentList) {
-                sb.append("  <varlistentry>").append(EOL);
-                sb.append("    <term><option>");
-                final Character shortID = a.getShortIdentifier();
-                if (shortID != null) {
-                    sb.append("-").append(shortID.charValue());
-                }
-                final String longID = a.getLongIdentifier();
-                if (shortID != null && longID != null) {
-                    sb.append(" | ");
-                }
-                if (longID != null) {
-                    sb.append("--").append(longID);
-                }
-                if (a.needsValue()) {
-                    sb.append(" ").append(a.getValuePlaceholder());
-                }
-                sb.append("</option></term>").append(EOL);
-                sb.append("    <listitem>").append(EOL);
-                sb.append("      <para>").append(a.getDescription()).append("</para>").append(EOL);
-
-                final String defaultValue = a.getDefaultValue();
-                if (defaultValue != null && !defaultValue.isEmpty()) {
-                    sb.append("      <para>Default: ").append(defaultValue).append("</para>").append(EOL);
-                }
+                Map<String, Object> option = new HashMap<String, Object>();
+                option.put("synopsis", getOptionSynopsis(a));
+                option.put("description", a.getDescription());
+                option.put("default", REF_DEFAULT.get(a.getDefaultValue()));
 
                 // If there is a supplement to the description for this argument,
-                // then for now it is already formatted in DocBook XML.
-                final LocalizableMessage aDocDescriptionSupplement = a.getDocDescriptionSupplement();
-                if (!LocalizableMessage.EMPTY.equals(aDocDescriptionSupplement)) {
-                    sb.append(aDocDescriptionSupplement.toString()).append(EOL);
-                }
-
-                sb.append("    </listitem>").append(EOL);
-                sb.append("  </varlistentry>").append(EOL);
+                // then it is already DocBook XML, so use it as is.
+                option.put("info", a.getDocDescriptionSupplement());
+                options.add(option);
             }
-            sb.append(" </variablelist>").append(EOL);
+            map.put("options", options);
         }
-
-        sb.append("</refsect2>").append(EOL);
+        applyTemplate(sb, "refEntry.ftl", map);
     }
 
     /**
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/DocGenerationHelper.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/DocGenerationHelper.java
new file mode 100644
index 0000000..a60d99a
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/DocGenerationHelper.java
@@ -0,0 +1,137 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2015 ForgeRock AS.
+ */
+package com.forgerock.opendj.cli;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateExceptionHandler;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Map;
+
+/**
+ * This class provides utility functions to help generate reference documentation.
+ */
+public final class DocGenerationHelper {
+
+    /** Prevent instantiation. */
+    private DocGenerationHelper() {
+        // Do nothing.
+    }
+
+    /** FreeMarker template configuration. */
+    private static Configuration configuration;
+
+    /**
+     * Gets a FreeMarker configuration for applying templates.
+     *
+     * @return              A FreeMarker configuration.
+     */
+    private static Configuration getConfiguration() {
+        if (configuration == null) {
+            configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
+            configuration.setClassForTemplateLoading(DocGenerationHelper.class, "/templates");
+            configuration.setDefaultEncoding("UTF-8");
+            configuration.setTemplateExceptionHandler(TemplateExceptionHandler.DEBUG_HANDLER);
+        }
+        return configuration;
+    }
+
+    /**
+     * Appends the String result from applying a FreeMarker template.
+     *
+     * @param builder       Append the result to this.
+     * @param template      The name of a template file found in {@code resources/templates/}.
+     * @param map           The map holding the data to use in the template.
+     */
+    public static void applyTemplate(StringBuilder builder, final String template, final Map<String, Object> map) {
+        // FixMe: This method is public so it can be used by the SubCommandUsageHandler
+        // in org.forgerock.opendj.config.dsconfig.DSConfig.
+
+        // FreeMarker requires a configuration to find the template.
+        configuration = getConfiguration();
+
+        // FreeMarker takes the data and a Writer to process the template.
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        Writer writer = new OutputStreamWriter(outputStream);
+        try {
+            Template configurationTemplate = configuration.getTemplate(template);
+            configurationTemplate.process(map, writer);
+            builder.append(outputStream.toString());
+        } catch (Exception e) {
+            throw new RuntimeException(e.getMessage(), e);
+        } finally {
+            org.forgerock.util.Utils.closeSilently(writer, outputStream);
+        }
+    }
+
+    /**
+     * Returns an option synopsis.
+     *
+     * <br>
+     *
+     * Note: The synopsis might contain characters that must be escaped in XML.
+     *
+     * @param argument  The argument option.
+     * @return          A synopsis.
+     */
+    static String getOptionSynopsis(final Argument argument) {
+        StringBuilder builder = new StringBuilder();
+
+        final Character shortID = argument.getShortIdentifier();
+        if (shortID != null) {
+            builder.append("-").append(shortID.charValue());
+        }
+        final String longID = argument.getLongIdentifier();
+        if (shortID != null && longID != null) {
+            builder.append(" | ");
+        }
+        if (longID != null) {
+            builder.append("--").append(longID);
+        }
+        if (argument.needsValue()) {
+            builder.append(" ").append(argument.getValuePlaceholder());
+        }
+
+        return builder.toString();
+    }
+
+    /**
+     * Returns true when the argument handles properties.
+     *
+     * @param argument  The argument.
+     * @return True if the argument handles properties.
+     */
+    public static boolean doesHandleProperties(final Argument argument) {
+        // FixMe: This method is public so it can be used by the SubCommandUsageHandler
+        // in org.forgerock.opendj.config.dsconfig.DSConfig.
+
+        final String id = argument.getLongIdentifier();
+        return ("add".equals(id) || "remove".equals(id) || "reset".equals(id) || "set".equals(id));
+    }
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/SubCommandArgumentParser.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/SubCommandArgumentParser.java
index f6fb9d0..6777007 100644
--- a/opendj-cli/src/main/java/com/forgerock/opendj/cli/SubCommandArgumentParser.java
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/SubCommandArgumentParser.java
@@ -28,6 +28,7 @@
 
 import static com.forgerock.opendj.cli.ArgumentConstants.*;
 import static com.forgerock.opendj.cli.CliMessages.*;
+import static com.forgerock.opendj.cli.DocGenerationHelper.*;
 import static com.forgerock.opendj.cli.Utils.*;
 import static com.forgerock.opendj.util.StaticUtils.*;
 
@@ -1102,7 +1103,17 @@
         }
     }
 
-    /** Generate reference documentation for dsconfig subcommands. */
+    /**
+     * Appends a list generated DocBook XML RefSect2 elements to the StringBuilder, one per subcommand.
+     *
+     * <br>
+     *
+     * Note: The result is not a complete XML document.
+     * Instead you must wrap the resulting list of RefSect2 elements in a RefSect1.
+     *
+     * @param builder   Append the list of RefSect2 elements to this.
+     * @param values    The SubCommands containing the reference information.
+     */
     private void generateReferenceDoc(final StringBuilder builder, Collection<SubCommand> values) {
         for (SubCommand s : values) {
             toRefSect2(s, builder);
@@ -1110,58 +1121,12 @@
     }
 
     /**
-     * Generate reference documentation for dsconfig subcommands in DocBook 5 XML format. As the number of categories is
-     * large, the subcommand entries are sorted here by name for inclusion in a &lt;refsect1&gt; covering all dsconfig
-     * Subcommands as part of the &lt;refentry&gt; for dsconfig (in man-dsconfig.xml).
-     * <p>
-     * Although it would be possible to categorize subcommands in the same way as they are categorized in dsconfig
-     * interactive mode, this generator does not use the categories.
-     * <p>
-     * It would also be possible to generate the sort of information provided by the configuration reference, such that
-     * this reference would not stop at simply listing an option like --set {PROP:VAL}, but instead would also provide
-     * the list of PROPs and their possible VALs. A future improvement could no doubt merge the configuration reference
-     * with this content, though perhaps the problem calls for hypertext rather than the linear structure of a
-     * &lt;refentry&gt;.
-     * <p>
-     * Each individual subcommand results in a &lt;refsect2&gt; element similar to the following.
-     *
-     * <pre>
-     *     &lt;refsect2 xml:id=&quot;dsconfig-create-local-db-index&quot;&gt;
-     *      &lt;title&gt;dsconfig create-local-db-index&lt;/title&gt;
-     *      &lt;para&gt;Creates Local DB Indexes&lt;/para&gt;
-     *      &lt;variablelist&gt;
-     *       &lt;varlistentry&gt;
-     *        &lt;term&gt;&lt;option&gt;--backend-name {name}
-     *         &lt;/option&gt;&lt;/term&gt;
-     *        &lt;listitem&gt;
-     *         &lt;para&gt;The name of the Local DB Backend&lt;/para&gt;
-     *        &lt;/listitem&gt;
-     *       &lt;/varlistentry&gt;
-     *       &lt;varlistentry&gt;
-     *        &lt;term&gt;&lt;option&gt;--index-name {OID}&lt;/option&gt;&lt;/term&gt;
-     *        &lt;listitem&gt;
-     *         &lt;para&gt;The name of the new Local DB Index which will also be used
-     *         as the value of the &quot;attribute&quot; property: Specifies the name
-     *         of the attribute for which the index is to be maintained.&lt;/para&gt;
-     *        &lt;/listitem&gt;
-     *       &lt;/varlistentry&gt;
-     *       &lt;varlistentry&gt;
-     *        &lt;term&gt;&lt;option&gt;--set {PROP:VALUE}&lt;/option&gt;&lt;/term&gt;
-     *        &lt;listitem&gt;
-     *         &lt;para&gt;Assigns a value to a property where PROP is the name of the
-     *         property and VALUE is the single value to be assigned. Specify the same
-     *         property multiple times in order to assign more than one value to
-     *         it&lt;/para&gt;
-     *        &lt;/listitem&gt;
-     *       &lt;/varlistentry&gt;
-     *      &lt;/variablelist&gt;
-     *      &lt;/refsect2&gt;
-     * </pre>
+     * Appends a generated DocBook XML RefSect2 element for a single subcommand to the StringBuilder.
      *
      * @param sc
      *            The SubCommand containing reference information.
      * @param sb
-     *            The string builder where to output the Refsect2 representation of the subcommand
+     *            Append the RefSect2 element to this.
      */
     private void toRefSect2(SubCommand sc, StringBuilder sb) {
         final String scriptName = getScriptName();
@@ -1170,90 +1135,50 @@
                     + PROPERTY_SCRIPT_NAME + "'.");
         }
 
-        final String idRef = scriptName + "-" + sc.getName();
-        final String nameRef = scriptName + " " + sc.getName();
-        sb.append("<refsect2 xml:id=\"").append(idRef).append("\">").append(EOL);
-        sb.append(" <title>").append(nameRef).append("</title>").append(EOL);
-        sb.append(" <para>").append(sc.getDescription()).append("</para>").append(EOL);
+        // Model for a FreeMarker template.
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("id", scriptName + "-" + sc.getName());
+        final String name = scriptName + " " + sc.getName();
+        map.put("name", name);
+        map.put("description", sc.getDescription());
+        map.put("optsTitle", REF_TITLE_OPTIONS.get());
+        map.put("optsIntro", REF_INTRO_OPTIONS.get(name));
 
         // If there is a supplement to the description for this subcommand,
-        // then it is formatted for use in generated reference documentation.
-        // In other words, it is already DocBook XML, so append it as is.
-        final LocalizableMessage scDocDescriptionSupplement = sc.getDocDescriptionSupplement();
-        if (!LocalizableMessage.EMPTY.equals(scDocDescriptionSupplement)) {
-            sb.append(scDocDescriptionSupplement.toString()).append(EOL);
-        }
-
+        // then it is already DocBook XML, so use it as is.
+        map.put("info", sc.getDocDescriptionSupplement());
         if (!sc.getArguments().isEmpty()) {
-            sb.append(" <refsect3 xml:id=\"").append(idRef).append("-options\">").append(EOL);
-            sb.append("   <title>Options</title>").append(EOL);
-            sb.append("   <variablelist>").append(EOL);
-            sb.append("     <para>").append(EOL);
-            sb.append("       The <command>").append(nameRef)
-              .append("</command> command supports the following options.").append(EOL);
-            sb.append("     </para>").append(EOL);
+            List<Map<String, Object>> options = new LinkedList<Map<String, Object>>();
             String nameOption = null;
             for (Argument a : sc.getArguments()) {
-                sb.append("     <varlistentry>").append(EOL);
-                sb.append("       <term>");
-                final String option = getOption(a);
-                sb.append(option);
-                sb.append("</term>").append(EOL);
-                sb.append("       <listitem>").append(EOL);
-                sb.append("         <para>").append(a.getDescription()).append("</para>").append(EOL);
-                final String longID = a.getLongIdentifier();
-                if (!"set".equals(longID)
-                        && !"reset".equals(longID)
-                        && !"add".equals(longID)
-                        && !"remove".equals(longID)) {
-                    nameOption = option;
-                }
+                Map<String, Object> option = new HashMap<String, Object>();
+                String optionSynopsis = getOptionSynopsis(a);
+                option.put("synopsis", optionSynopsis);
+                option.put("description", a.getDescription());
+                Map<String, Object> info = new HashMap<String, Object>();
                 if (subCommandUsageHandler != null) {
-                    subCommandUsageHandler.appendArgumentAdditionalInfo(sb, sc, a, nameOption);
-                } else {
-                    final String defaultValue = a.getDefaultValue();
-                    if (defaultValue != null && !defaultValue.isEmpty()) {
-                        sb.append("         <para>Default: ").append(defaultValue).append("</para>").append(EOL);
+                    if (!doesHandleProperties(a)) {
+                        nameOption = "<option>" + optionSynopsis + "</option>";
                     }
 
+                    // Let this build its own arbitrarily formatted additional info.
+                    info.put("usage", subCommandUsageHandler.getArgumentAdditionalInfo(sc, a, nameOption));
+                } else {
+                    info.put("default", REF_DEFAULT.get(a.getDefaultValue()));
+
                     // If there is a supplement to the description for this argument,
-                    // then for now it is already formatted in DocBook XML.
-                    final LocalizableMessage aDocDescriptionSupplement = a.getDocDescriptionSupplement();
-                    if (!LocalizableMessage.EMPTY.equals(aDocDescriptionSupplement)) {
-                        sb.append(aDocDescriptionSupplement.toString()).append(EOL);
-                    }
+                    // then it is already DocBook XML, so use it as is.
+                    info.put("doc", a.getDocDescriptionSupplement());
                 }
-                sb.append("       </listitem>").append(EOL);
-                sb.append("     </varlistentry>").append(EOL);
+                option.put("info", info);
+                options.add(option);
             }
-            sb.append("   </variablelist>").append(EOL);
-            sb.append(" </refsect3>").append(EOL);
+            map.put("options", options);
         }
+
         if (subCommandUsageHandler != null) {
-            subCommandUsageHandler.appendProperties(sb, sc);
+            map.put("properties", subCommandUsageHandler.getProperties(sc));
         }
-
-        sb.append("</refsect2>").append(EOL);
-    }
-
-    private String getOption(Argument a) {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("<option>");
-        final Character shortID = a.getShortIdentifier();
-        if (shortID != null) {
-            sb.append("-").append(shortID.charValue());
-        }
-        final String longID = a.getLongIdentifier();
-        if (shortID != null && longID != null) {
-            sb.append(" | ");
-        }
-        if (longID != null) {
-            sb.append("--").append(longID);
-        }
-        if (a.needsValue()) {
-            sb.append(" ").append(a.getValuePlaceholder());
-        }
-        sb.append("</option>");
-        return sb.toString();
+        applyTemplate(sb, "refSect2.ftl", map);
     }
 }
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/SubCommandUsageHandler.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/SubCommandUsageHandler.java
index 4884b73..8a7bf89 100644
--- a/opendj-cli/src/main/java/com/forgerock/opendj/cli/SubCommandUsageHandler.java
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/SubCommandUsageHandler.java
@@ -21,7 +21,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2015 ForgeRock AS
+ *      Copyright 2015 ForgeRock AS.
  */
 package com.forgerock.opendj.cli;
 
@@ -32,27 +32,25 @@
 public interface SubCommandUsageHandler {
 
     /**
-     * Appends properties information for the sub-command.
+     * Returns properties information for the sub-command.
      *
-     * @param builder
-     *          the string builder where to append
      * @param subCommand
      *          the sub command for which to print usage information
+     * @return  The properties information for the sub-command.
      */
-    void appendProperties(StringBuilder builder, SubCommand subCommand);
+    String getProperties(SubCommand subCommand);
 
     /**
-     * Appends additional information for the provided sub-command argument.
+     * Returns additional information for the provided sub-command argument.
      *
-     * @param builder
-     *          the string builder where to append
      * @param subCommand
      *          the sub command for which to print usage information
      * @param arg
      *          the argument for which to append additional information
      * @param nameOption
      *          the string representing the name option
+     * @return  The additional information for the sub-command argument.
      */
-    void appendArgumentAdditionalInfo(StringBuilder builder, SubCommand subCommand, Argument arg, String nameOption);
+    String getArgumentAdditionalInfo(SubCommand subCommand, Argument arg, String nameOption);
 
 }
diff --git a/opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties b/opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties
index 1d1c969..b216492 100755
--- a/opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties
+++ b/opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties
@@ -973,6 +973,13 @@
 ERR_ERROR_CANNOT_READ_BIND_NAME=Unable to read bind name
 ERR_ERROR_CANNOT_READ_HOST_NAME=Cannot read the host name
 
+# Strings for generated reference documentation.
+REF_TITLE_DESCRIPTION=Description
+REF_TITLE_OPTIONS=Options
+REF_INTRO_OPTIONS=The <command>%s</command> command takes the following options:
+REF_DEFAULT=Default: %s
+REF_TITLE_SUBCOMMANDS=Subcommands
+
 # Supplements to descriptions for generated reference documentation.
 SUPPLEMENT_DESCRIPTION_CONTROLS=<para>                                             \
     For some <replaceable>controloid</replaceable> values,                         \
diff --git a/opendj-cli/src/main/resources/templates/dscfgAppendProps.ftl b/opendj-cli/src/main/resources/templates/dscfgAppendProps.ftl
new file mode 100644
index 0000000..790d19a
--- /dev/null
+++ b/opendj-cli/src/main/resources/templates/dscfgAppendProps.ftl
@@ -0,0 +1,35 @@
+<#--
+ # CDDL HEADER START
+ #
+ # The contents of this file are subject to the terms of the
+ # Common Development and Distribution License, Version 1.0 only
+ # (the "License").  You may not use this file except in compliance
+ # with the License.
+ #
+ # You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ # or http://forgerock.org/license/CDDLv1.0.html.
+ # See the License for the specific language governing permissions
+ # and limitations under the License.
+ #
+ # When distributing Covered Code, include this CDDL HEADER in each
+ # file and include the License file at legal-notices/CDDLv1_0.txt.
+ # If applicable, add the following below this CDDL HEADER,
+ # with the fields enclosed by brackets "[]" replaced
+ # with your own identifying information:
+ #
+ #      Portions Copyright [yyyy] [name of copyright owner]
+ #
+ # CDDL HEADER END
+ #
+ #      Copyright 2015 ForgeRock AS.
+ #
+ #-->
+<refsect3 xml:id="${id}">
+  <title>${title}</title>
+
+  <para>
+   ${intro}
+  </para>
+
+  ${list}
+</refsect3>
diff --git a/opendj-cli/src/main/resources/templates/dscfgListSubtypes.ftl b/opendj-cli/src/main/resources/templates/dscfgListSubtypes.ftl
new file mode 100644
index 0000000..7aa96b1
--- /dev/null
+++ b/opendj-cli/src/main/resources/templates/dscfgListSubtypes.ftl
@@ -0,0 +1,56 @@
+<#--
+ # CDDL HEADER START
+ #
+ # The contents of this file are subject to the terms of the
+ # Common Development and Distribution License, Version 1.0 only
+ # (the "License").  You may not use this file except in compliance
+ # with the License.
+ #
+ # You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ # or http://forgerock.org/license/CDDLv1.0.html.
+ # See the License for the specific language governing permissions
+ # and limitations under the License.
+ #
+ # When distributing Covered Code, include this CDDL HEADER in each
+ # file and include the License file at legal-notices/CDDLv1_0.txt.
+ # If applicable, add the following below this CDDL HEADER,
+ # with the fields enclosed by brackets "[]" replaced
+ # with your own identifying information:
+ #
+ #      Portions Copyright [yyyy] [name of copyright owner]
+ #
+ # CDDL HEADER END
+ #
+ #      Copyright 2015 ForgeRock AS.
+ #
+ #-->
+<variablelist>
+
+ <para>
+   ${dependencies}
+ </para>
+
+ <para>
+   ${typesIntro}
+ </para>
+
+ <#list children as child>
+   <varlistentry>
+     <term>${child.name}</term>
+     <listitem>
+       <para>
+         ${child.default}
+       </para>
+
+       <para>
+         ${child.enabled}
+       </para>
+
+       <para>
+         ${child.link}
+       </para>
+     </listitem>
+   </varlistentry>
+ </#list>
+
+</variablelist>
diff --git a/opendj-cli/src/main/resources/templates/dscfgVarListEntry.ftl b/opendj-cli/src/main/resources/templates/dscfgVarListEntry.ftl
new file mode 100644
index 0000000..2e07620
--- /dev/null
+++ b/opendj-cli/src/main/resources/templates/dscfgVarListEntry.ftl
@@ -0,0 +1,32 @@
+<#--
+ # CDDL HEADER START
+ #
+ # The contents of this file are subject to the terms of the
+ # Common Development and Distribution License, Version 1.0 only
+ # (the "License").  You may not use this file except in compliance
+ # with the License.
+ #
+ # You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ # or http://forgerock.org/license/CDDLv1.0.html.
+ # See the License for the specific language governing permissions
+ # and limitations under the License.
+ #
+ # When distributing Covered Code, include this CDDL HEADER in each
+ # file and include the License file at legal-notices/CDDLv1_0.txt.
+ # If applicable, add the following below this CDDL HEADER,
+ # with the fields enclosed by brackets "[]" replaced
+ # with your own identifying information:
+ #
+ #      Portions Copyright [yyyy] [name of copyright owner]
+ #
+ # CDDL HEADER END
+ #
+ #      Copyright 2015 ForgeRock AS.
+ #
+ #-->
+<varlistentry>
+  <term>${term}</term>
+  <listitem>
+    ${definition}
+  </listitem>
+</varlistentry>
diff --git a/opendj-cli/src/main/resources/templates/dscfgVariableList.ftl b/opendj-cli/src/main/resources/templates/dscfgVariableList.ftl
new file mode 100644
index 0000000..b3fd18d
--- /dev/null
+++ b/opendj-cli/src/main/resources/templates/dscfgVariableList.ftl
@@ -0,0 +1,51 @@
+<#--
+ # CDDL HEADER START
+ #
+ # The contents of this file are subject to the terms of the
+ # Common Development and Distribution License, Version 1.0 only
+ # (the "License").  You may not use this file except in compliance
+ # with the License.
+ #
+ # You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ # or http://forgerock.org/license/CDDLv1.0.html.
+ # See the License for the specific language governing permissions
+ # and limitations under the License.
+ #
+ # When distributing Covered Code, include this CDDL HEADER in each
+ # file and include the License file at legal-notices/CDDLv1_0.txt.
+ # If applicable, add the following below this CDDL HEADER,
+ # with the fields enclosed by brackets "[]" replaced
+ # with your own identifying information:
+ #
+ #      Portions Copyright [yyyy] [name of copyright owner]
+ #
+ # CDDL HEADER END
+ #
+ #      Copyright 2015 ForgeRock AS.
+ #
+ #-->
+<variablelist>
+
+<#list properties as property>
+
+  <varlistentry xml:id="${property.id}">
+    <term>${property.term}</term>
+    <listitem>
+      <variablelist>
+
+        <varlistentry>
+          <term>${property.descTitle}</term>
+          <listitem>
+            <para>
+              ${property.description}
+            </para>
+          </listitem>
+        </varlistentry>
+
+        ${property.list}
+      </variablelist>
+    </listitem>
+  </varlistentry>
+</#list>
+
+</variablelist>
diff --git a/opendj-cli/src/main/resources/templates/refEntry.ftl b/opendj-cli/src/main/resources/templates/refEntry.ftl
new file mode 100644
index 0000000..31542a0
--- /dev/null
+++ b/opendj-cli/src/main/resources/templates/refEntry.ftl
@@ -0,0 +1,135 @@
+<#--
+ # CDDL HEADER START
+ #
+ # The contents of this file are subject to the terms of the
+ # Common Development and Distribution License, Version 1.0 only
+ # (the "License").  You may not use this file except in compliance
+ # with the License.
+ #
+ # You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ # or http://forgerock.org/license/CDDLv1.0.html.
+ # See the License for the specific language governing permissions
+ # and limitations under the License.
+ #
+ # When distributing Covered Code, include this CDDL HEADER in each
+ # file and include the License file at legal-notices/CDDLv1_0.txt.
+ # If applicable, add the following below this CDDL HEADER,
+ # with the fields enclosed by brackets "[]" replaced
+ # with your own identifying information:
+ #
+ #      Portions Copyright [yyyy] [name of copyright owner]
+ #
+ # CDDL HEADER END
+ #
+ #      Copyright 2015 ForgeRock AS.
+ #
+ #-->
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ! CCPL HEADER START
+  !
+  ! This work is licensed under the Creative Commons
+  ! Attribution-NonCommercial-NoDerivs 3.0 Unported License.
+  ! To view a copy of this license, visit
+  ! http://creativecommons.org/licenses/by-nc-nd/3.0/
+  ! or send a letter to Creative Commons, 444 Castro Street,
+  ! Suite 900, Mountain View, California, 94041, USA.
+  !
+  ! You can also obtain a copy of the license at
+  ! trunk/opendj/legal-notices/CC-BY-NC-ND.txt.
+  ! See the License for the specific language governing permissions
+  ! and limitations under the License.
+  !
+  ! If applicable, add the following below this CCPL HEADER, with the fields
+  ! enclosed by brackets "[]" replaced with your own identifying information:
+  !      Portions Copyright [yyyy] [name of copyright owner]
+  !
+  ! CCPL HEADER END
+  !
+  !      Copyright 2011-${year} ForgeRock AS.
+  !
+-->
+<refentry xml:id="${name}-1"
+          xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="${locale}"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://docbook.org/ns/docbook
+                              http://docbook.org/xml/5.0/xsd/docbook.xsd"
+          xmlns:xlink="http://www.w3.org/1999/xlink"
+          xmlns:xinclude="http://www.w3.org/2001/XInclude">
+
+ <info>
+  <copyright>
+   <year>2011-${year}</year>
+   <holder>ForgeRock AS.</holder>
+  </copyright>
+ </info>
+
+ <refmeta>
+  <refentrytitle>${name}</refentrytitle><manvolnum>1</manvolnum>
+  <refmiscinfo class="software">OpenDJ</refmiscinfo>
+  <refmiscinfo class="version">${r"${project.version}"}</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>${name}</refname>
+  <refpurpose>TODO short description</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+  <cmdsynopsis>
+   <command>${name}</command>
+   <arg choice="plain">${args}</arg>
+  </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1 xml:id="${name}-description">
+   <title>${descTitle}</title>
+
+   <para>
+     ${description}
+   </para>
+
+   <#if info??>${info}</#if>
+ </refsect1>
+
+ <#if options??>
+   <refsect1>
+    <title>${optsTitle}</title>
+
+    <variablelist>
+     <para>
+      ${optsIntro}
+     </para>
+
+     <#list options as option>
+       <varlistentry>
+         <term><option>${option.synopsis?xml}</option></term>
+         <listitem>
+           <para>
+             ${option.description}
+           </para>
+
+           <#if option.default??>
+             <para>
+               ${option.default}
+             </para>
+           </#if>
+
+           <#if option.info??>${option.info}</#if>
+         </listitem>
+       </varlistentry>
+
+     </#list>
+
+     </variablelist>
+   </refsect1>
+ </#if>
+
+ <!-- TODO: subcommands -->
+ <!-- TODO: filter -->
+ <!-- TODO: attribute -->
+ <!-- TODO: exitCodes -->
+ <!-- TODO: files -->
+ <!-- TODO: examples -->
+ <!-- TODO: seeAlso -->
+</refentry>
diff --git a/opendj-cli/src/main/resources/templates/refSect2.ftl b/opendj-cli/src/main/resources/templates/refSect2.ftl
new file mode 100644
index 0000000..2529f3d
--- /dev/null
+++ b/opendj-cli/src/main/resources/templates/refSect2.ftl
@@ -0,0 +1,76 @@
+<#--
+ # CDDL HEADER START
+ #
+ # The contents of this file are subject to the terms of the
+ # Common Development and Distribution License, Version 1.0 only
+ # (the "License").  You may not use this file except in compliance
+ # with the License.
+ #
+ # You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ # or http://forgerock.org/license/CDDLv1.0.html.
+ # See the License for the specific language governing permissions
+ # and limitations under the License.
+ #
+ # When distributing Covered Code, include this CDDL HEADER in each
+ # file and include the License file at legal-notices/CDDLv1_0.txt.
+ # If applicable, add the following below this CDDL HEADER,
+ # with the fields enclosed by brackets "[]" replaced
+ # with your own identifying information:
+ #
+ #      Portions Copyright [yyyy] [name of copyright owner]
+ #
+ # CDDL HEADER END
+ #
+ #      Copyright 2015 ForgeRock AS.
+ #
+ #-->
+<refsect2 xml:id="${id}">
+  <title>${name}</title>
+
+  <para>
+   ${description}
+  </para>
+
+  <#if info??>${info}</#if>
+
+  <#if options??>
+    <refsect3 xml:id="${id}-options">
+      <title>${optsTitle}</title>
+
+      <variablelist>
+        <para>
+         ${optsIntro}
+        </para>
+
+        <#list options as option>
+
+        <varlistentry>
+          <term><option>${option.synopsis?xml}</option></term>
+          <listitem>
+           <para>
+             ${option.description}
+           </para>
+
+           <#if option.info??>
+             <#if info.usage??>${option.info.usage}</#if>
+
+             <#if info.default??>
+                <para>
+                  ${option.info.default}
+                </para>
+             </#if>
+
+             <#if info.doc??>${option.info.doc}</#if>
+           </#if>
+          </listitem>
+        </varlistentry>
+
+        </#list>
+      </variablelist>
+    </refsect3>
+  </#if>
+
+  <#if properties??>
+    ${properties}
+  </#if>
+</refsect2>
diff --git a/opendj-config/src/main/java/org/forgerock/opendj/config/dsconfig/DSConfig.java b/opendj-config/src/main/java/org/forgerock/opendj/config/dsconfig/DSConfig.java
index 261028a..a3288c0 100644
--- a/opendj-config/src/main/java/org/forgerock/opendj/config/dsconfig/DSConfig.java
+++ b/opendj-config/src/main/java/org/forgerock/opendj/config/dsconfig/DSConfig.java
@@ -22,12 +22,13 @@
  *
  *
  *      Copyright 2007-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2012-2015 ForgeRock AS
+ *      Portions Copyright 2012-2015 ForgeRock AS.
  */
 package org.forgerock.opendj.config.dsconfig;
 
 import static com.forgerock.opendj.cli.ArgumentConstants.*;
 import static com.forgerock.opendj.cli.CliMessages.*;
+import static com.forgerock.opendj.cli.DocGenerationHelper.*;
 import static com.forgerock.opendj.cli.Utils.*;
 import static com.forgerock.opendj.dsconfig.DsconfigMessages.*;
 import static com.forgerock.opendj.util.StaticUtils.*;
@@ -54,6 +55,7 @@
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
@@ -127,93 +129,61 @@
  */
 public final class DSConfig extends ConsoleApplication {
 
-    private static final String ALLOW_UNLIMITED = "A value of \"-1\" or \"unlimited\" for no limit.";
-    private static final String ACI_SYNTAX_REL_URL =
-        "<link" + EOL
-            + " xlink:show=\"new\"" + EOL
-            + " xlink:href=\"admin-guide#about-acis\"" + EOL
-            + " xlink:role=\"http://docbook.org/xlink/role/olink\">" + EOL
-            + "<citetitle>About Access Control Instructions</citetitle></link>" + EOL;
-    private static final String DURATION_SYNTAX_REL_URL =
-        "  <itemizedlist>" + EOL
-            + "    <para>Some property values take a time duration. Durations are expressed" + EOL
-            + "    as numbers followed by units. For example <literal>1 s</literal> means" + EOL
-            + "    one second, and <literal>2 w</literal> means two weeks. Some durations" + EOL
-            + "    have minimum granularity or maximum units, so you cannot necessary specify" + EOL
-            + "    every duration in milliseconds or weeks for example. Some durations allow" + EOL
-            + "    you to use a special value to mean unlimited. Units are specified as" + EOL
-            + "    follows.</para>" + EOL
-            + "    <listitem><para><literal>ms</literal>: milliseconds</para></listitem>" + EOL
-            + "    <listitem><para><literal>s</literal>: seconds</para></listitem>" + EOL
-            + "    <listitem><para><literal>m</literal>: minutes</para></listitem>" + EOL
-            + "    <listitem><para><literal>h</literal>: hours</para></listitem>" + EOL
-            + "    <listitem><para><literal>d</literal>: days</para></listitem>" + EOL
-            + "    <listitem><para><literal>w</literal>: weeks</para></listitem>" + EOL
-            + "  </itemizedlist>" + EOL;
-
-    // FIXME: I18n support. Today all the strings are hardcoded in this file
+    /**
+     * This class provides additional information about subcommands for generated reference documentation.
+     */
     private final class DSConfigSubCommandUsageHandler implements SubCommandUsageHandler {
 
+        /** Marker to open a DocBook XML paragraph. */
+        private String op = "<para>";
+
+        /** Marker to close a DocBook XML paragraph. */
+        private String cp = "</para>";
+
         /** {@inheritDoc} */
         @Override
-        public void appendArgumentAdditionalInfo(StringBuilder sb, SubCommand sc, Argument a, String nameOption) {
+        public String getArgumentAdditionalInfo(SubCommand sc, Argument a, String nameOption) {
+            StringBuilder sb = new StringBuilder();
             final AbstractManagedObjectDefinition<?, ?> defn = getManagedObjectDefinition(sc);
             if (defn == null) {
-                return;
+                return "";
             }
-            final String longID = a.getLongIdentifier();
-            if ("set".equals(longID)
-                    || "reset".equals(longID)
-                    || "add".equals(longID)
-                    || "remove".equals(longID)) {
-                sb.append("         <para>").append(EOL);
+            if (doesHandleProperties(a)) {
                 final LocalizableMessage name = defn.getUserFriendlyName();
-                sb.append("           ").append(name).append(" properties depend on the ").append(name)
-                  .append(" type, which depends on the ").append(nameOption).append(" option.").append(EOL);
-                sb.append("         </para>").append(EOL);
+                sb.append(op).append(REF_DSCFG_ARG_ADDITIONAL_INFO.get(name, name, nameOption)).append(cp).append(EOL);
             } else {
                 listSubtypes(sb, a, defn);
             }
-            return;
+            return sb.toString();
         }
 
         private void listSubtypes(StringBuilder sb, Argument a, AbstractManagedObjectDefinition<?, ?> defn) {
             final LocalizableMessage placeholder = a.getValuePlaceholder();
-            sb.append("         <variablelist>").append(EOL);
-            sb.append("           <para>").append(EOL);
-            final LocalizableMessage name = defn.getUserFriendlyName();
-            sb.append("             ").append(name).append(" properties depend on the ").append(name)
-                .append(" type, which depends on the ").append(placeholder).append(" you provide.").append(EOL);
-            sb.append("           </para>").append(EOL);
-            sb.append("           <para>").append(EOL);
-            sb.append("             By default, OpenDJ directory server supports the following ")
-                .append(name).append(" types:").append(EOL);
-            sb.append("           </para>").append(EOL);
 
+            Map<String, Object> map = new HashMap<String, Object>();
+
+            final LocalizableMessage name = defn.getUserFriendlyName();
+            map.put("dependencies", REF_DSCFG_SUBTYPE_DEPENDENCIES.get(name, name, placeholder));
+            map.put("typesIntro", REF_DSCFG_SUBTYPE_TYPES_INTRO.get(name));
+
+            List<Map<String, Object>> children = new LinkedList<Map<String, Object>>();
             for (AbstractManagedObjectDefinition<?, ?> childDefn : getLeafChildren(defn)) {
-                sb.append("           <varlistentry>").append(EOL);
-                sb.append("             <term>").append(childDefn.getName()).append("</term>").append(EOL);
-                sb.append("             <listitem>").append(EOL);
-                sb.append("               <para>").append(EOL);
-                sb.append("                 Default ").append(placeholder).append(": ")
-                    .append(childDefn.getUserFriendlyName()).append(EOL);
-                sb.append("               </para>").append(EOL);
-                sb.append("               <para>").append(EOL);
-                final boolean isEnabled = propertyExists(childDefn, "enabled");
-                sb.append("                 Enabled by default: ").append(isEnabled).append(EOL);
-                sb.append("               </para>").append(EOL);
-                sb.append("               <para>").append(EOL);
-                sb.append("                 See <xref linkend=\"")
-                    .append(getScriptName()).append("-")
-                    .append(a.getLongIdentifier()).append("-")
-                    .append(defn.getName()).append("-prop-").append(childDefn.getName())
-                    .append("\" /> for the properties of this ").append(defn.getUserFriendlyName())
-                    .append(" type.").append(EOL);
-                sb.append("               </para>").append(EOL);
-                sb.append("             </listitem>").append(EOL);
-                sb.append("           </varlistentry>").append(EOL);
+
+                Map<String, Object> child = new HashMap<String, Object>();
+
+                child.put("name", childDefn.getName());
+                child.put("default", REF_DSCFG_CHILD_DEFAULT.get(placeholder, childDefn.getUserFriendlyName()));
+                child.put("enabled", REF_DSCFG_CHILD_ENABLED_BY_DEFAULT.get(propertyExists(childDefn, "enabled")));
+
+                final String link = getLink(getScriptName() + "-" + a.getLongIdentifier()
+                        + "-" + defn.getName() + "-prop-" + childDefn.getName());
+                child.put("link", REF_DSCFG_CHILD_LINK.get(link, defn.getUserFriendlyName()));
+
+                children.add(child);
             }
-            sb.append("         </variablelist>").append(EOL);
+            map.put("children", children);
+
+            applyTemplate(sb, "dscfgListSubtypes.ftl", map);
         }
 
         private boolean propertyExists(AbstractManagedObjectDefinition<?, ?> defn, String name) {
@@ -226,27 +196,26 @@
 
         /** {@inheritDoc} */
         @Override
-        public void appendProperties(StringBuilder sb, SubCommand sc) {
+        public String getProperties(SubCommand sc) {
+            StringBuilder sb = new StringBuilder();
             final AbstractManagedObjectDefinition<?, ?> defn = getManagedObjectDefinition(sc);
             if (defn == null) {
-                return;
+                return "";
             }
 
             for (AbstractManagedObjectDefinition<?, ?> childDefn : getLeafChildren(defn)) {
                 final List<PropertyDefinition<?>> props =
                     new ArrayList<PropertyDefinition<?>>(childDefn.getAllPropertyDefinitions());
                 Collections.sort(props);
-
+                Map<String, Object> map = new HashMap<String, Object>();
                 final String propPrefix = getScriptName() + "-" + sc.getName() + "-" + childDefn.getName();
-                sb.append(" <refsect3 xml:id=\"").append(propPrefix).append("\">").append(EOL);
-                sb.append("   <title>").append(childDefn.getUserFriendlyName()).append("</title>").append(EOL);
-                sb.append("   <para>").append(EOL);
-                sb.append("     ").append(defn.getUserFriendlyPluralName()).append(" of type ")
-                  .append(childDefn.getName()).append(" have the following properties:").append(EOL);
-                sb.append("   </para>").append(EOL);
-                toVariableList(props, defn, propPrefix, sb);
-                sb.append(" </refsect3>").append(EOL);
+                map.put("id", propPrefix);
+                map.put("title", childDefn.getUserFriendlyName());
+                map.put("intro", REF_DSCFG_PROPS_INTRO.get(defn.getUserFriendlyPluralName(), childDefn.getName()));
+                map.put("list", toVariableList(props, defn, propPrefix));
+                applyTemplate(sb, "dscfgAppendProps.ftl", map);
             }
+            return sb.toString();
         }
 
         private AbstractManagedObjectDefinition<?, ?> getManagedObjectDefinition(SubCommand sc) {
@@ -298,57 +267,57 @@
             return null;
         }
 
-        private void toVariableList(List<PropertyDefinition<?>> props, AbstractManagedObjectDefinition<?, ?> defn,
-                String propPrefix, StringBuilder b) {
-            final String indent = "            ";
-            b.append("    <variablelist>").append(EOL);
+        private String toVariableList(List<PropertyDefinition<?>> props, AbstractManagedObjectDefinition<?, ?> defn,
+                String propPrefix) {
+            StringBuilder b = new StringBuilder();
+            Map<String, Object> map = new HashMap<String, Object>();
+
+            List<Map<String, Object>> properties = new LinkedList<Map<String, Object>>();
             for (PropertyDefinition<?> prop : props) {
-                b.append("      <varlistentry xml:id=\"")
-                    .append(propPrefix).append("-").append(prop.getName()).append("\">").append(EOL);
-                b.append("        <term>").append(prop.getName()).append("</term>").append(EOL);
-                b.append("        <listitem>").append(EOL);
-                b.append("          <variablelist>").append(EOL);
-                appendVarlistentry(b, "Description", getDescriptionString(prop), indent);
-                appendDefaultBehavior(b, indent, prop);
-                appendAllowedValues(b, prop, indent);
-                appendVarlistentry(b, "Multi-valued", getYN(prop, MULTI_VALUED), indent);
-                appendVarlistentry(b, "Required", getYN(prop, MANDATORY), indent);
-                appendVarlistentry(b, "Admin Action Required", getAdminActionRequired(prop, defn), indent);
-                appendVarlistentry(b, "Advanced Property", getYNAdvanced(prop, ADVANCED), indent);
-                appendVarlistentry(b, "Read-only", getYN(prop, READ_ONLY), indent);
-                b.append("          </variablelist>").append(EOL);
-                b.append("        </listitem>").append(EOL);
-                b.append("      </varlistentry>").append(EOL);
+                Map<String, Object> property = new HashMap<String, Object>();
+                property.put("id", propPrefix + "-" + prop.getName());
+                property.put("term", prop.getName());
+                property.put("descTitle", REF_TITLE_DESCRIPTION.get());
+                property.put("description", getDescriptionString(prop));
+
+                final StringBuilder sb = new StringBuilder();
+                appendDefaultBehavior(sb, prop);
+                appendAllowedValues(sb, prop);
+                appendVarListEntry(sb, REF_DSCFG_PROPS_LABEL_MULTI_VALUED.get().toString(), getYN(prop, MULTI_VALUED));
+                appendVarListEntry(sb, REF_DSCFG_PROPS_LABEL_REQUIRED.get().toString(), getYN(prop, MANDATORY));
+                appendVarListEntry(sb, REF_DSCFG_PROPS_LABEL_ADMIN_ACTION_REQUIRED.get().toString(),
+                        getAdminActionRequired(prop, defn));
+                appendVarListEntry(sb, REF_DSCFG_PROPS_LABEL_ADVANCED_PROPERTY.get().toString(),
+                        getYNAdvanced(prop, ADVANCED));
+                appendVarListEntry(sb, REF_DSCFG_PROPS_LABEL_READ_ONLY.get().toString(), getYN(prop, READ_ONLY));
+                property.put("list", sb.toString());
+
+                properties.add(property);
             }
-            b.append("    </variablelist>").append(EOL);
+            map.put("properties", properties);
+
+            applyTemplate(b, "dscfgVariableList.ftl", map);
+            return b.toString();
         }
 
-        private StringBuilder appendVarlistentry(StringBuilder b, String term, Object para, String indent) {
-            b.append(indent).append("<varlistentry>").append(EOL);
-            b.append(indent).append("  <term>").append(term).append("</term>").append(EOL);
-            b.append(indent).append("  <listitem>").append(EOL);
-            b.append(indent).append("    <para>").append(para).append("</para>").append(EOL);
-            b.append(indent).append("  </listitem>").append(EOL);
-            b.append(indent).append("</varlistentry>").append(EOL);
+        private StringBuilder appendVarListEntry(StringBuilder b, String term, Object definition) {
+            Map<String, Object> map = new HashMap<String, Object>();
+            map.put("term", term);
+            map.put("definition", definition);
+            applyTemplate(b, "dscfgVarListEntry.ftl", map);
             return b;
         }
 
-        private void appendDefaultBehavior(StringBuilder b, final String indent, PropertyDefinition<?> prop) {
-            b.append(indent).append("<varlistentry>").append(EOL);
-            b.append(indent).append("  <term>").append("Default Value").append("</term>").append(EOL);
-            b.append(indent).append("  <listitem>").append(EOL);
-            appendDefaultBehaviorString(b, indent + "    ", prop);
-            b.append(indent).append("  </listitem>").append(EOL);
-            b.append(indent).append("</varlistentry>").append(EOL);
+        private void appendDefaultBehavior(StringBuilder b, PropertyDefinition<?> prop) {
+            StringBuilder sb = new StringBuilder();
+            appendDefaultBehaviorString(sb, prop);
+            appendVarListEntry(b, REF_DSCFG_PROPS_LABEL_DEFAULT_VALUE.get().toString(), sb.toString());
         }
 
-        private void appendAllowedValues(StringBuilder b, PropertyDefinition<?> prop, String indent) {
-            b.append(indent).append("<varlistentry>").append(EOL);
-            b.append(indent).append("  <term>").append("Allowed Values").append("</term>").append(EOL);
-            b.append(indent).append("  <listitem>").append(EOL);
-            appendSyntax(b, prop, indent + "    ");
-            b.append(indent).append("  </listitem>").append(EOL);
-            b.append(indent).append("</varlistentry>").append(EOL);
+        private void appendAllowedValues(StringBuilder b, PropertyDefinition<?> prop) {
+            StringBuilder sb = new StringBuilder();
+            appendSyntax(sb, prop);
+            appendVarListEntry(b, REF_DSCFG_PROPS_LABEL_ALLOWED_VALUES.get().toString(), sb.toString());
         }
 
         private Object getDescriptionString(PropertyDefinition<?> prop) {
@@ -363,152 +332,146 @@
                 final Type actionType = adminAction.getType();
                 final StringBuilder action = new StringBuilder();
                 if (actionType == Type.COMPONENT_RESTART) {
-                    action.append("The ").append(defn.getUserFriendlyName())
-                             .append(" must be disabled and re-enabled for changes to this setting to take effect");
+                    action.append(op)
+                            .append(REF_DSCFG_ADMIN_ACTION_COMPONENT_RESTART.get(defn.getUserFriendlyName()))
+                            .append(cp);
                 } else if (actionType == Type.SERVER_RESTART) {
-                    action.append("Restart the server");
+                    action.append(op).append(REF_DSCFG_ADMIN_ACTION_SERVER_RESTART.get()).append(cp);
                 } else if (actionType == Type.NONE) {
-                    action.append("None");
+                    action.append(op).append(REF_DSCFG_ADMIN_ACTION_NONE.get()).append(cp);
                 }
                 if (synopsis != null) {
-                    if (action.length() > 0) {
-                        action.append(". ");
-                    }
-                    action.append(synopsis);
+                    action.append(op).append(synopsis).append(cp);
                 }
                 return action.toString();
             }
-            return "None";
+            return op + REF_DSCFG_ADMIN_ACTION_NONE.get() + cp;
         }
 
         private String getYN(PropertyDefinition<?> prop, PropertyOption option) {
-            return prop.hasOption(option) ? "Yes" : "No";
+            LocalizableMessage msg = prop.hasOption(option) ? REF_DSCFG_PROP_YES.get() : REF_DSCFG_PROP_NO.get();
+            return op + msg + cp;
         }
 
         private String getYNAdvanced(PropertyDefinition<?> prop, PropertyOption option) {
-            return prop.hasOption(option) ? "Yes (Use --advanced in interactive mode.)" : "No";
+            LocalizableMessage msg = prop.hasOption(option)
+                    ? REF_DSCFG_PROP_YES_ADVANCED.get() : REF_DSCFG_PROP_NO.get();
+            return op + msg + cp;
         }
 
-        private void appendDefaultBehaviorString(StringBuilder b, String indent, PropertyDefinition<?> prop) {
-            b.append(indent);
+        private void appendDefaultBehaviorString(StringBuilder b, PropertyDefinition<?> prop) {
             final DefaultBehaviorProvider<?> defaultBehavior = prop.getDefaultBehaviorProvider();
             if (defaultBehavior instanceof UndefinedDefaultBehaviorProvider) {
-                b.append("<para>None</para>").append(EOL);
-                return;
+                b.append(op).append(REF_DSCFG_DEFAULT_BEHAVIOR_NONE.get()).append(cp).append(EOL);
             } else if (defaultBehavior instanceof DefinedDefaultBehaviorProvider) {
                 DefinedDefaultBehaviorProvider<?> behavior = (DefinedDefaultBehaviorProvider<?>) defaultBehavior;
                 final Collection<String> defaultValues = behavior.getDefaultValues();
                 if (defaultValues.size() == 0) {
-                    b.append("<para>None</para>").append(EOL);
+                    b.append(op).append(REF_DSCFG_DEFAULT_BEHAVIOR_NONE.get()).append(cp).append(EOL);
                 } else if (defaultValues.size() == 1) {
-                    b.append("<para>").append(defaultValues.iterator().next()).append("</para>").append(EOL);
+                    b.append(op).append(REF_DSCFG_DEFAULT_BEHAVIOR.get(defaultValues.iterator().next()))
+                            .append(cp).append(EOL);
                 } else {
                     final Iterator<String> it = defaultValues.iterator();
-                    b.append("<para>").append(it.next()).append("</para>");
+                    b.append(op).append(REF_DSCFG_DEFAULT_BEHAVIOR.get(it.next())).append(cp);
                     for (; it.hasNext();) {
-                        final String str = it.next();
-                        b.append(EOL).append(indent).append("<para>").append(str).append("</para>");
+                        b.append(EOL).append(op).append(REF_DSCFG_DEFAULT_BEHAVIOR.get(it.next())).append(cp);
                     }
                     b.append(EOL);
                 }
-                return;
             } else if (defaultBehavior instanceof AliasDefaultBehaviorProvider) {
                 AliasDefaultBehaviorProvider<?> behavior = (AliasDefaultBehaviorProvider<?>) defaultBehavior;
-                b.append("<para>").append(behavior.getSynopsis()).append("</para>").append(EOL);
-                return;
+                b.append(op).append(REF_DSCFG_DEFAULT_BEHAVIOR.get(behavior.getSynopsis())).append(cp).append(EOL);
             } else if (defaultBehavior instanceof RelativeInheritedDefaultBehaviorProvider) {
                 final RelativeInheritedDefaultBehaviorProvider<?> behavior =
                         (RelativeInheritedDefaultBehaviorProvider<?>) defaultBehavior;
-                appendDefaultBehaviorString(b, indent,
+                appendDefaultBehaviorString(b,
                         behavior.getManagedObjectDefinition().getPropertyDefinition(behavior.getPropertyName()));
-                return;
             } else if (defaultBehavior instanceof AbsoluteInheritedDefaultBehaviorProvider) {
                 final AbsoluteInheritedDefaultBehaviorProvider<?> behavior =
                         (AbsoluteInheritedDefaultBehaviorProvider<?>) defaultBehavior;
-                appendDefaultBehaviorString(b, indent,
+                appendDefaultBehaviorString(b,
                         behavior.getManagedObjectDefinition().getPropertyDefinition(behavior.getPropertyName()));
-                return;
             }
         }
 
-        private void appendSyntax(final StringBuilder b, PropertyDefinition<?> prop, final String indent) {
+        private void appendSyntax(final StringBuilder b, PropertyDefinition<?> prop) {
             // Create a visitor for performing syntax specific processing.
             PropertyDefinitionVisitor<String, Void> visitor = new PropertyDefinitionVisitor<String, Void>() {
 
                 @Override
                 public String visitACI(ACIPropertyDefinition prop, Void p) {
-                    b.append(indent).append("<para>").append(ACI_SYNTAX_REL_URL).append("</para>").append(EOL);
+                    b.append(op).append(REF_DSCFG_ACI_SYNTAX_REL_URL.get()).append(cp).append(EOL);
                     return null;
                 }
 
                 @Override
                 public String visitAggregation(AggregationPropertyDefinition prop, Void p) {
-                    b.append(indent).append("<para>");
+                    b.append(op);
                     final RelationDefinition<?, ?> rel = prop.getRelationDefinition();
-                    final String linkStr = getLink(rel.getName() + ".html");
-                    b.append("The DN of any ").append(linkStr).append(". ");
+                    final String linkStr = getLink(rel.getName());
+                    b.append(REF_DSCFG_AGGREGATION.get(linkStr)).append(". ");
                     final LocalizableMessage synopsis = prop.getSourceConstraintSynopsis();
                     if (synopsis != null) {
                         b.append(synopsis);
                     }
-                    b.append("</para>").append(EOL);
+                    b.append(cp).append(EOL);
                     return null;
                 }
 
                 @Override
                 public String visitAttributeType(AttributeTypePropertyDefinition prop, Void p) {
-                    b.append(indent).append("<para>");
-                    b.append("The name of an attribute type defined in the server schema.");
-                    b.append("</para>").append(EOL);
+                    b.append(op).append(REF_DSCFG_ANY_ATTRIBUTE.get()).append(".").append(cp).append(EOL);
                     return null;
                 }
 
                 @Override
                 public String visitBoolean(BooleanPropertyDefinition prop, Void p) {
-                    b.append(indent).append("<para>true</para>").append(EOL);
-                    b.append(indent).append("<para>false</para>").append(EOL);
+                    b.append(op).append("true").append(cp).append(EOL);
+                    b.append(op).append("false").append(cp).append(EOL);
                     return null;
                 }
 
                 @Override
                 public String visitClass(ClassPropertyDefinition prop, Void p) {
-                    b.append(indent).append("<para>");
-                    b.append("A java class that implements or extends the class(es) :")
-                        .append(Utils.joinAsString(EOL, prop.getInstanceOfInterface()));
-                    b.append("</para>").append(EOL);
+                    b.append(op).append(REF_DSCFG_JAVA_PLUGIN.get()).append(" ")
+                            .append(Utils.joinAsString(EOL, prop.getInstanceOfInterface())).append(cp).append(EOL);
                     return null;
                 }
 
                 @Override
                 public String visitDN(DNPropertyDefinition prop, Void p) {
-                    b.append(indent).append("<para>");
-                    b.append("A valid DN.");
+                    b.append(op).append(REF_DSCFG_VALID_DN.get());
                     final DN baseDN = prop.getBaseDN();
                     if (baseDN != null) {
-                        b.append(" ").append(baseDN);
+                        b.append(": ").append(baseDN);
+                    } else {
+                        b.append(".");
                     }
-                    b.append("</para>").append(EOL);
+                    b.append(cp).append(EOL);
                     return null;
                 }
 
                 @Override
                 public String visitDuration(DurationPropertyDefinition prop, Void p) {
-                    b.append(indent).append("<para>");
-                    b.append(DURATION_SYNTAX_REL_URL).append(". ");
+                    b.append(REF_DSCFG_DURATION_SYNTAX_REL_URL.get()).append(EOL);
+                    b.append(op);
                     if (prop.isAllowUnlimited()) {
-                        b.append(ALLOW_UNLIMITED).append(" ");
+                        b.append(REF_DSCFG_ALLOW_UNLIMITED.get()).append(" ");
                     }
                     if (prop.getMaximumUnit() != null) {
-                        b.append("Maximum unit is \"").append(prop.getMaximumUnit().getLongName()).append("\". ");
+                        final String maxUnitName = prop.getMaximumUnit().getLongName();
+                        b.append(REF_DSCFG_DURATION_MAX_UNIT.get(maxUnitName)).append(".");
                     }
                     final DurationUnit baseUnit = prop.getBaseUnit();
-                    b.append("Lower limit is ").append(valueOf(baseUnit, prop.getLowerLimit()))
-                     .append(" ").append(baseUnit.getLongName()).append(". ");
+                    final long lowerLimit = valueOf(baseUnit, prop.getLowerLimit());
+                    final String unitName = baseUnit.getLongName();
+                    b.append(REF_DSCFG_DURATION_LOWER_LIMIT.get(lowerLimit, unitName)).append(".");
                     if (prop.getUpperLimit() != null) {
-                        b.append("Upper limit is ").append(valueOf(baseUnit, prop.getUpperLimit()))
-                         .append(" ").append(baseUnit.getLongName()).append(". ");
+                        final long upperLimit = valueOf(baseUnit, prop.getUpperLimit());
+                        b.append(REF_DSCFG_DURATION_UPPER_LIMIT.get(upperLimit, unitName)).append(".");
                     }
-                    b.append("</para>").append(EOL);
+                    b.append(cp).append(EOL);
                     return null;
                 }
 
@@ -518,79 +481,77 @@
 
                 @Override
                 public String visitEnum(EnumPropertyDefinition prop, Void p) {
-                    b.append(indent).append("<para>").append(EOL);
-                    b.append(indent).append("  <variablelist>").append(EOL);
+                    b.append("<variablelist>").append(EOL);
                     final Class<?> en = prop.getEnumClass();
                     final Object[] constants = en.getEnumConstants();
                     for (Object enumConstant : constants) {
                         final LocalizableMessage valueSynopsis = prop.getValueSynopsis((Enum) enumConstant);
-                        appendVarlistentry(b, enumConstant.toString(), valueSynopsis, indent + "    ");
+                        appendVarListEntry(b, enumConstant.toString(), valueSynopsis);
                     }
-                    b.append(indent).append("  </variablelist>").append(EOL);
-                    b.append(indent).append("</para>").append(EOL);
+                    b.append("</variablelist>").append(EOL);
                     return null;
                 }
 
                 @Override
                 public String visitInteger(IntegerPropertyDefinition prop, Void p) {
-                    b.append(indent).append("<para>");
-                    b.append("An integer value. Lower value is ").append(prop.getLowerLimit()).append(".");
+                    b.append(op).append(REF_DSCFG_INT.get()).append(". ")
+                            .append(REF_DSCFG_INT_LOWER_LIMIT.get(prop.getLowerLimit())).append(".");
                     if (prop.getUpperLimit() != null) {
-                        b.append(" Upper value is ").append(prop.getUpperLimit()).append(".");
+                        b.append(" ").append(REF_DSCFG_INT_UPPER_LIMIT.get(prop.getUpperLimit())).append(".");
                     }
                     if (prop.isAllowUnlimited()) {
-                        b.append(" ").append(ALLOW_UNLIMITED);
+                        b.append(" ").append(REF_DSCFG_ALLOW_UNLIMITED.get());
                     }
                     if (prop.getUnitSynopsis() != null) {
-                        b.append(" Unit is ").append(prop.getUnitSynopsis()).append(".");
+                        b.append(" ").append(REF_DSCFG_INT_UNIT.get(prop.getUnitSynopsis())).append(".");
                     }
-                    b.append("</para>").append(EOL);
+                    b.append(cp).append(EOL);
                     return null;
                 }
 
                 @Override
                 public String visitIPAddress(IPAddressPropertyDefinition prop, Void p) {
-                    b.append(indent).append("<para>An IP address</para>").append(EOL);
+                    b.append(op).append(REF_DSCFG_IP_ADDRESS.get()).append(cp).append(EOL);
                     return null;
                 }
 
                 @Override
                 public String visitIPAddressMask(IPAddressMaskPropertyDefinition prop, Void p) {
-                    b.append(indent).append("<para>An IP address mask</para>").append(EOL);
+                    b.append(op).append(REF_DSCFG_IP_ADDRESS_MASK.get()).append(cp).append(EOL);
                     return null;
                 }
 
                 @Override
                 public String visitSize(SizePropertyDefinition prop, Void p) {
-                    b.append(indent).append("<para>");
+                    b.append(op);
                     if (prop.getLowerLimit() != 0) {
-                        b.append(" Lower value is ").append(prop.getLowerLimit()).append(".");
+                        b.append(REF_DSCFG_INT_LOWER_LIMIT.get(prop.getLowerLimit())).append(".");
                     }
                     if (prop.getUpperLimit() != null) {
-                        b.append(" Upper value is ").append(prop.getUpperLimit()).append(" .");
+                        b.append(REF_DSCFG_INT_UPPER_LIMIT.get(prop.getUpperLimit())).append(".");
                     }
                     if (prop.isAllowUnlimited()) {
-                        b.append(" ").append(ALLOW_UNLIMITED);
+                        b.append(REF_DSCFG_ALLOW_UNLIMITED.get());
                     }
-                    b.append("</para>").append(EOL);
+                    b.append(cp).append(EOL);
                     return null;
                 }
 
                 @Override
                 public String visitString(StringPropertyDefinition prop, Void p) {
-                    b.append(indent).append("<para>");
+                    b.append(op);
                     if (prop.getPatternSynopsis() != null) {
                         b.append(prop.getPatternSynopsis());
                     } else {
-                        b.append("A String");
+                        b.append(REF_DSCFG_STRING.get());
                     }
-                    b.append("</para>").append(EOL);
+                    b.append(cp).append(EOL);
                     return null;
                 }
 
                 @Override
                 public String visitUnknown(PropertyDefinition prop, Void p) {
-                    b.append(indent).append("<para>Unknown</para>").append(EOL);
+                    b.append(op).append(REF_DSCFG_UNKNOWN.get()).append(cp).append(EOL);
                     return null;
                 }
             };
@@ -600,7 +561,7 @@
         }
 
         private String getLink(String target) {
-            return " <xref linkend=" + target + " />";
+            return " <xref linkend=\"" + target + "\" />";
         }
     }
 
diff --git a/opendj-config/src/main/resources/com/forgerock/opendj/dsconfig/dsconfig.properties b/opendj-config/src/main/resources/com/forgerock/opendj/dsconfig/dsconfig.properties
index 385717c..8b167a3 100644
--- a/opendj-config/src/main/resources/com/forgerock/opendj/dsconfig/dsconfig.properties
+++ b/opendj-config/src/main/resources/com/forgerock/opendj/dsconfig/dsconfig.properties
@@ -20,7 +20,7 @@
 # CDDL HEADER END
 #
 #      Copyright 2006-2010 Sun Microsystems, Inc.
-#      Portions Copyright 2011-2014 ForgeRock AS
+#      Portions Copyright 2011-2015 ForgeRock AS.
 #
 # Format string definitions
 #
@@ -436,3 +436,70 @@
 INFO_DSCFG_BATCH_FILE_PATH_256=Path to a batch file containing \
 a set of dsconfig commands to be executed
 
+# Strings for generated reference documentation.
+REF_DSCFG_ALLOW_UNLIMITED_1000=A value of "-1" or "unlimited" for no limit.
+REF_DSCFG_ACI_SYNTAX_REL_URL_1001=<link xlink:show="new" \
+ xlink:href="admin-guide#about-acis" \
+ xlink:role="http://docbook.org/xlink/role/olink" \
+ ><citetitle>About Access Control Instructions</citetitle></link>
+REF_DSCFG_DURATION_SYNTAX_REL_URL_1002=<itemizedlist>                    \
+ <para>                                                                  \
+   Some property values take a time duration.                            \
+   Durations are expressed as numbers followed by units.                 \
+   For example <literal>1 s</literal> means one second,                  \
+   and <literal>2 w</literal> means two weeks.                           \
+   Some durations have minimum granularity or maximum units,             \
+   so you cannot necessary specify every duration                        \
+   in milliseconds or weeks for example.                                 \
+   Some durations allow you to use a special value to mean unlimited.    \
+   Units are specified as follows.                                       \
+ </para>                                                                 \
+                                                                         \
+ <listitem><para><literal>ms</literal>: milliseconds</para></listitem>   \
+ <listitem><para><literal>s</literal>: seconds</para></listitem>         \
+ <listitem><para><literal>m</literal>: minutes</para></listitem>         \
+ <listitem><para><literal>h</literal>: hours</para></listitem>           \
+ <listitem><para><literal>d</literal>: days</para></listitem>            \
+ <listitem><para><literal>w</literal>: weeks</para></listitem>           \
+</itemizedlist>
+REF_DSCFG_ARG_ADDITIONAL_INFO_1003=%s properties depend on the %s type, \
+ which depends on the %s option.
+REF_DSCFG_SUBTYPE_DEPENDENCIES_1004=%s properties depend on the %s type, \
+ which depends on the %s you provide.
+REF_DSCFG_SUBTYPE_TYPES_INTRO_1005=By default, OpenDJ directory server \
+ supports the following %s types:
+REF_DSCFG_CHILD_DEFAULT_1006=Default %s: %s
+REF_DSCFG_CHILD_ENABLED_BY_DEFAULT_1007=Enabled by default: %b
+REF_DSCFG_CHILD_LINK_1008=See %s for the properties of this %s type.
+REF_DSCFG_PROPS_INTRO_1009=%s of type %s have the following properties:
+REF_DSCFG_PROPS_LABEL_DEFAULT_VALUE_1010=Default Value
+REF_DSCFG_PROPS_LABEL_ALLOWED_VALUES_1011=Allowed Values
+REF_DSCFG_PROPS_LABEL_MULTI_VALUED_1012=Multi-valued
+REF_DSCFG_PROPS_LABEL_REQUIRED_1013=Required
+REF_DSCFG_PROPS_LABEL_ADMIN_ACTION_REQUIRED_1014=Admin Action Required
+REF_DSCFG_PROPS_LABEL_ADVANCED_PROPERTY_1015=Advanced Property
+REF_DSCFG_PROPS_LABEL_READ_ONLY_1016=Read-only
+REF_DSCFG_ADMIN_ACTION_COMPONENT_RESTART_1017=The %s must be disabled \
+ and re-enabled for changes to this setting to take effect
+REF_DSCFG_ADMIN_ACTION_SERVER_RESTART_1018=Restart the server
+REF_DSCFG_ADMIN_ACTION_NONE_1019=None
+REF_DSCFG_PROP_YES_1020=Yes
+REF_DSCFG_PROP_YES_ADVANCED_1021=Yes (Use --advanced in interactive mode.)
+REF_DSCFG_PROP_NO_1022=No
+REF_DSCFG_DEFAULT_BEHAVIOR_NONE_1023=None
+REF_DSCFG_DEFAULT_BEHAVIOR_1024=%s
+REF_DSCFG_AGGREGATION_1025=The DN of any %s
+REF_DSCFG_ANY_ATTRIBUTE_1026=The name of an attribute type defined in the server schema
+REF_DSCFG_JAVA_PLUGIN_1027=A Java class that implements or extends the class(es):
+REF_DSCFG_VALID_DN_1028=A valid DN
+REF_DSCFG_DURATION_MAX_UNIT_1029=Maximum unit is "%s"
+REF_DSCFG_DURATION_LOWER_LIMIT_1030=Lower limit is %d %s
+REF_DSCFG_DURATION_UPPER_LIMIT_1031=Upper limit is %d %s
+REF_DSCFG_INT_1032=An integer value
+REF_DSCFG_INT_LOWER_LIMIT_1033=Lower value is %d
+REF_DSCFG_INT_UPPER_LIMIT_1034=Upper value is %d
+REF_DSCFG_INT_UNIT_1035=Unit is %s
+REF_DSCFG_IP_ADDRESS_1036=An IP address
+REF_DSCFG_IP_ADDRESS_MASK_1037=An IP address mask
+REF_DSCFG_STRING_1038=A String
+REF_DSCFG_UNKNOWN_1039=Unknown

--
Gitblit v1.10.0