From 314e6e2709c4b0ebfe509e262ca366874f9069da Mon Sep 17 00:00:00 2001
From: Mark Craig <mark.craig@forgerock.com>
Date: Wed, 11 Mar 2015 11:59:20 +0000
Subject: [PATCH] CR-6316 OPENDJ-1786 Automate integration of generated content

---
 opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java |  311 ++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 220 insertions(+), 91 deletions(-)

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 a55aa2e..2dee2ad 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
@@ -65,7 +65,7 @@
  * file to obtain default values for arguments there if they are not specified
  * on the command-line.
  */
-public class ArgumentParser {
+public class ArgumentParser implements ToolRefDocContainer {
 
     private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
     /**
@@ -617,23 +617,14 @@
      */
     private LocalizableMessage shortToolDescription;
 
-    /**
-     * Gets a short description for this tool, suitable in a man page summary line.
-     *
-     * @return  A short description for this tool,
-     *          suitable in a man page summary line,
-     *          or LocalizableMessage.EMPTY if there is no short description.
-     */
-    LocalizableMessage getShortToolDescription() {
+    /** {@inheritDoc} */
+    @Override
+    public LocalizableMessage getShortToolDescription() {
         return shortToolDescription != null ? shortToolDescription : LocalizableMessage.EMPTY;
     }
 
-    /**
-     * Sets a short description for this tool, suitable in a man page summary line.
-     *
-     * @param   shortDescription    The short description for this tool,
-     *                              suitable in a man page summary line.
-     */
+    /** {@inheritDoc} */
+    @Override
     public void setShortToolDescription(final LocalizableMessage shortDescription) {
         this.shortToolDescription = shortDescription;
     }
@@ -642,29 +633,86 @@
      * A supplement to the description for this tool
      * intended for use in generated reference documentation.
      */
-    private LocalizableMessage docToolDescriptionSupplement;
+    private DocDescriptionSupplement docToolDescriptionSupplement;
 
-    /**
-     * Retrieves a supplement to the description for this tool,
-     * intended for use in generated reference documentation.
-     *
-     * @return A supplement to the description for this tool
-     *         intended for use in generated reference documentation,
-     *         or LocalizableMessage.EMPTY if there is no supplement.
-     */
-    LocalizableMessage getDocToolDescriptionSupplement() {
-        return docToolDescriptionSupplement != null ? docToolDescriptionSupplement : LocalizableMessage.EMPTY;
+    /** {@inheritDoc} */
+    @Override
+    public LocalizableMessage getDocToolDescriptionSupplement() {
+        this.docToolDescriptionSupplement =
+                constructIfNull(this.docToolDescriptionSupplement);
+        return this.docToolDescriptionSupplement.getDocDescriptionSupplement();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setDocToolDescriptionSupplement(final LocalizableMessage supplement) {
+        this.docToolDescriptionSupplement =
+                constructIfNull(this.docToolDescriptionSupplement);
+        this.docToolDescriptionSupplement.setDocDescriptionSupplement(supplement);
     }
 
     /**
-     * Sets a supplement to the description for this tool,
+     * A supplement to the description for all subcommands of this tool,
      * intended for use in generated reference documentation.
-     *
-     * @param docToolDescriptionSupplement  The supplement to the description for this tool
-     *                                      intended for use in generated reference documentation.
      */
-    public void setDocToolDescriptionSupplement(final LocalizableMessage docToolDescriptionSupplement) {
-        this.docToolDescriptionSupplement = docToolDescriptionSupplement;
+    private class DocSubcommandsDescriptionSupplement implements DocDescriptionSupplement {
+        /**
+         * A supplement to the description intended for use in generated reference documentation.
+         */
+        private LocalizableMessage docDescriptionSupplement;
+
+        /** {@inheritDoc} */
+        public LocalizableMessage getDocDescriptionSupplement() {
+            return docDescriptionSupplement != null ? docDescriptionSupplement : LocalizableMessage.EMPTY;
+        }
+
+        /** {@inheritDoc} */
+        public void setDocDescriptionSupplement(final LocalizableMessage docDescriptionSupplement) {
+            this.docDescriptionSupplement = docDescriptionSupplement;
+        }
+    }
+
+    private DocDescriptionSupplement docSubcommandsDescriptionSupplement;
+
+    /** {@inheritDoc} */
+    @Override
+    public LocalizableMessage getDocSubcommandsDescriptionSupplement() {
+        this.docSubcommandsDescriptionSupplement =
+                constructIfNull(this.docSubcommandsDescriptionSupplement);
+        return this.docSubcommandsDescriptionSupplement.getDocDescriptionSupplement();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setDocSubcommandsDescriptionSupplement(final LocalizableMessage supplement) {
+        this.docSubcommandsDescriptionSupplement =
+                constructIfNull(this.docSubcommandsDescriptionSupplement);
+        this.docSubcommandsDescriptionSupplement.setDocDescriptionSupplement(supplement);
+    }
+
+    private DocDescriptionSupplement constructIfNull(DocDescriptionSupplement supplement) {
+        if (supplement != null) {
+            return supplement;
+        }
+        return new DocSubcommandsDescriptionSupplement();
+    }
+
+    /**
+     * Additional paths to DocBook XML {@code RefSect1} documents
+     * to be appended after generated content in reference documentation.
+     */
+    private String[] pathsToTrailingRefSect1s;
+
+    /** {@inheritDoc} */
+    @Override
+    public String[] getPathsToTrailingRefSect1s() {
+        return pathsToTrailingRefSect1s != null ? pathsToTrailingRefSect1s : new String[0];
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void setPathsToTrailingRefSect1s(final String... paths) {
+        this.pathsToTrailingRefSect1s = paths;
     }
 
     /**
@@ -689,7 +737,7 @@
         final StringBuilder buffer = new StringBuilder();
         usageOrVersionDisplayed = true;
         if (System.getProperty("org.forgerock.opendj.gendoc") != null) {
-            toRefEntry(buffer);
+            toRefEntry(buffer, getSynopsisArgs(), argumentList);
         } else {
             getUsage(buffer);
         }
@@ -697,56 +745,128 @@
     }
 
     /**
+     * Return the list of arguments for the generated reference documentation.
+     *
+     * @return  The list of arguments for the generated reference documentation.
+     */
+    String getSynopsisArgs() {
+        if (allowsTrailingArguments()) {
+            if (trailingArgsDisplayName != null) {
+                return trailingArgsDisplayName;
+            } else {
+                return INFO_ARGPARSER_USAGE_TRAILINGARGS.get().toString();
+            }
+        }
+        return null;
+    }
+
+    /**
      * Appends a generated DocBook XML RefEntry (man page) to the StringBuilder.
      *
-     * @param sb    Append the RefEntry element to this.
+     * @param builder       Append the RefEntry element to this.
+     * @param synopsisArgs  List of arguments for the command synopsis.
+     * @param argList       List of (global) arguments for this tool.
      */
-    private void toRefEntry(StringBuilder sb) {
+    void toRefEntry(StringBuilder builder, String synopsisArgs, List<Argument> argList) {
         final String scriptName = getScriptName();
         if (scriptName == null) {
             throw new RuntimeException("The script name should have been set via the environment property '"
                     + PROPERTY_SCRIPT_NAME + "'.");
         }
 
-        // 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("shortDesc", getShortToolDescription());
         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("args", synopsisArgs);
         map.put("description", getToolDescription());
-
-        // If there is a supplement to the description for this utility,
-        // then it is already DocBook XML, so use it as is.
         map.put("info", getDocToolDescriptionSupplement());
-        if (!argumentList.isEmpty()) {
-            List<Map<String, Object>> options = new LinkedList<Map<String, Object>>();
-            for (Argument a : argumentList) {
-                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 it is already DocBook XML, so use it as is.
-                option.put("info", a.getDocDescriptionSupplement());
-                options.add(option);
-            }
-            map.put("options", options);
+        if (!argList.isEmpty()) {
+            map.put("optionSection", getOptionsRefSect1(scriptName));
         }
-        applyTemplate(sb, "refEntry.ftl", map);
+        map.put("subcommands", null);
+        map.put("trailingSections", pathsToXIncludes(getPathsToTrailingRefSect1s()));
+        applyTemplate(builder, "refEntry.ftl", map);
+    }
+
+
+    /**
+     * Returns a generated DocBook XML RefSect1 element for all command options.
+     * @param scriptName    The name of this script.
+     * @return              The RefSect1 element as a String.
+     */
+    protected String getOptionsRefSect1(String scriptName) {
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("name", scriptName);
+        map.put("title", REF_TITLE_OPTIONS.get());
+        map.put("intro", REF_INTRO_OPTIONS.get(scriptName));
+
+        Argument helpArgument = null;
+        final boolean printHeaders = printUsageGroupHeaders();
+        List<Map<String, Object>> groups = new LinkedList<Map<String, Object>>();
+        for (final ArgumentGroup argGroup : argumentGroups) {
+            Map<String, Object> group = new HashMap<String, Object>();
+
+            // Add the group's description if any
+            if (argGroup.containsArguments() && printHeaders) {
+                LocalizableMessage description = argGroup.getDescription();
+                if (description != LocalizableMessage.EMPTY) {
+                    group.put("description", argGroup.getDescription());
+                } else {
+                    group.put("description", INFO_SUBCMDPARSER_WHERE_OPTIONS_INCLUDE.get());
+                }
+            }
+
+            List<Map<String, Object>> options = new LinkedList<Map<String, Object>>();
+            final SortedSet<Argument> args = sortArguments(argGroup.getArguments());
+            for (final Argument a : args) {
+                if (a.isHidden()) {
+                    continue;
+                }
+
+                // The help argument should be added at the end.
+                if (isUsageArgument(a)) {
+                    helpArgument = a;
+                    continue;
+                }
+
+                options.add(getArgumentMap(a));
+            }
+            group.put("options", options);
+            if (!options.isEmpty()) {
+                groups.add(group);
+            }
+        }
+        if (helpArgument != null) {
+            Map<String, Object> helpGroup = new HashMap<String, Object>();
+            helpGroup.put("description", null);
+            List<Map<String, Object>> options = new LinkedList<Map<String, Object>>();
+            options.add(getArgumentMap(helpArgument));
+            helpGroup.put("options", options);
+            groups.add(helpGroup);
+        }
+        map.put("groups", groups);
+
+        StringBuilder sb = new StringBuilder();
+        applyTemplate(sb, "optionsRefSect1.ftl", map);
+        return sb.toString();
+    }
+
+    /**
+     * Returns a map containing information about an argument option.
+     * @param   a   The argument
+     * @return      A map containing information about an argument option
+     */
+    private Map<String, Object> getArgumentMap(final Argument a) {
+        Map<String, Object> option = new HashMap<String, Object>();
+        option.put("synopsis", getOptionSynopsis(a));
+        option.put("description", a.getDescription());
+        String dv = a.getDefaultValue();
+        option.put("default", dv != null ? REF_DEFAULT.get(dv) : null);
+        option.put("info", a.getDocDescriptionSupplement());
+        return option;
     }
 
     /**
@@ -805,33 +925,8 @@
                 }
             }
 
-            final SortedSet<Argument> args = new TreeSet<Argument>(new Comparator<Argument>() {
-
-                /** {@inheritDoc} */
-                @Override
-                public int compare(final Argument o1, final Argument o2) {
-                    final String s1 = getIdentifier(o1);
-                    final String s2 = getIdentifier(o2);
-                    final int res = s1.compareToIgnoreCase(s2);
-                    if (res != 0) {
-                        return res;
-                    }
-                    // Lowercase options first then uppercase.
-                    return -s1.compareTo(s2);
-                }
-
-                private String getIdentifier(final Argument o1) {
-                    if (o1.getShortIdentifier() != null) {
-                        return o1.getShortIdentifier().toString();
-                    }
-                    return o1.getLongIdentifier();
-                }
-
-            });
-            args.addAll(argGroup.getArguments());
-
+            final SortedSet<Argument> args = sortArguments(argGroup.getArguments());
             for (final Argument a : args) {
-                // If this argument is hidden, then skip it.
                 if (a.isHidden()) {
                     continue;
                 }
@@ -854,6 +949,40 @@
     }
 
     /**
+     * Sorts arguments by identifier, lowercase options first then uppercase.
+     *
+     * @param arguments     The arguments to sort.
+     * @return              The set of arguments in sorted order.
+     */
+    SortedSet<Argument> sortArguments(final List<Argument> arguments) {
+        final SortedSet<Argument> result = new TreeSet<Argument>(new Comparator<Argument>() {
+
+            /** {@inheritDoc} */
+            @Override
+            public int compare(final Argument o1, final Argument o2) {
+                final String s1 = getIdentifier(o1);
+                final String s2 = getIdentifier(o2);
+                final int res = s1.compareToIgnoreCase(s2);
+                if (res != 0) {
+                    return res;
+                }
+                // Lowercase options first then uppercase.
+                return -s1.compareTo(s2);
+            }
+
+            private String getIdentifier(final Argument o1) {
+                if (o1.getShortIdentifier() != null) {
+                    return o1.getShortIdentifier().toString();
+                }
+                return o1.getLongIdentifier();
+            }
+
+        });
+        result.addAll(arguments);
+        return result;
+    }
+
+    /**
      * Returns the script name or a Java equivalent command-line string.
      *
      * @return the script name or a Java equivalent command-line string

--
Gitblit v1.10.0