/* * 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 2007-2008 Sun Microsystems, Inc. * Portions Copyright 2011-2015 ForgeRock AS */ package org.forgerock.opendj.config.dsconfig; import static com.forgerock.opendj.cli.CliMessages.*; import static com.forgerock.opendj.cli.Utils.*; import static com.forgerock.opendj.dsconfig.DsconfigMessages.*; import java.io.PrintStream; import java.util.Collection; 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; import java.util.TreeSet; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.LocalizableMessageBuilder; import org.forgerock.opendj.config.AbsoluteInheritedDefaultBehaviorProvider; import org.forgerock.opendj.config.AbstractManagedObjectDefinition; import org.forgerock.opendj.config.AdministratorAction; import org.forgerock.opendj.config.AggregationPropertyDefinition; import org.forgerock.opendj.config.AliasDefaultBehaviorProvider; import org.forgerock.opendj.config.DefaultBehaviorProviderVisitor; import org.forgerock.opendj.config.DefinedDefaultBehaviorProvider; import org.forgerock.opendj.config.EnumPropertyDefinition; import org.forgerock.opendj.config.PropertyDefinition; import org.forgerock.opendj.config.PropertyDefinitionUsageBuilder; import org.forgerock.opendj.config.PropertyDefinitionVisitor; import org.forgerock.opendj.config.PropertyOption; import org.forgerock.opendj.config.RelativeInheritedDefaultBehaviorProvider; import org.forgerock.opendj.config.StringPropertyDefinition; import org.forgerock.opendj.config.Tag; import org.forgerock.opendj.config.UndefinedDefaultBehaviorProvider; import org.forgerock.opendj.config.client.ManagedObject; import com.forgerock.opendj.cli.ArgumentException; import com.forgerock.opendj.cli.BooleanArgument; import com.forgerock.opendj.cli.ClientException; import com.forgerock.opendj.cli.ConsoleApplication; import com.forgerock.opendj.cli.MenuResult; import com.forgerock.opendj.cli.StringArgument; import com.forgerock.opendj.cli.SubCommand; import com.forgerock.opendj.cli.SubCommandArgumentParser; import com.forgerock.opendj.cli.TableBuilder; import com.forgerock.opendj.cli.TablePrinter; import com.forgerock.opendj.cli.TextTablePrinter; /** * A sub-command handler which is used to display help about managed objects and their properties. * <p> * This sub-command implements the help-properties sub-command. */ final class HelpSubCommandHandler extends SubCommandHandler { /** This class is used to print the default behavior of a property. */ private static class DefaultBehaviorPrinter { /** * The default behavior printer visitor implementation. * * @param <T> * The property type. */ private static class DefaultVisitor<T> implements DefaultBehaviorProviderVisitor<T, LocalizableMessage, PropertyDefinition<T>> { /** {@inheritDoc} */ public LocalizableMessage visitAbsoluteInherited(AbsoluteInheritedDefaultBehaviorProvider<T> d, PropertyDefinition<T> p) { return INFO_DSCFG_HELP_FIELD_INHERITED_ABS.get(d.getPropertyName(), d.getManagedObjectPath() .getRelationDefinition().getUserFriendlyName()); } /** {@inheritDoc} */ public LocalizableMessage visitAlias(AliasDefaultBehaviorProvider<T> d, PropertyDefinition<T> p) { return d.getSynopsis(); } /** {@inheritDoc} */ public LocalizableMessage visitDefined(DefinedDefaultBehaviorProvider<T> d, PropertyDefinition<T> p) { LocalizableMessageBuilder builder = new LocalizableMessageBuilder(); PropertyValuePrinter printer = new PropertyValuePrinter(null, null, false); boolean isFirst = true; for (String s : d.getDefaultValues()) { if (!isFirst) { builder.append(", "); } T value = p.decodeValue(s); builder.append(printer.print(p, value)); } return builder.toMessage(); } /** {@inheritDoc} */ public LocalizableMessage visitRelativeInherited(RelativeInheritedDefaultBehaviorProvider<T> d, PropertyDefinition<T> p) { if (d.getRelativeOffset() == 0) { return INFO_DSCFG_HELP_FIELD_INHERITED_THIS.get(d.getPropertyName(), d.getManagedObjectDefinition() .getUserFriendlyName()); } else { return INFO_DSCFG_HELP_FIELD_INHERITED_PARENT.get(d.getPropertyName(), d .getManagedObjectDefinition().getUserFriendlyName()); } } /** {@inheritDoc} */ public LocalizableMessage visitUndefined(UndefinedDefaultBehaviorProvider<T> d, PropertyDefinition<T> p) { return INFO_DSCFG_HELP_FIELD_UNDEFINED.get(); } } /** * Create a new default behavior printer. */ public DefaultBehaviorPrinter() { // No implementation required. } /** * Get a user-friendly description of a property's default behavior. * * @param <T> * The type of the property definition. * @param pd * The property definition. * @return Returns the user-friendly description of a property's default behavior. */ public <T> LocalizableMessage print(PropertyDefinition<T> pd) { DefaultVisitor<T> v = new DefaultVisitor<>(); return pd.getDefaultBehaviorProvider().accept(v, pd); } } /** This class is used to print detailed syntax information about a property. */ private static class SyntaxPrinter { /** The syntax printer visitor implementation. */ private static final class Visitor extends PropertyDefinitionVisitor<Void, PrintStream> { /** Private constructor. */ private Visitor() { // No implementation required. } /** {@inheritDoc} */ @Override public <E extends Enum<E>> Void visitEnum(EnumPropertyDefinition<E> d, PrintStream p) { displayUsage(p, INFO_DSCFG_HELP_FIELD_ENUM.get()); p.println(); TableBuilder builder = new TableBuilder(); boolean isFirst = true; for (E value : EnumSet.<E> allOf(d.getEnumClass())) { if (!isFirst) { builder.startRow(); } builder.startRow(); builder.appendCell(); builder.appendCell(); builder.appendCell(value.toString()); builder.appendCell(HEADING_SEPARATOR); builder.appendCell(d.getValueSynopsis(value)); isFirst = false; } TextTablePrinter factory = new TextTablePrinter(p); factory.setDisplayHeadings(false); factory.setColumnWidth(0, HEADING_WIDTH); factory.setColumnWidth(1, HEADING_SEPARATOR.length()); factory.setColumnWidth(4, 0); factory.setPadding(0); builder.print(factory); return null; } /** {@inheritDoc} */ @Override public Void visitString(StringPropertyDefinition d, PrintStream p) { PropertyDefinitionUsageBuilder usageBuilder = new PropertyDefinitionUsageBuilder(false); TableBuilder builder = new TableBuilder(); builder.startRow(); builder.appendCell(INFO_DSCFG_HELP_HEADING_SYNTAX.get()); builder.appendCell(HEADING_SEPARATOR); builder.appendCell(usageBuilder.getUsage(d)); if (d.getPattern() != null) { builder.startRow(); builder.startRow(); builder.appendCell(); builder.appendCell(); builder.appendCell(d.getPatternSynopsis()); } TextTablePrinter factory = new TextTablePrinter(p); factory.setDisplayHeadings(false); factory.setColumnWidth(0, HEADING_WIDTH); factory.setColumnWidth(2, 0); factory.setPadding(0); builder.print(factory); return null; } /** {@inheritDoc} */ @Override public <T> Void visitUnknown(PropertyDefinition<T> d, PrintStream p) { PropertyDefinitionUsageBuilder usageBuilder = new PropertyDefinitionUsageBuilder(true); displayUsage(p, usageBuilder.getUsage(d)); return null; } /** Common usage. */ private void displayUsage(PrintStream p, LocalizableMessage usage) { TableBuilder builder = new TableBuilder(); builder.startRow(); builder.appendCell(INFO_DSCFG_HELP_HEADING_SYNTAX.get()); builder.appendCell(HEADING_SEPARATOR); builder.appendCell(usage); TextTablePrinter factory = new TextTablePrinter(p); factory.setDisplayHeadings(false); factory.setColumnWidth(0, HEADING_WIDTH); factory.setColumnWidth(2, 0); factory.setPadding(0); builder.print(factory); } } /** The private implementation. */ private final Visitor pimpl; /** * Creates a new syntax printer which can be used to print detailed syntax information about a property. */ public SyntaxPrinter() { this.pimpl = new Visitor(); } /** * Print detailed syntax information about a property definition. * * @param out * The output stream. * @param pd * The property definition. */ public void print(PrintStream out, PropertyDefinition<?> pd) { pd.accept(pimpl, out); } } /** Strings used in property help. */ private static final String HEADING_SEPARATOR = " : "; /** Width of biggest heading (need to be careful of I18N). */ private static final int HEADING_WIDTH; /** * The value for the long option category. */ private static final String OPTION_DSCFG_LONG_CATEGORY = "category"; /** * The value for the long option inherited. */ private static final String OPTION_DSCFG_LONG_INHERITED = "inherited"; /** * The value for the long option type. */ private static final String OPTION_DSCFG_LONG_TYPE = "type"; /** * The value for the short option category. */ private static final Character OPTION_DSCFG_SHORT_CATEGORY = 'c'; /** * The value for the short option inherited. */ private static final Character OPTION_DSCFG_SHORT_INHERITED = null; /** * The value for the short option type. */ private static final Character OPTION_DSCFG_SHORT_TYPE = 't'; static { int tmp = INFO_DSCFG_HELP_HEADING_SYNTAX.get().length(); tmp = Math.max(tmp, INFO_DSCFG_HELP_HEADING_DEFAULT.get().length()); tmp = Math.max(tmp, INFO_DSCFG_HELP_HEADING_MULTI_VALUED.get().length()); tmp = Math.max(tmp, INFO_DSCFG_HELP_HEADING_MANDATORY.get().length()); tmp = Math.max(tmp, INFO_DSCFG_HELP_HEADING_READ_ONLY.get().length()); HEADING_WIDTH = tmp; } /** * Creates a new help-properties sub-command. * * @param parser * The sub-command argument parser. * @return Returns the new help-properties sub-command. * @throws ArgumentException * If the sub-command could not be created successfully. */ public static HelpSubCommandHandler create(SubCommandArgumentParser parser) throws ArgumentException { return new HelpSubCommandHandler(parser); } /** * Displays detailed help about a single component to the specified output stream. * * @param app * The application console. * @param mo * The managed object. * @param c * The collection of properties to be displayed. */ public static void displaySingleComponent(ConsoleApplication app, ManagedObject<?> mo, Collection<PropertyDefinition<?>> c) { String ufn = mo.getManagedObjectPath().getName(); if (ufn == null) { ufn = mo.getManagedObjectDefinition().getUserFriendlyName().toString(); } // Display the title. app.println(INFO_DSCFG_HELP_HEADING_COMPONENT.get(ufn)); final AbstractManagedObjectDefinition<?, ?> d = mo.getManagedObjectDefinition(); app.println(); app.println(d.getSynopsis()); if (d.getDescription() != null) { app.println(); app.println(d.getDescription()); } app.println(); app.println(); displayPropertyOptionKey(app); app.println(); app.println(); // Headings. final TableBuilder builder = new TableBuilder(); builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_NAME.get()); builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_OPTIONS.get()); builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_SYNTAX.get()); // Sort keys. builder.addSortKey(0); // Output summary of each property. for (final PropertyDefinition<?> pd : c) { // Display the property. builder.startRow(); // Display the property name. builder.appendCell(pd.getName()); // Display the options. builder.appendCell(getPropertyOptionSummary(pd)); // Display the syntax. final PropertyDefinitionUsageBuilder v = new PropertyDefinitionUsageBuilder(false); builder.appendCell(v.getUsage(pd)); } builder.print(new TextTablePrinter(app.getErrorStream())); } /** * Displays detailed help about a single property to the specified output stream. * * @param app * The application console. * @param d * The managed object definition. * @param name * The name of the property definition. */ public static void displayVerboseSingleProperty(ConsoleApplication app, AbstractManagedObjectDefinition<?, ?> d, String name) { PropertyDefinition<?> pd = d.getPropertyDefinition(name); // Display the title. app.println(INFO_DSCFG_HELP_HEADING_PROPERTY.get(name)); // Display the property synopsis and description. app.println(); app.println(pd.getSynopsis(), 4); if (pd.getDescription() != null) { app.println(); app.println(pd.getDescription(), 4); } if (pd instanceof AggregationPropertyDefinition) { AggregationPropertyDefinition<?, ?> apd = (AggregationPropertyDefinition<?, ?>) pd; if (apd.getSourceConstraintSynopsis() != null) { app.println(); app.println(apd.getSourceConstraintSynopsis(), 4); } } // Display the syntax. app.println(); SyntaxPrinter syntaxPrinter = new SyntaxPrinter(); syntaxPrinter.print(app.getErrorStream(), pd); // Display remaining information in a table. app.println(); TableBuilder builder = new TableBuilder(); // Display the default behavior. DefaultBehaviorPrinter defaultPrinter = new DefaultBehaviorPrinter(); builder.startRow(); builder.appendCell(INFO_DSCFG_HELP_HEADING_DEFAULT.get()); builder.appendCell(HEADING_SEPARATOR); builder.appendCell(defaultPrinter.print(pd)); // Display options. builder.startRow(); builder.appendCell(INFO_DSCFG_HELP_HEADING_ADVANCED.get()); builder.appendCell(HEADING_SEPARATOR); builder.appendCell(hasOptionYN(pd, PropertyOption.ADVANCED)); builder.startRow(); builder.appendCell(INFO_DSCFG_HELP_HEADING_MULTI_VALUED.get()); builder.appendCell(HEADING_SEPARATOR); builder.appendCell(hasOptionYN(pd, PropertyOption.MULTI_VALUED)); builder.startRow(); builder.appendCell(INFO_DSCFG_HELP_HEADING_MANDATORY.get()); builder.appendCell(HEADING_SEPARATOR); builder.appendCell(hasOptionYN(pd, PropertyOption.MANDATORY)); builder.startRow(); builder.appendCell(INFO_DSCFG_HELP_HEADING_READ_ONLY.get()); builder.appendCell(HEADING_SEPARATOR); if (pd.hasOption(PropertyOption.MONITORING)) { builder.appendCell(INFO_DSCFG_HELP_FIELD_MONITORING.get()); } else if (pd.hasOption(PropertyOption.READ_ONLY)) { builder.appendCell(INFO_DSCFG_HELP_FIELD_READ_ONLY.get(d.getUserFriendlyName())); } else { builder.appendCell(INFO_GENERAL_NO.get()); } TextTablePrinter factory = new TextTablePrinter(app.getErrorStream()); factory.setDisplayHeadings(false); factory.setColumnWidth(0, HEADING_WIDTH); factory.setColumnWidth(2, 0); factory.setPadding(0); builder.print(factory); // Administrator action. LocalizableMessage synopsis = getSynopsis(d, pd); if (synopsis != null) { app.println(); app.println(synopsis); } } private static LocalizableMessage hasOptionYN(PropertyDefinition<?> pd, PropertyOption option) { return pd.hasOption(option) ? INFO_GENERAL_YES.get() : INFO_GENERAL_NO.get(); } private static LocalizableMessage getSynopsis(AbstractManagedObjectDefinition<?, ?> d, PropertyDefinition<?> pd) { AdministratorAction action = pd.getAdministratorAction(); LocalizableMessage synopsis = action.getSynopsis(); if (synopsis != null) { return synopsis; } switch (action.getType()) { case COMPONENT_RESTART: return INFO_DSCFG_HELP_FIELD_COMPONENT_RESTART.get(d.getUserFriendlyName()); case SERVER_RESTART: return INFO_DSCFG_HELP_FIELD_SERVER_RESTART.get(); default: return null; } } /** Displays the property option summary key. */ private static void displayPropertyOptionKey(ConsoleApplication app) { LocalizableMessageBuilder builder; app.println(INFO_DSCFG_HELP_DESCRIPTION_OPTION.get()); app.println(); builder = new LocalizableMessageBuilder(); builder.append(" r -- "); builder.append(INFO_DSCFG_HELP_DESCRIPTION_READ.get()); app.println(builder.toMessage()); builder = new LocalizableMessageBuilder(); builder.append(" w -- "); builder.append(INFO_DSCFG_HELP_DESCRIPTION_WRITE.get()); app.println(builder.toMessage()); builder = new LocalizableMessageBuilder(); builder.append(" m -- "); builder.append(INFO_DSCFG_HELP_DESCRIPTION_MANDATORY.get()); app.println(builder.toMessage()); builder = new LocalizableMessageBuilder(); builder.append(" s -- "); builder.append(INFO_DSCFG_HELP_DESCRIPTION_SINGLE_VALUED.get()); app.println(builder.toMessage()); builder = new LocalizableMessageBuilder(); builder.append(" a -- "); builder.append(INFO_DSCFG_HELP_DESCRIPTION_ADMIN_ACTION.get()); app.println(builder.toMessage()); } /** Compute the options field. */ private static String getPropertyOptionSummary(PropertyDefinition<?> pd) { StringBuilder b = new StringBuilder(); if (pd.hasOption(PropertyOption.MONITORING) || pd.hasOption(PropertyOption.READ_ONLY)) { b.append("r-"); } else { b.append("rw"); } if (pd.hasOption(PropertyOption.MANDATORY)) { b.append('m'); } else { b.append('-'); } if (pd.hasOption(PropertyOption.MULTI_VALUED)) { b.append('-'); } else { b.append('s'); } AdministratorAction action = pd.getAdministratorAction(); if (action.getType() != AdministratorAction.Type.NONE) { b.append('a'); } else { b.append('-'); } return b.toString(); } /** * The argument which should be used to specify the category of managed object to be retrieved. */ private final StringArgument categoryArgument; /** * A table listing all the available types of managed object indexed on their parent type. */ private final Map<String, Map<String, AbstractManagedObjectDefinition<?, ?>>> categoryMap = new TreeMap<>(); /** The argument which should be used to display inherited properties. */ private BooleanArgument inheritedModeArgument; /** The sub-command associated with this handler. */ private final SubCommand subCommand; /** * A table listing all the available types of managed object indexed on their tag(s). */ private final Map<Tag, Map<String, AbstractManagedObjectDefinition<?, ?>>> tagMap = new HashMap<>(); /** * The argument which should be used to specify the sub-type of managed object to be retrieved. */ private final StringArgument typeArgument; /** Private constructor. */ private HelpSubCommandHandler(SubCommandArgumentParser parser) throws ArgumentException { // Create the sub-command. String name = "list-properties"; LocalizableMessage desc = INFO_DSCFG_DESCRIPTION_SUBCMD_HELPPROP.get(); this.subCommand = new SubCommand(parser, name, false, 0, 0, null, desc); this.categoryArgument = new StringArgument(OPTION_DSCFG_LONG_CATEGORY, OPTION_DSCFG_SHORT_CATEGORY, OPTION_DSCFG_LONG_CATEGORY, false, false, true, INFO_CATEGORY_PLACEHOLDER.get(), null, null, INFO_DSCFG_DESCRIPTION_HELP_CATEGORY.get()); this.subCommand.addArgument(this.categoryArgument); this.typeArgument = new StringArgument(OPTION_DSCFG_LONG_TYPE, OPTION_DSCFG_SHORT_TYPE, OPTION_DSCFG_LONG_TYPE, false, false, true, INFO_TYPE_PLACEHOLDER.get(), null, null, INFO_DSCFG_DESCRIPTION_HELP_TYPE.get()); this.subCommand.addArgument(this.typeArgument); this.inheritedModeArgument = new BooleanArgument(OPTION_DSCFG_LONG_INHERITED, OPTION_DSCFG_SHORT_INHERITED, OPTION_DSCFG_LONG_INHERITED, INFO_DSCFG_DESCRIPTION_HELP_INHERITED.get()); subCommand.addArgument(inheritedModeArgument); // Register common arguments. registerPropertyNameArgument(this.subCommand); setCommandBuilderUseful(false); } /** {@inheritDoc} */ @Override public SubCommand getSubCommand() { return subCommand; } /** * Registers a managed object definition with this help properties sub-command. * * @param d * The managed object definition. */ public void registerManagedObjectDefinition(AbstractManagedObjectDefinition<?, ?> d) { // Determine the definition's base name. AbstractManagedObjectDefinition<?, ?> parent = d; while (!parent.getParent().isTop()) { parent = parent.getParent(); } String baseName = parent.getName(); String typeName = null; if (parent == d) { // This was a top-level definition. typeName = DSConfig.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<>(); categoryMap.put(baseName, subTypes); } subTypes.put(typeName, d); // Get the tag mapping, creating it if necessary. for (Tag tag : d.getAllTags()) { subTypes = tagMap.get(tag); if (subTypes == null) { subTypes = new TreeMap<>(); tagMap.put(tag, subTypes); } subTypes.put(typeName, d); } } /** {@inheritDoc} */ @Override public MenuResult<Integer> run(ConsoleApplication app, LDAPManagementContextFactory factory) throws ArgumentException, ClientException { String categoryName = categoryArgument.getValue(); String typeName = typeArgument.getValue(); Tag tag = null; Set<String> propertyNames = getPropertyNames(); // Reset the command builder getCommandBuilder().clearArguments(); // Update the command builder. updateCommandBuilderWithSubCommand(); List<AbstractManagedObjectDefinition<?, ?>> dlist = new LinkedList<>(); 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); } subTypes = tagMap.get(tag); if (subTypes == null) { throw ArgumentExceptionFactory.unknownCategory(categoryName); } } else { // Cache the generic definition for improved errors later on. tmp = subTypes.get(DSConfig.GENERIC_TYPE); } if (typeName != null) { AbstractManagedObjectDefinition<?, ?> d = subTypes.get(typeName); if (d == null) { throw ArgumentExceptionFactory.unknownTypeForCategory(typeName, categoryName); } 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.unknownTypeForCategory(typeName, categoryName); } } 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 (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) { // Ignore for now. } } if (!isFound) { if (tmp != null) { throw ArgumentExceptionFactory.unknownProperty(tmp, propertyName); } else { throw ArgumentExceptionFactory.unknownProperty(propertyName); } } } // Output everything to the output stream. if (!app.isVerbose()) { displayNonVerbose(app, categoryName, typeName, tag, propertyNames); } else { displayVerbose(app, categoryName, typeName, tag, propertyNames); } return MenuResult.success(0); } /** Output property summary table. */ private void displayNonVerbose(ConsoleApplication app, String categoryName, String typeName, Tag tag, Set<String> propertyNames) { if (!app.isScriptFriendly()) { displayPropertyOptionKey(app); app.println(); app.println(); } // Headings. TableBuilder builder = new TableBuilder(); builder.appendHeading(INFO_DSCFG_HEADING_COMPONENT_NAME.get()); builder.appendHeading(INFO_DSCFG_HEADING_COMPONENT_TYPE.get()); builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_NAME.get()); builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_OPTIONS.get()); builder.appendHeading(INFO_DSCFG_HEADING_PROPERTY_SYNTAX.get()); // Sort keys. builder.addSortKey(0); builder.addSortKey(1); builder.addSortKey(2); // Generate the table content. 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; } // Display help for each property. AbstractManagedObjectDefinition<?, ?> mod = subTypes.get(type); if (cannotDisplay(app, mod, tag)) { continue; } Set<PropertyDefinition<?>> pds = getPropertyDefinitions(mod); for (PropertyDefinition<?> pd : pds) { if (cannotDisplay(app, pd, propertyNames)) { continue; } // Display the property: // - component category and type, // - property name, options // - syntax builder.startRow(); builder.appendCell(category); builder.appendCell(type); builder.appendCell(pd.getName()); builder.appendCell(getPropertyOptionSummary(pd)); PropertyDefinitionUsageBuilder v = new PropertyDefinitionUsageBuilder(false); builder.appendCell(v.getUsage(pd)); } } } TablePrinter printer; if (app.isScriptFriendly()) { printer = createScriptFriendlyTablePrinter(app.getOutputStream()); } else { printer = new TextTablePrinter(app.getOutputStream()); } builder.print(printer); } /** Display detailed help on managed objects and their properties. */ private void displayVerbose(ConsoleApplication app, String categoryName, String typeName, Tag tag, Set<String> propertyNames) { // Construct line used to separate consecutive sections. LocalizableMessage c1 = buildLine('=', MAX_LINE_WIDTH); LocalizableMessage c2 = buildLine('-', MAX_LINE_WIDTH); // Display help for each managed object. boolean isFirstManagedObject = true; 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; } // Display help for each property. AbstractManagedObjectDefinition<?, ?> mod = subTypes.get(type); if (cannotDisplay(app, mod, tag)) { continue; } boolean isFirstProperty = true; Set<PropertyDefinition<?>> pds = getPropertyDefinitions(mod); for (PropertyDefinition<?> pd : pds) { if (cannotDisplay(app, pd, propertyNames)) { continue; } if (isFirstProperty) { // User has requested properties relating to this managed // object definition, so display the summary of the managed object. if (!isFirstManagedObject) { app.println(); app.println(c1); app.println(); } else { isFirstManagedObject = false; } // Display the title. app.println(INFO_DSCFG_HELP_HEADING_COMPONENT.get(mod.getUserFriendlyName())); app.println(); app.println(mod.getSynopsis()); if (mod.getDescription() != null) { app.println(); app.println(mod.getDescription()); } } app.println(); app.println(c2); app.println(); displayVerboseSingleProperty(app, mod, pd.getName()); isFirstProperty = false; } } } } private LocalizableMessage buildLine(char c, int length) { LocalizableMessageBuilder mb = new LocalizableMessageBuilder(); for (int i = 0; i < length; i++) { mb.append(c); } return mb.toMessage(); } private Set<PropertyDefinition<?>> getPropertyDefinitions(AbstractManagedObjectDefinition<?, ?> mod) { Set<PropertyDefinition<?>> pds = new TreeSet<>(); 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()); } } return pds; } }