mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Mark Craig
31.18.2015 a24bfd8e6439a14568046dd646f427f1cf095fec
CR-6519 OPENDJ-1899 Generate multiple man pages for dsconfig

This patch splits the dsconfig man page into a dsconfig page
that lists the subcommands, and a page per subcommand.

In the Reference the subcommands pages are included
in their own reference section,
because there's no good way to present the whole list
in the midst of the tools reference.

As a side effect this patch makes it possible to build PDF again
(and to build man pages) without running out of memory.
3 files added
6 files modified
409 ■■■■■ changed files
opendj-cli/src/main/java/com/forgerock/opendj/cli/SubCommandArgumentParser.java 115 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties 2 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/resources/templates/dscfgAppendProps.ftl 4 ●●●● patch | view | raw | blame | history
opendj-cli/src/main/resources/templates/dscfgListItem.ftl 36 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/resources/templates/dscfgReference.ftl 46 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/resources/templates/dscfgSubcommand.ftl 106 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/resources/templates/refSect1.ftl 4 ●●● patch | view | raw | blame | history
opendj-maven-plugin/src/main/java/org/forgerock/opendj/maven/GenerateRefEntriesMojo.java 92 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/docbkx/reference/index.xml 4 ●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/SubCommandArgumentParser.java
@@ -396,7 +396,7 @@
    @Override
    public void setUsageArgument(Argument argument, OutputStream outputStream) {
        super.setUsageArgument(argument, outputStream);
        usageGroupArguments.put(argument, Collections.<SubCommand> emptySet());
        usageGroupArguments.put(argument, Collections.<SubCommand>emptySet());
    }
    /**
@@ -1127,7 +1127,11 @@
    }
    /**
     * Appends a generated DocBook XML RefEntry (man page) to the StringBuilder.
     * Appends one or more generated DocBook XML RefEntry elements (man pages) to the StringBuilder.
     * <br>
     * If the result contains more than one RefEntry,
     * then the RefEntry elements are separated with a marker:
     * {@code @@@scriptName + "-" + subCommand.getName() + @@@}.
     *
     * @param builder       Append the RefEntry element to this.
     * @param subCommands   Collection of subcommands for this tool.
@@ -1155,6 +1159,11 @@
        map.put("subcommands", toRefSect1(scriptName, subCommands));
        map.put("trailingSectionString", System.getProperty("org.forgerock.opendj.gendoc.trailing"));
        applyTemplate(builder, "refEntry.ftl", map);
        // For dsconfig, generate one page per subcommand.
        if (scriptName.equals("dsconfig")) {
            appendSubCommandPages(builder, scriptName, subCommands);
        }
    }
    /**
@@ -1171,10 +1180,17 @@
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("name", scriptName);
        map.put("info", getDocSubcommandsDescriptionSupplement());
        if (scriptName.equals("dsconfig")) {
            // Break dsconfig into multiple pages, so use only the list here.
            map.put("isItemizedList", true);
        }
        List<String> scUsageList = new ArrayList<String>();
        for (SubCommand subCommand : subCommands) {
            scUsageList.add(toRefSect2(scriptName, subCommand));
            if (scriptName.equals("dsconfig")) {
                scUsageList.add(getSubCommandListItem(scriptName, subCommand));
            } else {
                scUsageList.add(toRefSect2(scriptName, subCommand));
            }
        }
        map.put("subcommands", scUsageList);
@@ -1184,6 +1200,22 @@
    }
    /**
     * Returns a DocBook XML ListItem element linking to the subcommand page.
     * @param scriptName    The name of this script.
     * @param subCommand    The SubCommand to reference.
     * @return A DocBook XML ListItem element linking to the subcommand page.
     */
    private String getSubCommandListItem(String scriptName, SubCommand subCommand) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("id", scriptName + "-" + subCommand.getName());
        map.put("name", scriptName + " " + subCommand.getName());
        map.put("description", subCommand.getDescription());
        StringBuilder sb = new StringBuilder();
        applyTemplate(sb, "dscfgListItem.ftl", map);
        return sb.toString();
    }
    /**
     * Returns a generated DocBook XML RefSect2 element for a single subcommand to the StringBuilder.
     *
     * @param scriptName    The name of this script.
@@ -1203,6 +1235,22 @@
        // If there is a supplement to the description for this subcommand,
        // then it is already DocBook XML, so use it as is.
        map.put("info", subCommand.getDocDescriptionSupplement());
        setSubCommandOptionsInfo(map, subCommand);
        StringBuilder sb = new StringBuilder();
        applyTemplate(sb, "refSect2.ftl", map);
        return sb.toString();
    }
    /**
     * Sets information for the subcommand options in the map.
     * <br>
     * The map is expected to be used in a FreeMarker template to generate docs.
     *
     * @param map           The map in which to set the information.
     * @param subCommand    The subcommand containing the information.
     */
    private void setSubCommandOptionsInfo(Map<String, Object> map, SubCommand subCommand) {
        if (!subCommand.getArguments().isEmpty()) {
            List<Map<String, Object>> options = new LinkedList<Map<String, Object>>();
            String nameOption = null;
@@ -1236,9 +1284,62 @@
        if (subCommandUsageHandler != null) {
            map.put("propertiesInfo", subCommandUsageHandler.getProperties(subCommand));
        }
    }
        StringBuilder sb = new StringBuilder();
        applyTemplate(sb, "refSect2.ftl", map);
        return sb.toString();
    /**
     * Appends a generated DocBook XML RefEntry element for each subcommand to the StringBuilder.
     * <br>
     * The RefEntry elements are separated with a marker:
     * {@code @@@scriptName + "-" + subCommand.getName() + @@@}.
     *
     * @param builder       Append the RefEntry elements to this.
     * @param scriptName    The name of the tool with subcommands.
     * @param subCommands   SubCommands containing reference information.
     */
    private void appendSubCommandPages(StringBuilder builder, String scriptName, Collection<SubCommand> subCommands) {
        for (SubCommand subCommand : subCommands) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("marker", "@@@" + scriptName + "-" + subCommand.getName() + "@@@");
            map.put("locale", Locale.getDefault().getLanguage());
            map.put("year", new SimpleDateFormat("yyyy").format(new Date()));
            map.put("id", scriptName + "-" + subCommand.getName());
            map.put("name", scriptName + " " + subCommand.getName());
            map.put("purpose", subCommand.getDescription());
            map.put("args", INFO_SUBCMDPARSER_OPTIONS.get());
            map.put("descTitle", REF_TITLE_DESCRIPTION.get());
            map.put("description", subCommand.getDescription());
            map.put("info", subCommand.getDocDescriptionSupplement());
            map.put("optionsTitle", REF_TITLE_OPTIONS.get());
            map.put("optionsIntro", REF_INTRO_OPTIONS.get(scriptName + " " + subCommand.getName()));
            setSubCommandOptionsInfo(map, subCommand);
            applyTemplate(builder, "dscfgSubcommand.ftl", map);
        }
        appendSubCommandReference(builder, scriptName, subCommands);
    }
    /**
     * Appends a generated DocBook XML Reference element XIncluding subcommands.
     *
     * @param builder       Append the Reference element to this.
     * @param scriptName    The name of the tool with subcommands.
     * @param subCommands   SubCommands containing reference information.
     */
    private void appendSubCommandReference(StringBuilder builder,
                                           String scriptName,
                                           Collection<SubCommand> subCommands) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("marker", "@@@" + scriptName + "-subcommands-ref" + "@@@");
        map.put("name", scriptName);
        map.put("locale", Locale.getDefault().getLanguage());
        map.put("title", REF_PART_TITLE_SUBCOMMANDS.get(scriptName));
        map.put("partintro", REF_PART_INTRO_SUBCOMMANDS.get(scriptName));
        List<Map<String, Object>> commands = new LinkedList<Map<String, Object>>();
        for (SubCommand subCommand : subCommands) {
            Map<String, Object> scMap = new HashMap<String, Object>();
            scMap.put("id", scriptName + "-" + subCommand.getName());
            commands.add(scMap);
        }
        map.put("subcommands", commands);
        applyTemplate(builder, "dscfgReference.ftl", map);
    }
}
opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties
@@ -976,6 +976,8 @@
REF_INTRO_OPTIONS=The <command>%s</command> command takes the following options:
REF_DEFAULT=Default: %s
REF_TITLE_SUBCOMMANDS=Subcommands
REF_PART_TITLE_SUBCOMMANDS=%s Subcommands Reference
REF_PART_INTRO_SUBCOMMANDS=This section covers <command>%s</command> subcommands.
REF_SHORT_DESC_UNINSTALL=remove OpenDJ directory server software
# Supplements to descriptions for generated reference documentation.
opendj-cli/src/main/resources/templates/dscfgAppendProps.ftl
@@ -24,7 +24,7 @@
 #      Copyright 2015 ForgeRock AS.
 #
 #-->
