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

matthew_swift
31.48.2007 bedc84d04ab003c2dfec1119e95478c59599c043
Fix issue 1991: dsconfig: split "component" field in list-properties

Make the list-properties sub-command more consistent with the rest of dsconfig. Rather than display the full component name in the left hand column, dsconfig now splits the name into two columns: the first being the generic component name and the second containing the sub-type name. For example, previously an LDAP connection handler would have the component name "ldap-connection-handler". Now the name is split in two using the generic name "connection-handler" and the sub-type "ldap". This is consistent with the create-xxx sub-commands, where the generic name is typically the sub-command suffix (e.g. create-connection-handler) and the sub-type is the value of the -t flag (e.g. ldap).

To make the sub-command more usable, it now has the following three additional options:

-c, --category {CATEGORY}
The category of components whose properties should be described
-t, --type {TYPE}
The type of components whose properties should be described. The value for
TYPE must be one of the component types associated with the CATEGORY
specified using the "--category" option
--inherited
Modifies the display output to show the inherited properties of components

The CATEGORY can be either a generic component name (e.g. backend or connection-handler), or it can be one of the --help-xxx categories (e.g. logging). The TYPE, if specified, must be a sub-type associated with the specified CATEGORY (e.g. ldap or jmx for connection handlers). The option "inherited" forces the sub-command to display all the properties associated with each component type, including inherited properties.

All the options are optional (e.g. the -t option does not require the -c option).
4 files modified
559 ■■■■ changed files
opends/src/server/org/opends/server/messages/ToolMessages.java 81 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/ArgumentExceptionFactory.java 53 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/HelpSubCommandHandler.java 423 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/ListSubCommandHandler.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/ToolMessages.java
@@ -7299,9 +7299,9 @@
  /**
   * The message ID for the message that will be used as the
   * heading of the managed object type column in tables.
   * heading of the component category column in tables.
   */
  public static final int MSGID_DSCFG_HEADING_MANAGED_OBJECT_NAME =
  public static final int MSGID_DSCFG_HEADING_COMPONENT_NAME =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1018;
@@ -7912,7 +7912,7 @@
   * The message ID for the message that will be used as the
   * heading of the managed object type column in list tables.
   */
  public static final int MSGID_DSCFG_HEADING_MANAGED_OBJECT_TYPE =
  public static final int MSGID_DSCFG_HEADING_COMPONENT_TYPE =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1082;
@@ -9553,7 +9553,7 @@
  /**
   * The message ID for the message which will be used in dsconfig
   * help for the component heading.
   * help for the component name heading.
   */
  public static final int MSGID_DSCFG_HELP_HEADING_COMPONENT =
    CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1264;