<refsect3 xml:id="${id}">
<refsect1 xml:id="${id}">
  <title>${title}</title>
  <para>
@@ -32,4 +32,4 @@
  </para>
  ${list}
</refsect3>
</refsect1>
opendj-cli/src/main/resources/templates/dscfgListItem.ftl
New file
@@ -0,0 +1,36 @@
<#--
 # 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.
 #
 #-->
<listitem>
 <para>
  <link
   <#-- Link to the Reference. Change this if the pages move to another document. -->
   xlink:href="reference#${id}"
   xlink:role="http://docbook.org/xlink/role/olink"
   xlink:show="new"
  ><command>${name}</command></link>: ${description}
 </para>
</listitem>
opendj-cli/src/main/resources/templates/dscfgReference.ftl
New file
@@ -0,0 +1,46 @@
<#--
 # 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.
 #
 #-->
${marker}
<reference xml:id="${name}-subcommands-ref"
           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:xinclude="http://www.w3.org/2001/XInclude">
 <title>${title}</title>
 <partintro>
  <para>
   ${partintro}
  </para>
 </partintro>
 <#list subcommands as subcommand>
 <xinclude:include href="../man-pages/man-${subcommand.id}.xml" />
 </#list>
</reference>
opendj-cli/src/main/resources/templates/dscfgSubcommand.ftl
New file
@@ -0,0 +1,106 @@
${marker}
<?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="${id}"
          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>${purpose}</refpurpose>
 </refnamediv>
 <refsynopsisdiv>
  <cmdsynopsis>
   <command>${name}</command>
   <arg choice="plain">${args}</arg>
  </cmdsynopsis>
 </refsynopsisdiv>
 <refsect1 xml:id="${id}-description">
  <title>${descTitle}</title>
  <para>
   ${description}
  </para>
  <#if info??>${info}</#if>
 </refsect1>
 <#if options??>
 <refsect1 xml:id="${id}-options">
  <title>${optionsTitle}</title>
  <variablelist>
   <para>
    ${optionsIntro}
   </para>
   <#list options as option>
   <varlistentry>
    <term><option>${option.synopsis?xml}</option></term>
    <listitem>
     <para>
      ${option.description}
     </para>
     <#if option.info??>
       <#if option.info.usage??>${option.info.usage}</#if>
       <#if option.info.default??>
       <para>
        ${option.info.default}
       </para>
       </#if>
       <#if option.info.doc??>${option.info.doc}</#if>
     </#if>
    </listitem>
   </varlistentry>
   </#list>
  </variablelist>
 </refsect1>
 </#if>
 <#if propertiesInfo??>${propertiesInfo}</#if>
</refentry>
opendj-cli/src/main/resources/templates/refSect1.ftl
@@ -35,7 +35,9 @@
   The <command>${name}</command> utility supports the following subcommands.
  </para>
  <#if isItemizedList??><itemizedlist></#if>
  <#list subcommands as subcommand>
    ${subcommand}
   ${subcommand}
  </#list>
  <#if isItemizedList??></itemizedlist></#if>
</refsect1>
opendj-maven-plugin/src/main/java/org/forgerock/opendj/maven/GenerateRefEntriesMojo.java
@@ -34,9 +34,14 @@
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.forgerock.util.Utils;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
@@ -45,10 +50,14 @@
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * Generate DocBook RefEntry source documents for command-line tools man pages.
@@ -165,6 +174,14 @@
        } catch (IOException e) {
            throw new MojoExecutionException(toolClass + " not found", e);
        }
        if (tool.getName().equals("dsconfig")) {
            try {
                splitPage(manPage);
            } catch (IOException e) {
                throw new MojoExecutionException("Failed to split "  + manPage.getName(), e);
            }
        }
    }
    /**
@@ -287,9 +304,80 @@
                writer.write(EOL);
            }
        } finally {
            if (writer != null) {
                writer.close();
            Utils.closeSilently(writer);
        }
    }
    /**
     * Splits the content of a single man page into multiple pages.
     * <br>
     * RefEntry elements must be separated with a marker:
     * {@code @@@scriptName + "-" + subCommand.getName() + @@@}.
     *
     * @param page          The page to split.
     * @throws IOException  Failed to split the page.
     */
    private void splitPage(final File page) throws IOException {
        // Read from a copy of the page.
        final File pageCopy = new File(page.getPath() + ".tmp");
        copyFile(page, pageCopy);
        final BufferedReader reader = new BufferedReader(new FileReader(pageCopy));
        try {
            // Write first to the page, then to pages named according to marker values.
            File output = page;
            getLog().info("Rewriting man page: " + page.getPath());
            final Pattern marker = Pattern.compile("@@@(.+?)@@@");
            final StringBuilder builder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                final Matcher matcher = marker.matcher(line);
                if (matcher.find()) {
                    writeToFile(builder.toString(), output);
                    builder.setLength(0);
                    output = new File(page.getParentFile(), "man-" + matcher.group(1) + ".xml");
                    getLog().info("Writing man page: " + output.getPath());
                } else {
                    builder.append(line).append(System.getProperty("line.separator"));
                }
            }
            writeToFile(builder.toString(), output);
            if (!pageCopy.delete()) {
                throw new IOException("Failed to delete " +  pageCopy.getName());
            }
        } finally {
            Utils.closeSilently(reader);
        }
    }
    /**
     * Writes the content of the input to the output file.
     * @param input         The UTF-8 input to write.
     * @param output        The file to write it to.
     * @throws IOException  Failed to write the content of the input.
     */
    private void writeToFile(final String input, final File output) throws IOException {
        InputStream is = new ByteArrayInputStream(input.getBytes(Charset.forName("UTF-8")));
        writeToFile(is, output);
    }
    /**
     * Copies the content of the original file to the copy.
     * @param original      The original file.
     * @param copy          The copy.
     * @throws IOException  Failed to make the copy.
     */
    private void copyFile(File original, File copy) throws IOException {
        if (!copy.exists() && !copy.createNewFile()) {
            throw new IOException("Failed to create " + copy);
        }
        FileChannel in  = null;
        FileChannel out = null;
        try {
            in  = new FileInputStream(original).getChannel();
            out = new FileOutputStream(copy).getChannel();
            out.transferFrom(in, 0, in.size());
        } finally {
            Utils.closeSilently(in, out);
        }
    }
}
opendj-server-legacy/src/main/docbkx/reference/index.xml
@@ -187,6 +187,10 @@
  <xinclude:include href='../man-pages/man-windows-service.xml' />
 </reference>
 <xinclude:include href="../man-pages/man-dsconfig-subcommands-ref.xml">
  <xinclude:fallback><!-- Failed to include page --></xinclude:fallback>
 </xinclude:include>
 <xinclude:include href="../shared/glossary.xml" />
 <xinclude:include href="appendix-rest2ldap.xml" />