@@ -9705,6 +9705,49 @@
  public static final int MSGID_DSCFG_CONFIRM_MODIFY_FAIL =
    CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1285;
  /**
   * The message ID for the message that will be used as the
   * description of the list-properties category argument. This does
   * not take any arguments.
   */
  public static final int MSGID_DSCFG_DESCRIPTION_HELP_CATEGORY =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1286;
  /**
   * The message ID for the message that will be used if the user
   * requests help for a component category but specifies an unknown
   * category name. This takes a single argument which is the invalid
   * argument.
   */
  public static final int MSGID_DSCFG_ERROR_CATEGORY_UNRECOGNIZED =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1287;
  /**
   * The message ID for the message that will be used if the user
   * requests help for a component category and sub-type but specifies
   * an unknown sub-type name. This takes two arguments which are the
   * invalid type argument and the category.
   */
  public static final int MSGID_DSCFG_ERROR_CATEGORY_TYPE_UNRECOGNIZED =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1288;
  /**
   * The message ID for the message that will be used if the user
   * attempts to access a property which is not part of any managed
   * object. This takes a single argument which is the name of the
   * invalid property.
   */
  public static final int MSGID_DSCFG_ERROR_PROPERTY_UNRECOGNIZED_NO_DEFN =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_SEVERE_ERROR | 1289;
  /**
   * The message ID for the message that will be used as the
   * description of the list-properties category argument. This does
   * not take any arguments.
   */
  public static final int MSGID_DSCFG_DESCRIPTION_HELP_INHERITED =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 1290;
  /**
@@ -12035,9 +12078,19 @@
                    "value of the \"%s\" property: %s");
    registerMessage(MSGID_DSCFG_DESCRIPTION_HELP_TYPE,
                    "The type(s) of components whose properties should be " +
                    "The type of components whose properties should be " +
                    "described. The value for TYPE must be one of the " +
                    "component types associated with the CATEGORY specified " +
                    "using the \"--category\" option");
    registerMessage(MSGID_DSCFG_DESCRIPTION_HELP_CATEGORY,
                    "The category of components whose properties should be " +
                    "described");
    registerMessage(MSGID_DSCFG_DESCRIPTION_HELP_INHERITED,
                    "Modifies the display output to show the inherited " +
                    "properties of components");
    registerMessage(MSGID_DSCFG_DESCRIPTION_TYPE,
                    "The type of %s which should be created. The value " +
                    "for TYPE can be one of: %s");
@@ -12119,6 +12172,9 @@
    registerMessage(MSGID_DSCFG_ERROR_PROPERTY_UNRECOGNIZED,
                    "The property \"%s\" is not a recognized property of %s");
    registerMessage(MSGID_DSCFG_ERROR_PROPERTY_UNRECOGNIZED_NO_DEFN,
                    "The property \"%s\" is not a recognized property");
    registerMessage(MSGID_DSCFG_ERROR_PROPERTY_INVALID_VALUE,
                    "The value \"%s\" is not a valid value for the %s " +
                    "property \"%s\" which has the following syntax: %s");
@@ -12168,10 +12224,10 @@
    registerMessage(MSGID_DSCFG_ERROR_ILLEGAL_NAME_UNKNOWN,
                    "The name \"%s\" is not a valid name for the %s");
    registerMessage(MSGID_DSCFG_HEADING_MANAGED_OBJECT_NAME,
    registerMessage(MSGID_DSCFG_HEADING_COMPONENT_NAME,
                    "Component");
    registerMessage(MSGID_DSCFG_HEADING_MANAGED_OBJECT_TYPE,
    registerMessage(MSGID_DSCFG_HEADING_COMPONENT_TYPE,
                    "Type");
    registerMessage(MSGID_DSCFG_HEADING_PROPERTY_NAME,
@@ -12386,7 +12442,14 @@
                    "It should be one of: %s");
    registerMessage(MSGID_DSCFG_ERROR_TYPE_UNRECOGNIZED,
                    "\"%s\" is not a recognized type of managed object");
                    "\"%s\" is not a recognized component type");
    registerMessage(MSGID_DSCFG_ERROR_CATEGORY_UNRECOGNIZED,
                    "\"%s\" is not a recognized component category");
    registerMessage(MSGID_DSCFG_ERROR_CATEGORY_TYPE_UNRECOGNIZED,
                    "\"%s\" is not a recognized component type in " +
                    "category \"%s\"");
    registerMessage(MSGID_DSCFG_ERROR_WRONG_MANAGED_OBJECT_TYPE,
                    "The %s could not be retrieved because it was the " +
@@ -12789,7 +12852,7 @@
    registerMessage(MSGID_DSCFG_HELP_HEADING_PROPERTY,
        "Property: %s");
    registerMessage(MSGID_DSCFG_HELP_HEADING_COMPONENT,
        "Component: %s");
        "Component name: %s");
    registerMessage(MSGID_DSCFG_HELP_HEADING_DEFAULT,
        "Default behavior");
    registerMessage(MSGID_DSCFG_HELP_HEADING_MANDATORY,
opends/src/server/org/opends/server/tools/dsconfig/ArgumentExceptionFactory.java
@@ -421,6 +421,22 @@
  /**
   * Creates an argument exception which should be used when a
   * component category argument is not recognized.
   *
   * @param categoryName
   *          The unrecognized component category.
   * @return Returns an argument exception.
   */
  public static ArgumentException unknownCategory(String categoryName) {
    int msgID = MSGID_DSCFG_ERROR_CATEGORY_UNRECOGNIZED;
    String msg = getMessage(msgID, categoryName);
    return new ArgumentException(msgID, msg);
  }
  /**
   * Creates an argument exception which should be used when a
   * property name is not recognized.
   *
   * @param d
@@ -440,6 +456,22 @@
  /**
   * Creates an argument exception which should be used when a
   * property name is not recognized.
   *
   * @param name
   *          The unrecognized property name.
   * @return Returns an argument exception.
   */
  public static ArgumentException unknownProperty(String name) {
    int msgID = MSGID_DSCFG_ERROR_PROPERTY_UNRECOGNIZED_NO_DEFN;
    String message = getMessage(msgID, name);
    return new ArgumentException(msgID, message);
  }
  /**
   * Creates an argument exception which should be used when a
   * sub-type argument in a create-xxx sub-command is not recognized.
   *
   * @param r
@@ -465,7 +497,7 @@
   * object type argument is not recognized.
   *
   * @param typeName
   *          The unrecognized property sub-type.
   *          The unrecognized component type.
   * @return Returns an argument exception.
   */
  public static ArgumentException unknownType(String typeName) {
@@ -478,6 +510,25 @@
  /**
   * Creates an argument exception which should be used when a managed
   * object type argument is not associated with a category.
   *
   * @param categoryName
   *          The component category.
   * @param typeName
   *          The unrecognized component type.
   * @return Returns an argument exception.
   */
  public static ArgumentException unknownTypeInCategory(String categoryName,
      String typeName) {
    int msgID = MSGID_DSCFG_ERROR_CATEGORY_TYPE_UNRECOGNIZED;
    String msg = getMessage(msgID, typeName, categoryName);
    return new ArgumentException(msgID, msg);
  }
  /**
   * Creates an argument exception which should be used when a managed
   * object is retrieved but does not have the correct type
   * appropriate for the associated sub-command.
   *
opends/src/server/org/opends/server/tools/dsconfig/HelpSubCommandHandler.java
@@ -35,9 +35,10 @@
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
@@ -56,10 +57,12 @@
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
import org.opends.server.admin.StringPropertyDefinition;
import org.opends.server.admin.Tag;
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.admin.UnknownPropertyDefinitionException;
import org.opends.server.tools.ClientException;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.args.BooleanArgument;
import org.opends.server.util.args.StringArgument;
import org.opends.server.util.args.SubCommand;
import org.opends.server.util.args.SubCommandArgumentParser;
@@ -351,6 +354,11 @@
    }
  }
  /**
   * The type component name to be used for top-level definitions.
   */
  private static final String GENERIC_TYPE = "generic";
  // Strings used in property help.
  private final static String HEADING_SEPARATOR = " : ";
@@ -358,17 +366,35 @@
  private final static int HEADING_WIDTH;
  /**
   * The value for the long option inherited.
   */
  private static final String OPTION_DSCFG_LONG_INHERITED = "inherited";
  /**
   * The value for the short option inherited.
   */
  private static final Character OPTION_DSCFG_SHORT_INHERITED = null;
  /**
   * The value for the long option type.
   */
  private static final String OPTION_DSCFG_LONG_TYPE = "type";
  /**
   * The value for the short option type.
   */
  private static final Character OPTION_DSCFG_SHORT_TYPE = 't';
  /**
   * The value for the long option category.
   */
  private static final String OPTION_DSCFG_LONG_CATEGORY = "category";
  /**
   * The value for the short option category.
   */
  private static final Character OPTION_DSCFG_SHORT_CATEGORY = 'c';
  static {
    int tmp = getMessage(MSGID_DSCFG_HELP_HEADING_SYNTAX).length();
    tmp = Math.max(tmp, getMessage(MSGID_DSCFG_HELP_HEADING_DEFAULT).length());
@@ -381,6 +407,8 @@
    HEADING_WIDTH = tmp;
  }
  /**
   * Creates a new help-properties sub-command.
   *
@@ -513,17 +541,31 @@
    }
  }
  // The sub-command associated with this handler.
  private final SubCommand subCommand;
  // The argument which should be used to specify the type of managed
  // object to be retrieved.
  // The argument which should be used to specify the category of
  // managed object to be retrieved.
  private final StringArgument categoryArgument;
  //The argument which should be used to display inherited properties.
  private BooleanArgument inheritedModeArgument;
  // The argument which should be used to specify the sub-type of
  // managed object to be retrieved.
  private final StringArgument typeArgument;
  // A table listing all the available types of managed object.
  private final Map<String, AbstractManagedObjectDefinition<?, ?>> types;
  // A table listing all the available types of managed object indexed
  // on their parent type.
  private final Map<String,
    Map<String, AbstractManagedObjectDefinition<?, ?>>> categoryMap;
  // A table listing all the available types of managed object indexed
  // on their tag(s).
  private final Map<Tag,
    Map<String, AbstractManagedObjectDefinition<?, ?>>> tagMap;
  // Private constructor.
  private HelpSubCommandHandler(ConsoleApplication app,
@@ -536,17 +578,30 @@
    this.subCommand = new SubCommand(parser, name, false, 0, 0, null,
        descriptionID);
    this.categoryArgument = new StringArgument(OPTION_DSCFG_LONG_CATEGORY,
        OPTION_DSCFG_SHORT_CATEGORY, OPTION_DSCFG_LONG_CATEGORY, false, false,
        true, "{CATEGORY}", null, null, MSGID_DSCFG_DESCRIPTION_HELP_CATEGORY);
    this.subCommand.addArgument(this.categoryArgument);
    this.typeArgument = new StringArgument(OPTION_DSCFG_LONG_TYPE,
        OPTION_DSCFG_SHORT_TYPE, OPTION_DSCFG_LONG_TYPE, false, false, true,
        "{TYPE}", null, null, MSGID_DSCFG_DESCRIPTION_HELP_TYPE);
    this.subCommand.addArgument(this.typeArgument);
    this.inheritedModeArgument = new BooleanArgument(
        OPTION_DSCFG_LONG_INHERITED, OPTION_DSCFG_SHORT_INHERITED,
        OPTION_DSCFG_LONG_INHERITED, MSGID_DSCFG_DESCRIPTION_HELP_INHERITED);
    subCommand.addArgument(inheritedModeArgument);
    // Register common arguments.
    registerAdvancedModeArgument(this.subCommand,
        MSGID_DSCFG_DESCRIPTION_ADVANCED_HELP);
    registerPropertyNameArgument(this.subCommand);
    this.types = new TreeMap<String, AbstractManagedObjectDefinition<?, ?>>();
    this.categoryMap = new TreeMap<String,
      Map<String, AbstractManagedObjectDefinition<?, ?>>>();
    this.tagMap = new HashMap<Tag,
      Map<String, AbstractManagedObjectDefinition<?, ?>>>();
  }
@@ -570,7 +625,47 @@
   */
  public void registerManagedObjectDefinition(
      AbstractManagedObjectDefinition<?, ?> d) {
    types.put(d.getName(), d);
    // Determine the definition's base name.
    AbstractManagedObjectDefinition<?, ?> parent = d;
    while (parent.getParent() != null) {
      parent = parent.getParent();
    }
    String baseName = parent.getName();
    String typeName = null;
    if (parent == d) {
      // This was a top-level definition.
      typeName = GENERIC_TYPE;
    } else {
      // For the type name we shorten it, if possible, by stripping
      // off the trailing part of the name which matches the
      // base-type.
      String suffix = "-" + baseName;
      typeName = d.getName();
      if (typeName.endsWith(suffix)) {
        typeName = typeName.substring(0, typeName.length() - suffix.length());
      }
    }
    // Get the sub-type mapping, creating it if necessary.
    Map<String, AbstractManagedObjectDefinition<?, ?>> subTypes = categoryMap
        .get(baseName);
    if (subTypes == null) {
      subTypes = new TreeMap<String, AbstractManagedObjectDefinition<?, ?>>();
      categoryMap.put(baseName, subTypes);
    }
    subTypes.put(typeName, d);
    // Get the tag mapping, creating it if necessary.
    for (Tag tag : d.getAllTags()) {
      subTypes = tagMap.get(baseName);
      if (subTypes == null) {
        subTypes = new TreeMap<String, AbstractManagedObjectDefinition<?, ?>>();
        tagMap.put(tag, subTypes);
      }
      subTypes.put(typeName, d);
    }
  }
@@ -579,53 +674,118 @@
   * {@inheritDoc}
   */
  @Override
  public int run()
      throws ArgumentException, ClientException {
  public int run() throws ArgumentException, ClientException {
    String categoryName = categoryArgument.getValue();
    String typeName = typeArgument.getValue();
    Tag tag = null;
    Set<String> propertyNames = getPropertyNames();
    AbstractManagedObjectDefinition<?, ?> d = null;
    if (typeName != null) {
      // Requested help regarding a single managed object type.
      d = types.get(typeName);
      if (d == null) {
    List<AbstractManagedObjectDefinition<?, ?>> dlist =
      new LinkedList<AbstractManagedObjectDefinition<?, ?>>();
    AbstractManagedObjectDefinition<?, ?> tmp = null;
    if (categoryName != null) {
      // User requested a category of components.
      Map<String, AbstractManagedObjectDefinition<?, ?>> subTypes = categoryMap
          .get(categoryName);
      if (subTypes == null) {
        // Try a tag-base look-up.
        try {
          tag = Tag.valueOf(categoryName);
        } catch (IllegalArgumentException e) {
          throw ArgumentExceptionFactory.unknownCategory(categoryName);
        }
        categoryName = null;
        subTypes = tagMap.get(tag);
        if (subTypes == null) {
          throw ArgumentExceptionFactory.unknownCategory(categoryName);
        }
      } else {
        // Cache the generic definition for improved errors later on.
        tmp = subTypes.get(GENERIC_TYPE);
      }
      if (typeName != null) {
        AbstractManagedObjectDefinition<?, ?> d = subTypes.get(typeName);
        if (d == null) {
          throw ArgumentExceptionFactory.unknownTypeInCategory(categoryName,
              typeName);
        }
        dlist.add(d);
        // Cache the generic definition for improved errors later on.
        tmp = d;
      } else {
        dlist.addAll(subTypes.values());
      }
    } else if (typeName != null) {
      // User requested just the sub-type which could appear in
      // multiple categories.
      boolean isFound = false;
      for (Map<String, AbstractManagedObjectDefinition<?, ?>> subTypes :
        categoryMap.values()) {
        AbstractManagedObjectDefinition<?, ?> d = subTypes.get(typeName);
        if (d != null) {
          dlist.add(d);
          isFound = true;
        }
      }
      if (!isFound) {
        throw ArgumentExceptionFactory.unknownType(typeName);
      }
    } else {
      // User did not specify a category nor a sub-type.
      for (Map<String, AbstractManagedObjectDefinition<?, ?>> subTypes :
        categoryMap.values()) {
        dlist.addAll(subTypes.values());
      }
    }
    // Validate property names if the type was specified.
    if (d != null) {
      for (String propertyName : propertyNames) {
    // Validate property names.
    if (dlist.size() == 1) {
      // Cache the generic definition for improved errors later on.
      tmp = dlist.get(0);
    }
    for (String propertyName : propertyNames) {
      boolean isFound = false;
      for (AbstractManagedObjectDefinition<?, ?> d : dlist) {
        try {
          d.getPropertyDefinition(propertyName);
          isFound = true;
        } catch (IllegalArgumentException e) {
          throw ArgumentExceptionFactory.unknownProperty(d, propertyName);
          // Ignore for now.
        }
      }
      if (!isFound) {
        if (tmp != null) {
          throw ArgumentExceptionFactory.unknownProperty(tmp, propertyName);
        } else {
          throw ArgumentExceptionFactory.unknownProperty(propertyName);
        }
      }
    }
    // Determine the set of managed objects to be displayed.
    Collection<AbstractManagedObjectDefinition<?, ?>> defns;
    if (d == null) {
      defns = types.values();
    if (!getConsoleApplication().isVerbose()) {
      displayNonVerbose(categoryName, typeName, tag, propertyNames);
    } else {
      defns = Collections.<AbstractManagedObjectDefinition<?, ?>> singleton(d);
      displayVerbose(categoryName, typeName, tag, propertyNames);
    }
    if (!getConsoleApplication().isVerbose()) {
      displayNonVerbose(defns, propertyNames);
    } else {
      displayVerbose(defns, propertyNames);
    }
    return 0;
  }
  // Output property summary table.
  private void displayNonVerbose(
      Collection<AbstractManagedObjectDefinition<?, ?>> defns,
      Set<String> propertyNames) {
  private void displayNonVerbose(String categoryName, String typeName,
      Tag tag, Set<String> propertyNames) {
    PrintStream out = getConsoleApplication().getOutputStream();
    if (!getConsoleApplication().isScriptFriendly()) {
      out.println(getMessage(MSGID_DSCFG_HELP_DESCRIPTION_OPTION));
@@ -647,7 +807,8 @@
    // Headings.
    TableBuilder builder = new TableBuilder();
    builder.appendHeading(getMessage(MSGID_DSCFG_HEADING_MANAGED_OBJECT_NAME));
    builder.appendHeading(getMessage(MSGID_DSCFG_HEADING_COMPONENT_NAME));
    builder.appendHeading(getMessage(MSGID_DSCFG_HEADING_COMPONENT_TYPE));
    builder.appendHeading(getMessage(MSGID_DSCFG_HEADING_PROPERTY_NAME));
    builder.appendHeading(getMessage(MSGID_DSCFG_HEADING_PROPERTY_OPTIONS));
    builder.appendHeading(getMessage(MSGID_DSCFG_HEADING_PROPERTY_SYNTAX));
@@ -655,45 +816,78 @@
    // Sort keys.
    builder.addSortKey(0);
    builder.addSortKey(1);
    builder.addSortKey(2);
    // Generate the table content.
    for (AbstractManagedObjectDefinition<?, ?> mod : defns) {
      Collection<PropertyDefinition<?>> pds;
      if (getConsoleApplication().isScriptFriendly()) {
        pds = mod.getAllPropertyDefinitions();
      } else {
        pds = mod.getPropertyDefinitions();
    for (String category : categoryMap.keySet()) {
      // Skip if this is the wrong category.
      if (categoryName != null && !categoryName.equals(category)) {
        continue;
      }
      for (PropertyDefinition<?> pd : pds) {
        if (pd.hasOption(PropertyOption.HIDDEN)) {
      // Process the sub-types.
      Map<String, AbstractManagedObjectDefinition<?, ?>> subTypes = categoryMap
          .get(category);
      for (String type : subTypes.keySet()) {
        // Skip if this is the wrong sub-type.
        if (typeName != null && !typeName.equals(type)) {
          continue;
        }
        if (!isAdvancedMode() && pd.hasOption(PropertyOption.ADVANCED)) {
        // Display help for each property.
        AbstractManagedObjectDefinition<?, ?> mod = subTypes.get(type);
        // Skip if this does not have the required tag.
        if (tag != null && !mod.hasTag(tag)) {
          continue;
        }
        if (!propertyNames.isEmpty() && !propertyNames.contains(pd.getName())) {
          continue;
        Set<PropertyDefinition<?>> pds = new TreeSet<PropertyDefinition<?>>();
        if (inheritedModeArgument.isPresent()) {
          pds.addAll(mod.getAllPropertyDefinitions());
        } else {
          pds.addAll(mod.getPropertyDefinitions());
          // The list will still contain overridden properties.
          if (mod.getParent() != null) {
            pds.removeAll(mod.getParent().getAllPropertyDefinitions());
          }
        }
        // Display the property.
        builder.startRow();
        for (PropertyDefinition<?> pd : pds) {
          if (pd.hasOption(PropertyOption.HIDDEN)) {
            continue;
          }
        // Display the managed object type if necessary.
        builder.appendCell(mod.getName());
          if (!isAdvancedMode() && pd.hasOption(PropertyOption.ADVANCED)) {
            continue;
          }
        // Display the property name.
        builder.appendCell(pd.getName());
          if (!propertyNames.isEmpty() &&
              !propertyNames.contains(pd.getName())) {
            continue;
          }
        // Display the options.
        builder.appendCell(getPropertyOptionSummary(pd));
          // Display the property.
          builder.startRow();
        // Display the syntax.
        PropertyDefinitionUsageBuilder v =
          new PropertyDefinitionUsageBuilder(false);
        builder.appendCell(v.getUsage(pd));
          // Display the component category.
          builder.appendCell(category);
          // Display the component type.
          builder.appendCell(type);
          // Display the property name.
          builder.appendCell(pd.getName());
          // Display the options.
          builder.appendCell(getPropertyOptionSummary(pd));
          // Display the syntax.
          PropertyDefinitionUsageBuilder v = new PropertyDefinitionUsageBuilder(
              false);
          builder.appendCell(v.getUsage(pd));
        }
      }
    }
@@ -709,9 +903,8 @@
  // Display detailed help on managed objects and their properties.
  private void displayVerbose(
      Collection<AbstractManagedObjectDefinition<?, ?>> defns,
      Set<String> propertyNames) {
  private void displayVerbose(String categoryName, String typeName,
      Tag tag, Set<String> propertyNames) {
    PrintStream out = getConsoleApplication().getOutputStream();
    // Construct line used to separate consecutive sections.
@@ -722,54 +915,88 @@
    // Display help for each managed object.
    boolean isFirstManagedObject = true;
    for (AbstractManagedObjectDefinition<?, ?> mod : defns) {
      // Display help for each property.
      Set<PropertyDefinition<?>> pds =
        new TreeSet<PropertyDefinition<?>>(mod.getAllPropertyDefinitions());
      boolean isFirstProperty = true;
      for (PropertyDefinition<?> pd : pds) {
        if (pd.hasOption(PropertyOption.HIDDEN)) {
    for (String category : categoryMap.keySet()) {
      // Skip if this is the wrong category.
      if (categoryName != null && !categoryName.equals(category)) {
        continue;
      }
      // Process the sub-types.
      Map<String, AbstractManagedObjectDefinition<?, ?>> subTypes = categoryMap
          .get(category);
      for (String type : subTypes.keySet()) {
        // Skip if this is the wrong sub-type.
        if (typeName != null && !typeName.equals(type)) {
          continue;
        }
        if (!isAdvancedMode() && pd.hasOption(PropertyOption.ADVANCED)) {
        // Display help for each property.
        AbstractManagedObjectDefinition<?, ?> mod = subTypes.get(type);
        // Skip if this does not have the required tag.
        if (tag != null && !mod.hasTag(tag)) {
          continue;
        }
        if (!propertyNames.isEmpty() && !propertyNames.contains(pd.getName())) {
          continue;
        Set<PropertyDefinition<?>> pds = new TreeSet<PropertyDefinition<?>>();
        if (inheritedModeArgument.isPresent()) {
          pds.addAll(mod.getAllPropertyDefinitions());
        } else {
          pds.addAll(mod.getPropertyDefinitions());
          // The list will still contain overridden properties.
          if (mod.getParent() != null) {
            pds.removeAll(mod.getParent().getAllPropertyDefinitions());
          }
        }
        if (isFirstProperty) {
          // User has requested properties relating to this managed
          // object definition, so display the summary of the managed
          // object.
          if (!isFirstManagedObject) {
            out.println();
            out.println(c1);
            out.println();
          } else {
            isFirstManagedObject = false;
        boolean isFirstProperty = true;
        for (PropertyDefinition<?> pd : pds) {
          if (pd.hasOption(PropertyOption.HIDDEN)) {
            continue;
          }
          // Display the title.
          out.println(wrapText(getMessage(MSGID_DSCFG_HELP_HEADING_COMPONENT,
              mod.getUserFriendlyName()), MAX_LINE_WIDTH));
          if (!isAdvancedMode() && pd.hasOption(PropertyOption.ADVANCED)) {
            continue;
          }
          if (!propertyNames.isEmpty() &&
              !propertyNames.contains(pd.getName())) {
            continue;
          }
          if (isFirstProperty) {
            // User has requested properties relating to this managed
            // object definition, so display the summary of the
            // managed
            // object.
            if (!isFirstManagedObject) {
              out.println();
              out.println(c1);
              out.println();
            } else {
              isFirstManagedObject = false;
            }
            // Display the title.
            out.println(wrapText(getMessage(MSGID_DSCFG_HELP_HEADING_COMPONENT,
                mod.getUserFriendlyName()), MAX_LINE_WIDTH));
            out.println();
            out.println(wrapText(mod.getSynopsis(), MAX_LINE_WIDTH));
            if (mod.getDescription() != null) {
              out.println();
              out.println(wrapText(mod.getDescription(), MAX_LINE_WIDTH));
            }
          }
          out.println();
          out.println(wrapText(mod.getSynopsis(), MAX_LINE_WIDTH));
          if (mod.getDescription() != null) {
            out.println();
            out.println(wrapText(mod.getDescription(), MAX_LINE_WIDTH));
          }
          out.println(c2);
          out.println();
          displayVerboseSingleProperty(mod, pd.getName(), out);
          isFirstProperty = false;
        }
        out.println();
        out.println(c2);
        out.println();
        displayVerboseSingleProperty(mod, pd.getName(), out);
        isFirstProperty = false;
      }
    }
  }
opends/src/server/org/opends/server/tools/dsconfig/ListSubCommandHandler.java
@@ -331,7 +331,7 @@
      TableBuilder builder = new TableBuilder();
      builder.appendHeading(relation.getUserFriendlyName());
      builder
          .appendHeading(getMessage(MSGID_DSCFG_HEADING_MANAGED_OBJECT_TYPE));
          .appendHeading(getMessage(MSGID_DSCFG_HEADING_COMPONENT_TYPE));
      if (!propertyNames.isEmpty()) {
      }
      for (String propertyName : propertyNames) {