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

matthew_swift
29.40.2007 eaa23f4b7af97c108ecffa40c86c32e723a90594
Fix issue 1831: dsconfig interactive mode.


Overview:

This change completes the dsconfig interactive mode. dsconfig
now supports two levels of interactivity:

1) Usage: dsconfig

A top-level menu driven text based console. Using
this interactive mode the user is able able to perform
multiple administrative tasks via a menu-driven
interface

2) Usage: dsconfig <sub-command>

dsconfig will perform the administrative task
associated with the specified sub-command prompting
the user for any missing information required in
order to complete the task

In addition, both interactive modes will prompt the user
for any connection parameters not specified on the command
line.

Note that it is possible to run dsconfig non-interactively
using the "-n" option. Although this is only available when
a sub-command is provided.


Other changes:

1) part of this work required implementing a generic CLI
application framework. This includes an API for building
and running menu-based interfaces. The classes are located
in a new package called org.opends.server.util.cli

2) the dsconfig related messages have been relocated into
a separate dsconfig-specific properties file.
2 files copied
3 files deleted
10 files added
2 files renamed
26 files modified
8770 ■■■■ changed files
opends/build.xml 3 ●●●●● patch | view | raw | blame | history
opends/src/messages/messages/dsconfig.properties 409 ●●●●● patch | view | raw | blame | history
opends/src/messages/messages/tools.properties 301 ●●●●● patch | view | raw | blame | history
opends/src/messages/messages/utility.properties 28 ●●●●● patch | view | raw | blame | history
opends/src/messages/src/org/opends/messages/Category.java 6 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/PropertyDefinitionUsageBuilder.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/PropertyDefinitionVisitor.java 4 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/ManagedObject.java 19 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliGlobalAdmin.java 1 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliMain.java 1 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliParser.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliServer.java 1 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java 11 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/DBTest.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/ArgumentExceptionFactory.java 69 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/ConsoleApplication.java 497 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java 308 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java 422 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/DeleteSubCommandHandler.java 160 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/GetPropSubCommandHandler.java 96 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/HelpSubCommandHandler.java 435 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/InternalManagementContextFactory.java 1 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/LDAPManagementContextFactory.java 147 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/ListSubCommandHandler.java 91 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/ManagementContextFactory.java 1 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/PropertyValueEditor.java 1888 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/PropertyValuePrinter.java 88 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/PropertyValueReader.java 630 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/SetPropSubCommandHandler.java 150 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/SubCommandBuilder.java 304 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandler.java 323 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandlerFactory.java 357 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/cli/CLIException.java 92 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/cli/ConsoleApplication.java 453 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/cli/ErrorStreamConsoleApplication.java 105 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/cli/HelpCallback.java 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/cli/Menu.java 52 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/cli/MenuBuilder.java 818 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/cli/MenuCallback.java 21 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/cli/MenuResult.java 324 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/cli/OutputStreamConsoleApplication.java 105 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/cli/ValidationCallback.java 10 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/cli/package-info.java 29 ●●●● patch | view | raw | blame | history
opends/build.xml
@@ -214,6 +214,9 @@
    <genmsg sourceProps="${msg.prop.dir}/core.properties"
            destJava="${msg.javagen.dir}/org/opends/messages/CoreMessages.java">
    </genmsg>
    <genmsg sourceProps="${msg.prop.dir}/dsconfig.properties"
            destJava="${msg.javagen.dir}/org/opends/messages/DSConfigMessages.java">
    </genmsg>
    <genmsg sourceProps="${msg.prop.dir}/extension.properties"
            destJava="${msg.javagen.dir}/org/opends/messages/ExtensionMessages.java">
    </genmsg>
opends/src/messages/messages/dsconfig.properties
New file
@@ -0,0 +1,409 @@
# 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
# trunk/opends/resource/legal-notices/OpenDS.LICENSE
# or https://OpenDS.dev.java.net/OpenDS.LICENSE.
# 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
# trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
#
#      Portions Copyright 2006-2007 Sun Microsystems, Inc.
#
# Global directives
#
global.category=DSCONFIG
#
# Format string definitions
#
# Keys must be formatted as follows:
#
# [SEVERITY]_[DESCRIPTION]_[ORDINAL]
#
# where:
#
# SEVERITY is one of:
# [INFO, MILD_WARN, SEVERE_WARN, MILD_ERR, SEVERE_ERR, FATAL_ERR, DEBUG, NOTICE]
#
# DESCRIPTION is an upper case string providing a hint as to the context of
# the message in upper case with the underscore ('_') character serving as
# word separator
#
# ORDINAL is an integer unique among other ordinals in this file
#
SEVERE_ERR_DSCFG_ERROR_CANNOT_READ_CONNECTION_PARAMETERS_1000=The connection \
 parameters could not be read due to the following error: %s
SEVERE_ERR_DSCFG_ERROR_LDAP_SIMPLE_BIND_FAILED_1001=Unable to authenticate to \
 the server as %s
SEVERE_ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT_1002=Unable to connect to the \
 server at %s on port %s
SEVERE_ERR_DSCFG_ERROR_LDAP_SIMPLE_BIND_NOT_SUPPORTED_1003=Unable to \
 authenticate using simple authentication
INFO_DSCFG_DESCRIPTION_SUBCMD_CREATE_1004=Creates %s
INFO_DSCFG_DESCRIPTION_SUBCMD_DELETE_1005=Deletes %s
INFO_DSCFG_DESCRIPTION_SUBCMD_LIST_1006=Lists existing %s
INFO_DSCFG_DESCRIPTION_SUBCMD_GETPROP_1007=Shows %s properties
INFO_DSCFG_DESCRIPTION_SUBCMD_SETPROP_1008=Modifies %s properties
SEVERE_ERR_DSCFG_ERROR_MISSING_SUBCOMMAND_1009=A sub-command must be \
 specified
INFO_DSCFG_DESCRIPTION_TYPE_1010=The type of %s which should be created. The \
 value for TYPE can be one of: %s
SEVERE_ERR_DSCFG_ERROR_NO_PASSWORD_1011=No password was specified for \
 administrator "%s"
SEVERE_ERR_DSCFG_ERROR_PROPERTY_UNRECOGNIZED_1012=The property "%s" is not a \
 recognized property of %s
SEVERE_ERR_DSCFG_ERROR_PROPERTY_INVALID_VALUE_1013=The value "%s" is not a \
 valid value for the %s property "%s" which has the following syntax: %s
SEVERE_ERR_DSCFG_ERROR_PROPERTY_READ_ONLY_1014=The %s property "%s" is \
 read-only and cannot be modified
SEVERE_ERR_DSCFG_ERROR_PROPERTY_MANDATORY_1015=The %s property "%s" is \
 mandatory and must be specified
SEVERE_ERR_DSCFG_ERROR_PROPERTY_SINGLE_VALUED_1016=It is not possible to \
 specify multiple values for the %s property "%s" as it is single-valued
INFO_DSCFG_DESCRIPTION_SUBCMD_HELPPROP_1017=Describes managed objects and \
 their properties
INFO_DSCFG_HEADING_COMPONENT_NAME_1018=Component
INFO_DSCFG_HEADING_PROPERTY_NAME_1019=Property
INFO_DSCFG_HEADING_PROPERTY_VALUE_1020=Value(s)
INFO_DSCFG_HEADING_PROPERTY_SYNTAX_1021=Syntax
INFO_DSCFG_HEADING_PROPERTY_OPTIONS_1022=Options
INFO_DSCFG_HEADING_PROPERTY_DEFAULT_1023=Default
INFO_DSCFG_HEADING_PROPERTY_DESCRIPTION_1024=Description
INFO_DSCFG_DESCRIPTION_PROPERTY_SYNTAX_HELP_1025=See detailed help
SEVERE_ERR_DSCFG_ERROR_GET_PARENT_DDE_1026=The parent %s could not be \
 retrieved because its type could not be determined. This is probably due to \
 the %s having an invalid LDAP entry. Check that the %s has the correct object \
 classes
SEVERE_ERR_DSCFG_ERROR_GET_PARENT_MODE_1027=The parent %s could not be \
 retrieved because of the reasons listed below:
SEVERE_ERR_DSCFG_ERROR_GET_PARENT_MONFE_1028=The parent %s does not exist
SEVERE_ERR_DSCFG_ERROR_GET_PARENT_AUTHZ_1029=The parent %s could not be \
 retrieved because you do not have the correct authorization
SEVERE_ERR_DSCFG_ERROR_GET_PARENT_CE_1030=The parent %s could not be \
 retrieved due to a communications problem: %s
SEVERE_ERR_DSCFG_ERROR_GET_PARENT_CME_1031=The parent %s could not be \
 retrieved because another client is currently making conflicting \
 configuration changes
SEVERE_ERR_DSCFG_ERROR_CREATE_MMPE_1032=The %s could not be created because \
 the following mandatory properties must be defined: %s
SEVERE_ERR_DSCFG_ERROR_CREATE_MOAEE_1033=The %s could not be created because \
 there is already an existing one with the same name
SEVERE_ERR_DSCFG_ERROR_CREATE_AUTHZ_1034=The %s could not be created because \
 you do not have the correct authorization
SEVERE_ERR_DSCFG_ERROR_CREATE_CE_1035=The %s could not be created due to a \
 communications problem: %s
SEVERE_ERR_DSCFG_ERROR_CREATE_CME_1036=The %s could not be created because \
 another client is currently making conflicting configuration changes
SEVERE_ERR_DSCFG_ERROR_CREATE_ORE_1037=The server prevented the %s from being \
 created because of the following reason: %s
SEVERE_ERR_DSCFG_ERROR_DELETE_MONFE_1038=The %s could not be deleted because \
 it does not exist
SEVERE_ERR_DSCFG_ERROR_DELETE_AUTHZ_1039=The %s could not be deleted because \
 you do not have the correct authorization
SEVERE_ERR_DSCFG_ERROR_DELETE_ORE_1040=The server prevented the %s from being \
 deleted because of the following reason: %s
SEVERE_ERR_DSCFG_ERROR_DELETE_CE_1041=The %s could not be deleted due to a \
 communications problem: %s
SEVERE_ERR_DSCFG_ERROR_DELETE_CME_1042=The %s could not be deleted because \
 another client is currently making conflicting configuration changes
SEVERE_ERR_DSCFG_ERROR_GET_CHILD_DDE_1043=The %s could not be retrieved \
 because its type could not be determined. This is probably due to the %s \
 having an invalid LDAP entry. Check that the %s object classes are correct
SEVERE_ERR_DSCFG_ERROR_GET_CHILD_MODE_1044=The %s could not be retrieved \
 because of the reasons listed below:
SEVERE_ERR_DSCFG_ERROR_GET_CHILD_MONFE_1045=The %s does not exist
SEVERE_ERR_DSCFG_ERROR_GET_CHILD_AUTHZ_1046=The %s could not be accessed \
 because you do not have the correct authorization
SEVERE_ERR_DSCFG_ERROR_GET_CHILD_CE_1047=The %s could not be accessed due to \
 a communications problem: %s
SEVERE_ERR_DSCFG_ERROR_GET_CHILD_CME_1048=The %s could not be accessed \
 because another client is currently making conflicting configuration changes
SEVERE_ERR_DSCFG_ERROR_MODIFY_MONFE_1049=The %s could not be modified because \
 it does not exist
SEVERE_ERR_DSCFG_ERROR_MODIFY_AUTHZ_1050=The %s could not be modified because \
 you do not have the correct authorization
SEVERE_ERR_DSCFG_ERROR_MODIFY_CE_1051=The %s could not be modified due to a \
 communications problem: %s
SEVERE_ERR_DSCFG_ERROR_MODIFY_CME_1052=The %s could not be modified because \
 another client is currently making conflicting configuration changes
SEVERE_ERR_DSCFG_ERROR_MODIFY_ORE_1053=The server prevented the %s from being \
 modified because of the following reason: %s
SEVERE_ERR_DSCFG_ERROR_LIST_DDE_1054=The %s could not be retrieved because \
 its type could not be determined. This is probably due to the %s having an \
 invalid LDAP entry. Check that the %s object classes are correct
SEVERE_ERR_DSCFG_ERROR_LIST_MODE_1055=The %s could not be retrieved because \
 of the reasons listed below:
SEVERE_ERR_DSCFG_ERROR_LIST_MONFE_1056=The %s does not exist
SEVERE_ERR_DSCFG_ERROR_LIST_AUTHZ_1057=The %s could not be listed because you \
 do not have the correct authorization
SEVERE_ERR_DSCFG_ERROR_LIST_CE_1058=The %s could not be listed due to a \
 communications problem: %s
SEVERE_ERR_DSCFG_ERROR_LIST_CME_1059=The %s could not be listed because \
 another client is currently making conflicting configuration changes
SEVERE_ERR_DSCFG_ERROR_PROPERTY_UNKNOWN_ERROR_1060=The value(s) of the %s \
 property "%s" could not be determined due to an unknown error: %s
SEVERE_ERR_DSCFG_ERROR_PROPERTY_DEFAULT_BEHAVIOR_1061=The default value(s) of \
 the %s property "%s" could not be determined due to the following reason: %s
SEVERE_ERR_DSCFG_ERROR_PROPERTY_INHERITED_DEFAULT_BEHAVIOR_1062=The inherited \
 default value(s) of the %s property "%s" could not be determined
SEVERE_ERR_DSCFG_ERROR_NO_SEPARATOR_IN_PROPERTY_VALUE_1063=The property \
 argument "%s" does not contain a name/value separator. The argument should \
 have the following syntax: property:value
SEVERE_ERR_DSCFG_ERROR_NO_NAME_IN_PROPERTY_VALUE_1064=The property argument \
 "%s" does not contain a property name. The argument should have the following \
 syntax: property:value
SEVERE_ERR_DSCFG_ERROR_NO_VALUE_IN_PROPERTY_VALUE_1065=The property argument \
 "%s" does not contain a property value. The argument should have the \
 following syntax: property:value
SEVERE_ERR_DSCFG_ERROR_SUB_TYPE_UNRECOGNIZED_1066=The sub-type "%s" is not a \
 recognized type of %s. It should be one of: %s
SEVERE_ERR_DSCFG_ERROR_TYPE_UNRECOGNIZED_1067="%s" is not a recognized \
 component type
SEVERE_ERR_DSCFG_ERROR_NO_SEPARATOR_IN_PROPERTY_MOD_1068=The property \
 modification "%s" does not contain a name/value separator. The argument \
 should have the following syntax: property[+|-]:value
SEVERE_ERR_DSCFG_ERROR_NO_NAME_IN_PROPERTY_MOD_1069=The property modification \
 "%s" does not contain a property name. The argument should have the following \
 syntax: property[+|-]:value
SEVERE_ERR_DSCFG_ERROR_NO_VALUE_IN_PROPERTY_MOD_1070=The property \
 modification "%s" does not contain a property value. The argument should have \
 the following syntax: property[+|-]:value
SEVERE_ERR_DSCFG_ERROR_INCOMPATIBLE_PROPERTY_MOD_1071=The property \
 modification "%s" is incompatible with a previous modification to the same \
 property
SEVERE_ERR_DSCFG_ERROR_WRONG_MANAGED_OBJECT_TYPE_1072=The %s could not be \
 retrieved because it was the wrong type of managed object: %s
INFO_DSCFG_DESCRIPTION_TYPE_DEFAULT_1073=The type of %s which should be \
 created (Default: %s). The value for TYPE can be one of: %s
INFO_DSCFG_DESCRIPTION_RECORD_1074=Modifies the display output to show one \
 property value per line
INFO_DSCFG_DESCRIPTION_UNIT_TIME_1078=Display time data using the specified \
 unit. The value for UNIT can be one of ms, s, m, h, d, or w (milliseconds, \
 seconds, minutes, hours, days, or weeks)
INFO_DSCFG_DESCRIPTION_UNIT_SIZE_1079=Display size data using the specified \
 unit. The value for UNIT can be one of b, kb, mb, gb, or tb (bytes, \
 kilobytes, megabytes, gigabytes, or terabytes)
INFO_DSCFG_ERROR_TIME_UNIT_UNRECOGNIZED_1080=The time unit "%s" is invalid. \
 The valid time units are ms, s, m, h, d, or w (milliseconds, seconds, \
 minutes, hours, days, or weeks)
INFO_DSCFG_ERROR_SIZE_UNIT_UNRECOGNIZED_1081=The size unit "%s" is invalid. \
 The valid size units are b, kb, mb, gb, or tb (bytes, kilobytes, megabytes, \
 gigabytes, or terabytes)
INFO_DSCFG_HEADING_COMPONENT_TYPE_1082=Type
INFO_DSCFG_DESCRIPTION_SHOW_GROUP_USAGE_1083=Display subcommands relating to \
 %s
INFO_DSCFG_DESCRIPTION_SHOW_GROUP_USAGE_ALL_1084=Display all subcommands
INFO_DSCFG_DESCRIPTION_SHOW_GROUP_USAGE_SUMMARY_1085=Display summary usage \
 information
INFO_DSCFG_DESCRIPTION_NAME_1086=The name of the %s
INFO_DSCFG_DESCRIPTION_PROP_1087=The name of a property to be displayed
INFO_DSCFG_DESCRIPTION_PROP_VAL_1088=Assigns a value to a property where PROP \
 is the name of the property and VAL is the single value to be assigned. \
 Specify the same property multiple times in order to assign more than one \
 value to it
INFO_DSCFG_DESCRIPTION_ADD_PROP_VAL_1089=Adds a single value to a property \
 where PROP is the name of the property and VAL is the single value to be \
 added
INFO_DSCFG_DESCRIPTION_REMOVE_PROP_VAL_1090=Removes a single value from a \
 property where PROP is the name of the property and VAL is the single value \
 to be removed
INFO_DSCFG_DESCRIPTION_RESET_PROP_1091=Resets a property back to its default \
 values where PROP is the name of the property to be reset
INFO_DSCFG_DESCRIPTION_HELP_TYPE_1092=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
SEVERE_ERR_DSCFG_ERROR_BIND_PASSWORD_NONINTERACTIVE_1093=The LDAP bind \
 password was not specified and cannot be read interactively
INFO_DSCFG_DESCRIPTION_FORCE_1196=Ignore non-existent %s
SEVERE_ERR_DSCFG_ERROR_UNABLE_TO_RESET_MANDATORY_PROPERTY_1200=The %s \
 property "%s" is mandatory cannot be reset. Use the "%s" option to specify a \
 new value
SEVERE_ERR_DSCFG_ERROR_ILLEGAL_NAME_SYNTAX_1204=The name "%s" is not a valid \
 name for the %s which has the following syntax: %s
SEVERE_ERR_DSCFG_ERROR_ILLEGAL_NAME_EMPTY_1205=Empty names are not permitted \
 for %s
SEVERE_ERR_DSCFG_ERROR_ILLEGAL_NAME_BLANK_1206=Blank names are not permitted \
 for %s
SEVERE_ERR_DSCFG_ERROR_ILLEGAL_NAME_UNKNOWN_1207=The name "%s" is not a valid \
 name for the %s
INFO_DSCFG_DESCRIPTION_NAME_CREATE_1208=The name of the new %s
INFO_DSCFG_DESCRIPTION_NAME_CREATE_EXT_1209=The name of the new %s which will \
 also be used as the value of the "%s" property: %s
SEVERE_ERR_DSCFG_ERROR_UNABLE_TO_SET_NAMING_PROPERTY_1210=The property "%s" \
 cannot be set as it is defined implicitly by the name of the %s
INFO_DSCFG_DESCRIPTION_ADVANCED_GET_1216=Modifies the display output to show \
 the advanced properties of the %s
INFO_DSCFG_DESCRIPTION_ADVANCED_SET_1217=Allows the configuration of advanced \
 properties during interactive mode
INFO_DSCFG_DESCRIPTION_ADVANCED_HELP_1218=Modifies the display output to show \
 the advanced properties of components
SEVERE_ERR_DSCFG_ERROR_MISSING_NON_INTERACTIVE_ARG_1223=The argument "--%s" \
 must be specified when this application is used non-interactively
SEVERE_ERR_DSCFG_ERROR_CANNOT_READ_CONSOLE_INPUT_1224=The response could not \
 be read from the console due to the following error: %s
INFO_DSCFG_CREATE_TYPE_PROMPT_1225=>>>> Select the type of %s that you want to \
 create:
INFO_DSCFG_CREATE_NAME_PROMPT_1226=>>>> Enter a name for the %s that you want to \
 create:
SEVERE_ERR_DSCFG_ERROR_CREATE_NAME_ALREADY_EXISTS_1227=There is already \
 another %s with the name "%s"
INFO_DSCFG_DESCRIPTION_CREATE_HELP_HEADING_TYPE_1228=Type
INFO_DSCFG_DESCRIPTION_CREATE_HELP_HEADING_DESCR_1229=Description
SEVERE_ERR_DSCFG_ERROR_FINDER_NO_CHILDREN_1230=Unable to continue since there \
 are no %s currently configured on the server
SEVERE_ERR_DSCFG_ERROR_FINDER_SINGLE_CHILD_REJECTED_1231=Unable to continue \
 because the only available %s was not selected
INFO_DSCFG_FINDER_PROMPT_SINGLE_1232=>>>> There is only one %s: "%s". Are you sure \
 that this is the correct one?
INFO_DSCFG_FINDER_PROMPT_MANY_1233=>>>> Select the %s from the following list:
INFO_DSCFG_GENERAL_CHOICE_PROMPT_NOHELP_1237=Enter choice [1 - %d]:
INFO_DSCFG_GENERAL_CHOICE_PROMPT_HELP_1238=Enter choice [1 - %d,  ? - help]:
SEVERE_ERR_DSCFG_ERROR_GENERAL_CHOICE_1239=Invalid response. Please enter a \
 value between 1 and %d
INFO_DSCFG_HELP_FIELD_ENUM_1254=one of the following values:
INFO_DSCFG_HELP_FIELD_UNDEFINED_1255=undefined
INFO_DSCFG_HELP_FIELD_INHERITED_ABS_1256=inherits from the property "%s" in \
 the %s
INFO_DSCFG_HELP_FIELD_INHERITED_PARENT_1257=inherits from the property "%s" \
 in the parent %s
INFO_DSCFG_HELP_FIELD_INHERITED_THIS_1258=inherits from the property "%s" in \
 this %s
INFO_DSCFG_HELP_FIELD_SERVER_RESTART_1259=The server must be restarted in \
 order for changes to this property to take effect
INFO_DSCFG_HELP_FIELD_COMPONENT_RESTART_1260=The %s must be restarted in \
 order for changes to this property to take effect
INFO_DSCFG_HELP_FIELD_READ_ONLY_1261=read-only - this property can only be \
 specified when the %s is created
INFO_DSCFG_HELP_FIELD_MONITORING_1262=monitoring - this property is \
 automatically generated by the server
INFO_DSCFG_HELP_HEADING_PROPERTY_1263=Property: %s
INFO_DSCFG_HELP_HEADING_COMPONENT_1264=Component name: %s
INFO_DSCFG_HELP_HEADING_DEFAULT_1265=Default behavior
INFO_DSCFG_HELP_HEADING_MANDATORY_1266=Mandatory
INFO_DSCFG_HELP_HEADING_ADVANCED_1267=Advanced
INFO_DSCFG_HELP_HEADING_MULTI_VALUED_1268=Multi-valued
INFO_DSCFG_HELP_HEADING_READ_ONLY_1269=Read-only
INFO_DSCFG_HELP_HEADING_SYNTAX_1270=Syntax
INFO_DSCFG_HELP_DESCRIPTION_OPTION_1271=Option Types:
INFO_DSCFG_HELP_DESCRIPTION_READ_1272=Property value(s) are readable
INFO_DSCFG_HELP_DESCRIPTION_WRITE_1273=Property value(s) are writable
INFO_DSCFG_HELP_DESCRIPTION_MANDATORY_1274=The property is mandatory
INFO_DSCFG_HELP_DESCRIPTION_SINGLE_VALUED_1275=The property is single-valued
INFO_DSCFG_HELP_DESCRIPTION_ADMIN_ACTION_1276=Administrative action is \
 required for changes to take effect
INFO_DSCFG_CONFIRM_CREATE_1277=Are you sure that you want to create the %s?
INFO_DSCFG_CONFIRM_DELETE_1278=Are you sure that you want to delete the %s?
INFO_DSCFG_CONFIRM_MODIFY_1279=Are you sure that you want to modify the %s?
INFO_DSCFG_CONFIRM_CREATE_SUCCESS_1280=The %s was created successfully
INFO_DSCFG_CONFIRM_DELETE_SUCCESS_1281=The %s was deleted successfully
INFO_DSCFG_CONFIRM_MODIFY_SUCCESS_1282=The %s was modified successfully
INFO_DSCFG_CONFIRM_CREATE_FAIL_1283=The %s was not created
INFO_DSCFG_CONFIRM_DELETE_FAIL_1284=The %s was not deleted
INFO_DSCFG_CONFIRM_MODIFY_FAIL_1285=The %s was not modified
INFO_DSCFG_DESCRIPTION_HELP_CATEGORY_1286=The category of components whose \
 properties should be described
SEVERE_ERR_DSCFG_ERROR_CATEGORY_UNRECOGNIZED_1287="%s" is not a recognized \
 component category
SEVERE_ERR_DSCFG_ERROR_CATEGORY_TYPE_UNRECOGNIZED_1288="%s" is not a \
 recognized component type in category "%s"
SEVERE_ERR_DSCFG_ERROR_PROPERTY_UNRECOGNIZED_NO_DEFN_1289=The property "%s" \
 is not a recognized property
INFO_DSCFG_DESCRIPTION_HELP_INHERITED_1290=Modifies the display output to \
 show the inherited properties of components
INFO_VALUE_TRUE_1291=true
INFO_VALUE_FALSE_1292=false
INFO_VALUE_UNLIMITED_1293=unlimited
INFO_EDITOR_PROMPT_SELECT_VALUE_SINGLE_1294=Select a value for the "%s" property:
INFO_EDITOR_PROMPT_SELECT_VALUE_MULTI_1295=Select one or more values for the "%s" property:
INFO_EDITOR_HEADING_SYNTAX_1296=Syntax: %s
INFO_EDITOR_HEADING_VALUES_SUMMARY_1297=The "%s" property has the following values:
INFO_EDITOR_PROMPT_SELECT_VALUES_ADD_1299=Select the values you wish to add:
INFO_EDITOR_PROMPT_SELECT_VALUES_REMOVE_1302=Select the values you wish to remove:
INFO_EDITOR_PROMPT_MODIFY_MENU_1303=Do you want to modify the "%s" property?
INFO_EDITOR_OPTION_VALUES_1298=%d)
INFO_EDITOR_OPTION_ADD_ALL_VALUES_1300=Add all values
INFO_EDITOR_OPTION_ADD_ONE_OR_MORE_VALUES_1304=Add one or more values
INFO_EDITOR_OPTION_REMOVE_ONE_OR_MORE_VALUES_1305=Remove one or more values
INFO_EDITOR_OPTION_REMOVE_ALL_VALUES_1306=Remove all values
INFO_EDITOR_OPTION_REVERT_CHANGES_1307=Revert changes
INFO_EDITOR_OPTION_LEAVE_UNDEFINED_1311=Leave undefined
INFO_EDITOR_OPTION_USE_DEFAULT_ALIAS_1308=Use the default behavior: %s
INFO_EDITOR_OPTION_USE_DEFAULT_INHERITED_ALIAS_1309=Use the inherited default behavior: %s
INFO_EDITOR_OPTION_USE_DEFAULT_INHERITED_ALIAS_UNDEFINED_1310=Use the inherited default behavior: undefined
INFO_EDITOR_OPTION_USE_VALUE_1312=Use the value: %s
INFO_EDITOR_OPTION_USE_DEFAULT_VALUE_1313=Use the default value: %s
INFO_EDITOR_OPTION_USE_INHERITED_DEFAULT_VALUE_1314=Use the inherited default value: %s
INFO_EDITOR_OPTION_USE_VALUES_1315=Use these values
INFO_EDITOR_OPTION_USE_DEFAULT_VALUES_1316=Use these default values
INFO_EDITOR_OPTION_USE_INHERITED_DEFAULT_VALUES_1317=Use these inherited default values
INFO_EDITOR_OPTION_KEEP_DEFAULT_ALIAS_1318=Keep the default behavior: %s
INFO_EDITOR_OPTION_KEEP_DEFAULT_INHERITED_ALIAS_1319=Keep the inherited default behavior: %s
INFO_EDITOR_OPTION_KEEP_DEFAULT_INHERITED_ALIAS_UNDEFINED_1320=Keep the inherited default behavior: undefined
INFO_EDITOR_OPTION_KEEP_VALUE_1321=Keep the value: %s
INFO_EDITOR_OPTION_KEEP_DEFAULT_VALUE_1322=Keep the default value: %s
INFO_EDITOR_OPTION_KEEP_INHERITED_DEFAULT_VALUE_1323=Keep the inherited default value: %s
INFO_EDITOR_OPTION_KEEP_VALUES_1324=Keep these values
INFO_EDITOR_OPTION_KEEP_DEFAULT_VALUES_1325=Keep these default values
INFO_EDITOR_OPTION_KEEP_INHERITED_DEFAULT_VALUES_1326=Keep these inherited default values
INFO_EDITOR_OPTION_RESET_DEFAULT_ALIAS_1327=Reset to the default behavior: %s
INFO_EDITOR_OPTION_RESET_DEFAULT_INHERITED_ALIAS_1328=Reset to the inherited default behavior: %s
INFO_EDITOR_OPTION_RESET_DEFAULT_INHERITED_ALIAS_UNDEFINED_1329=Reset to the inherited default behavior: undefined
INFO_EDITOR_OPTION_RESET_DEFAULT_VALUE_1331=Reset to the default value: %s
INFO_EDITOR_OPTION_RESET_INHERITED_DEFAULT_VALUE_1332=Reset to the inherited default value: %s
INFO_EDITOR_OPTION_RESET_DEFAULT_VALUES_1334=Reset to the default values: %s
INFO_EDITOR_OPTION_RESET_INHERITED_DEFAULT_VALUES_1335=Reset to the inherited default values: %s
INFO_EDITOR_HEADING_READ_ONLY_ALIAS_UNDEFINED_1336=The "%s" property is undefined
INFO_EDITOR_HEADING_READ_ONLY_ALIAS_1337=The "%s" property is undefined: %s
INFO_EDITOR_HEADING_READ_ONLY_VALUE_1338=The "%s" property has the following value: %s
INFO_EDITOR_HEADING_READ_ONLY_VALUES_1339=The "%s" property has the following values:
INFO_EDITOR_PROMPT_READ_ONLY_1340=This property is read-only and cannot be modified. Would you like to view its help documentation?
INFO_EDITOR_OPTION_CHANGE_TO_DEFAULT_VALUE_1341=Change it to the default value: %s
INFO_EDITOR_OPTION_CHANGE_TO_VALUE_1342=Change it to the value: %s
INFO_EDITOR_OPTION_CHANGE_VALUE_1343=Change the value
INFO_EDITOR_HEADING_CONFIGURE_PROPERTY_1344=>>>> Configuring the "%s" property
INFO_EDITOR_PROMPT_READ_FIRST_VALUE_1345=Enter a value for the "%s" property:
INFO_EDITOR_PROMPT_READ_FIRST_VALUE_OPTIONAL_1346=Enter a value for the "%s" property [continue]:
INFO_EDITOR_PROMPT_READ_NEXT_VALUE_1347=Enter another value for the "%s" property [continue]:
SEVERE_ERR_EDITOR_READ_FIRST_DUPLICATE_1348=This property already contains the value "%s". Please enter a different value
SEVERE_ERR_EDITOR_READ_NEXT_DUPLICATE_1349=This property already contains the value "%s". Please enter a different value, or press RETURN to continue
INFO_EDITOR_HEADING_CONFIGURE_COMPONENT_1350=>>>> Configure the properties of the %s
INFO_EDITOR_OPTION_FINISH_CREATE_COMPONENT_1351=finish - create the new %s
INFO_EDITOR_OPTION_FINISH_MODIFY_COMPONENT_1352=finish - apply any changes to the %s
INFO_EDITOR_OPTION_FINISH_KEY_1353=f
INFO_EDITOR_HEADING_CONFIGURE_PROPERTY_CONT_1354=>>>> Configuring the "%s" property (Continued)
INFO_DSCFG_CREATE_NAME_PROMPT_NAMING_1355=>>>> Specify a name for the %s. This name will be used as the value for the "%s" property which has the following description:
INFO_DSCFG_CREATE_NAME_PROMPT_NAMING_CONT_1356=Enter a name for the %s that you want to create:
INFO_DSCFG_HEADING_MAIN_MENU_TITLE_1357=>>>> OpenDS configuration console main menu
INFO_DSCFG_HEADING_MAIN_MENU_PROMPT_1358=What do you want to configure?
INFO_DSCFG_HEADING_COMPONENT_MENU_TITLE_1359=>>>> %s management menu
INFO_DSCFG_HEADING_COMPONENT_MENU_PROMPT_1360=What would you like to do?
INFO_DSCFG_OPTION_COMPONENT_MENU_CREATE_1361=Create a new %s
INFO_DSCFG_OPTION_COMPONENT_MENU_MODIFY_SINGULAR_1362=View and edit the %s
INFO_DSCFG_OPTION_COMPONENT_MENU_MODIFY_PLURAL_1363=View and edit an existing %s
INFO_DSCFG_OPTION_COMPONENT_MENU_DELETE_1364=Delete an existing %s
INFO_DSCFG_OPTION_COMPONENT_MENU_LIST_PLURAL_1365=List existing %s
INFO_DSCFG_OPTION_COMPONENT_MENU_LIST_SINGULAR_1366=Show the %s
INFO_DSCFG_PROMPT_HOST_NAME_1367=Directory server hostname or IP address [%s]:
INFO_DSCFG_PROMPT_PORT_NUMBER_1368=Directory server port number [%d]:
INFO_DSCFG_PROMPT_BIND_DN_1369=Administrator user bind DN [%s]:
SEVERE_ERR_DSCFG_BAD_HOST_NAME_1370=The hostname "%s" could not be resolved. Please check you have have the correct address
SEVERE_ERR_DSCFG_BAD_PORT_NUMBER_1371=Invalid port number "%s". Please enter a valid port number between 1 and 65535
INFO_DSCFG_GENERIC_TYPE_OPTION_1372=Generic %s
INFO_DSCFG_HEADING_CONNECTION_PARAMETERS_1373=>>>> Specify OpenDS LDAP connection parameters
opends/src/messages/messages/tools.properties
@@ -1601,193 +1601,10 @@
 start with "dn:" to indicate a user DN
INFO_DESCRIPTION_PRODUCT_VERSION_891=Display Directory Server version \
 information
SEVERE_ERR_DSCFG_ERROR_CANNOT_READ_LDAP_BIND_PASSWORD_1000=The LDAP bind \
 password could not be read due to the following error: %s
SEVERE_ERR_DSCFG_ERROR_LDAP_SIMPLE_BIND_FAILED_1001=Unable to authenticate to \
 the server as %s
SEVERE_ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT_1002=Unable to connect to the \
 server at %s on port %s
SEVERE_ERR_DSCFG_ERROR_LDAP_SIMPLE_BIND_NOT_SUPPORTED_1003=Unable to \
 authenticate using simple authentication
INFO_DSCFG_DESCRIPTION_SUBCMD_CREATE_1004=Creates %s
INFO_DSCFG_DESCRIPTION_SUBCMD_DELETE_1005=Deletes %s
INFO_DSCFG_DESCRIPTION_SUBCMD_LIST_1006=Lists existing %s
INFO_DSCFG_DESCRIPTION_SUBCMD_GETPROP_1007=Shows %s properties
INFO_DSCFG_DESCRIPTION_SUBCMD_SETPROP_1008=Modifies %s properties
SEVERE_ERR_DSCFG_ERROR_MISSING_SUBCOMMAND_1009=A sub-command must be \
 specified
INFO_DSCFG_DESCRIPTION_TYPE_1010=The type of %s which should be created. The \
 value for TYPE can be one of: %s
SEVERE_ERR_DSCFG_ERROR_NO_PASSWORD_1011=No password was specified for \
 administrator "%s"
SEVERE_ERR_DSCFG_ERROR_PROPERTY_UNRECOGNIZED_1012=The property "%s" is not a \
 recognized property of %s
SEVERE_ERR_DSCFG_ERROR_PROPERTY_INVALID_VALUE_1013=The value "%s" is not a \
 valid value for the %s property "%s" which has the following syntax: %s
SEVERE_ERR_DSCFG_ERROR_PROPERTY_READ_ONLY_1014=The %s property "%s" is \
 read-only and cannot be modified
SEVERE_ERR_DSCFG_ERROR_PROPERTY_MANDATORY_1015=The %s property "%s" is \
 mandatory and must be specified
SEVERE_ERR_DSCFG_ERROR_PROPERTY_SINGLE_VALUED_1016=It is not possible to \
 specify multiple values for the %s property "%s" as it is single-valued
INFO_DSCFG_DESCRIPTION_SUBCMD_HELPPROP_1017=Describes managed objects and \
 their properties
INFO_DSCFG_HEADING_COMPONENT_NAME_1018=Component
INFO_DSCFG_HEADING_PROPERTY_NAME_1019=Property
INFO_DSCFG_HEADING_PROPERTY_VALUE_1020=Value(s)
INFO_DSCFG_HEADING_PROPERTY_SYNTAX_1021=Syntax
INFO_DSCFG_HEADING_PROPERTY_OPTIONS_1022=Options
INFO_DSCFG_HEADING_PROPERTY_DEFAULT_1023=Default
INFO_DSCFG_HEADING_PROPERTY_DESCRIPTION_1024=Description
INFO_DSCFG_DESCRIPTION_PROPERTY_SYNTAX_HELP_1025=See detailed help
SEVERE_ERR_DSCFG_ERROR_GET_PARENT_DDE_1026=The parent %s could not be \
 retrieved because its type could not be determined. This is probably due to \
 the %s having an invalid LDAP entry. Check that the %s has the correct object \
 classes
SEVERE_ERR_DSCFG_ERROR_GET_PARENT_MODE_1027=The parent %s could not be \
 retrieved because of the reasons listed below:
SEVERE_ERR_DSCFG_ERROR_GET_PARENT_MONFE_1028=The parent %s does not exist
SEVERE_ERR_DSCFG_ERROR_GET_PARENT_AUTHZ_1029=The parent %s could not be \
 retrieved because you do not have the correct authorization
SEVERE_ERR_DSCFG_ERROR_GET_PARENT_CE_1030=The parent %s could not be \
 retrieved due to a communications problem: %s
SEVERE_ERR_DSCFG_ERROR_GET_PARENT_CME_1031=The parent %s could not be \
 retrieved because another client is currently making conflicting \
 configuration changes
SEVERE_ERR_DSCFG_ERROR_CREATE_MMPE_1032=The %s could not be created because \
 the following mandatory properties must be defined: %s
SEVERE_ERR_DSCFG_ERROR_CREATE_MOAEE_1033=The %s could not be created because \
 there is already an existing one with the same name
SEVERE_ERR_DSCFG_ERROR_CREATE_AUTHZ_1034=The %s could not be created because \
 you do not have the correct authorization
SEVERE_ERR_DSCFG_ERROR_CREATE_CE_1035=The %s could not be created due to a \
 communications problem: %s
SEVERE_ERR_DSCFG_ERROR_CREATE_CME_1036=The %s could not be created because \
 another client is currently making conflicting configuration changes
SEVERE_ERR_DSCFG_ERROR_CREATE_ORE_1037=The server prevented the %s from being \
 created because of the following reason: %s
SEVERE_ERR_DSCFG_ERROR_DELETE_MONFE_1038=The %s could not be deleted because \
 it does not exist
SEVERE_ERR_DSCFG_ERROR_DELETE_AUTHZ_1039=The %s could not be deleted because \
 you do not have the correct authorization
SEVERE_ERR_DSCFG_ERROR_DELETE_ORE_1040=The server prevented the %s from being \
 deleted because of the following reason: %s
SEVERE_ERR_DSCFG_ERROR_DELETE_CE_1041=The %s could not be deleted due to a \
 communications problem: %s
SEVERE_ERR_DSCFG_ERROR_DELETE_CME_1042=The %s could not be deleted because \
 another client is currently making conflicting configuration changes
SEVERE_ERR_DSCFG_ERROR_GET_CHILD_DDE_1043=The %s could not be retrieved \
 because its type could not be determined. This is probably due to the %s \
 having an invalid LDAP entry. Check that the %s object classes are correct
SEVERE_ERR_DSCFG_ERROR_GET_CHILD_MODE_1044=The %s could not be retrieved \
 because of the reasons listed below:
SEVERE_ERR_DSCFG_ERROR_GET_CHILD_MONFE_1045=The %s does not exist
SEVERE_ERR_DSCFG_ERROR_GET_CHILD_AUTHZ_1046=The %s could not be accessed \
 because you do not have the correct authorization
SEVERE_ERR_DSCFG_ERROR_GET_CHILD_CE_1047=The %s could not be accessed due to \
 a communications problem: %s
SEVERE_ERR_DSCFG_ERROR_GET_CHILD_CME_1048=The %s could not be accessed \
 because another client is currently making conflicting configuration changes
SEVERE_ERR_DSCFG_ERROR_MODIFY_MONFE_1049=The %s could not be modified because \
 it does not exist
SEVERE_ERR_DSCFG_ERROR_MODIFY_AUTHZ_1050=The %s could not be modified because \
 you do not have the correct authorization
SEVERE_ERR_DSCFG_ERROR_MODIFY_CE_1051=The %s could not be modified due to a \
 communications problem: %s
SEVERE_ERR_DSCFG_ERROR_MODIFY_CME_1052=The %s could not be modified because \
 another client is currently making conflicting configuration changes
SEVERE_ERR_DSCFG_ERROR_MODIFY_ORE_1053=The server prevented the %s from being \
 modified because of the following reason: %s
SEVERE_ERR_DSCFG_ERROR_LIST_DDE_1054=The %s could not be retrieved because \
 its type could not be determined. This is probably due to the %s having an \
 invalid LDAP entry. Check that the %s object classes are correct
SEVERE_ERR_DSCFG_ERROR_LIST_MODE_1055=The %s could not be retrieved because \
 of the reasons listed below:
SEVERE_ERR_DSCFG_ERROR_LIST_MONFE_1056=The %s does not exist
SEVERE_ERR_DSCFG_ERROR_LIST_AUTHZ_1057=The %s could not be listed because you \
 do not have the correct authorization
SEVERE_ERR_DSCFG_ERROR_LIST_CE_1058=The %s could not be listed due to a \
 communications problem: %s
SEVERE_ERR_DSCFG_ERROR_LIST_CME_1059=The %s could not be listed because \
 another client is currently making conflicting configuration changes
SEVERE_ERR_DSCFG_ERROR_PROPERTY_UNKNOWN_ERROR_1060=The value(s) of the %s \
 property "%s" could not be determined due to an unknown error: %s
SEVERE_ERR_DSCFG_ERROR_PROPERTY_DEFAULT_BEHAVIOR_1061=The default value(s) of \
 the %s property "%s" could not be determined due to the following reason: %s
SEVERE_ERR_DSCFG_ERROR_PROPERTY_INHERITED_DEFAULT_BEHAVIOR_1062=The inherited \
 default value(s) of the %s property "%s" could not be determined
SEVERE_ERR_DSCFG_ERROR_NO_SEPARATOR_IN_PROPERTY_VALUE_1063=The property \
 argument "%s" does not contain a name/value separator. The argument should \
 have the following syntax: property:value
SEVERE_ERR_DSCFG_ERROR_NO_NAME_IN_PROPERTY_VALUE_1064=The property argument \
 "%s" does not contain a property name. The argument should have the following \
 syntax: property:value
SEVERE_ERR_DSCFG_ERROR_NO_VALUE_IN_PROPERTY_VALUE_1065=The property argument \
 "%s" does not contain a property value. The argument should have the \
 following syntax: property:value
SEVERE_ERR_DSCFG_ERROR_SUB_TYPE_UNRECOGNIZED_1066=The sub-type "%s" is not a \
 recognized type of %s. It should be one of: %s
SEVERE_ERR_DSCFG_ERROR_TYPE_UNRECOGNIZED_1067="%s" is not a recognized \
 component type
SEVERE_ERR_DSCFG_ERROR_NO_SEPARATOR_IN_PROPERTY_MOD_1068=The property \
 modification "%s" does not contain a name/value separator. The argument \
 should have the following syntax: property[+|-]:value
SEVERE_ERR_DSCFG_ERROR_NO_NAME_IN_PROPERTY_MOD_1069=The property modification \
 "%s" does not contain a property name. The argument should have the following \
 syntax: property[+|-]:value
SEVERE_ERR_DSCFG_ERROR_NO_VALUE_IN_PROPERTY_MOD_1070=The property \
 modification "%s" does not contain a property value. The argument should have \
 the following syntax: property[+|-]:value
SEVERE_ERR_DSCFG_ERROR_INCOMPATIBLE_PROPERTY_MOD_1071=The property \
 modification "%s" is incompatible with a previous modification to the same \
 property
SEVERE_ERR_DSCFG_ERROR_WRONG_MANAGED_OBJECT_TYPE_1072=The %s could not be \
 retrieved because it was the wrong type of managed object: %s
INFO_DSCFG_DESCRIPTION_TYPE_DEFAULT_1073=The type of %s which should be \
 created (Default: %s). The value for TYPE can be one of: %s
INFO_DSCFG_DESCRIPTION_RECORD_1074=Modifies the display output to show one \
 property value per line
INFO_DESCRIPTION_QUIET_1075=Use quiet mode
INFO_DESCRIPTION_SCRIPT_FRIENDLY_1076=Use script-friendly mode
INFO_DESCRIPTION_NO_PROMPT_1077=Use non-interactive mode.  If some data in \
the command is missing the user will not be prompted and the tool will fail
INFO_DSCFG_DESCRIPTION_UNIT_TIME_1078=Display time data using the specified \
 unit. The value for UNIT can be one of ms, s, m, h, d, or w (milliseconds, \
 seconds, minutes, hours, days, or weeks)
INFO_DSCFG_DESCRIPTION_UNIT_SIZE_1079=Display size data using the specified \
 unit. The value for UNIT can be one of b, kb, mb, gb, or tb (bytes, \
 kilobytes, megabytes, gigabytes, or terabytes)
INFO_DSCFG_ERROR_TIME_UNIT_UNRECOGNIZED_1080=The time unit "%s" is invalid. \
 The valid time units are ms, s, m, h, d, or w (milliseconds, seconds, \
 minutes, hours, days, or weeks)
INFO_DSCFG_ERROR_SIZE_UNIT_UNRECOGNIZED_1081=The size unit "%s" is invalid. \
 The valid size units are b, kb, mb, gb, or tb (bytes, kilobytes, megabytes, \
 gigabytes, or terabytes)
INFO_DSCFG_HEADING_COMPONENT_TYPE_1082=Type
INFO_DSCFG_DESCRIPTION_SHOW_GROUP_USAGE_1083=Display subcommands relating to \
 %s
INFO_DSCFG_DESCRIPTION_SHOW_GROUP_USAGE_ALL_1084=Display all subcommands
INFO_DSCFG_DESCRIPTION_SHOW_GROUP_USAGE_SUMMARY_1085=Display summary usage \
 information
INFO_DSCFG_DESCRIPTION_NAME_1086=The name of the %s
INFO_DSCFG_DESCRIPTION_PROP_1087=The name of a property to be displayed
INFO_DSCFG_DESCRIPTION_PROP_VAL_1088=Assigns a value to a property where PROP \
 is the name of the property and VAL is the single value to be assigned. \
 Specify the same property multiple times in order to assign more than one \
 value to it
INFO_DSCFG_DESCRIPTION_ADD_PROP_VAL_1089=Adds a single value to a property \
 where PROP is the name of the property and VAL is the single value to be \
 added
INFO_DSCFG_DESCRIPTION_REMOVE_PROP_VAL_1090=Removes a single value from a \
 property where PROP is the name of the property and VAL is the single value \
 to be removed
INFO_DSCFG_DESCRIPTION_RESET_PROP_1091=Resets a property back to its default \
 values where PROP is the name of the property to be reset
INFO_DSCFG_DESCRIPTION_HELP_TYPE_1092=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
SEVERE_ERR_DSCFG_ERROR_BIND_PASSWORD_NONINTERACTIVE_1093=The LDAP bind \
 password was not specified and cannot be read interactively
INFO_PWPSTATE_TOOL_DESCRIPTION_1094=This utility may be used to retrieve and \
 manipulate the values of password policy state variables
INFO_PWPSTATE_DESCRIPTION_HOST_1095=Directory server hostname or IP address
@@ -1995,7 +1812,6 @@
INFO_LDIFIMPORT_DESCRIPTION_COUNT_REJECTS_1195=Count the number of entries \
 rejected by the server and return that value as the exit code (values > 255 \
 will be reduced to 255 due to exit code restrictions)
INFO_DSCFG_DESCRIPTION_FORCE_1196=Ignore non-existent %s
INFO_LDIFIMPORT_DESCRIPTION_SKIP_FILE_1197=Write skipped entries to the \
 specified file
SEVERE_ERR_LDIFIMPORT_CANNOT_OPEN_SKIP_FILE_1198=An error occurred while \
@@ -2003,27 +1819,11 @@
INFO_VERIFYINDEX_DESCRIPTION_COUNT_ERRORS_1199=Count the number of errors \
 found during the verification and return that value as the exit code (values \
 > 255 will be reduced to 255 due to exit code restrictions)
SEVERE_ERR_DSCFG_ERROR_UNABLE_TO_RESET_MANDATORY_PROPERTY_1200=The %s \
 property "%s" is mandatory cannot be reset. Use the "%s" option to specify a \
 new value
INFO_PWPSTATE_LABEL_PASSWORD_HISTORY_1201=Password History
INFO_DESCRIPTION_PWPSTATE_GET_PASSWORD_HISTORY_1202=Display password history \
 state values for the user
INFO_DESCRIPTION_PWPSTATE_CLEAR_PASSWORD_HISTORY_1203=Clear password history \
 state values for the user.  This should be used only for testing purposes
SEVERE_ERR_DSCFG_ERROR_ILLEGAL_NAME_SYNTAX_1204=The name "%s" is not a valid \
 name for the %s which has the following syntax: %s
SEVERE_ERR_DSCFG_ERROR_ILLEGAL_NAME_EMPTY_1205=Empty names are not permitted \
 for %s
SEVERE_ERR_DSCFG_ERROR_ILLEGAL_NAME_BLANK_1206=Blank names are not permitted \
 for %s
SEVERE_ERR_DSCFG_ERROR_ILLEGAL_NAME_UNKNOWN_1207=The name "%s" is not a valid \
 name for the %s
INFO_DSCFG_DESCRIPTION_NAME_CREATE_1208=The name of the new %s
INFO_DSCFG_DESCRIPTION_NAME_CREATE_EXT_1209=The name of the new %s which will \
 also be used as the value of the "%s" property: %s
SEVERE_ERR_DSCFG_ERROR_UNABLE_TO_SET_NAMING_PROPERTY_1210=The property "%s" \
 cannot be set as it is defined implicitly by the name of the %s
SEVERE_ERR_CONFIGDS_PORT_ALREADY_SPECIFIED_1211=ERROR:  You have specified \
 the value %s for different ports
SEVERE_ERR_CLI_ERROR_PROPERTY_UNRECOGNIZED_1212=The property "%s" is not a \
@@ -2033,12 +1833,6 @@
SEVERE_ERR_CLI_ERROR_INVALID_PROPERTY_VALUE_1214=The value "%s" specified for \
 the property "%s" is invalid
INFO_CLI_HEADING_PROPERTY_DEFAULT_VALUE_1215=Default value
INFO_DSCFG_DESCRIPTION_ADVANCED_GET_1216=Modifies the display output to show \
 the advanced properties of the %s
INFO_DSCFG_DESCRIPTION_ADVANCED_SET_1217=Allows the configuration of advanced \
 properties during interactive mode
INFO_DSCFG_DESCRIPTION_ADVANCED_HELP_1218=Modifies the display output to show \
 the advanced properties of components
INFO_REVERT_DESCRIPTION_DIRECTORY_1219=Directory where reversion files are \
 stored.  This should be one of the child directories of the 'history' \
 directory that is created when the upgrade tool is run
@@ -2047,50 +1841,6 @@
INFO_REVERT_DESCRIPTION_INTERACTIVE_1221=Prompt for any required information \
 rather than fail
INFO_REVERT_DESCRIPTION_SILENT_1222=Perform a quiet reversion
SEVERE_ERR_DSCFG_ERROR_MISSING_NON_INTERACTIVE_ARG_1223=The argument "--%s" \
 must be specified when this application is used non-interactively
SEVERE_ERR_DSCFG_ERROR_CANNOT_READ_CONSOLE_INPUT_1224=The response could not \
 be read from the console due to the following error: %s
INFO_DSCFG_CREATE_TYPE_PROMPT_1225=Select the type of %s that you want to \
 create:
INFO_DSCFG_CREATE_NAME_PROMPT_1226=Enter a name for the %s that you want to \
 create:
SEVERE_ERR_DSCFG_ERROR_CREATE_NAME_ALREADY_EXISTS_1227=There is already \
 another %s with the name "%s"
INFO_DSCFG_DESCRIPTION_CREATE_HELP_HEADING_TYPE_1228=Type
INFO_DSCFG_DESCRIPTION_CREATE_HELP_HEADING_DESCR_1229=Description
SEVERE_ERR_DSCFG_ERROR_FINDER_NO_CHILDREN_1230=Unable to continue since there \
 are no %s currently configured on the server
SEVERE_ERR_DSCFG_ERROR_FINDER_SINGLE_CHILD_REJECTED_1231=Unable to continue \
 because the only available %s was not selected
INFO_DSCFG_FINDER_PROMPT_SINGLE_1232=There is only one %s: "%s". Are you sure \
 that this is the correct one?
INFO_DSCFG_FINDER_PROMPT_MANY_1233=Select the %s from the following list:
INFO_DSCFG_GENERAL_CONFIRM_NO_1234=no
INFO_DSCFG_GENERAL_CONFIRM_YES_1235=yes
SEVERE_ERR_DSCFG_ERROR_GENERAL_CONFIRM_1236=Invalid response. Please enter \
 "%s" or "%s"
INFO_DSCFG_GENERAL_CHOICE_PROMPT_NOHELP_1237=Enter choice [1 - %d]:
INFO_DSCFG_GENERAL_CHOICE_PROMPT_HELP_1238=Enter choice [1 - %d,  ? - help]:
SEVERE_ERR_DSCFG_ERROR_GENERAL_CHOICE_1239=Invalid response. Please enter a \
 value between 1 and %d
INFO_DSCFG_VALUE_READER_MENU_RESET_1240=reset the value back to its default
INFO_DSCFG_VALUE_READER_MENU_SET_1241=modify the value
INFO_DSCFG_VALUE_READER_MENU_ADD_1242=add a value
INFO_DSCFG_VALUE_READER_MENU_REMOVE_1243=remove a value
INFO_DSCFG_VALUE_READER_MENU_CONTINUE_1244=continue
INFO_DSCFG_VALUE_READER_PROMPT_REMOVE_1245=Select the value to be removed \
 from the "%s" property:
INFO_DSCFG_VALUE_READER_PROMPT_SELECT_VALUE_1246=Select a value for the "%s" \
 property:
INFO_DSCFG_VALUE_READER_PROMPT_ENTER_VALUE_1247=Enter a value for the "%s" \
 property:
INFO_DSCFG_VALUE_READER_MENU_TITLE_1248=Select a property to be edited, or \
 enter "%d" to continue:
INFO_DSCFG_VALUE_READER_PROMPT_MANDATORY_1249=The property "%s" is mandatory \
 and must have a value specified
INFO_DSCFG_VALUE_READER_PROMPT_MODIFY_MENU_1250=Do you want to modify the \
 "%s" property?
INFO_LDIFIMPORT_DESCRIPTION_CLEAR_BACKEND_1251=Remove all entries for all \
 base DNs in the backend before importing
SEVERE_ERR_LDIFIMPORT_MISSING_BACKEND_ARGUMENT_1252=Neither the %s or the %s \
@@ -2099,56 +1849,6 @@
SEVERE_ERR_LDIFIMPORT_MISSING_CLEAR_BACKEND_1253=Importing to a backend \
 without the append argument will remove all entries for all base DNs (%s) in \
 the backend. The %s argument must be given to continue with import
INFO_DSCFG_HELP_FIELD_ENUM_1254=one of the following values:
INFO_DSCFG_HELP_FIELD_UNDEFINED_1255=undefined
INFO_DSCFG_HELP_FIELD_INHERITED_ABS_1256=inherits from the property "%s" in \
 the %s
INFO_DSCFG_HELP_FIELD_INHERITED_PARENT_1257=inherits from the property "%s" \
 in the parent %s
INFO_DSCFG_HELP_FIELD_INHERITED_THIS_1258=inherits from the property "%s" in \
 this %s
INFO_DSCFG_HELP_FIELD_SERVER_RESTART_1259=The server must be restarted in \
 order for changes to this property to take effect
INFO_DSCFG_HELP_FIELD_COMPONENT_RESTART_1260=The %s must be restarted in \
 order for changes to this property to take effect
INFO_DSCFG_HELP_FIELD_READ_ONLY_1261=read-only - this property can only be \
 specified when the %s is created
INFO_DSCFG_HELP_FIELD_MONITORING_1262=monitoring - this property is \
 automatically generated by the server
INFO_DSCFG_HELP_HEADING_PROPERTY_1263=Property: %s
INFO_DSCFG_HELP_HEADING_COMPONENT_1264=Component name: %s
INFO_DSCFG_HELP_HEADING_DEFAULT_1265=Default behavior
INFO_DSCFG_HELP_HEADING_MANDATORY_1266=Mandatory
INFO_DSCFG_HELP_HEADING_ADVANCED_1267=Advanced
INFO_DSCFG_HELP_HEADING_MULTI_VALUED_1268=Multi-valued
INFO_DSCFG_HELP_HEADING_READ_ONLY_1269=Read-only
INFO_DSCFG_HELP_HEADING_SYNTAX_1270=Syntax
INFO_DSCFG_HELP_DESCRIPTION_OPTION_1271=Option Types:
INFO_DSCFG_HELP_DESCRIPTION_READ_1272=Property value(s) are readable
INFO_DSCFG_HELP_DESCRIPTION_WRITE_1273=Property value(s) are writable
INFO_DSCFG_HELP_DESCRIPTION_MANDATORY_1274=The property is mandatory
INFO_DSCFG_HELP_DESCRIPTION_SINGLE_VALUED_1275=The property is single-valued
INFO_DSCFG_HELP_DESCRIPTION_ADMIN_ACTION_1276=Administrative action is \
 required for changes to take effect
INFO_DSCFG_CONFIRM_CREATE_1277=Are you sure that you want to create the %s?
INFO_DSCFG_CONFIRM_DELETE_1278=Are you sure that you want to delete the %s?
INFO_DSCFG_CONFIRM_MODIFY_1279=Are you sure that you want to modify the %s?
INFO_DSCFG_CONFIRM_CREATE_SUCCESS_1280=The %s was created successfully
INFO_DSCFG_CONFIRM_DELETE_SUCCESS_1281=The %s was deleted successfully
INFO_DSCFG_CONFIRM_MODIFY_SUCCESS_1282=The %s was modified successfully
INFO_DSCFG_CONFIRM_CREATE_FAIL_1283=The %s was not created
INFO_DSCFG_CONFIRM_DELETE_FAIL_1284=The %s was not deleted
INFO_DSCFG_CONFIRM_MODIFY_FAIL_1285=The %s was not modified
INFO_DSCFG_DESCRIPTION_HELP_CATEGORY_1286=The category of components whose \
 properties should be described
SEVERE_ERR_DSCFG_ERROR_CATEGORY_UNRECOGNIZED_1287="%s" is not a recognized \
 component category
SEVERE_ERR_DSCFG_ERROR_CATEGORY_TYPE_UNRECOGNIZED_1288="%s" is not a \
 recognized component type in category "%s"
SEVERE_ERR_DSCFG_ERROR_PROPERTY_UNRECOGNIZED_NO_DEFN_1289=The property "%s" \
 is not a recognized property
INFO_DSCFG_DESCRIPTION_HELP_INHERITED_1290=Modifies the display output to \
 show the inherited properties of components
MILD_ERR_MAKELDIF_TAG_LIST_NO_ARGUMENTS_1291=The list tag on line %d of the \
 template file does not contain any arguments to specify the list values.  At \
 least one list value must be provided
@@ -2298,3 +1998,4 @@
  min/max data size %s as a integer: %s
SEVERE_ERR_CONFIGDS_CANNOT_ENABLE_ADS_TRUST_STORE_1373=An error occurred while \
 attempting to enable the ADS trust store: %s
SEVERE_ERR_DBTEST_MISSING_SUBCOMMAND_1374=A sub-command must be specified
opends/src/messages/messages/utility.properties
@@ -472,4 +472,30 @@
SEVERE_ERR_BASE64_CANNOT_WRITE_RAW_DATA_199=An error occurred while \
 attempting to write the decoded data:  %s
SEVERE_ERR_BASE64_UNKNOWN_SUBCOMMAND_200=Unknown subcommand %s
INFO_GENERAL_NO_201=no
INFO_GENERAL_YES_202=yes
SEVERE_ERR_CONSOLE_APP_CONFIRM_203=Invalid response. Please enter \
 "%s" or "%s"
INFO_MENU_OPTION_HELP_204=help
INFO_MENU_OPTION_HELP_KEY_205=?
INFO_MENU_OPTION_CANCEL_206=cancel
INFO_MENU_OPTION_CANCEL_KEY_207=c
INFO_MENU_OPTION_QUIT_208=quit
INFO_MENU_OPTION_QUIT_KEY_209=q
INFO_MENU_NUMERIC_OPTION_210=%d)
INFO_MENU_CHAR_OPTION_211=%c)
SEVERE_ERR_MENU_BAD_CHOICE_MULTI_212=Invalid response. Please enter one or \
more valid menu options
SEVERE_ERR_MENU_BAD_CHOICE_SINGLE_213=Invalid response. Please enter a valid \
menu option
SEVERE_ERR_MENU_BAD_CHOICE_MULTI_DUPE_214=The option "%s" was specified \
more than once. Please enter one or more valid menu options
INFO_MENU_PROMPT_SINGLE_215=Enter choice:
INFO_MENU_PROMPT_SINGLE_DEFAULT_216=Enter choice [%s]:
INFO_MENU_PROMPT_MULTI_217=Enter one or more choices separated by commas:
INFO_MENU_PROMPT_MULTI_DEFAULT_218=Enter one or more choices separated by commas [%s]:
INFO_MENU_PROMPT_RETURN_TO_CONTINUE_219=Press RETURN to continue
INFO_MENU_PROMPT_CONFIRM_220=%s (%s / %s) [%s]:
SEVERE_ERR_CONSOLE_INPUT_ERROR_221=The response could not be read from the console due to the following error: %s
INFO_MENU_OPTION_BACK_222=back
INFO_MENU_OPTION_BACK_KEY_223=b
opends/src/messages/src/org/opends/messages/Category.java
@@ -140,6 +140,12 @@
  ADMIN_TOOL(0x01100000),
  /**
   * The category used for messages associated with the dsconfig
   * administration tool.
   */
  DSCONFIG(0x01200000),
  /**
   * The category that will be used for messages associated with
   * third-party (including user-defined) modules.
   */
opends/src/server/org/opends/server/admin/PropertyDefinitionUsageBuilder.java
@@ -312,7 +312,7 @@
     * {@inheritDoc}
     */
    @Override
    public Message visitUnknown(PropertyDefinition<?> d, Void p)
    public <T> Message visitUnknown(PropertyDefinition<T> d, Void p)
        throws UnknownPropertyDefinitionException {
      return Message.raw("?");
    }
opends/src/server/org/opends/server/admin/PropertyDefinitionVisitor.java
@@ -241,6 +241,8 @@
   * {@link UnknownPropertyDefinitionException}. Sub-classes can
   * override this method with their own default behavior.
   *
   * @param <T>
   *          The type of the underlying property.
   * @param d
   *          The property definition to visit.
   * @param p
@@ -250,7 +252,7 @@
   *           Visitor implementations may optionally throw this
   *           exception.
   */
  public R visitUnknown(PropertyDefinition<?> d, P p)
  public <T> R visitUnknown(PropertyDefinition<T> d, P p)
      throws UnknownPropertyDefinitionException {
    throw new UnknownPropertyDefinitionException(d, p);
  }
opends/src/server/org/opends/server/admin/client/ManagedObject.java
@@ -443,6 +443,25 @@
  /**
   * Determines whether or not the specified property is set. If the
   * property is unset, then any default behavior associated with the
   * property applies.
   *
   * @param pd
   *          The property definition.
   * @return Returns <code>true</code> if the property has been set,
   *         or <code>false</code> if it is unset and any default
   *         behavior associated with the property applies.
   * @throws IllegalArgumentException
   *           If the property definition is not associated with this
   *           managed object's definition.
   */
  boolean isPropertyPresent(PropertyDefinition<?> pd)
      throws IllegalArgumentException;
  /**
   * Determines whether or not the optional managed object associated
   * with the specified optional relations exists.
   *
opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliGlobalAdmin.java
@@ -29,6 +29,7 @@
import org.opends.messages.MessageBuilder;
import static org.opends.messages.AdminMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import static org.opends.messages.ToolMessages.*;
import static org.opends.server.tools.ToolConstants.*;
opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliMain.java
@@ -39,6 +39,7 @@
import static org.opends.server.admin.client.cli.DsFrameworkCliReturnCode.*;
import static org.opends.messages.AdminMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import static org.opends.messages.ToolMessages.*;
import org.opends.messages.MessageBuilder;
import static org.opends.server.util.ServerConstants.*;
opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliParser.java
@@ -28,7 +28,7 @@
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.messages.AdminMessages.*;
import static org.opends.messages.ToolMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import org.opends.messages.Message;
import static org.opends.server.tools.ToolConstants.*;
import static org.opends.server.util.ServerConstants.MAX_LINE_WIDTH;
opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliServer.java
@@ -29,6 +29,7 @@
import org.opends.messages.MessageBuilder;
import static org.opends.messages.AdminMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import static org.opends.messages.ToolMessages.*;
import static org.opends.server.tools.ToolConstants.*;
opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
@@ -675,6 +675,17 @@
  /**
   * {@inheritDoc}
   */
  public boolean isPropertyPresent(PropertyDefinition<?> pd)
      throws IllegalArgumentException {
    Property<?> p = properties.getProperty(pd);
    return !p.isEmpty();
  }
  /**
   * {@inheritDoc}
   */
  public <C extends ConfigurationClient, S extends Configuration>
  boolean hasChild(OptionalRelationDefinition<C, S> r)
      throws IllegalArgumentException, ConcurrentModificationException,
opends/src/server/org/opends/server/tools/DBTest.java
@@ -434,7 +434,7 @@
    // Make sure that we have a sub-command.
    if (parser.getSubCommand() == null)
    {
      Message message = ERR_DSCFG_ERROR_MISSING_SUBCOMMAND.get();
      Message message = ERR_DBTEST_MISSING_SUBCOMMAND.get();
      displayMessageAndUsageReference(message);
      return 1;
    }
opends/src/server/org/opends/server/tools/dsconfig/ArgumentExceptionFactory.java
@@ -25,12 +25,12 @@
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
import static org.opends.messages.DSConfigMessages.*;
import org.opends.messages.Message;
import static org.opends.messages.ToolMessages.*;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.DefaultBehaviorException;
import org.opends.server.admin.IllegalPropertyValueException;
@@ -47,6 +47,7 @@
import org.opends.server.admin.client.MissingMandatoryPropertiesException;
import org.opends.server.util.args.Argument;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.cli.CLIException;
@@ -57,16 +58,16 @@
public final class ArgumentExceptionFactory {
  /**
   * Creates an argument exception from an illegal managed object name
   * Creates a CLI exception from an illegal managed object name
   * exception.
   *
   * @param e
   *          The illegal managed object name exception.
   * @param d
   *          The managed object definition.
   * @return Returns an argument exception.
   * @return Returns a CLI exception.
   */
  public static ArgumentException adaptIllegalManagedObjectNameException(
  public static CLIException adaptIllegalManagedObjectNameException(
      IllegalManagedObjectNameException e,
      AbstractManagedObjectDefinition<?, ?> d) {
    String illegalName = e.getIllegalName();
@@ -75,11 +76,11 @@
    if (illegalName.length() == 0) {
      Message message =
          ERR_DSCFG_ERROR_ILLEGAL_NAME_EMPTY.get(d.getUserFriendlyPluralName());
      return new ArgumentException(message);
      return new CLIException(message);
    } else if (illegalName.trim().length() == 0) {
      Message message =
          ERR_DSCFG_ERROR_ILLEGAL_NAME_BLANK.get(d.getUserFriendlyPluralName());
      return new ArgumentException(message);
      return new CLIException(message);
    } else if (pd != null) {
      try {
        pd.decodeValue(illegalName);
@@ -90,13 +91,13 @@
        Message message = ERR_DSCFG_ERROR_ILLEGAL_NAME_SYNTAX.get(
            illegalName, d.getUserFriendlyName(), syntax);
        return new ArgumentException(message);
        return new CLIException(message);
      }
    }
    Message message = ERR_DSCFG_ERROR_ILLEGAL_NAME_UNKNOWN.get(
        illegalName, d.getUserFriendlyName());
    return new ArgumentException(message);
    return new CLIException(message);
  }
@@ -320,16 +321,18 @@
  /**
   * Creates an argument exception which should be used when the bind
   * password could not be read from the standard input.
   * Creates an argument exception which should be used when the
   * connection parameters could not be read from the standard input.
   *
   * @param cause
   *          The reason why the bind password could not be read.
   *          The reason why the connection parameters could not be
   *          read.
   * @return Returns an argument exception.
   */
  public static ArgumentException unableToReadBindPassword(Exception cause) {
    Message message =
        ERR_DSCFG_ERROR_CANNOT_READ_LDAP_BIND_PASSWORD.get(cause.getMessage());
  public static ArgumentException unableToReadConnectionParameters(
      Exception cause) {
    Message message = ERR_DSCFG_ERROR_CANNOT_READ_CONNECTION_PARAMETERS
        .get(cause.getMessage());
    return new ArgumentException(message, cause);
  }
@@ -350,22 +353,6 @@
  /**
   * Creates an argument exception which should be used when
   * interaction with the console fails due to an IO exception.
   *
   * @param cause
   *          The reason why console input failed.
   * @return Returns an argument exception.
   */
  public static ArgumentException unableToReadConsoleInput(Exception cause) {
    Message message =
        ERR_DSCFG_ERROR_CANNOT_READ_CONSOLE_INPUT.get(cause.getMessage());
    return new ArgumentException(message, cause);
  }
  /**
   * Creates an argument exception which should be used when an
   * attempt is made to reset a mandatory property that does not have
   * any default values.
@@ -513,7 +500,7 @@
  /**
   * Creates an argument exception which should be used when a managed
   * Creates a CLI exception which should be used when a managed
   * object is retrieved but does not have the correct type
   * appropriate for the associated sub-command.
   *
@@ -521,13 +508,13 @@
   *          The relation definition.
   * @param d
   *          The definition of the managed object that was retrieved.
   * @return Returns an argument exception.
   * @return Returns a CLI exception.
   */
  public static ArgumentException wrongManagedObjectType(
      RelationDefinition<?, ?> r, ManagedObjectDefinition<?, ?> d) {
    Message msg = ERR_DSCFG_ERROR_TYPE_UNRECOGNIZED.get(
        d.getUserFriendlyName());
    return new ArgumentException(msg);
  public static CLIException wrongManagedObjectType(RelationDefinition<?, ?> r,
      ManagedObjectDefinition<?, ?> d) {
    Message msg = ERR_DSCFG_ERROR_TYPE_UNRECOGNIZED
        .get(d.getUserFriendlyName());
    return new CLIException(msg);
  }
opends/src/server/org/opends/server/tools/dsconfig/ConsoleApplication.java
File was deleted
opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java
@@ -25,13 +25,11 @@
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
import org.opends.messages.Message;
import static org.opends.messages.ToolMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -44,6 +42,7 @@
import java.util.TreeMap;
import java.util.TreeSet;
import org.opends.messages.Message;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.ConfigurationClient;
@@ -57,6 +56,7 @@
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.OptionalRelationDefinition;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyDefinitionUsageBuilder;
import org.opends.server.admin.PropertyException;
import org.opends.server.admin.PropertyIsSingleValuedException;
import org.opends.server.admin.PropertyOption;
@@ -68,6 +68,7 @@
import org.opends.server.admin.client.IllegalManagedObjectNameException;
import org.opends.server.admin.client.ManagedObject;
import org.opends.server.admin.client.ManagedObjectDecodingException;
import org.opends.server.admin.client.ManagementContext;
import org.opends.server.admin.client.MissingMandatoryPropertiesException;
import org.opends.server.admin.client.OperationRejectedException;
import org.opends.server.protocols.ldap.LDAPResultCode;
@@ -76,6 +77,13 @@
import org.opends.server.util.args.StringArgument;
import org.opends.server.util.args.SubCommand;
import org.opends.server.util.args.SubCommandArgumentParser;
import org.opends.server.util.cli.CLIException;
import org.opends.server.util.cli.ConsoleApplication;
import org.opends.server.util.cli.HelpCallback;
import org.opends.server.util.cli.Menu;
import org.opends.server.util.cli.MenuBuilder;
import org.opends.server.util.cli.MenuResult;
import org.opends.server.util.cli.ValidationCallback;
import org.opends.server.util.table.TableBuilder;
import org.opends.server.util.table.TextTablePrinter;
@@ -94,7 +102,6 @@
final class CreateSubCommandHandler<C extends ConfigurationClient,
    S extends Configuration> extends SubCommandHandler {
  /**
   * A property provider which uses the command-line arguments to
   * provide initial property values.
@@ -220,7 +227,8 @@
  /**
   * A help call-back which displays help about available component types.
   * A help call-back which displays help about available component
   * types.
   */
  private final class TypeHelpCallback implements HelpCallback {
@@ -228,14 +236,15 @@
     * {@inheritDoc}
     */
    public void display(ConsoleApplication app) {
      // Create a table containing a description of each component type.
      // Create a table containing a description of each component
      // type.
      TableBuilder builder = new TableBuilder();
      builder.appendHeading(
              INFO_DSCFG_DESCRIPTION_CREATE_HELP_HEADING_TYPE.get());
      builder.appendHeading(INFO_DSCFG_DESCRIPTION_CREATE_HELP_HEADING_TYPE
          .get());
      builder.appendHeading(
              INFO_DSCFG_DESCRIPTION_CREATE_HELP_HEADING_DESCR.get());
      builder.appendHeading(INFO_DSCFG_DESCRIPTION_CREATE_HELP_HEADING_DESCR
          .get());
      boolean isFirst = true;
      for (ManagedObjectDefinition<?, ?> d : types.values()) {
@@ -261,6 +270,8 @@
      printer.setColumnWidth(1, 0);
      printer.setColumnSeparator(":");
      builder.print(printer);
      app.println();
      app.pressReturnToContinue();
    }
  }
@@ -300,8 +311,6 @@
   *          The type of managed object which can be created.
   * @param <S>
   *          The type of server managed object which can be created.
   * @param app
   *          The console application.
   * @param parser
   *          The sub-command argument parser.
   * @param p
@@ -314,10 +323,9 @@
   */
  public static <C extends ConfigurationClient, S extends Configuration>
      CreateSubCommandHandler<C, S> create(
      ConsoleApplication app, SubCommandArgumentParser parser,
      ManagedObjectPath<?, ?> p, InstantiableRelationDefinition<C, S> r)
      throws ArgumentException {
    return new CreateSubCommandHandler<C, S>(app, parser, p, r, r
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p,
      InstantiableRelationDefinition<C, S> r) throws ArgumentException {
    return new CreateSubCommandHandler<C, S>(parser, p, r, r
        .getNamingPropertyDefinition(), p.child(r, "DUMMY"));
  }
@@ -330,8 +338,6 @@
   *          The type of managed object which can be created.
   * @param <S>
   *          The type of server managed object which can be created.
   * @param app
   *          The console application.
   * @param parser
   *          The sub-command argument parser.
   * @param p
@@ -344,11 +350,9 @@
   */
  public static <C extends ConfigurationClient, S extends Configuration>
      CreateSubCommandHandler<C, S> create(
      ConsoleApplication app, SubCommandArgumentParser parser,
      ManagedObjectPath<?, ?> p, OptionalRelationDefinition<C, S> r)
      throws ArgumentException {
    return new CreateSubCommandHandler<C, S>(app, parser, p, r, null, p
        .child(r));
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p,
      OptionalRelationDefinition<C, S> r) throws ArgumentException {
    return new CreateSubCommandHandler<C, S>(parser, p, r, null, p.child(r));
  }
  // The sub-commands naming arguments.
@@ -385,24 +389,20 @@
  // Common constructor.
  private CreateSubCommandHandler(ConsoleApplication app,
      SubCommandArgumentParser parser,
      ManagedObjectPath<?, ?> p, RelationDefinition<C, S> r,
      PropertyDefinition<?> pd, ManagedObjectPath<?, ?> c)
      throws ArgumentException {
    super(app);
  private CreateSubCommandHandler(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p,
      RelationDefinition<C, S> r, PropertyDefinition<?> pd,
      ManagedObjectPath<?, ?> c) throws ArgumentException {
    this.path = p;
    this.relation = r;
    this.namingPropertyDefinition = pd;
    // Create the sub-command.
    String name = "create-" + r.getName();
    Message description = INFO_DSCFG_DESCRIPTION_SUBCMD_CREATE.get(
      r.getChildDefinition().getUserFriendlyPluralName()
    );
    this.subCommand = new SubCommand(parser, name, false, 0,
            0, null, description);
    Message description = INFO_DSCFG_DESCRIPTION_SUBCMD_CREATE.get(r
        .getChildDefinition().getUserFriendlyPluralName());
    this.subCommand = new SubCommand(parser, name, false, 0, 0, null,
        description);
    // Create the -t argument which is used to specify the type of
    // managed object to be created.
@@ -418,9 +418,8 @@
    // Create the --property argument which is used to specify
    // property values.
    this.propertySetArgument = new StringArgument(OPTION_DSCFG_LONG_SET,
        OPTION_DSCFG_SHORT_SET, OPTION_DSCFG_LONG_SET, false, true,
        true, "{PROP:VALUE}", null, null,
        INFO_DSCFG_DESCRIPTION_PROP_VAL.get());
        OPTION_DSCFG_SHORT_SET, OPTION_DSCFG_LONG_SET, false, true, true,
        "{PROP:VALUE}", null, null, INFO_DSCFG_DESCRIPTION_PROP_VAL.get());
    this.subCommand.addArgument(this.propertySetArgument);
    // Build the -t option usage.
@@ -463,6 +462,19 @@
  /**
   * Gets the relation definition associated with the type of
   * component that this sub-command handles.
   *
   * @return Returns the relation definition associated with the type
   *         of component that this sub-command handles.
   */
  public RelationDefinition<?, ?> getRelationDefinition() {
    return relation;
  }
  /**
   * {@inheritDoc}
   */
  @Override
@@ -476,27 +488,57 @@
   * {@inheritDoc}
   */
  @Override
  public int run() throws ArgumentException, ClientException {
  public MenuResult<Integer> run(ConsoleApplication app,
      ManagementContextFactory factory) throws ArgumentException,
      ClientException, CLIException {
    // Determine the type of managed object to be created.
    String typeName;
    if (!typeArgument.isPresent()) {
      if (getConsoleApplication().isInteractive()) {
      if (app.isInteractive()) {
        // Let the user choose.
        // If there is only one choice then return immediately.
        if (types.size() == 1) {
          typeName = types.keySet().iterator().next();
        } else {
          List<String> values = new ArrayList<String>(types.keySet());
          List<Message> descriptions = new ArrayList<Message>(values.size());
          for (ManagedObjectDefinition<?, ?> d : types.values()) {
            descriptions.add(d.getUserFriendlyName());
          MenuBuilder<String> builder = new MenuBuilder<String>(app);
          Message msg = INFO_DSCFG_CREATE_TYPE_PROMPT.get(relation
              .getChildDefinition().getUserFriendlyName());
          builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD);
          builder.setPrompt(msg);
          for (String type : types.keySet()) {
            ManagedObjectDefinition<?, ?> d = types.get(type);
            Message option = d.getUserFriendlyName();
            if (type.equals(GENERIC_TYPE)) {
              option = INFO_DSCFG_GENERIC_TYPE_OPTION.get(option);
          }
          Message msg = INFO_DSCFG_CREATE_TYPE_PROMPT.get(
                  relation.getChildDefinition().getUserFriendlyName());
          typeName = getConsoleApplication().readChoice(msg, descriptions,
              values, new TypeHelpCallback());
            builder.addNumberedOption(option, MenuResult.success(type));
          }
          builder.addHelpOption(new TypeHelpCallback());
          if (app.isMenuDrivenMode()) {
            builder.addCancelOption(true);
          }
          builder.addQuitOption();
          Menu<String> menu = builder.toMenu();
          app.println();
          app.println();
          MenuResult<String> result = menu.run();
          if (result.isSuccess()) {
            typeName = result.getValue();
          } else if (result.isCancel()) {
            return MenuResult.cancel();
          } else {
            // Must be quit.
            if (!app.isMenuDrivenMode()) {
              msg = INFO_DSCFG_CONFIRM_CREATE_FAIL.get(relation
                  .getUserFriendlyName());
              app.printVerboseMessage(msg);
            }
            return MenuResult.quit();
          }
        }
      } else if (typeArgument.getDefaultValue() != null) {
        typeName = typeArgument.getDefaultValue();
@@ -515,7 +557,7 @@
    }
    // Get the naming argument values.
    List<String> names = getNamingArgValues(namingArgs);
    List<String> names = getNamingArgValues(app, namingArgs);
    // Encode the provided properties.
    List<String> propertyArgs = propertySetArgument.getValues();
@@ -523,13 +565,13 @@
        namingPropertyDefinition, propertyArgs);
    // Add the child managed object.
    ManagedObject<?> parent;
    ManagementContext context = factory.getManagementContext(app);
    MenuResult<ManagedObject<?>> result;
    try {
      parent = getManagedObject(path, names);
      result = getManagedObject(app, context, path, names);
    } catch (AuthorizationException e) {
      Message msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(d.getUserFriendlyName());
      throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS,
          msg);
      throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS, msg);
    } catch (DefinitionDecodingException e) {
      Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_GET_PARENT_DDE.get(ufn, ufn, ufn);
@@ -539,19 +581,32 @@
      Message msg = ERR_DSCFG_ERROR_GET_PARENT_MODE.get(ufn);
      throw new ClientException(LDAPResultCode.OPERATIONS_ERROR, msg);
    } catch (CommunicationException e) {
      Message msg = ERR_DSCFG_ERROR_CREATE_CE.get(
          d.getUserFriendlyName(), e.getMessage());
      Message msg = ERR_DSCFG_ERROR_CREATE_CE.get(d.getUserFriendlyName(), e
          .getMessage());
      throw new ClientException(LDAPResultCode.CLIENT_SIDE_SERVER_DOWN, msg);
    } catch (ConcurrentModificationException e) {
      Message msg = ERR_DSCFG_ERROR_CREATE_CME.get(d.getUserFriendlyName());
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION,
          msg);
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
    } catch (ManagedObjectNotFoundException e) {
      Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_GET_PARENT_MONFE.get(ufn);
      throw new ClientException(LDAPResultCode.NO_SUCH_OBJECT, msg);
    }
    if (result.isQuit()) {
      if (!app.isMenuDrivenMode()) {
        // User chose to cancel creation.
        Message msg = INFO_DSCFG_CONFIRM_CREATE_FAIL.get(d
            .getUserFriendlyName());
        app.printVerboseMessage(msg);
      }
      return MenuResult.quit();
    } else if (result.isCancel()) {
      // Must be menu driven, so no need for error message.
      return MenuResult.cancel();
    }
    ManagedObject<?> parent = result.getValue();
    try {
      ManagedObject<? extends C> child;
      List<DefaultBehaviorException> exceptions =
@@ -561,8 +616,9 @@
          (InstantiableRelationDefinition<C, S>) relation;
        String name = names.get(names.size() - 1);
        if (name == null) {
          if (getConsoleApplication().isInteractive()) {
            child = createChildInteractively(parent, irelation, d, exceptions);
          if (app.isInteractive()) {
            child = createChildInteractively(app, parent, irelation,
                d, exceptions);
          } else {
            throw ArgumentExceptionFactory
                .missingMandatoryNonInteractiveArgument(namingArgs.get(names
@@ -570,7 +626,8 @@
          }
        } else {
          try {
            child = parent.createChild(irelation, d, name, exceptions);
            child = parent.createChild(irelation, d, name,
                exceptions);
          } catch (IllegalManagedObjectNameException e) {
            throw ArgumentExceptionFactory
                .adaptIllegalManagedObjectNameException(e, d);
@@ -591,7 +648,7 @@
      }
      // Interactively set properties if applicable.
      if (getConsoleApplication().isInteractive()) {
      if (app.isInteractive()) {
        SortedSet<PropertyDefinition<?>> properties =
          new TreeSet<PropertyDefinition<?>>();
@@ -600,10 +657,6 @@
            continue;
          }
          if (pd.hasOption(PropertyOption.MONITORING)) {
            continue;
          }
          if (!isAdvancedMode() && pd.hasOption(PropertyOption.ADVANCED)) {
            continue;
          }
@@ -611,72 +664,65 @@
          properties.add(pd);
        }
        PropertyValueReader reader =
          new PropertyValueReader(getConsoleApplication());
        reader.readAll(child, properties);
        PropertyValueEditor editor = new PropertyValueEditor(app);
        MenuResult<Void> result2 = editor.edit(child, properties, true);
        if (result2.isQuit()) {
          if (!app.isMenuDrivenMode()) {
            Message msg = INFO_DSCFG_CONFIRM_CREATE_FAIL.get(d
                .getUserFriendlyName());
            app.printVerboseMessage(msg);
      }
      // Confirm commit.
      Message prompt = INFO_DSCFG_CONFIRM_CREATE.get(d.getUserFriendlyName());
      if (!getConsoleApplication().confirmAction(prompt)) {
        // Output failure message.
        Message msg =
            INFO_DSCFG_CONFIRM_CREATE_FAIL.get(d.getUserFriendlyName());
        getConsoleApplication().printVerboseMessage(msg);
        return 1;
          return MenuResult.quit();
        } else if (result2.isCancel()) {
          return MenuResult.cancel();
        }
      }
      // Add the managed object.
      child.commit();
      // Output success message.
      Message msg =
          INFO_DSCFG_CONFIRM_CREATE_SUCCESS.get(d.getUserFriendlyName());
      getConsoleApplication().printVerboseMessage(msg);
      Message msg = INFO_DSCFG_CONFIRM_CREATE_SUCCESS.get(d
          .getUserFriendlyName());
      app.printVerboseMessage(msg);
    } catch (MissingMandatoryPropertiesException e) {
      throw ArgumentExceptionFactory.adaptMissingMandatoryPropertiesException(
          e, d);
    } catch (AuthorizationException e) {
      Message msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(d.getUserFriendlyName());
      throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS,
          msg);
      throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS, msg);
    } catch (ManagedObjectAlreadyExistsException e) {
      Message msg = ERR_DSCFG_ERROR_CREATE_MOAEE.get(d.getUserFriendlyName());
      throw new ClientException(LDAPResultCode.ENTRY_ALREADY_EXISTS,
          msg);
      throw new ClientException(LDAPResultCode.ENTRY_ALREADY_EXISTS, msg);
    } catch (ConcurrentModificationException e) {
      Message msg = ERR_DSCFG_ERROR_CREATE_CME.get(d.getUserFriendlyName());
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION,
          msg);
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
    } catch (OperationRejectedException e) {
      Message msg = ERR_DSCFG_ERROR_CREATE_ORE.get(
          d.getUserFriendlyName(), e.getMessage());
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION,
          msg);
      Message msg = ERR_DSCFG_ERROR_CREATE_ORE.get(d.getUserFriendlyName(), e
          .getMessage());
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
    } catch (CommunicationException e) {
      Message msg = ERR_DSCFG_ERROR_CREATE_CE.get(
          d.getUserFriendlyName(), e.getMessage());
      Message msg = ERR_DSCFG_ERROR_CREATE_CE.get(d.getUserFriendlyName(), e
          .getMessage());
      throw new ClientException(LDAPResultCode.CLIENT_SIDE_SERVER_DOWN, msg);
    }
    return 0;
    return MenuResult.success(0);
  }
  // Interactively create the child by prompting for the name.
  private ManagedObject<? extends C> createChildInteractively(
      final ManagedObject<?> parent,
      ConsoleApplication app, final ManagedObject<?> parent,
      final InstantiableRelationDefinition<C, S> irelation,
      final ManagedObjectDefinition<? extends C, ? extends S> d,
      final List<DefaultBehaviorException> exceptions)
      throws ArgumentException, ClientException {
    Message msg = INFO_DSCFG_CREATE_NAME_PROMPT.get(d.getUserFriendlyName());
      final List<DefaultBehaviorException> exceptions) throws CLIException {
    ValidationCallback<ManagedObject<? extends C>> validator =
      new ValidationCallback<ManagedObject<? extends C>>() {
      public ManagedObject<? extends C> validate(ConsoleApplication app,
          String input) throws ClientException {
          String input) throws CLIException {
        ManagedObject<? extends C> child;
        // First attempt to create the child, this will guarantee that
@@ -684,10 +730,11 @@
        try {
          child = parent.createChild(irelation, d, input, exceptions);
        } catch (IllegalManagedObjectNameException e) {
          ArgumentException ae = ArgumentExceptionFactory
          CLIException ae = ArgumentExceptionFactory
              .adaptIllegalManagedObjectNameException(e, d);
          app.println();
          app.printMessage(ae.getMessageObject());
          app.println(ae.getMessageObject());
          app.println();
          return null;
        }
@@ -697,19 +744,17 @@
          // Attempt to retrieve a child using this name.
          parent.getChild(irelation, input);
        } catch (AuthorizationException e) {
          Message msg =
              ERR_DSCFG_ERROR_CREATE_AUTHZ.get(irelation.getUserFriendlyName());
          throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS,
              msg);
          Message msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(irelation
              .getUserFriendlyName());
          throw new CLIException(msg);
        } catch (ConcurrentModificationException e) {
          Message msg =
              ERR_DSCFG_ERROR_CREATE_CME.get(irelation.getUserFriendlyName());
          throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
          Message msg = ERR_DSCFG_ERROR_CREATE_CME.get(irelation
              .getUserFriendlyName());
          throw new CLIException(msg);
        } catch (CommunicationException e) {
          Message msg = ERR_DSCFG_ERROR_CREATE_CE.get(
              irelation.getUserFriendlyName(), e.getMessage());
          throw new ClientException(LDAPResultCode.CLIENT_SIDE_SERVER_DOWN,
              msg);
          Message msg = ERR_DSCFG_ERROR_CREATE_CE.get(irelation
              .getUserFriendlyName(), e.getMessage());
          throw new CLIException(msg);
        } catch (DefinitionDecodingException e) {
          // Do nothing.
        } catch (ManagedObjectDecodingException e) {
@@ -720,16 +765,45 @@
        }
        // A child with the specified name must already exist.
        Message msg = ERR_DSCFG_ERROR_CREATE_NAME_ALREADY_EXISTS.get(
                relation.getUserFriendlyName(), input);
        Message msg = ERR_DSCFG_ERROR_CREATE_NAME_ALREADY_EXISTS.get(relation
            .getUserFriendlyName(), input);
        app.println();
        app.printMessage(msg);
        app.println(msg);
        app.println();
        return null;
      }
    };
    return getConsoleApplication().readValidatedInput(msg, validator);
    app.println();
    app.println();
    // Display additional help if the name is a naming property.
    Message ufn = d.getUserFriendlyName();
    PropertyDefinition<?> pd = irelation.getNamingPropertyDefinition();
    if (pd != null) {
      app.println(INFO_DSCFG_CREATE_NAME_PROMPT_NAMING.get(ufn, pd.getName()));
      app.println();
      app.println(pd.getSynopsis(), 4);
      if (pd.getDescription() != null) {
        app.println();
        app.println(pd.getDescription(), 4);
      }
      PropertyDefinitionUsageBuilder b = new PropertyDefinitionUsageBuilder(
          true);
      app.println();
      app.println(INFO_EDITOR_HEADING_SYNTAX.get(b.getUsage(pd)), 4);
      app.println();
      return app.readValidatedInput(INFO_DSCFG_CREATE_NAME_PROMPT_NAMING_CONT
          .get(ufn), validator);
    } else {
      return app.readValidatedInput(INFO_DSCFG_CREATE_NAME_PROMPT.get(ufn),
          validator);
    }
  }
@@ -752,8 +826,8 @@
    // Process its sub-definitions.
    String suffix = "-" + d.getName();
    for (AbstractManagedObjectDefinition<? extends C, ? extends S> c :
        d.getAllChildren()) {
    for (AbstractManagedObjectDefinition<? extends C, ? extends S> c : d
        .getAllChildren()) {
      if (c instanceof ManagedObjectDefinition) {
        ManagedObjectDefinition<? extends C, ? extends S> mod =
          (ManagedObjectDefinition<? extends C, ? extends S>) c;
opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java
@@ -25,12 +25,12 @@
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
import org.opends.messages.Message;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.messages.DSConfigMessages.*;
import static org.opends.messages.ToolMessages.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.tools.ToolConstants.*;
import static org.opends.server.util.StaticUtils.*;
@@ -39,18 +39,21 @@
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import org.opends.messages.Message;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.AttributeTypePropertyDefinition;
import org.opends.server.admin.ClassLoaderProvider;
import org.opends.server.admin.ClassPropertyDefinition;
import org.opends.server.admin.InstantiableRelationDefinition;
import org.opends.server.admin.PropertyException;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.Tag;
import org.opends.server.admin.client.ManagedObjectDecodingException;
import org.opends.server.admin.client.ManagementContext;
import org.opends.server.admin.client.cli.SecureConnectionCliParser;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.tools.ClientException;
@@ -62,6 +65,13 @@
import org.opends.server.util.args.BooleanArgument;
import org.opends.server.util.args.SubCommand;
import org.opends.server.util.args.SubCommandArgumentParser;
import org.opends.server.util.cli.CLIException;
import org.opends.server.util.cli.ConsoleApplication;
import org.opends.server.util.cli.Menu;
import org.opends.server.util.cli.MenuBuilder;
import org.opends.server.util.cli.MenuCallback;
import org.opends.server.util.cli.MenuResult;
import org.opends.server.util.cli.OutputStreamConsoleApplication;
import org.opends.server.util.table.TableBuilder;
import org.opends.server.util.table.TextTablePrinter;
@@ -74,6 +84,172 @@
public final class DSConfig extends ConsoleApplication {
  /**
   * A menu call-back which runs a sub-command interactively.
   */
  public class SubCommandHandlerMenuCallback implements MenuCallback<Integer> {
    // The sub-command handler.
    private final SubCommandHandler handler;
    /**
     * Creates a new sub-command handler call-back.
     *
     * @param handler
     *          The sub-command handler.
     */
    public SubCommandHandlerMenuCallback(SubCommandHandler handler) {
      this.handler = handler;
    }
    /**
     * {@inheritDoc}
     */
    public MenuResult<Integer> invoke(ConsoleApplication app)
        throws CLIException {
      try {
        MenuResult<Integer> result = handler.run(app, factory);
        if (result.isQuit()) {
          return result;
        } else {
          // Success or cancel.
          app.println();
          app.pressReturnToContinue();
          return MenuResult.again();
        }
      } catch (ArgumentException e) {
        app.println(e.getMessageObject());
        return MenuResult.success(1);
      } catch (ClientException e) {
        app.println(e.getMessageObject());
        return MenuResult.success(e.getExitCode());
      }
    }
  }
  /**
   * The interactive mode sub-menu implementation.
   */
  public class SubMenuCallback implements MenuCallback<Integer> {
    // The menu.
    private final Menu<Integer> menu;
    /**
     * Creates a new sub-menu implementation.
     *
     * @param app
     *          The console application.
     * @param rd
     *          The relation definition.
     * @param ch
     *          The optional create sub-command.
     * @param dh
     *          The optional delete sub-command.
     * @param lh
     *          The optional list sub-command.
     * @param sh
     *          The option set-prop sub-command.
     */
    public SubMenuCallback(ConsoleApplication app, RelationDefinition<?, ?> rd,
        CreateSubCommandHandler<?, ?> ch, DeleteSubCommandHandler dh,
        ListSubCommandHandler lh, SetPropSubCommandHandler sh) {
      Message ufn = rd.getUserFriendlyName();
      Message ufpn = null;
      if (rd instanceof InstantiableRelationDefinition) {
        InstantiableRelationDefinition<?, ?> ir =
          (InstantiableRelationDefinition<?, ?>) rd;
        ufpn = ir.getUserFriendlyPluralName();
      }
      MenuBuilder<Integer> builder = new MenuBuilder<Integer>(app);
      builder.setTitle(INFO_DSCFG_HEADING_COMPONENT_MENU_TITLE.get(ufn));
      builder.setPrompt(INFO_DSCFG_HEADING_COMPONENT_MENU_PROMPT.get());
      if (lh != null) {
        SubCommandHandlerMenuCallback callback =
          new SubCommandHandlerMenuCallback(lh);
        if (ufpn != null) {
          builder.addNumberedOption(
              INFO_DSCFG_OPTION_COMPONENT_MENU_LIST_PLURAL.get(ufpn), callback);
        } else {
          builder
              .addNumberedOption(INFO_DSCFG_OPTION_COMPONENT_MENU_LIST_SINGULAR
                  .get(ufn), callback);
        }
      }
      if (ch != null) {
        SubCommandHandlerMenuCallback callback =
          new SubCommandHandlerMenuCallback(ch);
        builder.addNumberedOption(INFO_DSCFG_OPTION_COMPONENT_MENU_CREATE
            .get(ufn), callback);
      }
      if (sh != null) {
        SubCommandHandlerMenuCallback callback =
          new SubCommandHandlerMenuCallback(sh);
        if (ufpn != null) {
          builder
              .addNumberedOption(INFO_DSCFG_OPTION_COMPONENT_MENU_MODIFY_PLURAL
                  .get(ufn), callback);
        } else {
          builder.addNumberedOption(
              INFO_DSCFG_OPTION_COMPONENT_MENU_MODIFY_SINGULAR.get(ufn),
              callback);
        }
      }
      if (dh != null) {
        SubCommandHandlerMenuCallback callback =
          new SubCommandHandlerMenuCallback(dh);
        builder.addNumberedOption(INFO_DSCFG_OPTION_COMPONENT_MENU_DELETE
            .get(ufn), callback);
      }
      builder.addBackOption(true);
      builder.addQuitOption();
      this.menu = builder.toMenu();
    }
    /**
     * {@inheritDoc}
     */
    public final MenuResult<Integer> invoke(ConsoleApplication app)
        throws CLIException {
      try {
        app.println();
        app.println();
        MenuResult<Integer> result = menu.run();
        if (result.isCancel()) {
          return MenuResult.again();
        }
        return result;
      } catch (CLIException e) {
        app.println(e.getMessageObject());
        return MenuResult.success(1);
      }
    }
  }
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
@@ -126,7 +302,7 @@
        app.initializeClientEnvironment();
      } catch (InitializationException e) {
        // TODO: is this ok as an error message?
        app.printMessage(e.getMessageObject());
        app.println(e.getMessageObject());
        return 1;
      }
    }
@@ -147,10 +323,16 @@
  // already been initialized.
  private boolean globalArgumentsInitialized = false;
  // The sub-command handler factory.
  private SubCommandHandlerFactory handlerFactory = null;
  // Mapping of sub-commands to their implementations;
  private final Map<SubCommand, SubCommandHandler> handlers =
    new HashMap<SubCommand, SubCommandHandler>();
  // Indicates whether or not a sub-command was provided.
  private boolean hasSubCommand = true;
  // The argument which should be used to request non interactive
  // behavior.
  private BooleanArgument noPromptArgument;
@@ -168,10 +350,6 @@
  // The argument which should be used to request usage information.
  private BooleanArgument showUsageArgument;
  // Flag indicating whether or not the sub-commands have
  // already been initialized.
  private boolean subCommandsInitialized = false;
  // The argument which should be used to request verbose output.
  private BooleanArgument verboseArgument;
@@ -240,6 +418,16 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isMenuDrivenMode() {
    return !hasSubCommand;
  }
  /**
   * {@inheritDoc}
   */
  public boolean isQuiet() {
    return quietArgument.isPresent();
  }
@@ -264,21 +452,11 @@
  /**
   * {@inheritDoc}
   */
  public ManagementContext getManagementContext() throws ArgumentException,
      ClientException {
    return factory.getManagementContext(this);
  }
  // Displays the provided message followed by a help usage reference.
  private void displayMessageAndUsageReference(Message message) {
    printMessage(message);
    printMessage(Message.EMPTY);
    printMessage(parser.getHelpUsageReference());
    println(message);
    println();
    println(parser.getHelpUsageReference());
  }
@@ -297,8 +475,8 @@
      quietArgument = new BooleanArgument(
          SecureConnectionCliParser.QUIET_OPTION_LONG,
          SecureConnectionCliParser.QUIET_OPTION_SHORT,
          SecureConnectionCliParser.QUIET_OPTION_LONG,
          INFO_DESCRIPTION_QUIET.get());
          SecureConnectionCliParser.QUIET_OPTION_LONG, INFO_DESCRIPTION_QUIET
              .get());
      scriptFriendlyArgument = new BooleanArgument("script-friendly", 's',
          "script-friendly", INFO_DESCRIPTION_SCRIPT_FRIENDLY.get());
@@ -310,8 +488,8 @@
          INFO_DESCRIPTION_NO_PROMPT.get());
      showUsageArgument = new BooleanArgument("showUsage", OPTION_SHORT_HELP,
              OPTION_LONG_HELP,
              INFO_DSCFG_DESCRIPTION_SHOW_GROUP_USAGE_SUMMARY.get());
          OPTION_LONG_HELP, INFO_DSCFG_DESCRIPTION_SHOW_GROUP_USAGE_SUMMARY
              .get());
      // Register the global arguments.
      parser.addGlobalArgument(showUsageArgument);
@@ -340,7 +518,9 @@
   *           If a sub-command could not be created.
   */
  private void initializeSubCommands() throws ArgumentException {
    if (subCommandsInitialized == false) {
    if (handlerFactory == null) {
      handlerFactory = new SubCommandHandlerFactory(parser);
      Comparator<SubCommand> c = new Comparator<SubCommand>() {
        public int compare(SubCommand o1, SubCommand o2) {
@@ -348,12 +528,11 @@
        }
      };
      SubCommandBuilder builder = new SubCommandBuilder();
      Map<Tag, SortedSet<SubCommand>> groups =
        new TreeMap<Tag, SortedSet<SubCommand>>();
      SortedSet<SubCommand> allSubCommands = new TreeSet<SubCommand>(c);
      for (SubCommandHandler handler : builder.getSubCommandHandlers(this,
          parser)) {
      for (SubCommandHandler handler : handlerFactory
          .getAllSubCommandHandlers()) {
        SubCommand sc = handler.getSubCommand();
        handlers.put(sc, handler);
@@ -391,8 +570,6 @@
      parser.addGlobalArgument(arg);
      parser.setUsageGroupArgument(arg, allSubCommands);
      subCommandsInitialized = true;
    }
  }
@@ -415,7 +592,7 @@
      initializeSubCommands();
    } catch (ArgumentException e) {
      Message message = ERR_CANNOT_INITIALIZE_ARGS.get(e.getMessage());
      printMessage(message);
      println(message);
      return 1;
    }
@@ -434,26 +611,18 @@
      return 0;
    }
    // Make sure that we have a sub-command.
    if (parser.getSubCommand() == null) {
      Message message = ERR_ERROR_PARSING_ARGS.get(
              ERR_DSCFG_ERROR_MISSING_SUBCOMMAND.get());
      displayMessageAndUsageReference(message);
      return 1;
    }
    // Check for conflicting arguments.
    if (quietArgument.isPresent() && verboseArgument.isPresent()) {
      Message message = ERR_TOOL_CONFLICTING_ARGS.get(
              quietArgument.getLongIdentifier(),
          verboseArgument.getLongIdentifier());
      Message message = ERR_TOOL_CONFLICTING_ARGS.get(quietArgument
          .getLongIdentifier(), verboseArgument.getLongIdentifier());
      displayMessageAndUsageReference(message);
      return 1;
    }
    if (quietArgument.isPresent() && !noPromptArgument.isPresent()) {
      Message message = ERR_DSCFG_ERROR_QUIET_AND_INTERACTIVE_INCOMPATIBLE.get(
              quietArgument.getLongIdentifier(),
          noPromptArgument.getLongIdentifier());
          quietArgument.getLongIdentifier(), noPromptArgument
              .getLongIdentifier());
      displayMessageAndUsageReference(message);
      return 1;
    }
@@ -469,28 +638,177 @@
    try {
      factory.validateGlobalArguments();
    } catch (ArgumentException e) {
      printMessage(e.getMessageObject());
      println(e.getMessageObject());
      return 1;
    }
    if (parser.getSubCommand() == null) {
      hasSubCommand = false;
      if (isInteractive()) {
        // Top-level interactive mode.
        return runInteractiveMode();
      } else {
        Message message = ERR_ERROR_PARSING_ARGS
            .get(ERR_DSCFG_ERROR_MISSING_SUBCOMMAND.get());
        displayMessageAndUsageReference(message);
        return 1;
      }
    } else {
      hasSubCommand = true;
    // Retrieve the sub-command implementation and run it.
    SubCommandHandler handler = handlers.get(parser.getSubCommand());
      return runSubCommand(handler);
    }
  }
  // Run the top-level interactive console.
  private int runInteractiveMode() {
    // In interactive mode, redirect all output to stdout.
    ConsoleApplication app = new OutputStreamConsoleApplication(this);
    // Build menu structure.
    Comparator<RelationDefinition<?, ?>> c =
      new Comparator<RelationDefinition<?, ?>>() {
      public int compare(RelationDefinition<?, ?> rd1,
          RelationDefinition<?, ?> rd2) {
        String s1 = rd1.getUserFriendlyName().toString();
        String s2 = rd2.getUserFriendlyName().toString();
        return s1.compareToIgnoreCase(s2);
      }
    };
    Set<RelationDefinition<?, ?>> relations;
    Map<RelationDefinition<?, ?>, CreateSubCommandHandler<?, ?>> createHandlers;
    Map<RelationDefinition<?, ?>, DeleteSubCommandHandler> deleteHandlers;
    Map<RelationDefinition<?, ?>, ListSubCommandHandler> listHandlers;
    Map<RelationDefinition<?, ?>, GetPropSubCommandHandler> getPropHandlers;
    Map<RelationDefinition<?, ?>, SetPropSubCommandHandler> setPropHandlers;
    relations = new TreeSet<RelationDefinition<?, ?>>(c);
    createHandlers =
      new HashMap<RelationDefinition<?, ?>, CreateSubCommandHandler<?, ?>>();
    deleteHandlers =
      new HashMap<RelationDefinition<?, ?>, DeleteSubCommandHandler>();
    listHandlers =
      new HashMap<RelationDefinition<?, ?>, ListSubCommandHandler>();
    getPropHandlers =
      new HashMap<RelationDefinition<?, ?>, GetPropSubCommandHandler>();
    setPropHandlers =
      new HashMap<RelationDefinition<?, ?>, SetPropSubCommandHandler>();
    for (CreateSubCommandHandler<?, ?> ch : handlerFactory
        .getCreateSubCommandHandlers()) {
      relations.add(ch.getRelationDefinition());
      createHandlers.put(ch.getRelationDefinition(), ch);
    }
    for (DeleteSubCommandHandler dh : handlerFactory
        .getDeleteSubCommandHandlers()) {
      relations.add(dh.getRelationDefinition());
      deleteHandlers.put(dh.getRelationDefinition(), dh);
    }
    for (ListSubCommandHandler lh :
      handlerFactory.getListSubCommandHandlers()) {
      relations.add(lh.getRelationDefinition());
      listHandlers.put(lh.getRelationDefinition(), lh);
    }
    for (GetPropSubCommandHandler gh : handlerFactory
        .getGetPropSubCommandHandlers()) {
      relations.add(gh.getRelationDefinition());
      getPropHandlers.put(gh.getRelationDefinition(), gh);
    }
    for (SetPropSubCommandHandler sh : handlerFactory
        .getSetPropSubCommandHandlers()) {
      relations.add(sh.getRelationDefinition());
      setPropHandlers.put(sh.getRelationDefinition(), sh);
    }
    // Main menu.
    MenuBuilder<Integer> builder = new MenuBuilder<Integer>(app);
    builder.setTitle(INFO_DSCFG_HEADING_MAIN_MENU_TITLE.get());
    builder.setPrompt(INFO_DSCFG_HEADING_MAIN_MENU_PROMPT.get());
    builder.setMultipleColumnThreshold(0);
    for (RelationDefinition<?, ?> rd : relations) {
      MenuCallback<Integer> callback = new SubMenuCallback(app, rd,
          createHandlers.get(rd), deleteHandlers.get(rd), listHandlers.get(rd),
          setPropHandlers.get(rd));
      builder.addNumberedOption(rd.getUserFriendlyName(), callback);
    }
    builder.addQuitOption();
    Menu<Integer> menu = builder.toMenu();
    try {
      return handler.run();
      // Force retrieval of management context.
      factory.getManagementContext(app);
    } catch (ArgumentException e) {
      printMessage(e.getMessageObject());
      app.println(e.getMessageObject());
      return 1;
    } catch (ClientException e) {
      app.println(e.getMessageObject());
      return 1;
    }
    try {
      app.println();
      app.println();
      MenuResult<Integer> result = menu.run();
      if (result.isQuit()) {
        return 0;
      } else {
        return result.getValue();
      }
    } catch (CLIException e) {
      app.println(e.getMessageObject());
      return 1;
    }
  }
  // Run the provided sub-command handler.
  private int runSubCommand(SubCommandHandler handler) {
    try {
      MenuResult<Integer> result = handler.run(this, factory);
      if (result.isSuccess()) {
        return result.getValue();
      } else {
        // User must have quit.
        return 1;
      }
    } catch (ArgumentException e) {
      println(e.getMessageObject());
      return 1;
    } catch (CLIException e) {
      println(e.getMessageObject());
      return 1;
    } catch (ClientException e) {
      // If the client exception was caused by a decoding exception
      // then we should display the causes.
      printMessage(e.getMessageObject());
      println(e.getMessageObject());
      Throwable cause = e.getCause();
      if (cause instanceof ManagedObjectDecodingException) {
        ManagedObjectDecodingException de =
          (ManagedObjectDecodingException) cause;
        printMessage(Message.EMPTY);
        println();
        TableBuilder builder = new TableBuilder();
        for (PropertyException pe : de.getCauses()) {
          AbstractManagedObjectDefinition<?, ?> d = de
@@ -506,7 +824,7 @@
        printer.setDisplayHeadings(false);
        printer.setColumnWidth(1, 0);
        builder.print(printer);
        printMessage(Message.EMPTY);
        println();
      }
      return 1;
@@ -514,7 +832,7 @@
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      printMessage(Message.raw(StaticUtils.stackTraceToString(e)));
      println(Message.raw(StaticUtils.stackTraceToString(e)));
      return 1;
    }
  }
opends/src/server/org/opends/server/tools/dsconfig/DeleteSubCommandHandler.java
@@ -25,14 +25,14 @@
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
import org.opends.messages.Message;
import static org.opends.messages.ToolMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import java.util.List;
import org.opends.messages.Message;
import org.opends.server.admin.DefinitionDecodingException;
import org.opends.server.admin.InstantiableRelationDefinition;
import org.opends.server.admin.ManagedObjectNotFoundException;
@@ -44,6 +44,7 @@
import org.opends.server.admin.client.ConcurrentModificationException;
import org.opends.server.admin.client.ManagedObject;
import org.opends.server.admin.client.ManagedObjectDecodingException;
import org.opends.server.admin.client.ManagementContext;
import org.opends.server.admin.client.OperationRejectedException;
import org.opends.server.protocols.ldap.LDAPResultCode;
import org.opends.server.tools.ClientException;
@@ -52,6 +53,9 @@
import org.opends.server.util.args.StringArgument;
import org.opends.server.util.args.SubCommand;
import org.opends.server.util.args.SubCommandArgumentParser;
import org.opends.server.util.cli.CLIException;
import org.opends.server.util.cli.ConsoleApplication;
import org.opends.server.util.cli.MenuResult;
@@ -79,8 +83,6 @@
   * Creates a new delete-xxx sub-command for an instantiable
   * relation.
   *
   * @param app
   *          The console application.
   * @param parser
   *          The sub-command argument parser.
   * @param p
@@ -91,10 +93,10 @@
   * @throws ArgumentException
   *           If the sub-command could not be created successfully.
   */
  public static DeleteSubCommandHandler create(ConsoleApplication app,
  public static DeleteSubCommandHandler create(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p,
      InstantiableRelationDefinition<?, ?> r) throws ArgumentException {
    return new DeleteSubCommandHandler(app, parser, p, r, p.child(r, "DUMMY"));
    return new DeleteSubCommandHandler(parser, p, r, p.child(r, "DUMMY"));
  }
@@ -102,8 +104,6 @@
  /**
   * Creates a new delete-xxx sub-command for an optional relation.
   *
   * @param app
   *          The console application.
   * @param parser
   *          The sub-command argument parser.
   * @param p
@@ -114,10 +114,10 @@
   * @throws ArgumentException
   *           If the sub-command could not be created successfully.
   */
  public static DeleteSubCommandHandler create(ConsoleApplication app,
  public static DeleteSubCommandHandler create(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p,
      OptionalRelationDefinition<?, ?> r) throws ArgumentException {
    return new DeleteSubCommandHandler(app, parser, p, r, p.child(r));
    return new DeleteSubCommandHandler(parser, p, r, p.child(r));
  }
  // The argument which should be used to force deletion.
@@ -139,12 +139,10 @@
  // Private constructor.
  private DeleteSubCommandHandler(ConsoleApplication app,
  private DeleteSubCommandHandler(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p,
      RelationDefinition<?, ?> r, ManagedObjectPath<?, ?> c)
      throws ArgumentException {
    super(app);
    this.path = p;
    this.relation = r;
@@ -171,6 +169,19 @@
  /**
   * Gets the relation definition associated with the type of
   * component that this sub-command handles.
   *
   * @return Returns the relation definition associated with the type
   *         of component that this sub-command handles.
   */
  public RelationDefinition<?, ?> getRelationDefinition() {
    return relation;
  }
  /**
   * {@inheritDoc}
   */
  @Override
@@ -184,19 +195,21 @@
   * {@inheritDoc}
   */
  @Override
  public int run() throws ArgumentException, ClientException {
  public MenuResult<Integer> run(ConsoleApplication app,
      ManagementContextFactory factory) throws ArgumentException,
      ClientException, CLIException {
    // Get the naming argument values.
    List<String> names = getNamingArgValues(namingArgs);
    List<String> names = getNamingArgValues(app, namingArgs);
    // Delete the child managed object.
    ManagedObject<?> parent = null;
    ManagementContext context = factory.getManagementContext(app);
    MenuResult<ManagedObject<?>> result;
    try {
      parent = getManagedObject(path, names);
      result = getManagedObject(app, context, path, names);
    } catch (AuthorizationException e) {
      Message msg =
          ERR_DSCFG_ERROR_DELETE_AUTHZ.get(relation.getUserFriendlyName());
      throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS,
          msg);
      Message msg = ERR_DSCFG_ERROR_DELETE_AUTHZ.get(relation
          .getUserFriendlyName());
      throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS, msg);
    } catch (DefinitionDecodingException e) {
      Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_GET_PARENT_DDE.get(ufn, ufn, ufn);
@@ -206,95 +219,124 @@
      Message msg = ERR_DSCFG_ERROR_GET_PARENT_MODE.get(ufn);
      throw new ClientException(LDAPResultCode.OPERATIONS_ERROR, msg);
    } catch (CommunicationException e) {
      Message msg = ERR_DSCFG_ERROR_DELETE_CE.get(
          relation.getUserFriendlyName(), e.getMessage());
      Message msg = ERR_DSCFG_ERROR_DELETE_CE.get(relation
          .getUserFriendlyName(), e.getMessage());
      throw new ClientException(LDAPResultCode.CLIENT_SIDE_SERVER_DOWN, msg);
    } catch (ConcurrentModificationException e) {
      Message msg =
          ERR_DSCFG_ERROR_DELETE_CME.get(relation.getUserFriendlyName());
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION,
          msg);
      Message msg = ERR_DSCFG_ERROR_DELETE_CME.get(relation
          .getUserFriendlyName());
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
    } catch (ManagedObjectNotFoundException e) {
      // Ignore the error if the deletion is being forced.
      if (!forceArgument.isPresent()) {
        Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
        Message msg = ERR_DSCFG_ERROR_GET_PARENT_MONFE.get(ufn);
        throw new ClientException(LDAPResultCode.NO_SUCH_OBJECT, msg);
      } else {
        return MenuResult.success(0);
      }
    }
    if (parent != null) {
    if (result.isQuit()) {
      if (!app.isMenuDrivenMode()) {
        // User chose to cancel deletion.
        Message msg = INFO_DSCFG_CONFIRM_DELETE_FAIL.get(relation
            .getUserFriendlyName());
        app.printVerboseMessage(msg);
      }
      return MenuResult.quit();
    } else if (result.isCancel()) {
      // Must be menu driven, so no need for error message.
      return MenuResult.cancel();
    }
    ManagedObject<?> parent = result.getValue();
      try {
        if (relation instanceof InstantiableRelationDefinition) {
          InstantiableRelationDefinition<?, ?> irelation =
            (InstantiableRelationDefinition<?, ?>) relation;
          String childName = names.get(names.size() - 1);
          if (childName == null) {
            childName = readChildName(parent, irelation, null);
          MenuResult<String> sresult =
            readChildName(app, parent, irelation, null);
          if (sresult.isQuit()) {
            if (!app.isMenuDrivenMode()) {
              // User chose to cancel deletion.
              Message msg = INFO_DSCFG_CONFIRM_DELETE_FAIL.get(relation
                  .getUserFriendlyName());
              app.printVerboseMessage(msg);
            }
            return MenuResult.quit();
          } else if (sresult.isCancel()) {
            // Must be menu driven, so no need for error message.
            return MenuResult.cancel();
          } else {
            childName = sresult.getValue();
          }
          }
          if (confirmDeletion()) {
        if (confirmDeletion(app)) {
            parent.removeChild(irelation, childName);
          } else {
            return 1;
          return MenuResult.cancel();
          }
        } else if (relation instanceof OptionalRelationDefinition) {
          OptionalRelationDefinition<?, ?> orelation =
            (OptionalRelationDefinition<?, ?>) relation;
          if (confirmDeletion()) {
        if (confirmDeletion(app)) {
            parent.removeChild(orelation);
          } else {
            return 1;
          return MenuResult.cancel();
          }
        }
      } catch (AuthorizationException e) {
        Message msg =
            ERR_DSCFG_ERROR_DELETE_AUTHZ.get(relation.getUserFriendlyName());
        throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS,
            msg);
      Message msg = ERR_DSCFG_ERROR_DELETE_AUTHZ.get(relation
          .getUserFriendlyName());
      throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS, msg);
      } catch (OperationRejectedException e) {
        Message msg = ERR_DSCFG_ERROR_DELETE_ORE.get(
            relation.getUserFriendlyName(), e.getMessage());
      Message msg = ERR_DSCFG_ERROR_DELETE_ORE.get(relation
          .getUserFriendlyName(), e.getMessage());
        throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
      } catch (ManagedObjectNotFoundException e) {
        // Ignore the error if the deletion is being forced.
        if (!forceArgument.isPresent()) {
          Message msg =
              ERR_DSCFG_ERROR_DELETE_MONFE.get(relation.getUserFriendlyName());
        Message msg = ERR_DSCFG_ERROR_DELETE_MONFE.get(relation
            .getUserFriendlyName());
          throw new ClientException(LDAPResultCode.NO_SUCH_OBJECT, msg);
        }
      } catch (ConcurrentModificationException e) {
        Message msg =
            ERR_DSCFG_ERROR_DELETE_CME.get(relation.getUserFriendlyName());
      Message msg = ERR_DSCFG_ERROR_DELETE_CME.get(relation
          .getUserFriendlyName());
        throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
      } catch (CommunicationException e) {
        Message msg = ERR_DSCFG_ERROR_DELETE_CE.get(
            relation.getUserFriendlyName(), e.getMessage());
        throw new ClientException(LDAPResultCode.CLIENT_SIDE_SERVER_DOWN,
            msg);
      }
      Message msg = ERR_DSCFG_ERROR_DELETE_CE.get(relation
          .getUserFriendlyName(), e.getMessage());
      throw new ClientException(LDAPResultCode.CLIENT_SIDE_SERVER_DOWN, msg);
    }
    // Output success message.
    Message msg =
        INFO_DSCFG_CONFIRM_DELETE_SUCCESS.get(relation.getUserFriendlyName());
    getConsoleApplication().printVerboseMessage(msg);
    Message msg = INFO_DSCFG_CONFIRM_DELETE_SUCCESS.get(relation
        .getUserFriendlyName());
    app.printVerboseMessage(msg);
    return 0;
    return MenuResult.success(0);
  }
  // Confirm deletion.
  private boolean confirmDeletion() throws ArgumentException {
    Message prompt =
        INFO_DSCFG_CONFIRM_DELETE.get(relation.getUserFriendlyName());
    if (!getConsoleApplication().confirmAction(prompt)) {
  private boolean confirmDeletion(ConsoleApplication app) throws CLIException {
    Message prompt = INFO_DSCFG_CONFIRM_DELETE.get(relation
        .getUserFriendlyName());
    app.println();
    if (!app.confirmAction(prompt, false)) {
      // Output failure message.
      Message msg =
          INFO_DSCFG_CONFIRM_DELETE_FAIL.get(relation.getUserFriendlyName());
      getConsoleApplication().printVerboseMessage(msg);
      Message msg = INFO_DSCFG_CONFIRM_DELETE_FAIL.get(relation
          .getUserFriendlyName());
      app.printVerboseMessage(msg);
      return false;
    } else {
      return true;
opends/src/server/org/opends/server/tools/dsconfig/GetPropSubCommandHandler.java
@@ -25,11 +25,10 @@
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
import org.opends.messages.Message;
import static org.opends.messages.ToolMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import java.io.PrintStream;
import java.util.Collection;
@@ -38,6 +37,7 @@
import java.util.Set;
import java.util.SortedSet;
import org.opends.messages.Message;
import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
import org.opends.server.admin.AliasDefaultBehaviorProvider;
import org.opends.server.admin.DefaultBehaviorProviderVisitor;
@@ -59,12 +59,16 @@
import org.opends.server.admin.client.ConcurrentModificationException;
import org.opends.server.admin.client.ManagedObject;
import org.opends.server.admin.client.ManagedObjectDecodingException;
import org.opends.server.admin.client.ManagementContext;
import org.opends.server.protocols.ldap.LDAPResultCode;
import org.opends.server.tools.ClientException;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.args.StringArgument;
import org.opends.server.util.args.SubCommand;
import org.opends.server.util.args.SubCommandArgumentParser;
import org.opends.server.util.cli.CLIException;
import org.opends.server.util.cli.ConsoleApplication;
import org.opends.server.util.cli.MenuResult;
import org.opends.server.util.table.TableBuilder;
import org.opends.server.util.table.TablePrinter;
import org.opends.server.util.table.TextTablePrinter;
@@ -83,8 +87,6 @@
   * Creates a new get-xxx-prop sub-command for an instantiable
   * relation.
   *
   * @param app
   *          The console application.
   * @param parser
   *          The sub-command argument parser.
   * @param path
@@ -95,10 +97,10 @@
   * @throws ArgumentException
   *           If the sub-command could not be created successfully.
   */
  public static GetPropSubCommandHandler create(ConsoleApplication app,
  public static GetPropSubCommandHandler create(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path,
      InstantiableRelationDefinition<?, ?> r) throws ArgumentException {
    return new GetPropSubCommandHandler(app, parser, path.child(r, "DUMMY"), r);
    return new GetPropSubCommandHandler(parser, path.child(r, "DUMMY"), r);
  }
@@ -106,8 +108,6 @@
  /**
   * Creates a new get-xxx-prop sub-command for an optional relation.
   *
   * @param app
   *          The console application.
   * @param parser
   *          The sub-command argument parser.
   * @param path
@@ -118,10 +118,10 @@
   * @throws ArgumentException
   *           If the sub-command could not be created successfully.
   */
  public static GetPropSubCommandHandler create(ConsoleApplication app,
  public static GetPropSubCommandHandler create(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path,
      OptionalRelationDefinition<?, ?> r) throws ArgumentException {
    return new GetPropSubCommandHandler(app, parser, path.child(r), r);
    return new GetPropSubCommandHandler(parser, path.child(r), r);
  }
@@ -129,8 +129,6 @@
  /**
   * Creates a new get-xxx-prop sub-command for a singleton relation.
   *
   * @param app
   *          The console application.
   * @param parser
   *          The sub-command argument parser.
   * @param path
@@ -141,10 +139,10 @@
   * @throws ArgumentException
   *           If the sub-command could not be created successfully.
   */
  public static GetPropSubCommandHandler create(ConsoleApplication app,
  public static GetPropSubCommandHandler create(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path,
      SingletonRelationDefinition<?, ?> r) throws ArgumentException {
    return new GetPropSubCommandHandler(app, parser, path.child(r), r);
    return new GetPropSubCommandHandler(parser, path.child(r), r);
  }
  // The sub-commands naming arguments.
@@ -159,17 +157,15 @@
  // Private constructor.
  private GetPropSubCommandHandler(ConsoleApplication app,
  private GetPropSubCommandHandler(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path,
      RelationDefinition<?, ?> r) throws ArgumentException {
    super(app);
    this.path = path;
    // Create the sub-command.
    String name = "get-" + r.getName() + "-prop";
    Message message = INFO_DSCFG_DESCRIPTION_SUBCMD_GETPROP.get(
            r.getChildDefinition().getUserFriendlyName());
    Message message = INFO_DSCFG_DESCRIPTION_SUBCMD_GETPROP.get(r
        .getChildDefinition().getUserFriendlyName());
    this.subCommand = new SubCommand(parser, name, false, 0, 0, null, message);
    // Create the naming arguments.
@@ -190,6 +186,19 @@
  /**
   * Gets the relation definition associated with the type of
   * component that this sub-command handles.
   *
   * @return Returns the relation definition associated with the type
   *         of component that this sub-command handles.
   */
  public RelationDefinition<?, ?> getRelationDefinition() {
    return path.getRelationDefinition();
  }
  /**
   * {@inheritDoc}
   */
  @Override
@@ -203,47 +212,51 @@
   * {@inheritDoc}
   */
  @Override
  public int run() throws ArgumentException, ClientException {
  public MenuResult<Integer> run(ConsoleApplication app,
      ManagementContextFactory factory) throws ArgumentException,
      ClientException, CLIException {
    // Get the property names.
    Set<String> propertyNames = getPropertyNames();
    PropertyValuePrinter valuePrinter = new PropertyValuePrinter(getSizeUnit(),
        getTimeUnit(), getConsoleApplication().isScriptFriendly());
        getTimeUnit(), app.isScriptFriendly());
    // Get the naming argument values.
    List<String> names = getNamingArgValues(namingArgs);
    List<String> names = getNamingArgValues(app, namingArgs);
    // Get the targeted managed object.
    ManagedObject<?> child;
    Message ufn = path.getRelationDefinition().getUserFriendlyName();
    ManagementContext context = factory.getManagementContext(app);
    MenuResult<ManagedObject<?>> result;
    try {
      child = getManagedObject(path, names);
      result = getManagedObject(app, context, path, names);
    } catch (AuthorizationException e) {
      Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_GET_CHILD_AUTHZ.get(ufn);
      throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS,
          msg);
      throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS, msg);
    } catch (DefinitionDecodingException e) {
      Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_GET_CHILD_DDE.get(ufn, ufn, ufn);
      throw new ClientException(LDAPResultCode.OPERATIONS_ERROR, msg);
    } catch (ManagedObjectDecodingException e) {
      Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_GET_CHILD_MODE.get(ufn);
      throw new ClientException(LDAPResultCode.OPERATIONS_ERROR, msg);
    } catch (CommunicationException e) {
      Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_GET_CHILD_CE.get(ufn, e.getMessage());
      throw new ClientException(LDAPResultCode.CLIENT_SIDE_SERVER_DOWN, msg);
    } catch (ConcurrentModificationException e) {
      Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_GET_CHILD_CME.get(ufn);
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
    } catch (ManagedObjectNotFoundException e) {
      Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_GET_CHILD_MONFE.get(ufn);
      throw new ClientException(LDAPResultCode.NO_SUCH_OBJECT, msg);
    }
    if (result.isQuit()) {
      return MenuResult.quit();
    } else if (result.isCancel()) {
      return MenuResult.cancel();
    }
    // Validate the property names.
    ManagedObject<?> child = result.getValue();
    ManagedObjectDefinition<?, ?> d = child.getManagedObjectDefinition();
    Collection<PropertyDefinition<?>> pdList;
    if (propertyNames.isEmpty()) {
@@ -274,12 +287,12 @@
      }
      if (propertyNames.isEmpty() || propertyNames.contains(pd.getName())) {
        displayProperty(builder, child, pd, valuePrinter);
        displayProperty(app, builder, child, pd, valuePrinter);
      }
    }
    PrintStream out = getConsoleApplication().getOutputStream();
    if (getConsoleApplication().isScriptFriendly()) {
    PrintStream out = app.getOutputStream();
    if (app.isScriptFriendly()) {
      TablePrinter printer = createScriptFriendlyTablePrinter(out);
      builder.print(printer);
    } else {
@@ -289,14 +302,15 @@
      builder.print(printer);
    }
    return 0;
    return MenuResult.success(0);
  }
  // Display the set of values associated with a property.
  private <T> void displayProperty(TableBuilder builder, ManagedObject<?> mo,
      PropertyDefinition<T> pd, PropertyValuePrinter valuePrinter) {
  private <T> void displayProperty(final ConsoleApplication app,
      TableBuilder builder, ManagedObject<?> mo, PropertyDefinition<T> pd,
      PropertyValuePrinter valuePrinter) {
    SortedSet<T> values = mo.getPropertyValues(pd);
    if (values.isEmpty()) {
      // There are no values or default values. Display the default
@@ -314,7 +328,7 @@
        public Message visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) {
          if (getConsoleApplication().isVerbose()) {
          if (app.isVerbose()) {
            return d.getSynopsis();
          } else {
            return null;
@@ -352,7 +366,7 @@
      Message content = pd.getDefaultBehaviorProvider().accept(visitor, null);
      if (content == null) {
        if (getConsoleApplication().isScriptFriendly()) {
        if (app.isScriptFriendly()) {
          builder.appendCell();
        } else {
          builder.appendCell("-");
@@ -371,7 +385,7 @@
        builder.startRow();
        builder.appendCell(pd.getName());
        if (getConsoleApplication().isScriptFriendly()) {
        if (app.isScriptFriendly()) {
          for (T value : values) {
            builder.appendCell(valuePrinter.print(pd, value));
          }
opends/src/server/org/opends/server/tools/dsconfig/HelpSubCommandHandler.java
@@ -26,16 +26,14 @@
 */
package org.opends.server.tools.dsconfig;
import org.opends.messages.MessageBuilder;
import org.opends.messages.Message;
import static org.opends.messages.ToolMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import static org.opends.messages.UtilityMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedList;
@@ -45,6 +43,8 @@
import java.util.TreeMap;
import java.util.TreeSet;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.AdministratorAction;
@@ -67,6 +67,10 @@
import org.opends.server.util.args.StringArgument;
import org.opends.server.util.args.SubCommand;
import org.opends.server.util.args.SubCommandArgumentParser;
import org.opends.server.util.cli.CLIException;
import org.opends.server.util.cli.ConsoleApplication;
import org.opends.server.util.cli.MenuResult;
import org.opends.server.util.cli.OutputStreamConsoleApplication;
import org.opends.server.util.table.TableBuilder;
import org.opends.server.util.table.TablePrinter;
import org.opends.server.util.table.TextTablePrinter;
@@ -101,9 +105,9 @@
      public Message visitAbsoluteInherited(
          AbsoluteInheritedDefaultBehaviorProvider<T> d,
          PropertyDefinition<T> p) {
        return INFO_DSCFG_HELP_FIELD_INHERITED_ABS.get(d
            .getPropertyName(), d.getManagedObjectPath()
            .getRelationDefinition().getUserFriendlyName());
        return INFO_DSCFG_HELP_FIELD_INHERITED_ABS.get(d.getPropertyName(), d
            .getManagedObjectPath().getRelationDefinition()
            .getUserFriendlyName());
      }
@@ -148,12 +152,11 @@
          RelativeInheritedDefaultBehaviorProvider<T> d,
          PropertyDefinition<T> p) {
        if (d.getRelativeOffset() == 0) {
          return INFO_DSCFG_HELP_FIELD_INHERITED_THIS.get(d
              .getPropertyName(), d.getManagedObjectDefinition()
              .getUserFriendlyName());
          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()
          return INFO_DSCFG_HELP_FIELD_INHERITED_PARENT.get(
              d.getPropertyName(), d.getManagedObjectDefinition()
              .getUserFriendlyName());
        }
      }
@@ -298,7 +301,7 @@
       * {@inheritDoc}
       */
      @Override
      public Void visitUnknown(PropertyDefinition<?> d, PrintStream p)
      public <T> Void visitUnknown(PropertyDefinition<T> d, PrintStream p)
          throws UnknownPropertyDefinitionException {
        PropertyDefinitionUsageBuilder usageBuilder =
          new PropertyDefinitionUsageBuilder(true);
@@ -367,44 +370,41 @@
  private final static 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 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';
  /**
   * 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());
    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;
  }
@@ -413,17 +413,79 @@
  /**
   * Creates a new help-properties sub-command.
   *
   * @param app
   *          The console application.
   * @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(ConsoleApplication app,
      SubCommandArgumentParser parser) throws ArgumentException {
    return new HelpSubCommandHandler(app, parser);
  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 d
   *          The managed object definition.
   * @param c
   *          The collection of properties to be displayed.
   */
  public static void displaySingleComponent(ConsoleApplication app,
      AbstractManagedObjectDefinition<?, ?> d,
      Collection<PropertyDefinition<?>> c) {
    // Display the title.
    app.println(INFO_DSCFG_HELP_HEADING_COMPONENT.get(d.getUserFriendlyName()));
    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.
    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 (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.
      PropertyDefinitionUsageBuilder v = new PropertyDefinitionUsageBuilder(
          false);
      builder.appendCell(v.getUsage(pd));
    }
    TablePrinter printer = new TextTablePrinter(app.getErrorStream());
    builder.print(printer);
  }
@@ -432,35 +494,35 @@
   * 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.
   * @param out
   *          The output stream.
   */
  public static void displayVerboseSingleProperty(
      AbstractManagedObjectDefinition<?, ?> d, String name, PrintStream out) {
  public static void displayVerboseSingleProperty(ConsoleApplication app,
      AbstractManagedObjectDefinition<?, ?> d, String name) {
    PropertyDefinition<?> pd = d.getPropertyDefinition(name);
    // Display the title.
    out.println(INFO_DSCFG_HELP_HEADING_PROPERTY.get(name));
    app.println(INFO_DSCFG_HELP_HEADING_PROPERTY.get(name));
    // Display the property synopsis and description.
    out.println();
    out.println(wrapText(pd.getSynopsis(), MAX_LINE_WIDTH));
    app.println();
    app.println(pd.getSynopsis(), 4);
    if (pd.getDescription() != null) {
      out.println();
      out.println(wrapText(pd.getDescription(), MAX_LINE_WIDTH));
      app.println();
      app.println(pd.getDescription(), 4);
    }
    // Display the syntax.
    out.println();
    app.println();
    SyntaxPrinter syntaxPrinter = new SyntaxPrinter();
    syntaxPrinter.print(out, pd);
    syntaxPrinter.print(app.getErrorStream(), pd);
    // Display remaining information in a table.
    out.println();
    app.println();
    TableBuilder builder = new TableBuilder();
    // Display the default behavior.
@@ -476,27 +538,27 @@
    builder.appendCell(INFO_DSCFG_HELP_HEADING_ADVANCED.get());
    builder.appendCell(HEADING_SEPARATOR);
    if (pd.hasOption(PropertyOption.ADVANCED)) {
      builder.appendCell(INFO_DSCFG_GENERAL_CONFIRM_YES.get());
      builder.appendCell(INFO_GENERAL_YES.get());
    } else {
      builder.appendCell(INFO_DSCFG_GENERAL_CONFIRM_NO.get());
      builder.appendCell(INFO_GENERAL_NO.get());
    }
    builder.startRow();
    builder.appendCell(INFO_DSCFG_HELP_HEADING_MULTI_VALUED.get());
    builder.appendCell(HEADING_SEPARATOR);
    if (pd.hasOption(PropertyOption.MULTI_VALUED)) {
      builder.appendCell(INFO_DSCFG_GENERAL_CONFIRM_YES.get());
      builder.appendCell(INFO_GENERAL_YES.get());
    } else {
      builder.appendCell(INFO_DSCFG_GENERAL_CONFIRM_NO.get());
      builder.appendCell(INFO_GENERAL_NO.get());
    }
    builder.startRow();
    builder.appendCell(INFO_DSCFG_HELP_HEADING_MANDATORY.get());
    builder.appendCell(HEADING_SEPARATOR);
    if (pd.hasOption(PropertyOption.MANDATORY)) {
      builder.appendCell(INFO_DSCFG_GENERAL_CONFIRM_YES.get());
      builder.appendCell(INFO_GENERAL_YES.get());
    } else {
      builder.appendCell(INFO_DSCFG_GENERAL_CONFIRM_NO.get());
      builder.appendCell(INFO_GENERAL_NO.get());
    }
    builder.startRow();
@@ -508,10 +570,10 @@
      builder.appendCell(INFO_DSCFG_HELP_FIELD_READ_ONLY.get(d
          .getUserFriendlyName()));
    } else {
      builder.appendCell(INFO_DSCFG_GENERAL_CONFIRM_NO.get());
      builder.appendCell(INFO_GENERAL_NO.get());
    }
    TextTablePrinter factory = new TextTablePrinter(out);
    TextTablePrinter factory = new TextTablePrinter(app.getErrorStream());
    factory.setDisplayHeadings(false);
    factory.setColumnWidth(0, HEADING_WIDTH);
    factory.setColumnWidth(2, 0);
@@ -524,8 +586,8 @@
    if (synopsis == null) {
      switch (action.getType()) {
      case COMPONENT_RESTART:
        synopsis = INFO_DSCFG_HELP_FIELD_COMPONENT_RESTART.get(
            d.getUserFriendlyName());
        synopsis = INFO_DSCFG_HELP_FIELD_COMPONENT_RESTART.get(d
            .getUserFriendlyName());
        break;
      case SERVER_RESTART:
        synopsis = INFO_DSCFG_HELP_FIELD_SERVER_RESTART.get();
@@ -537,42 +599,110 @@
    }
    if (synopsis != null) {
      out.println();
      out.println(wrapText(synopsis, MAX_LINE_WIDTH));
      app.println();
      app.println(synopsis);
    }
  }
  // The sub-command associated with this handler.
  private final SubCommand subCommand;
  // Displays the property option summary key.
  private static void displayPropertyOptionKey(ConsoleApplication app) {
    MessageBuilder builder;
    app.println(INFO_DSCFG_HELP_DESCRIPTION_OPTION.get());
    app.println();
    builder = new MessageBuilder();
    builder.append(" r -- ");
    builder.append(INFO_DSCFG_HELP_DESCRIPTION_READ.get());
    app.println(builder.toMessage());
    builder = new MessageBuilder();
    builder.append(" w -- ");
    builder.append(INFO_DSCFG_HELP_DESCRIPTION_WRITE.get());
    app.println(builder.toMessage());
    builder = new MessageBuilder();
    builder.append(" m -- ");
    builder.append(INFO_DSCFG_HELP_DESCRIPTION_MANDATORY.get());
    app.println(builder.toMessage());
    builder = new MessageBuilder();
    builder.append(" s -- ");
    builder.append(INFO_DSCFG_HELP_DESCRIPTION_SINGLE_VALUED.get());
    app.println(builder.toMessage());
    builder = new MessageBuilder();
    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;
  //The argument which should be used to display inherited properties.
  // A table listing all the available types of managed object indexed
  // on their parent type.
  private final Map<String, Map<String,
    AbstractManagedObjectDefinition<?, ?>>> categoryMap;
  // 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;
  // 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 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,
      SubCommandArgumentParser parser) throws ArgumentException {
    super(app);
  private HelpSubCommandHandler(SubCommandArgumentParser parser)
      throws ArgumentException {
    // Create the sub-command.
    String name = "list-properties";
    Message desc = INFO_DSCFG_DESCRIPTION_SUBCMD_HELPPROP.get();
@@ -580,8 +710,8 @@
    this.categoryArgument = new StringArgument(OPTION_DSCFG_LONG_CATEGORY,
        OPTION_DSCFG_SHORT_CATEGORY, OPTION_DSCFG_LONG_CATEGORY, false, false,
        true, "{CATEGORY}", null, null,
        INFO_DSCFG_DESCRIPTION_HELP_CATEGORY.get());
        true, "{CATEGORY}", null, null, INFO_DSCFG_DESCRIPTION_HELP_CATEGORY
            .get());
    this.subCommand.addArgument(this.categoryArgument);
    this.typeArgument = new StringArgument(OPTION_DSCFG_LONG_TYPE,
@@ -591,8 +721,8 @@
    this.inheritedModeArgument = new BooleanArgument(
        OPTION_DSCFG_LONG_INHERITED, OPTION_DSCFG_SHORT_INHERITED,
        OPTION_DSCFG_LONG_INHERITED,
        INFO_DSCFG_DESCRIPTION_HELP_INHERITED.get());
        OPTION_DSCFG_LONG_INHERITED, INFO_DSCFG_DESCRIPTION_HELP_INHERITED
            .get());
    subCommand.addArgument(inheritedModeArgument);
    // Register common arguments.
@@ -600,10 +730,10 @@
        INFO_DSCFG_DESCRIPTION_ADVANCED_HELP.get());
    registerPropertyNameArgument(this.subCommand);
    this.categoryMap = new TreeMap<String,
      Map<String, AbstractManagedObjectDefinition<?, ?>>>();
    this.tagMap = new HashMap<Tag,
      Map<String, AbstractManagedObjectDefinition<?, ?>>>();
    this.categoryMap =
      new TreeMap<String, Map<String, AbstractManagedObjectDefinition<?, ?>>>();
    this.tagMap =
      new HashMap<Tag, Map<String, AbstractManagedObjectDefinition<?, ?>>>();
  }
@@ -676,7 +806,9 @@
   * {@inheritDoc}
   */
  @Override
  public int run() throws ArgumentException, ClientException {
  public MenuResult<Integer> run(ConsoleApplication app,
      ManagementContextFactory factory) throws ArgumentException,
      ClientException, CLIException {
    String categoryName = categoryArgument.getValue();
    String typeName = typeArgument.getValue();
    Tag tag = null;
@@ -774,36 +906,26 @@
      }
    }
    if (!getConsoleApplication().isVerbose()) {
      displayNonVerbose(categoryName, typeName, tag, propertyNames);
    // Output everything to the output stream.
    app = new OutputStreamConsoleApplication(app);
    if (!app.isVerbose()) {
      displayNonVerbose(app, categoryName, typeName, tag, propertyNames);
    } else {
      displayVerbose(categoryName, typeName, tag, propertyNames);
      displayVerbose(app, categoryName, typeName, tag, propertyNames);
    }
    return 0;
    return MenuResult.success(0);
  }
  // Output property summary table.
  private void displayNonVerbose(String categoryName, String typeName,
      Tag tag, Set<String> propertyNames) {
    PrintStream out = getConsoleApplication().getOutputStream();
    if (!getConsoleApplication().isScriptFriendly()) {
      out.println(INFO_DSCFG_HELP_DESCRIPTION_OPTION.get());
      out.println();
      out.print(" r -- ");
      out.println(INFO_DSCFG_HELP_DESCRIPTION_READ.get());
      out.print(" w -- ");
      out.println(INFO_DSCFG_HELP_DESCRIPTION_WRITE.get());
      out.print(" m -- ");
      out.println(INFO_DSCFG_HELP_DESCRIPTION_MANDATORY.get());
      out.print(" s -- ");
      out.println(INFO_DSCFG_HELP_DESCRIPTION_SINGLE_VALUED.get());
      out.print(" a -- ");
      out.println(INFO_DSCFG_HELP_DESCRIPTION_ADMIN_ACTION.get());
      out.println();
      out.println();
  private void displayNonVerbose(ConsoleApplication app, String categoryName,
      String typeName, Tag tag, Set<String> propertyNames) {
    if (!app.isScriptFriendly()) {
      displayPropertyOptionKey(app);
      app.println();
      app.println();
    }
    // Headings.
@@ -894,10 +1016,10 @@
    }
    TablePrinter printer;
    if (getConsoleApplication().isScriptFriendly()) {
      printer = createScriptFriendlyTablePrinter(out);
    if (app.isScriptFriendly()) {
      printer = createScriptFriendlyTablePrinter(app.getOutputStream());
    } else {
      printer = new TextTablePrinter(out);
      printer = new TextTablePrinter(app.getOutputStream());
    }
    builder.print(printer);
  }
@@ -905,15 +1027,22 @@
  // Display detailed help on managed objects and their properties.
  private void displayVerbose(String categoryName, String typeName,
      Tag tag, Set<String> propertyNames) {
    PrintStream out = getConsoleApplication().getOutputStream();
  private void displayVerbose(ConsoleApplication app, String categoryName,
      String typeName, Tag tag, Set<String> propertyNames) {
    // Construct line used to separate consecutive sections.
    char[] c1 = new char[MAX_LINE_WIDTH];
    Arrays.fill(c1, '=');
    char[] c2 = new char[MAX_LINE_WIDTH];
    Arrays.fill(c2, '-');
    MessageBuilder mb;
    mb = new MessageBuilder();
    for (int i = 0; i < MAX_LINE_WIDTH; i++) {
      mb.append('=');
    }
    Message c1 = mb.toMessage();
    mb = new MessageBuilder();
    for (int i = 0; i < MAX_LINE_WIDTH; i++) {
      mb.append('-');
    }
    Message c2 = mb.toMessage();
    // Display help for each managed object.
    boolean isFirstManagedObject = true;
@@ -973,67 +1102,33 @@
            // managed
            // object.
            if (!isFirstManagedObject) {
              out.println();
              out.println(c1);
              out.println();
              app.println();
              app.println(c1);
              app.println();
            } else {
              isFirstManagedObject = false;
            }
            // Display the title.
            out.println(wrapText(INFO_DSCFG_HELP_HEADING_COMPONENT.get(
                    mod.getUserFriendlyName()), MAX_LINE_WIDTH));
            app.println(INFO_DSCFG_HELP_HEADING_COMPONENT.get(mod
                .getUserFriendlyName()));
            out.println();
            out.println(wrapText(mod.getSynopsis(), MAX_LINE_WIDTH));
            app.println();
            app.println(mod.getSynopsis());
            if (mod.getDescription() != null) {
              out.println();
              out.println(wrapText(mod.getDescription(), MAX_LINE_WIDTH));
              app.println();
              app.println(mod.getDescription());
            }
          }
          out.println();
          out.println(c2);
          out.println();
          app.println();
          app.println(c2);
          app.println();
          displayVerboseSingleProperty(mod, pd.getName(), out);
          displayVerboseSingleProperty(app, mod, pd.getName());
          isFirstProperty = false;
        }
      }
    }
  }
  // Compute the options field.
  private 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();
  }
}
opends/src/server/org/opends/server/tools/dsconfig/InternalManagementContextFactory.java
@@ -32,6 +32,7 @@
import org.opends.server.tools.ClientException;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.args.SubCommandArgumentParser;
import org.opends.server.util.cli.ConsoleApplication;
opends/src/server/org/opends/server/tools/dsconfig/LDAPManagementContextFactory.java
@@ -25,13 +25,17 @@
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
import org.opends.messages.Message;
import static org.opends.messages.DSConfigMessages.*;
import static org.opends.messages.ToolMessages.*;
import static org.opends.server.tools.ToolConstants.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.opends.messages.Message;
import org.opends.server.admin.client.AuthenticationException;
import org.opends.server.admin.client.AuthenticationNotSupportedException;
import org.opends.server.admin.client.CommunicationException;
@@ -46,6 +50,9 @@
import org.opends.server.util.args.IntegerArgument;
import org.opends.server.util.args.StringArgument;
import org.opends.server.util.args.SubCommandArgumentParser;
import org.opends.server.util.cli.CLIException;
import org.opends.server.util.cli.ConsoleApplication;
import org.opends.server.util.cli.ValidationCallback;
@@ -98,14 +105,132 @@
      throws ArgumentException, ClientException {
    // Lazily create the LDAP management context.
    if (context == null) {
      boolean isHeadingDisplayed = false;
      // Get the LDAP host.
      String hostName = hostArgument.getValue();
      final String tmpHostName = hostName;
      if (app.isInteractive() && !hostArgument.isPresent()) {
        if (!isHeadingDisplayed) {
          app.println();
          app.println();
          app.println(INFO_DSCFG_HEADING_CONNECTION_PARAMETERS.get());
          isHeadingDisplayed = true;
        }
        ValidationCallback<String> callback = new ValidationCallback<String>() {
          public String validate(ConsoleApplication app, String input)
              throws CLIException {
            String ninput = input.trim();
            if (ninput.length() == 0) {
              return tmpHostName;
            } else {
              try {
                InetAddress.getByName(ninput);
                return ninput;
              } catch (UnknownHostException e) {
                // Try again...
                app.println();
                app.println(ERR_DSCFG_BAD_HOST_NAME.get(ninput));
                app.println();
                return null;
              }
            }
          }
        };
        try {
          app.println();
          hostName = app.readValidatedInput(INFO_DSCFG_PROMPT_HOST_NAME
              .get(hostName), callback);
        } catch (CLIException e) {
          throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
        }
      }
      // Get the LDAP port.
      int portNumber = portArgument.getIntValue();
      final int tmpPortNumber = portNumber;
      if (app.isInteractive() && !portArgument.isPresent()) {
        if (!isHeadingDisplayed) {
          app.println();
          app.println();
          app.println(INFO_DSCFG_HEADING_CONNECTION_PARAMETERS.get());
          isHeadingDisplayed = true;
        }
        ValidationCallback<Integer> callback =
          new ValidationCallback<Integer>() {
          public Integer validate(ConsoleApplication app, String input)
              throws CLIException {
            String ninput = input.trim();
            if (ninput.length() == 0) {
              return tmpPortNumber;
            } else {
              try {
                int i = Integer.parseInt(ninput);
                if (i < 1 || i > 65535) {
                  throw new NumberFormatException();
                }
                return i;
              } catch (NumberFormatException e) {
                // Try again...
                app.println();
                app.println(ERR_DSCFG_BAD_PORT_NUMBER.get(ninput));
                app.println();
                return null;
              }
            }
          }
        };
        try {
          app.println();
          portNumber = app.readValidatedInput(INFO_DSCFG_PROMPT_PORT_NUMBER
              .get(portNumber), callback);
        } catch (CLIException e) {
          throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
        }
      }
      // Get the LDAP bind credentials.
      String bindDN = bindDNArgument.getValue();
      final String tmpBindDN = bindDN;
      if (app.isInteractive() && !bindDNArgument.isPresent()) {
        if (!isHeadingDisplayed) {
          app.println();
          app.println();
          app.println(INFO_DSCFG_HEADING_CONNECTION_PARAMETERS.get());
          isHeadingDisplayed = true;
        }
        ValidationCallback<String> callback = new ValidationCallback<String>() {
          public String validate(ConsoleApplication app, String input)
              throws CLIException {
            String ninput = input.trim();
            if (ninput.length() == 0) {
              return tmpBindDN;
            } else {
              return ninput;
            }
          }
        };
        try {
          app.println();
          bindDN = app.readValidatedInput(
              INFO_DSCFG_PROMPT_BIND_DN.get(bindDN), callback);
        } catch (CLIException e) {
          throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
        }
      }
      String bindPassword = bindPasswordArgument.getValue();
      if (bindPasswordFileArgument.isPresent()) {
@@ -122,11 +247,19 @@
              .unableToReadBindPasswordInteractively();
        }
        if (!isHeadingDisplayed) {
          app.println();
          app.println();
          app.println(INFO_DSCFG_HEADING_CONNECTION_PARAMETERS.get());
          isHeadingDisplayed = true;
        }
        try {
          app.println();
          Message prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(bindDN);
          bindPassword = app.readPassword(prompt);
        } catch (Exception e) {
          throw ArgumentExceptionFactory.unableToReadBindPassword(e);
          throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
        }
      }
@@ -143,9 +276,8 @@
        Message message = ERR_DSCFG_ERROR_LDAP_SIMPLE_BIND_FAILED.get(bindDN);
        throw new ClientException(LDAPResultCode.INVALID_CREDENTIALS, message);
      } catch (CommunicationException e) {
        Message message =
            ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get(
                    hostName, String.valueOf(portNumber));
        Message message = ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT.get(hostName,
            String.valueOf(portNumber));
        throw new ClientException(LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR,
            message);
      }
@@ -200,9 +332,8 @@
    // arguments.
    if (bindPasswordArgument.isPresent()
        && bindPasswordFileArgument.isPresent()) {
      Message message = ERR_TOOL_CONFLICTING_ARGS.
          get(bindPasswordArgument.getLongIdentifier(),
              bindPasswordFileArgument.getLongIdentifier());
      Message message = ERR_TOOL_CONFLICTING_ARGS.get(bindPasswordArgument
          .getLongIdentifier(), bindPasswordFileArgument.getLongIdentifier());
      throw new ArgumentException(message);
    }
  }
opends/src/server/org/opends/server/tools/dsconfig/ListSubCommandHandler.java
@@ -25,11 +25,10 @@
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
import org.opends.messages.Message;
import static org.opends.messages.ToolMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import java.io.PrintStream;
import java.util.List;
@@ -38,6 +37,7 @@
import java.util.SortedSet;
import java.util.TreeMap;
import org.opends.messages.Message;
import org.opends.server.admin.DefinitionDecodingException;
import org.opends.server.admin.InstantiableRelationDefinition;
import org.opends.server.admin.ManagedObjectDefinition;
@@ -51,12 +51,16 @@
import org.opends.server.admin.client.ConcurrentModificationException;
import org.opends.server.admin.client.ManagedObject;
import org.opends.server.admin.client.ManagedObjectDecodingException;
import org.opends.server.admin.client.ManagementContext;
import org.opends.server.protocols.ldap.LDAPResultCode;
import org.opends.server.tools.ClientException;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.args.StringArgument;
import org.opends.server.util.args.SubCommand;
import org.opends.server.util.args.SubCommandArgumentParser;
import org.opends.server.util.cli.CLIException;
import org.opends.server.util.cli.ConsoleApplication;
import org.opends.server.util.cli.MenuResult;
import org.opends.server.util.table.TableBuilder;
import org.opends.server.util.table.TablePrinter;
import org.opends.server.util.table.TextTablePrinter;
@@ -74,8 +78,6 @@
  /**
   * Creates a new list-xxx sub-command for an instantiable relation.
   *
   * @param app
   *          The console application.
   * @param parser
   *          The sub-command argument parser.
   * @param p
@@ -86,10 +88,10 @@
   * @throws ArgumentException
   *           If the sub-command could not be created successfully.
   */
  public static ListSubCommandHandler create(ConsoleApplication app,
  public static ListSubCommandHandler create(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p,
      InstantiableRelationDefinition<?, ?> r) throws ArgumentException {
    return new ListSubCommandHandler(app, parser, p, r, r.getPluralName(), r
    return new ListSubCommandHandler(parser, p, r, r.getPluralName(), r
        .getUserFriendlyPluralName());
  }
@@ -98,8 +100,6 @@
  /**
   * Creates a new list-xxx sub-command for an optional relation.
   *
   * @param app
   *          The console application.
   * @param parser
   *          The sub-command argument parser.
   * @param p
@@ -110,10 +110,10 @@
   * @throws ArgumentException
   *           If the sub-command could not be created successfully.
   */
  public static ListSubCommandHandler create(ConsoleApplication app,
  public static ListSubCommandHandler create(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p,
      OptionalRelationDefinition<?, ?> r) throws ArgumentException {
    return new ListSubCommandHandler(app, parser, p, r, r.getName(), r
    return new ListSubCommandHandler(parser, p, r, r.getName(), r
        .getUserFriendlyName());
  }
@@ -132,12 +132,10 @@
  // Private constructor.
  private ListSubCommandHandler(ConsoleApplication app,
  private ListSubCommandHandler(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p,
      RelationDefinition<?, ?> r, String rname, Message rufn)
      throws ArgumentException {
    super(app);
    this.path = p;
    this.relation = r;
@@ -161,6 +159,19 @@
  /**
   * Gets the relation definition associated with the type of
   * component that this sub-command handles.
   *
   * @return Returns the relation definition associated with the type
   *         of component that this sub-command handles.
   */
  public RelationDefinition<?, ?> getRelationDefinition() {
    return relation;
  }
  /**
   * {@inheritDoc}
   */
  @Override
@@ -174,8 +185,9 @@
   * {@inheritDoc}
   */
  @Override
  public int run()
      throws ArgumentException, ClientException {
  public MenuResult<Integer> run(ConsoleApplication app,
      ManagementContextFactory factory) throws ArgumentException,
      ClientException, CLIException {
    // Get the property names.
    Set<String> propertyNames = getPropertyNames();
@@ -186,10 +198,10 @@
    }
    PropertyValuePrinter valuePrinter = new PropertyValuePrinter(getSizeUnit(),
        getTimeUnit(), getConsoleApplication().isScriptFriendly());
        getTimeUnit(), app.isScriptFriendly());
    // Get the naming argument values.
    List<String> names = getNamingArgValues(namingArgs);
    List<String> names = getNamingArgValues(app, namingArgs);
    Message ufn;
    if (relation instanceof InstantiableRelationDefinition) {
@@ -201,20 +213,19 @@
    }
    // List the children.
    ManagedObject<?> parent;
    ManagementContext context = factory.getManagementContext(app);
    MenuResult<ManagedObject<?>> result;
    try {
      parent = getManagedObject(path, names);
      result = getManagedObject(app, context, path, names);
    } catch (AuthorizationException e) {
      Message msg = ERR_DSCFG_ERROR_LIST_AUTHZ.get(ufn);
      throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS,
          msg);
    } catch (DefinitionDecodingException e) {
      ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_GET_PARENT_DDE.get(ufn, ufn, ufn);
      throw new ClientException(LDAPResultCode.OPERATIONS_ERROR, msg);
    } catch (ManagedObjectDecodingException e) {
      ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_GET_PARENT_MODE.get(ufn);
      throw new ClientException(LDAPResultCode.OPERATIONS_ERROR, msg);
@@ -225,12 +236,18 @@
      Message msg = ERR_DSCFG_ERROR_LIST_CME.get(ufn);
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
    } catch (ManagedObjectNotFoundException e) {
      ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_GET_PARENT_MONFE.get(ufn);
      throw new ClientException(LDAPResultCode.NO_SUCH_OBJECT, msg);
    }
    if (result.isQuit()) {
      return MenuResult.quit();
    } else if (result.isCancel()) {
      return MenuResult.cancel();
    }
    ManagedObject<?> parent = result.getValue();
    SortedMap<String, ManagedObject<?>> children =
      new TreeMap<String, ManagedObject<?>>();
    if (relation instanceof InstantiableRelationDefinition) {
@@ -301,11 +318,10 @@
    }
    // Output the results.
    if (getConsoleApplication().isScriptFriendly()) {
    if (app.isScriptFriendly()) {
      // Output just the names of the children.
      PrintStream out = getConsoleApplication().getOutputStream();
      for (String name : children.keySet()) {
        out.println(name);
        app.println(Message.raw(name));
      }
    } else {
      // Create a table of their properties.
@@ -313,8 +329,6 @@
      builder.appendHeading(relation.getUserFriendlyName());
      builder
          .appendHeading(INFO_DSCFG_HEADING_COMPONENT_TYPE.get());
      if (!propertyNames.isEmpty()) {
      }
      for (String propertyName : propertyNames) {
        builder.appendHeading(Message.raw(propertyName));
      }
@@ -346,11 +360,11 @@
        for (String propertyName : propertyNames) {
          try {
            PropertyDefinition<?> pd = d.getPropertyDefinition(propertyName);
            displayProperty(builder, child, pd, valuePrinter);
            displayProperty(app, builder, child, pd, valuePrinter);
          } catch (IllegalArgumentException e) {
            // Assume this child managed object does not support this
            // property.
            if (getConsoleApplication().isScriptFriendly()) {
            if (app.isScriptFriendly()) {
              builder.appendCell();
            } else {
              builder.appendCell("-");
@@ -359,28 +373,35 @@
        }
      }
      PrintStream out = getConsoleApplication().getOutputStream();
      if (getConsoleApplication().isScriptFriendly()) {
      PrintStream out = app.getOutputStream();
      if (app.isScriptFriendly()) {
        TablePrinter printer = createScriptFriendlyTablePrinter(out);
        builder.print(printer);
      } else {
        if (app.isInteractive()) {
          // Make interactive mode prettier.
          app.println();
          app.println();
        }
        TextTablePrinter printer = new TextTablePrinter(out);
        printer.setColumnSeparator(":");
        builder.print(printer);
      }
    }
    return 0;
    return MenuResult.success(0);
  }
  // Display the set of values associated with a property.
  private <T> void displayProperty(TableBuilder builder, ManagedObject<?> mo,
      PropertyDefinition<T> pd, PropertyValuePrinter valuePrinter) {
  private <T> void displayProperty(ConsoleApplication app,
      TableBuilder builder, ManagedObject<?> mo, PropertyDefinition<T> pd,
      PropertyValuePrinter valuePrinter) {
    SortedSet<T> values = mo.getPropertyValues(pd);
    if (values.isEmpty()) {
      if (getConsoleApplication().isScriptFriendly()) {
      if (app.isScriptFriendly()) {
        builder.appendCell();
      } else {
        builder.appendCell("-");
opends/src/server/org/opends/server/tools/dsconfig/ManagementContextFactory.java
@@ -32,6 +32,7 @@
import org.opends.server.tools.ClientException;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.args.SubCommandArgumentParser;
import org.opends.server.util.cli.ConsoleApplication;
opends/src/server/org/opends/server/tools/dsconfig/PropertyValueEditor.java
New file
@@ -0,0 +1,1888 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
import static org.opends.messages.DSConfigMessages.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.AliasDefaultBehaviorProvider;
import org.opends.server.admin.BooleanPropertyDefinition;
import org.opends.server.admin.DefaultBehaviorProviderVisitor;
import org.opends.server.admin.DefinedDefaultBehaviorProvider;
import org.opends.server.admin.EnumPropertyDefinition;
import org.opends.server.admin.IllegalPropertyValueException;
import org.opends.server.admin.IllegalPropertyValueStringException;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyDefinitionUsageBuilder;
import org.opends.server.admin.PropertyDefinitionVisitor;
import org.opends.server.admin.PropertyIsMandatoryException;
import org.opends.server.admin.PropertyIsReadOnlyException;
import org.opends.server.admin.PropertyIsSingleValuedException;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.admin.UnknownPropertyDefinitionException;
import org.opends.server.admin.client.ManagedObject;
import org.opends.server.util.Validator;
import org.opends.server.util.cli.CLIException;
import org.opends.server.util.cli.ConsoleApplication;
import org.opends.server.util.cli.HelpCallback;
import org.opends.server.util.cli.Menu;
import org.opends.server.util.cli.MenuBuilder;
import org.opends.server.util.cli.MenuCallback;
import org.opends.server.util.cli.MenuResult;
import org.opends.server.util.table.TableBuilder;
import org.opends.server.util.table.TextTablePrinter;
/**
 * Common methods used for interactively editing properties.
 */
public final class PropertyValueEditor {
  /**
   * A help call-back which displays a description and summary of a
   * component and its properties.
   */
  private static final class ComponentHelpCallback implements HelpCallback {
    // The managed object being edited.
    private final ManagedObject<?> mo;
    // The properties that can be edited.
    private final Collection<PropertyDefinition<?>> properties;
    // Creates a new component helper for the specified property.
    private ComponentHelpCallback(ManagedObject<?> mo,
        Collection<PropertyDefinition<?>> c) {
      this.mo = mo;
      this.properties = c;
    }
    /**
     * {@inheritDoc}
     */
    public void display(ConsoleApplication app) {
      app.println();
      HelpSubCommandHandler.displaySingleComponent(app, mo
          .getManagedObjectDefinition(), properties);
      app.println();
      app.pressReturnToContinue();
    }
  }
  /**
   * A simple interface for querying and retrieving common default
   * behavior properties.
   */
  private static final class DefaultBehaviorQuery<T> {
    /**
     * The type of default behavior.
     */
    private enum Type {
      /**
       * Alias default behavior.
       */
      ALIAS,
      /**
       * Defined default behavior.
       */
      DEFINED,
      /**
       * Inherited default behavior.
       */
      INHERITED,
      /**
       * Undefined default behavior.
       */
      UNDEFINED;
    };
    /**
     * Create a new default behavior query object based on the provied
     * property definition.
     *
     * @param <T>
     *          The type of property definition.
     * @param pd
     *          The property definition.
     * @return The default behavior query object.
     */
    public static <T> DefaultBehaviorQuery<T> query(PropertyDefinition<T> pd) {
      DefaultBehaviorProviderVisitor<T, DefaultBehaviorQuery<T>,
        PropertyDefinition<T>> visitor =
          new DefaultBehaviorProviderVisitor<T, DefaultBehaviorQuery<T>,
          PropertyDefinition<T>>() {
        /**
         * {@inheritDoc}
         */
        public DefaultBehaviorQuery<T> visitAbsoluteInherited(
            AbsoluteInheritedDefaultBehaviorProvider<T> d,
            PropertyDefinition<T> p) {
          AbstractManagedObjectDefinition<?, ?> mod = d
              .getManagedObjectDefinition();
          String propertyName = d.getPropertyName();
          PropertyDefinition<?> pd2 = mod.getPropertyDefinition(propertyName);
          DefaultBehaviorQuery<?> query = query(pd2);
          return new DefaultBehaviorQuery<T>(Type.INHERITED, query
              .getAliasDescription());
        }
        /**
         * {@inheritDoc}
         */
        public DefaultBehaviorQuery<T> visitAlias(
            AliasDefaultBehaviorProvider<T> d, PropertyDefinition<T> p) {
          return new DefaultBehaviorQuery<T>(Type.ALIAS, d.getSynopsis());
        }
        /**
         * {@inheritDoc}
         */
        public DefaultBehaviorQuery<T> visitDefined(
            DefinedDefaultBehaviorProvider<T> d, PropertyDefinition<T> p) {
          return new DefaultBehaviorQuery<T>(Type.DEFINED, null);
        }
        /**
         * {@inheritDoc}
         */
        public DefaultBehaviorQuery<T> visitRelativeInherited(
            RelativeInheritedDefaultBehaviorProvider<T> d,
            PropertyDefinition<T> p) {
          AbstractManagedObjectDefinition<?, ?> mod = d
              .getManagedObjectDefinition();
          String propertyName = d.getPropertyName();
          PropertyDefinition<?> pd2 = mod.getPropertyDefinition(propertyName);
          DefaultBehaviorQuery<?> query = query(pd2);
          return new DefaultBehaviorQuery<T>(Type.INHERITED, query
              .getAliasDescription());
        }
        /**
         * {@inheritDoc}
         */
        public DefaultBehaviorQuery<T> visitUndefined(
            UndefinedDefaultBehaviorProvider<T> d, PropertyDefinition<T> p) {
          return new DefaultBehaviorQuery<T>(Type.UNDEFINED, null);
        }
      };
      return pd.getDefaultBehaviorProvider().accept(visitor, pd);
    }
    // The description of the behavior if it is an alias default
    // behavior.
    private final Message aliasDescription;
    // The type of behavior.
    private final Type type;
    // Private constructor.
    private DefaultBehaviorQuery(Type type, Message aliasDescription) {
      this.type = type;
      this.aliasDescription = aliasDescription;
    }
    /**
     * Gets the detailed description of this default behavior if it is
     * an alias default behavior or if it inherits from an alias
     * default behavior.
     *
     * @return Returns the detailed description of this default
     *         behavior if it is an alias default behavior or if it
     *         inherits from an alias default behavior, otherwise
     *         <code>null</code>.
     */
    public Message getAliasDescription() {
      return aliasDescription;
    }
    /**
     * Determines whether or not the default behavior is alias.
     *
     * @return Returns <code>true</code> if the default behavior is
     *         alias.
     */
    public boolean isAlias() {
      return type == Type.ALIAS;
    }
    /**
     * Determines whether or not the default behavior is defined.
     *
     * @return Returns <code>true</code> if the default behavior is
     *         defined.
     */
    public boolean isDefined() {
      return type == Type.DEFINED;
    }
    /**
     * Determines whether or not the default behavior is inherited.
     *
     * @return Returns <code>true</code> if the default behavior is
     *         inherited.
     */
    public boolean isInherited() {
      return type == Type.INHERITED;
    }
    /**
     * Determines whether or not the default behavior is undefined.
     *
     * @return Returns <code>true</code> if the default behavior is
     *         undefined.
     */
    public boolean isUndefined() {
      return type == Type.UNDEFINED;
    }
  }
  /**
   * A property definition visitor which initializes mandatory
   * properties.
   */
  private final class MandatoryPropertyInitializer extends
      PropertyDefinitionVisitor<MenuResult<Void>, ManagedObject<?>> {
    // Any exception that was caught during processing.
    private CLIException e = null;
    // Private constructor.
    private MandatoryPropertyInitializer() {
      // No implementation required.
    }
    /**
     * Read the initial value(s) for a mandatory property.
     *
     * @param mo
     *          The managed object.
     * @param pd
     *          The property definition.
     * @return Returns <code>true</code> if new values were read
     *         successfully, or <code>false</code> if no values were
     *         read and the user chose to quit.
     * @throws CLIException
     *           If the user input could not be retrieved for some
     *           reason.
     */
    public MenuResult<Void> read(ManagedObject<?> mo, PropertyDefinition<?> pd)
        throws CLIException {
      displayPropertyHeader(app, pd);
      MenuResult<Void> result = pd.accept(this, mo);
      if (e != null) {
        throw e;
      } else {
        return result;
      }
    }
    /**
     * /** {@inheritDoc}
     */
    @Override
    public MenuResult<Void> visitBoolean(BooleanPropertyDefinition d,
        ManagedObject<?> p) {
      MenuBuilder<Boolean> builder = new MenuBuilder<Boolean>(app);
      builder
          .setPrompt(INFO_EDITOR_PROMPT_SELECT_VALUE_SINGLE.get(d.getName()));
      builder
          .addNumberedOption(INFO_VALUE_TRUE.get(), MenuResult.success(true));
      builder.addNumberedOption(INFO_VALUE_FALSE.get(), MenuResult
          .success(false));
      builder.addHelpOption(new PropertyHelpCallback(p
          .getManagedObjectDefinition(), d));
      if (app.isMenuDrivenMode()) {
        builder.addCancelOption(true);
      }
      builder.addQuitOption();
      Menu<Boolean> menu = builder.toMenu();
      try {
        app.println();
        MenuResult<Boolean> result = menu.run();
        if (result.isQuit()) {
          return MenuResult.quit();
        } else if (result.isCancel()) {
          return MenuResult.cancel();
        } else {
          p.setPropertyValue(d, result.getValue());
          return MenuResult.success();
        }
      } catch (CLIException e) {
        this.e = e;
        return MenuResult.cancel();
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <E extends Enum<E>> MenuResult<Void> visitEnum(
        EnumPropertyDefinition<E> d, ManagedObject<?> p) {
      MenuBuilder<E> builder = new MenuBuilder<E>(app);
      builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD);
      if (d.hasOption(PropertyOption.MULTI_VALUED)) {
        builder
            .setPrompt(INFO_EDITOR_PROMPT_SELECT_VALUE_MULTI.get(d.getName()));
        builder.setAllowMultiSelect(true);
      } else {
        builder.setPrompt(INFO_EDITOR_PROMPT_SELECT_VALUE_SINGLE
            .get(d.getName()));
      }
      Set<E> values = new TreeSet<E>(d);
      values.addAll(EnumSet.allOf(d.getEnumClass()));
      for (E value : values) {
        Message option = getPropertyValues(d, Collections.singleton(value));
        builder.addNumberedOption(option, MenuResult.success(value));
      }
      builder.addHelpOption(new PropertyHelpCallback(p
          .getManagedObjectDefinition(), d));
      if (app.isMenuDrivenMode()) {
        builder.addCancelOption(true);
      }
      builder.addQuitOption();
      Menu<E> menu = builder.toMenu();
      try {
        app.println();
        MenuResult<E> result = menu.run();
        if (result.isQuit()) {
          return MenuResult.quit();
        } else if (result.isCancel()) {
          return MenuResult.cancel();
        } else {
          p.setPropertyValues(d, result.getValues());
          return MenuResult.success();
        }
      } catch (CLIException e) {
        this.e = e;
        return MenuResult.cancel();
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> MenuResult<Void> visitUnknown(PropertyDefinition<T> d,
        ManagedObject<?> p) throws UnknownPropertyDefinitionException {
      PropertyDefinitionUsageBuilder b = new PropertyDefinitionUsageBuilder(
          true);
      app.println();
      app.println(INFO_EDITOR_HEADING_SYNTAX.get(b.getUsage(d)), 4);
      // Set the new property value(s).
      try {
        p.setPropertyValues(d, readPropertyValues(app, p
            .getManagedObjectDefinition(), d));
        return MenuResult.success();
      } catch (CLIException e) {
        this.e = e;
        return MenuResult.cancel();
      }
    }
  }
  /**
   * A menu call-back for editing a modifiable multi-valued property.
   */
  private static final class MultiValuedPropertyEditor extends
      PropertyDefinitionVisitor<MenuResult<Boolean>, ConsoleApplication>
      implements MenuCallback<Boolean> {
    // Any exception that was caught during processing.
    private CLIException e = null;
    // The managed object being edited.
    private final ManagedObject<?> mo;
    // The property to be edited.
    private final PropertyDefinition<?> pd;
    // Creates a new property editor for the specified property.
    private MultiValuedPropertyEditor(ManagedObject<?> mo,
        PropertyDefinition<?> pd) {
      Validator.ensureTrue(pd.hasOption(PropertyOption.MULTI_VALUED));
      this.mo = mo;
      this.pd = pd;
    }
    /**
     * {@inheritDoc}
     */
    public MenuResult<Boolean> invoke(ConsoleApplication app)
        throws CLIException {
      displayPropertyHeader(app, pd);
      MenuResult<Boolean> result = pd.accept(this, app);
      if (e != null) {
        throw e;
      } else {
        return result;
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <T extends Enum<T>> MenuResult<Boolean> visitEnum(
        final EnumPropertyDefinition<T> d, ConsoleApplication app) {
      final SortedSet<T> defaultValues = mo.getPropertyDefaultValues(d);
      final SortedSet<T> oldValues = mo.getPropertyValues(d);
      final SortedSet<T> currentValues = mo.getPropertyValues(d);
      boolean isFirst = true;
      while (true) {
        if (!isFirst) {
          app.println();
          app.println(INFO_EDITOR_HEADING_CONFIGURE_PROPERTY_CONT.get(d
              .getName()));
        } else {
          isFirst = false;
        }
        if (currentValues.size() > 1) {
          app.println();
          app.println(INFO_EDITOR_HEADING_VALUES_SUMMARY.get(d.getName()));
          app.println();
          displayPropertyValues(app, d, currentValues);
        }
        // Create the add values call-back.
        MenuCallback<Boolean> addCallback = null;
        final EnumSet<T> values = EnumSet.allOf(d.getEnumClass());
        values.removeAll(currentValues);
        if (!values.isEmpty()) {
          addCallback = new MenuCallback<Boolean>() {
            public MenuResult<Boolean> invoke(ConsoleApplication app)
                throws CLIException {
              MenuBuilder<T> builder = new MenuBuilder<T>(app);
              builder.setPrompt(INFO_EDITOR_PROMPT_SELECT_VALUES_ADD.get());
              builder.setAllowMultiSelect(true);
              builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD);
              for (T value : values) {
                Message svalue = getPropertyValues(d, Collections
                    .singleton(value));
                builder.addNumberedOption(svalue, MenuResult.success(value));
              }
              if (values.size() > 1) {
                // No point in having this option if there's only one
                // possible value.
                builder.addNumberedOption(INFO_EDITOR_OPTION_ADD_ALL_VALUES
                    .get(), MenuResult.success(values));
              }
              builder.addHelpOption(new PropertyHelpCallback(mo
                  .getManagedObjectDefinition(), d));
              builder.addCancelOption(true);
              builder.addQuitOption();
              app.println();
              app.println();
              Menu<T> menu = builder.toMenu();
              MenuResult<T> result = menu.run();
              if (result.isSuccess()) {
                // Set the new property value(s).
                currentValues.addAll(result.getValues());
                app.println();
                app.pressReturnToContinue();
                return MenuResult.success(false);
              } else if (result.isCancel()) {
                app.println();
                app.pressReturnToContinue();
                return MenuResult.success(false);
              } else {
                return MenuResult.quit();
              }
            }
          };
        }
        // Create the remove values call-back.
        MenuCallback<Boolean> removeCallback = new MenuCallback<Boolean>() {
          public MenuResult<Boolean> invoke(ConsoleApplication app)
              throws CLIException {
            MenuBuilder<T> builder = new MenuBuilder<T>(app);
            builder.setPrompt(INFO_EDITOR_PROMPT_SELECT_VALUES_REMOVE.get());
            builder.setAllowMultiSelect(true);
            builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD);
            for (T value : currentValues) {
              Message svalue = getPropertyValues(d, Collections
                  .singleton(value));
              builder.addNumberedOption(svalue, MenuResult.success(value));
            }
            builder.addHelpOption(new PropertyHelpCallback(mo
                .getManagedObjectDefinition(), d));
            builder.addCancelOption(true);
            builder.addQuitOption();
            app.println();
            app.println();
            Menu<T> menu = builder.toMenu();
            MenuResult<T> result = menu.run();
            if (result.isSuccess()) {
              // Set the new property value(s).
              currentValues.removeAll(result.getValues());
              app.println();
              app.pressReturnToContinue();
              return MenuResult.success(false);
            } else if (result.isCancel()) {
              app.println();
              app.pressReturnToContinue();
              return MenuResult.success(false);
            } else {
              return MenuResult.quit();
            }
          }
        };
        MenuResult<Boolean> result = runMenu(d, app, defaultValues, oldValues,
            currentValues, addCallback, removeCallback);
        if (!result.isAgain()) {
          return result;
        }
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> MenuResult<Boolean> visitUnknown(final PropertyDefinition<T> d,
        ConsoleApplication app) {
      PropertyDefinitionUsageBuilder b = new PropertyDefinitionUsageBuilder(
          true);
      app.println();
      app.println(INFO_EDITOR_HEADING_SYNTAX.get(b.getUsage(d)), 4);
      final SortedSet<T> defaultValues = mo.getPropertyDefaultValues(d);
      final SortedSet<T> oldValues = mo.getPropertyValues(d);
      final SortedSet<T> currentValues = mo.getPropertyValues(d);
      boolean isFirst = true;
      while (true) {
        if (!isFirst) {
          app.println();
          app.println(INFO_EDITOR_HEADING_CONFIGURE_PROPERTY_CONT.get(d
              .getName()));
        } else {
          isFirst = false;
        }
        if (currentValues.size() > 1) {
          app.println();
          app.println(INFO_EDITOR_HEADING_VALUES_SUMMARY.get(d.getName()));
          app.println();
          displayPropertyValues(app, d, currentValues);
        }
        // Create the add values call-back.
        MenuCallback<Boolean> addCallback = new MenuCallback<Boolean>() {
          public MenuResult<Boolean> invoke(ConsoleApplication app)
              throws CLIException {
            app.println();
            readPropertyValues(app, mo.getManagedObjectDefinition(), d,
                currentValues);
            return MenuResult.success(false);
          }
        };
        // Create the remove values call-back.
        MenuCallback<Boolean> removeCallback = new MenuCallback<Boolean>() {
          public MenuResult<Boolean> invoke(ConsoleApplication app)
              throws CLIException {
            MenuBuilder<T> builder = new MenuBuilder<T>(app);
            builder.setPrompt(INFO_EDITOR_PROMPT_SELECT_VALUES_REMOVE.get());
            builder.setAllowMultiSelect(true);
            builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD);
            for (T value : currentValues) {
              Message svalue = getPropertyValues(d, Collections
                  .singleton(value));
              builder.addNumberedOption(svalue, MenuResult.success(value));
            }
            builder.addHelpOption(new PropertyHelpCallback(mo
                .getManagedObjectDefinition(), d));
            builder.addCancelOption(true);
            builder.addQuitOption();
            app.println();
            app.println();
            Menu<T> menu = builder.toMenu();
            MenuResult<T> result = menu.run();
            if (result.isSuccess()) {
              // Set the new property value(s).
              currentValues.removeAll(result.getValues());
              app.println();
              app.pressReturnToContinue();
              return MenuResult.success(false);
            } else if (result.isCancel()) {
              app.println();
              app.pressReturnToContinue();
              return MenuResult.success(false);
            } else {
              return MenuResult.quit();
            }
          }
        };
        MenuResult<Boolean> result = runMenu(d, app, defaultValues, oldValues,
            currentValues, addCallback, removeCallback);
        if (!result.isAgain()) {
          return result;
        }
      }
    }
    /**
     * Generate an appropriate menu option for a property which asks
     * the user whether or not they want to keep the property's
     * current settings.
     */
    private <T> Message getKeepDefaultValuesMenuOption(
        PropertyDefinition<T> pd, SortedSet<T> defaultValues,
        SortedSet<T> oldValues, SortedSet<T> currentValues) {
      DefaultBehaviorQuery<T> query = DefaultBehaviorQuery.query(pd);
      boolean isModified = !currentValues.equals(oldValues);
      boolean isDefault = currentValues.equals(defaultValues);
      if (isModified) {
        switch (currentValues.size()) {
        case 0:
          if (query.isAlias()) {
            return INFO_EDITOR_OPTION_USE_DEFAULT_ALIAS.get(query
                .getAliasDescription());
          } else if (query.isInherited()) {
            if (query.getAliasDescription() != null) {
              return INFO_EDITOR_OPTION_USE_DEFAULT_INHERITED_ALIAS.get(query
                  .getAliasDescription());
            } else {
              return INFO_EDITOR_OPTION_USE_DEFAULT_INHERITED_ALIAS_UNDEFINED
                  .get();
            }
          } else {
            return INFO_EDITOR_OPTION_LEAVE_UNDEFINED.get();
          }
        case 1:
          Message svalue = getPropertyValues(pd, currentValues);
          if (isDefault) {
            if (query.isInherited()) {
              return INFO_EDITOR_OPTION_USE_INHERITED_DEFAULT_VALUE.get(svalue);
            } else {
              return INFO_EDITOR_OPTION_USE_DEFAULT_VALUE.get(svalue);
            }
          } else {
            return INFO_EDITOR_OPTION_USE_VALUE.get(svalue);
          }
        default:
          if (isDefault) {
            if (query.isInherited()) {
              return INFO_EDITOR_OPTION_USE_INHERITED_DEFAULT_VALUES.get();
            } else {
              return INFO_EDITOR_OPTION_USE_DEFAULT_VALUES.get();
            }
          } else {
            return INFO_EDITOR_OPTION_USE_VALUES.get();
          }
        }
      } else {
        switch (currentValues.size()) {
        case 0:
          if (query.isAlias()) {
            return INFO_EDITOR_OPTION_KEEP_DEFAULT_ALIAS.get(query
                .getAliasDescription());
          } else if (query.isInherited()) {
            if (query.getAliasDescription() != null) {
              return INFO_EDITOR_OPTION_KEEP_DEFAULT_INHERITED_ALIAS.get(query
                  .getAliasDescription());
            } else {
              return INFO_EDITOR_OPTION_KEEP_DEFAULT_INHERITED_ALIAS_UNDEFINED
                  .get();
            }
          } else {
            return INFO_EDITOR_OPTION_LEAVE_UNDEFINED.get();
          }
        case 1:
          Message svalue = getPropertyValues(pd, currentValues);
          if (isDefault) {
            if (query.isInherited()) {
              return INFO_EDITOR_OPTION_KEEP_INHERITED_DEFAULT_VALUE
                  .get(svalue);
            } else {
              return INFO_EDITOR_OPTION_KEEP_DEFAULT_VALUE.get(svalue);
            }
          } else {
            return INFO_EDITOR_OPTION_KEEP_VALUE.get(svalue);
          }
        default:
          if (isDefault) {
            if (query.isInherited()) {
              return INFO_EDITOR_OPTION_KEEP_INHERITED_DEFAULT_VALUES.get();
            } else {
              return INFO_EDITOR_OPTION_KEEP_DEFAULT_VALUES.get();
            }
          } else {
            return INFO_EDITOR_OPTION_KEEP_VALUES.get();
          }
        }
      }
    }
    /**
     * Generate an appropriate menu option which should be used in the
     * case where a property can be reset to its default behavior.
     */
    private <T> Message getResetToDefaultValuesMenuOption(
        PropertyDefinition<T> pd, SortedSet<T> defaultValues,
        SortedSet<T> currentValues) {
      DefaultBehaviorQuery<T> query = DefaultBehaviorQuery.query(pd);
      boolean isMandatory = pd.hasOption(PropertyOption.MANDATORY);
      if (!isMandatory && query.isAlias()) {
        return INFO_EDITOR_OPTION_RESET_DEFAULT_ALIAS.get(query
            .getAliasDescription());
      } else if (query.isDefined()) {
        // Only show this option if the current value is different
        // to the default.
        if (!currentValues.equals(defaultValues)) {
          Message svalue = getPropertyValues(pd, defaultValues);
          if (defaultValues.size() > 1) {
            return INFO_EDITOR_OPTION_RESET_DEFAULT_VALUES.get(svalue);
          } else {
            return INFO_EDITOR_OPTION_RESET_DEFAULT_VALUE.get(svalue);
          }
        } else {
          return null;
        }
      } else if (!isMandatory && query.isInherited()) {
        if (defaultValues.isEmpty()) {
          if (query.getAliasDescription() != null) {
            return INFO_EDITOR_OPTION_RESET_DEFAULT_INHERITED_ALIAS.get(query
                .getAliasDescription());
          } else {
            return INFO_EDITOR_OPTION_RESET_DEFAULT_INHERITED_ALIAS_UNDEFINED
                .get();
          }
        } else {
          Message svalue = getPropertyValues(pd, defaultValues);
          if (defaultValues.size() > 1) {
            return INFO_EDITOR_OPTION_RESET_INHERITED_DEFAULT_VALUES
                .get(svalue);
          } else {
            return INFO_EDITOR_OPTION_RESET_INHERITED_DEFAULT_VALUE.get(svalue);
          }
        }
      } else if (!isMandatory && query.isUndefined()) {
        return INFO_EDITOR_OPTION_LEAVE_UNDEFINED.get();
      } else {
        return null;
      }
    }
    // Common menu processing.
    private <T> MenuResult<Boolean> runMenu(final PropertyDefinition<T> d,
        ConsoleApplication app, final SortedSet<T> defaultValues,
        final SortedSet<T> oldValues, final SortedSet<T> currentValues,
        MenuCallback<Boolean> addCallback,
        MenuCallback<Boolean> removeCallback) {
      // Construct a menu of actions.
      MenuBuilder<Boolean> builder = new MenuBuilder<Boolean>(app);
      builder.setPrompt(INFO_EDITOR_PROMPT_MODIFY_MENU.get(d.getName()));
      // First option is for leaving the property unchanged or
      // applying changes, but only if the state of the property is
      // valid.
      if (!(d.hasOption(PropertyOption.MANDATORY) && currentValues.isEmpty())) {
        MenuResult<Boolean> result;
        if (!oldValues.equals(currentValues)) {
          result = MenuResult.success(true);
        } else {
          result = MenuResult.<Boolean> cancel();
        }
        Message option = getKeepDefaultValuesMenuOption(d, defaultValues,
            oldValues, currentValues);
        builder.addNumberedOption(option, result);
        builder.setDefault(Message.raw("1"), result);
      }
      // Add an option for adding some values.
      if (addCallback != null) {
        int i = builder.addNumberedOption(
            INFO_EDITOR_OPTION_ADD_ONE_OR_MORE_VALUES.get(), addCallback);
        if (d.hasOption(PropertyOption.MANDATORY) && currentValues.isEmpty()) {
          builder.setDefault(Message.raw("%d", i), addCallback);
        }
      }
      // Add options for removing values if applicable.
      if (!currentValues.isEmpty()) {
        builder.addNumberedOption(INFO_EDITOR_OPTION_REMOVE_ONE_OR_MORE_VALUES
            .get(), removeCallback);
      }
      // Add options for removing all values and for resetting the
      // property to its default behavior.
      Message resetOption = null;
      if (!currentValues.equals(defaultValues)) {
        resetOption = getResetToDefaultValuesMenuOption(d, defaultValues,
            currentValues);
      }
      if (!currentValues.isEmpty()) {
        if (resetOption == null || !defaultValues.isEmpty()) {
          MenuCallback<Boolean> callback = new MenuCallback<Boolean>() {
            public MenuResult<Boolean> invoke(ConsoleApplication app)
                throws CLIException {
              currentValues.clear();
              app.println();
              app.pressReturnToContinue();
              return MenuResult.success(false);
            }
          };
          builder.addNumberedOption(INFO_EDITOR_OPTION_REMOVE_ALL_VALUES.get(),
              callback);
        }
      }
      if (resetOption != null) {
        MenuCallback<Boolean> callback = new MenuCallback<Boolean>() {
          public MenuResult<Boolean> invoke(ConsoleApplication app)
              throws CLIException {
            currentValues.clear();
            currentValues.addAll(defaultValues);
            app.println();
            app.pressReturnToContinue();
            return MenuResult.success(false);
          }
        };
        builder.addNumberedOption(resetOption, callback);
      }
      // Add an option for undoing any changes.
      if (!oldValues.equals(currentValues)) {
        MenuCallback<Boolean> callback = new MenuCallback<Boolean>() {
          public MenuResult<Boolean> invoke(ConsoleApplication app)
              throws CLIException {
            currentValues.clear();
            currentValues.addAll(oldValues);
            app.println();
            app.pressReturnToContinue();
            return MenuResult.success(false);
          }
        };
        builder.addNumberedOption(INFO_EDITOR_OPTION_REVERT_CHANGES.get(),
            callback);
      }
      builder.addHelpOption(new PropertyHelpCallback(mo
          .getManagedObjectDefinition(), d));
      builder.addQuitOption();
      Menu<Boolean> menu = builder.toMenu();
      MenuResult<Boolean> result;
      try {
        app.println();
        result = menu.run();
      } catch (CLIException e) {
        this.e = e;
        return null;
      }
      if (result.isSuccess()) {
        if (result.getValue() == true) {
          // Set the new property value(s).
          mo.setPropertyValues(d, currentValues);
          app.println();
          app.pressReturnToContinue();
          return MenuResult.success(false);
        } else {
          // Continue until cancel/apply changes.
          app.println();
          return MenuResult.again();
        }
      } else if (result.isCancel()) {
        app.println();
        app.pressReturnToContinue();
        return MenuResult.success(false);
      } else {
        return MenuResult.quit();
      }
    }
  }
  /**
   * A help call-back which displays a description and summary of a
   * single property.
   */
  private static final class PropertyHelpCallback implements HelpCallback {
    // The managed object definition.
    private final ManagedObjectDefinition<?, ?> d;
    // The property to be edited.
    private final PropertyDefinition<?> pd;
    // Creates a new property helper for the specified property.
    private PropertyHelpCallback(ManagedObjectDefinition<?, ?> d,
        PropertyDefinition<?> pd) {
      this.d = d;
      this.pd = pd;
    }
    /**
     * {@inheritDoc}
     */
    public void display(ConsoleApplication app) {
      app.println();
      HelpSubCommandHandler.displayVerboseSingleProperty(app, d, pd.getName());
      app.println();
      app.pressReturnToContinue();
    }
  }
  /**
   * A menu call-back for viewing a read-only properties.
   */
  private static final class ReadOnlyPropertyViewer extends
      PropertyDefinitionVisitor<MenuResult<Boolean>, ConsoleApplication>
      implements MenuCallback<Boolean> {
    // Any exception that was caught during processing.
    private CLIException e = null;
    // The managed object being edited.
    private final ManagedObject<?> mo;
    // The property to be edited.
    private final PropertyDefinition<?> pd;
    // Creates a new property editor for the specified property.
    private ReadOnlyPropertyViewer(ManagedObject<?> mo,
        PropertyDefinition<?> pd) {
      Validator.ensureTrue(!pd.hasOption(PropertyOption.MULTI_VALUED));
      this.mo = mo;
      this.pd = pd;
    }
    /**
     * {@inheritDoc}
     */
    public MenuResult<Boolean> invoke(ConsoleApplication app)
        throws CLIException {
      MenuResult<Boolean> result = pd.accept(this, app);
      if (e != null) {
        throw e;
      } else {
        return result;
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> MenuResult<Boolean> visitUnknown(PropertyDefinition<T> pd,
        ConsoleApplication app) {
      SortedSet<T> values = mo.getPropertyValues(pd);
      app.println();
      app.println();
      switch (values.size()) {
      case 0:
        // Only alias, undefined, or inherited alias or undefined
        // properties should apply here.
        DefaultBehaviorQuery<T> query = DefaultBehaviorQuery.query(pd);
        Message aliasDescription = query.getAliasDescription();
        if (aliasDescription == null) {
          app.println(INFO_EDITOR_HEADING_READ_ONLY_ALIAS_UNDEFINED.get(pd
              .getName()));
        } else {
          app.println(INFO_EDITOR_HEADING_READ_ONLY_ALIAS.get(pd.getName(),
              aliasDescription));
        }
        break;
      case 1:
        Message svalue = getPropertyValues(pd, mo);
        app.println(INFO_EDITOR_HEADING_READ_ONLY_VALUE.get(pd.getName(),
            svalue));
        break;
      default:
        app.println(INFO_EDITOR_HEADING_READ_ONLY_VALUES.get(pd.getName()));
        app.println();
        displayPropertyValues(app, pd, values);
        break;
      }
      app.println();
      boolean result;
      try {
        result = app.confirmAction(INFO_EDITOR_PROMPT_READ_ONLY.get(), false);
      } catch (CLIException e) {
        this.e = e;
        return null;
      }
      if (result) {
        app.println();
        HelpSubCommandHandler.displayVerboseSingleProperty(app, mo
            .getManagedObjectDefinition(), pd.getName());
        app.println();
        app.pressReturnToContinue();
      }
      return MenuResult.again();
    }
  }
  /**
   * A menu call-back for editing a modifiable single-valued property.
   */
  private static final class SingleValuedPropertyEditor extends
      PropertyDefinitionVisitor<MenuResult<Boolean>, ConsoleApplication>
      implements MenuCallback<Boolean> {
    // Any exception that was caught during processing.
    private CLIException e = null;
    // The managed object being edited.
    private final ManagedObject<?> mo;
    // The property to be edited.
    private final PropertyDefinition<?> pd;
    // Creates a new property editor for the specified property.
    private SingleValuedPropertyEditor(ManagedObject<?> mo,
        PropertyDefinition<?> pd) {
      Validator.ensureTrue(!pd.hasOption(PropertyOption.MULTI_VALUED));
      this.mo = mo;
      this.pd = pd;
    }
    /**
     * {@inheritDoc}
     */
    public MenuResult<Boolean> invoke(ConsoleApplication app)
        throws CLIException {
      displayPropertyHeader(app, pd);
      MenuResult<Boolean> result = pd.accept(this, app);
      if (e != null) {
        throw e;
      } else {
        return result;
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public MenuResult<Boolean> visitBoolean(BooleanPropertyDefinition d,
        ConsoleApplication app) {
      // Construct a menu of actions.
      MenuBuilder<Boolean> builder = new MenuBuilder<Boolean>(app);
      builder.setPrompt(INFO_EDITOR_PROMPT_MODIFY_MENU.get(d.getName()));
      DefaultBehaviorQuery<Boolean> query = DefaultBehaviorQuery.query(d);
      SortedSet<Boolean> currentValues = mo.getPropertyValues(d);
      SortedSet<Boolean> defaultValues = mo.getPropertyDefaultValues(d);
      Boolean currentValue = currentValues.isEmpty() ? null : currentValues
          .first();
      Boolean defaultValue = defaultValues.isEmpty() ? null : defaultValues
          .first();
      // First option is for leaving the property unchanged.
      Message option = getKeepDefaultValuesMenuOption(d);
      builder.addNumberedOption(option, MenuResult.<Boolean> cancel());
      builder.setDefault(Message.raw("1"), MenuResult.<Boolean> cancel());
      // The second (and possibly third) option is to always change
      // the property's value.
      if (currentValue == null || currentValue == false) {
        Message svalue = getPropertyValues(d, Collections.singleton(true));
        if (defaultValue != null && defaultValue == true) {
          option = INFO_EDITOR_OPTION_CHANGE_TO_DEFAULT_VALUE.get(svalue);
        } else {
          option = INFO_EDITOR_OPTION_CHANGE_TO_VALUE.get(svalue);
        }
        builder.addNumberedOption(option, MenuResult.success(true));
      }
      if (currentValue == null || currentValue == true) {
        Message svalue = getPropertyValues(d, Collections.singleton(false));
        if (defaultValue != null && defaultValue == false) {
          option = INFO_EDITOR_OPTION_CHANGE_TO_DEFAULT_VALUE.get(svalue);
        } else {
          option = INFO_EDITOR_OPTION_CHANGE_TO_VALUE.get(svalue);
        }
        builder.addNumberedOption(option, MenuResult.success(false));
      }
      // Final option is to reset the value back to its default.
      if (mo.isPropertyPresent(d) && !query.isDefined()) {
        option = getResetToDefaultValuesMenuOption(d);
        if (option != null) {
          builder.addNumberedOption(option, MenuResult.<Boolean> success());
        }
      }
      return runMenu(app, d, builder);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <E extends Enum<E>> MenuResult<Boolean> visitEnum(
        EnumPropertyDefinition<E> d, ConsoleApplication app) {
      // Construct a menu of actions.
      MenuBuilder<E> builder = new MenuBuilder<E>(app);
      builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD);
      builder.setPrompt(INFO_EDITOR_PROMPT_MODIFY_MENU.get(d.getName()));
      DefaultBehaviorQuery<E> query = DefaultBehaviorQuery.query(d);
      SortedSet<E> currentValues = mo.getPropertyValues(d);
      SortedSet<E> defaultValues = mo.getPropertyDefaultValues(d);
      E currentValue = currentValues.isEmpty() ? null : currentValues.first();
      E defaultValue = defaultValues.isEmpty() ? null : defaultValues.first();
      // First option is for leaving the property unchanged.
      Message option = getKeepDefaultValuesMenuOption(d);
      builder.addNumberedOption(option, MenuResult.<E> cancel());
      builder.setDefault(Message.raw("1"), MenuResult.<E> cancel());
      // Create options for changing to other values.
      Set<E> values = new TreeSet<E>(d);
      values.addAll(EnumSet.allOf(d.getEnumClass()));
      for (E value : values) {
        if (value.equals(currentValue) && query.isDefined()) {
          // This option is unnecessary.
          continue;
        }
        Message svalue = getPropertyValues(d, Collections.singleton(value));
        if (value.equals(defaultValue) && query.isDefined()) {
          option = INFO_EDITOR_OPTION_CHANGE_TO_DEFAULT_VALUE.get(svalue);
        } else {
          option = INFO_EDITOR_OPTION_CHANGE_TO_VALUE.get(svalue);
        }
        builder.addNumberedOption(option, MenuResult.success(value));
      }
      // Third option is to reset the value back to its default.
      if (mo.isPropertyPresent(d) && !query.isDefined()) {
        option = getResetToDefaultValuesMenuOption(d);
        if (option != null) {
          builder.addNumberedOption(option, MenuResult.<E> success());
        }
      }
      return runMenu(app, d, builder);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> MenuResult<Boolean> visitUnknown(final PropertyDefinition<T> d,
        ConsoleApplication app) {
      PropertyDefinitionUsageBuilder b = new PropertyDefinitionUsageBuilder(
          true);
      app.println();
      app.println(INFO_EDITOR_HEADING_SYNTAX.get(b.getUsage(d)), 4);
      // Construct a menu of actions.
      MenuBuilder<T> builder = new MenuBuilder<T>(app);
      builder.setPrompt(INFO_EDITOR_PROMPT_MODIFY_MENU.get(d.getName()));
      // First option is for leaving the property unchanged.
      Message option = getKeepDefaultValuesMenuOption(d);
      builder.addNumberedOption(option, MenuResult.<T> cancel());
      builder.setDefault(Message.raw("1"), MenuResult.<T> cancel());
      // The second option is to always change the property's value.
      builder.addNumberedOption(INFO_EDITOR_OPTION_CHANGE_VALUE.get(),
          new MenuCallback<T>() {
            public MenuResult<T> invoke(ConsoleApplication app)
                throws CLIException {
              app.println();
              Set<T> values = readPropertyValues(app, mo
                  .getManagedObjectDefinition(), d);
              return MenuResult.success(values);
            }
          });
      // Third option is to reset the value back to its default.
      if (mo.isPropertyPresent(d)) {
        option = getResetToDefaultValuesMenuOption(d);
        if (option != null) {
          builder.addNumberedOption(option, MenuResult.<T> success());
        }
      }
      return runMenu(app, d, builder);
    }
    /**
     * Generate an appropriate menu option for a property which asks
     * the user whether or not they want to keep the property's
     * current settings.
     */
    private <T> Message getKeepDefaultValuesMenuOption(
        PropertyDefinition<T> pd) {
      DefaultBehaviorQuery<T> query = DefaultBehaviorQuery.query(pd);
      SortedSet<T> currentValues = mo.getPropertyValues(pd);
      SortedSet<T> defaultValues = mo.getPropertyDefaultValues(pd);
      if (query.isDefined() && currentValues.equals(defaultValues)) {
        Message svalue = getPropertyValues(pd, currentValues);
        return INFO_EDITOR_OPTION_KEEP_DEFAULT_VALUE.get(svalue);
      } else if (mo.isPropertyPresent(pd)) {
        Message svalue = getPropertyValues(pd, currentValues);
        return INFO_EDITOR_OPTION_KEEP_VALUE.get(svalue);
      } else if (query.isAlias()) {
        return INFO_EDITOR_OPTION_KEEP_DEFAULT_ALIAS.get(query
            .getAliasDescription());
      } else if (query.isInherited()) {
        if (defaultValues.isEmpty()) {
          if (query.getAliasDescription() != null) {
            return INFO_EDITOR_OPTION_KEEP_DEFAULT_INHERITED_ALIAS.get(query
                .getAliasDescription());
          } else {
            return INFO_EDITOR_OPTION_KEEP_DEFAULT_INHERITED_ALIAS_UNDEFINED
                .get();
          }
        } else {
          Message svalue = getPropertyValues(pd, defaultValues);
          return INFO_EDITOR_OPTION_KEEP_INHERITED_DEFAULT_VALUE.get(svalue);
        }
      } else {
        return INFO_EDITOR_OPTION_LEAVE_UNDEFINED.get();
      }
    }
    /**
     * Generate an appropriate menu option which should be used in the
     * case where a property can be reset to its default behavior.
     */
    private <T> Message getResetToDefaultValuesMenuOption(
        PropertyDefinition<T> pd) {
      DefaultBehaviorQuery<T> query = DefaultBehaviorQuery.query(pd);
      SortedSet<T> currentValues = mo.getPropertyValues(pd);
      SortedSet<T> defaultValues = mo.getPropertyDefaultValues(pd);
      boolean isMandatory = pd.hasOption(PropertyOption.MANDATORY);
      if (!isMandatory && query.isAlias()) {
        return INFO_EDITOR_OPTION_RESET_DEFAULT_ALIAS.get(query
            .getAliasDescription());
      } else if (query.isDefined()) {
        // Only show this option if the current value is different
        // to the default.
        if (!currentValues.equals(defaultValues)) {
          Message svalue = getPropertyValues(pd, defaultValues);
          return INFO_EDITOR_OPTION_RESET_DEFAULT_VALUE.get(svalue);
        } else {
          return null;
        }
      } else if (!isMandatory && query.isInherited()) {
        if (defaultValues.isEmpty()) {
          if (query.getAliasDescription() != null) {
            return INFO_EDITOR_OPTION_RESET_DEFAULT_INHERITED_ALIAS.get(query
                .getAliasDescription());
          } else {
            return INFO_EDITOR_OPTION_RESET_DEFAULT_INHERITED_ALIAS_UNDEFINED
                .get();
          }
        } else {
          Message svalue = getPropertyValues(pd, defaultValues);
          return INFO_EDITOR_OPTION_RESET_INHERITED_DEFAULT_VALUE.get(svalue);
        }
      } else if (!isMandatory && query.isUndefined()) {
        return INFO_EDITOR_OPTION_LEAVE_UNDEFINED.get();
      } else {
        return null;
      }
    }
    // Common menu processing.
    private <T> MenuResult<Boolean> runMenu(ConsoleApplication app,
        final PropertyDefinition<T> d, MenuBuilder<T> builder)
        throws IllegalPropertyValueException, PropertyIsSingleValuedException,
        PropertyIsReadOnlyException, PropertyIsMandatoryException,
        IllegalArgumentException {
      builder.addHelpOption(new PropertyHelpCallback(mo
          .getManagedObjectDefinition(), d));
      builder.addQuitOption();
      Menu<T> menu = builder.toMenu();
      MenuResult<T> result;
      try {
        app.println();
        result = menu.run();
      } catch (CLIException e) {
        this.e = e;
        return null;
      }
      if (result.isSuccess()) {
        // Set the new property value(s).
        mo.setPropertyValues(d, result.getValues());
        app.println();
        app.pressReturnToContinue();
        return MenuResult.success(false);
      } else if (result.isCancel()) {
        app.println();
        app.pressReturnToContinue();
        return MenuResult.success(false);
      } else {
        return MenuResult.quit();
      }
    }
  }
  // Display a title and a description of the property.
  private static void displayPropertyHeader(ConsoleApplication app,
      PropertyDefinition<?> pd) {
    app.println();
    app.println();
    app.println(INFO_EDITOR_HEADING_CONFIGURE_PROPERTY.get(pd.getName()));
    app.println();
    app.println(pd.getSynopsis(), 4);
    if (pd.getDescription() != null) {
      app.println();
      app.println(pd.getDescription(), 4);
    }
  }
  // Display a table of property values.
  private static <T> void displayPropertyValues(ConsoleApplication app,
      PropertyDefinition<T> pd, Collection<T> values)
      throws IllegalArgumentException {
    TableBuilder builder = new TableBuilder();
    PropertyValuePrinter valuePrinter = new PropertyValuePrinter(null,
        null, false);
    int sz = values.size();
    boolean useMultipleColumns = (sz >= MULTI_COLUMN_THRESHOLD);
    int rows = sz;
    if (useMultipleColumns) {
      // Display in two columns the first column should contain
      // half the values. If there are an odd number of columns
      // then the first column should contain an additional value
      // (e.g. if there are 23 values, the first column should
      // contain 12 values and the second column 11 values).
      rows /= 2;
      rows += sz % 2;
    }
    List<T> vl = new ArrayList<T>(values);
    for (int i = 0, j = rows; i < rows; i++, j++) {
      builder.startRow();
      builder.appendCell();
      builder.appendCell(INFO_EDITOR_OPTION_VALUES.get(i + 1));
      builder.appendCell(valuePrinter.print(pd, vl.get(i)));
      if (useMultipleColumns && (j < sz)) {
        builder.appendCell();
        builder.appendCell(INFO_EDITOR_OPTION_VALUES.get(j + 1));
        builder.appendCell(valuePrinter.print(pd, vl.get(j)));
      }
    }
    TextTablePrinter printer = new TextTablePrinter(app.getErrorStream());
    printer.setDisplayHeadings(false);
    printer.setColumnWidth(0, 2);
    printer.setColumnWidth(2, 0);
    if (useMultipleColumns) {
      printer.setColumnWidth(3, 2);
      printer.setColumnWidth(5, 0);
    }
    builder.print(printer);
  }
  // Display the set of values associated with a property.
  private static <T> Message getPropertyValues(PropertyDefinition<T> pd,
      Collection<T> values) {
    if (values.isEmpty()) {
      // There are no values or default values. Display the default
      // behavior for alias values.
      DefaultBehaviorQuery<T> query = DefaultBehaviorQuery.query(pd);
      Message content = query.getAliasDescription();
      if (content == null) {
        return Message.raw("-");
      } else {
        return content;
      }
    } else {
      PropertyValuePrinter printer =
        new PropertyValuePrinter(null, null, false);
      MessageBuilder builder = new MessageBuilder();
      boolean isFirst = true;
      for (T value : values) {
        if (!isFirst) {
          builder.append(", ");
        }
        builder.append(printer.print(pd, value));
        isFirst = false;
      }
      return builder.toMessage();
    }
  }
  // Display the set of values associated with a property.
  private static <T> Message getPropertyValues(
      PropertyDefinition<T> pd,
      ManagedObject<?> mo) {
    SortedSet<T> values = mo.getPropertyValues(pd);
    return getPropertyValues(pd, values);
  }
  // Read new values for a property.
  private static <T> SortedSet<T> readPropertyValues(ConsoleApplication app,
      ManagedObjectDefinition<?, ?> d, PropertyDefinition<T> pd)
      throws CLIException {
    SortedSet<T> values = new TreeSet<T>(pd);
    readPropertyValues(app, d, pd, values);
    return values;
  }
  // Add values to a property.
  private static <T> void readPropertyValues(ConsoleApplication app,
      ManagedObjectDefinition<?, ?> d, PropertyDefinition<T> pd,
      SortedSet<T> values) throws CLIException {
    // Make sure there is at least one value if mandatory and empty.
    if (values.isEmpty()) {
      while (true) {
        try {
          Message prompt;
          if (pd.hasOption(PropertyOption.MANDATORY)) {
            prompt = INFO_EDITOR_PROMPT_READ_FIRST_VALUE.get(pd.getName());
          } else {
            prompt = INFO_EDITOR_PROMPT_READ_FIRST_VALUE_OPTIONAL.get(pd
                .getName());
          }
          app.println();
          String s = app.readLineOfInput(prompt);
          if (s.trim().length() == 0) {
            if (!pd.hasOption(PropertyOption.MANDATORY)) {
              return;
            }
          }
          T value = pd.decodeValue(s);
          if (values.contains(value)) {
            // Prevent addition of duplicates.
            app.println();
            app.println(ERR_EDITOR_READ_FIRST_DUPLICATE.get(s));
          } else {
            values.add(value);
          }
          break;
        } catch (IllegalPropertyValueStringException e) {
          app.println();
          app.println(ArgumentExceptionFactory.adaptPropertyException(e, d)
              .getMessageObject());
        }
      }
    }
    if (pd.hasOption(PropertyOption.MULTI_VALUED)) {
      // Prompt for more values if multi-valued.
      while (true) {
        try {
          Message prompt = INFO_EDITOR_PROMPT_READ_NEXT_VALUE.get(pd.getName());
          app.println();
          String s = app.readLineOfInput(prompt);
          if (s.trim().length() == 0) {
            return;
          }
          T value = pd.decodeValue(s);
          if (values.contains(value)) {
            // Prevent addition of duplicates.
            app.println();
            app.println(ERR_EDITOR_READ_NEXT_DUPLICATE.get(s));
          } else {
            values.add(value);
          }
        } catch (IllegalPropertyValueStringException e) {
          app.println();
          app.println(ArgumentExceptionFactory.adaptPropertyException(e, d)
              .getMessageObject());
          app.println();
        }
      }
    }
  }
  // The threshold above which choice menus should be displayed in
  // multiple columns.
  private static final int MULTI_COLUMN_THRESHOLD = 8;
  // The application console.
  private final ConsoleApplication app;
  /**
   * Create a new property value editor which will read from the
   * provided application console.
   *
   * @param app
   *          The application console.
   */
  public PropertyValueEditor(ConsoleApplication app) {
    this.app = app;
  }
  /**
   * Interactively edits the properties of a managed object. Only the
   * properties listed in the provided collection will be accessible
   * to the client. It is up to the caller to ensure that the list of
   * properties does not include read-only, monitoring, hidden, or
   * advanced properties as appropriate.
   *
   * @param mo
   *          The managed object.
   * @param c
   *          The collection of properties which can be edited.
   * @param isCreate
   *          Flag indicating whether or not the managed object is
   *          being created. If it is then read-only properties will
   *          be modifiable.
   * @return Returns {@link MenuResult#success()} if the changes made
   *         to the managed object should be applied, or
   *         {@link MenuResult#cancel()} if the user to chose to
   *         cancel any changes, or {@link MenuResult#quit()} if the
   *         user chose to quit the application.
   * @throws CLIException
   *           If the user input could not be retrieved for some
   *           reason.
   */
  public MenuResult<Void> edit(ManagedObject<?> mo,
      Collection<PropertyDefinition<?>> c, boolean isCreate)
      throws CLIException {
    // Get values for this missing mandatory property.
    MandatoryPropertyInitializer mpi = new MandatoryPropertyInitializer();
    for (PropertyDefinition<?> pd : c) {
      if (pd.hasOption(PropertyOption.MANDATORY)) {
        if (mo.getPropertyValues(pd).isEmpty()) {
          MenuResult<Void> result = mpi.read(mo, pd);
          if (!result.isSuccess()) {
            return result;
          }
        }
      }
    }
    while (true) {
      // Construct the main menu.
      MenuBuilder<Boolean> builder = new MenuBuilder<Boolean>(app);
      Message ufn = mo.getManagedObjectDefinition().getUserFriendlyName();
      builder.setPrompt(INFO_EDITOR_HEADING_CONFIGURE_COMPONENT.get(ufn));
      Message heading1 = INFO_DSCFG_HEADING_PROPERTY_NAME.get();
      Message heading2 = INFO_DSCFG_HEADING_PROPERTY_VALUE.get();
      builder.setColumnHeadings(heading1, heading2);
      builder.setColumnWidths(null, 0);
      // Create an option for editing/viewing each property.
      for (PropertyDefinition<?> pd : c) {
        // Determine whether this property should be modifiable.
        boolean isReadOnly = false;
        if (pd.hasOption(PropertyOption.MONITORING)) {
          isReadOnly = true;
        }
        if (!isCreate && pd.hasOption(PropertyOption.READ_ONLY)) {
          isReadOnly = true;
        }
        // Create the appropriate property action.
        MenuCallback<Boolean> callback;
        if (pd.hasOption(PropertyOption.MULTI_VALUED)) {
          if (isReadOnly) {
            callback = new ReadOnlyPropertyViewer(mo, pd);
          } else {
            callback = new MultiValuedPropertyEditor(mo, pd);
          }
        } else {
          if (isReadOnly) {
            callback = new ReadOnlyPropertyViewer(mo, pd);
          } else {
            callback = new SingleValuedPropertyEditor(mo, pd);
          }
        }
        // Create the numeric option.
        Message values = getPropertyValues(pd, mo);
        builder.addNumberedOption(Message.raw("%s", pd.getName()), callback,
            values);
      }
      // Add a help option which displays a summary of the managed
      // object's definition.
      HelpCallback helpCallback = new ComponentHelpCallback(mo, c);
      builder.addHelpOption(helpCallback);
      // Add an option to apply the changes.
      if (isCreate) {
        builder.addCharOption(INFO_EDITOR_OPTION_FINISH_KEY.get(),
            INFO_EDITOR_OPTION_FINISH_CREATE_COMPONENT.get(ufn), MenuResult
                .success(true));
      } else {
        builder.addCharOption(INFO_EDITOR_OPTION_FINISH_KEY.get(),
            INFO_EDITOR_OPTION_FINISH_MODIFY_COMPONENT.get(ufn), MenuResult
                .success(true));
      }
      builder.setDefault(INFO_EDITOR_OPTION_FINISH_KEY.get(), MenuResult
          .success(true));
      // Add options for canceling and quitting.
      if (app.isMenuDrivenMode()) {
        builder.addCancelOption(false);
      }
      builder.addQuitOption();
      // Run the menu - success indicates that any changes should be
      // committed.
      app.println();
      app.println();
      Menu<Boolean> menu = builder.toMenu();
      MenuResult<Boolean> result = menu.run();
      if (result.isSuccess()) {
        if (result.getValue()) {
          return MenuResult.<Void>success();
        }
      } else if (result.isCancel()) {
        return MenuResult.cancel();
      } else {
        return MenuResult.quit();
      }
    }
  }
}
opends/src/server/org/opends/server/tools/dsconfig/PropertyValuePrinter.java
@@ -29,7 +29,12 @@
import java.text.NumberFormat;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import static org.opends.messages.DSConfigMessages.*;
import org.opends.server.admin.BooleanPropertyDefinition;
import org.opends.server.admin.DurationPropertyDefinition;
import org.opends.server.admin.DurationUnit;
import org.opends.server.admin.PropertyDefinition;
@@ -49,7 +54,7 @@
   * Perform property type specific print formatting.
   */
  private static class MyPropertyValueVisitor extends
      PropertyValueVisitor<String, Void> {
      PropertyValueVisitor<Message, Void> {
    // The requested size unit (null if the property's unit should be
    // used).
@@ -91,31 +96,13 @@
     * {@inheritDoc}
     */
    @Override
    public String visitDuration(DurationPropertyDefinition d, Long v, Void p) {
      if (d.getUpperLimit() == null && (v < 0 || v == Long.MAX_VALUE)) {
        return "unlimited";
    public Message visitBoolean(BooleanPropertyDefinition d, Boolean v,
        Void p) {
      if (v == false) {
        return INFO_VALUE_FALSE.get();
      } else {
        return INFO_VALUE_TRUE.get();
      }
      long ms = d.getBaseUnit().toMilliSeconds(v);
      // Use human-readable string representation by default.
      if (timeUnit == null && !isScriptFriendly && ms != 0) {
        return DurationUnit.toString(ms);
      }
      // Use either the specified unit or the property definition's
      // base unit.
      DurationUnit unit = timeUnit;
      if (unit == null) {
        unit = d.getBaseUnit();
      }
      StringBuilder builder = new StringBuilder();
      builder.append(numberFormat.format(unit.fromMilliSeconds(ms)));
      builder.append(' ');
      builder.append(unit.getShortName());
      return builder.toString();
    }
@@ -124,9 +111,42 @@
     * {@inheritDoc}
     */
    @Override
    public String visitSize(SizePropertyDefinition d, Long v, Void p) {
    public Message visitDuration(DurationPropertyDefinition d, Long v, Void p) {
      if (d.getUpperLimit() == null && (v < 0 || v == Long.MAX_VALUE)) {
        return INFO_VALUE_UNLIMITED.get();
      }
      MessageBuilder builder = new MessageBuilder();
      long ms = d.getBaseUnit().toMilliSeconds(v);
      if (timeUnit == null && !isScriptFriendly && ms != 0) {
        // Use human-readable string representation by default.
        builder.append(DurationUnit.toString(ms));
      } else {
        // Use either the specified unit or the property definition's
        // base unit.
        DurationUnit unit = timeUnit;
        if (unit == null) {
          unit = d.getBaseUnit();
        }
        builder.append(numberFormat.format(unit.fromMilliSeconds(ms)));
        builder.append(' ');
        builder.append(unit.getShortName());
      }
      return builder.toMessage();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Message visitSize(SizePropertyDefinition d, Long v, Void p) {
      if (d.isAllowUnlimited() && v < 0) {
        return "unlimited";
        return INFO_VALUE_UNLIMITED.get();
      }
      SizeUnit unit = sizeUnit;
@@ -139,12 +159,12 @@
        }
      }
      StringBuilder builder = new StringBuilder();
      MessageBuilder builder = new MessageBuilder();
      builder.append(numberFormat.format(unit.fromBytes(v)));
      builder.append(' ');
      builder.append(unit.getShortName());
      return builder.toString();
      return builder.toMessage();
    }
@@ -153,18 +173,18 @@
     * {@inheritDoc}
     */
    @Override
    public <T> String visitUnknown(PropertyDefinition<T> d, T v, Void p) {
    public <T> Message visitUnknown(PropertyDefinition<T> d, T v, Void p) {
      // For all other property definition types the default encoding
      // will do.
      String s = d.encodeValue(v);
      if (isScriptFriendly) {
        return s;
        return Message.raw("%s", s);
      } else if (s.trim().length() == 0 || s.contains(",")) {
        // Quote empty strings or strings containing commas
        // non-scripting mode.
        return "\"" + s + "\"";
        return Message.raw("\"%s\"", s);
      } else {
        return s;
        return Message.raw("%s", s);
      }
    }
@@ -210,7 +230,7 @@
   *         encoded according to the rules of this property value
   *         printer.
   */
  public <T> String print(PropertyDefinition<T> pd, T value) {
  public <T> Message print(PropertyDefinition<T> pd, T value) {
    return pd.accept(pimpl, value, null);
  }
}
opends/src/server/org/opends/server/tools/dsconfig/PropertyValueReader.java
File was deleted
opends/src/server/org/opends/server/tools/dsconfig/SetPropSubCommandHandler.java
@@ -25,11 +25,10 @@
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
import org.opends.messages.Message;
import static org.opends.messages.ToolMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import java.util.HashMap;
import java.util.List;
@@ -38,6 +37,7 @@
import java.util.SortedSet;
import java.util.TreeSet;
import org.opends.messages.Message;
import org.opends.server.admin.DefinitionDecodingException;
import org.opends.server.admin.IllegalPropertyValueStringException;
import org.opends.server.admin.InstantiableRelationDefinition;
@@ -57,6 +57,7 @@
import org.opends.server.admin.client.ConcurrentModificationException;
import org.opends.server.admin.client.ManagedObject;
import org.opends.server.admin.client.ManagedObjectDecodingException;
import org.opends.server.admin.client.ManagementContext;
import org.opends.server.admin.client.MissingMandatoryPropertiesException;
import org.opends.server.admin.client.OperationRejectedException;
import org.opends.server.protocols.ldap.LDAPResultCode;
@@ -65,6 +66,9 @@
import org.opends.server.util.args.StringArgument;
import org.opends.server.util.args.SubCommand;
import org.opends.server.util.args.SubCommandArgumentParser;
import org.opends.server.util.cli.CLIException;
import org.opends.server.util.cli.ConsoleApplication;
import org.opends.server.util.cli.MenuResult;
@@ -143,8 +147,6 @@
   * Creates a new set-xxx-prop sub-command for an instantiable
   * relation.
   *
   * @param app
   *          The console application.
   * @param parser
   *          The sub-command argument parser.
   * @param path
@@ -155,10 +157,10 @@
   * @throws ArgumentException
   *           If the sub-command could not be created successfully.
   */
  public static SetPropSubCommandHandler create(ConsoleApplication app,
  public static SetPropSubCommandHandler create(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path,
      InstantiableRelationDefinition<?, ?> r) throws ArgumentException {
    return new SetPropSubCommandHandler(app, parser, path.child(r, "DUMMY"), r);
    return new SetPropSubCommandHandler(parser, path.child(r, "DUMMY"), r);
  }
@@ -166,8 +168,6 @@
  /**
   * Creates a new set-xxx-prop sub-command for an optional relation.
   *
   * @param app
   *          The console application.
   * @param parser
   *          The sub-command argument parser.
   * @param path
@@ -178,10 +178,10 @@
   * @throws ArgumentException
   *           If the sub-command could not be created successfully.
   */
  public static SetPropSubCommandHandler create(ConsoleApplication app,
  public static SetPropSubCommandHandler create(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path,
      OptionalRelationDefinition<?, ?> r) throws ArgumentException {
    return new SetPropSubCommandHandler(app, parser, path.child(r), r);
    return new SetPropSubCommandHandler(parser, path.child(r), r);
  }
@@ -189,8 +189,6 @@
  /**
   * Creates a new set-xxx-prop sub-command for a singleton relation.
   *
   * @param app
   *          The console application.
   * @param parser
   *          The sub-command argument parser.
   * @param path
@@ -201,10 +199,10 @@
   * @throws ArgumentException
   *           If the sub-command could not be created successfully.
   */
  public static SetPropSubCommandHandler create(ConsoleApplication app,
  public static SetPropSubCommandHandler create(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path,
      SingletonRelationDefinition<?, ?> r) throws ArgumentException {
    return new SetPropSubCommandHandler(app, parser, path.child(r), r);
    return new SetPropSubCommandHandler(parser, path.child(r), r);
  }
  // The sub-commands naming arguments.
@@ -235,17 +233,15 @@
  // Private constructor.
  private SetPropSubCommandHandler(ConsoleApplication app,
  private SetPropSubCommandHandler(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path,
      RelationDefinition<?, ?> r) throws ArgumentException {
    super(app);
    this.path = path;
    // Create the sub-command.
    String name = "set-" + r.getName() + "-prop";
    Message description = INFO_DSCFG_DESCRIPTION_SUBCMD_SETPROP.get(
      r.getChildDefinition().getUserFriendlyName());
    Message description = INFO_DSCFG_DESCRIPTION_SUBCMD_SETPROP.get(r
        .getChildDefinition().getUserFriendlyName());
    this.subCommand = new SubCommand(parser, name, false, 0, 0, null,
        description);
@@ -287,6 +283,19 @@
  /**
   * Gets the relation definition associated with the type of
   * component that this sub-command handles.
   *
   * @return Returns the relation definition associated with the type
   *         of component that this sub-command handles.
   */
  public RelationDefinition<?, ?> getRelationDefinition() {
    return path.getRelationDefinition();
  }
  /**
   * {@inheritDoc}
   */
  @Override
@@ -301,44 +310,52 @@
   */
  @SuppressWarnings("unchecked")
  @Override
  public int run()
      throws ArgumentException, ClientException {
  public MenuResult<Integer> run(ConsoleApplication app,
      ManagementContextFactory factory) throws ArgumentException,
      ClientException, CLIException {
    // Get the naming argument values.
    List<String> names = getNamingArgValues(namingArgs);
    List<String> names = getNamingArgValues(app, namingArgs);
    ManagedObject<?> child;
    // Get the targeted managed object.
    Message ufn = path.getRelationDefinition().getUserFriendlyName();
    ManagementContext context = factory.getManagementContext(app);
    MenuResult<ManagedObject<?>> result;
    try {
      child = getManagedObject(path, names);
      result = getManagedObject(app, context, path, names);
    } catch (AuthorizationException e) {
      Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_MODIFY_AUTHZ.get(ufn);
      throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS,
          msg);
      throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS, msg);
    } catch (DefinitionDecodingException e) {
      Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_GET_CHILD_DDE.get(ufn, ufn, ufn);
      throw new ClientException(LDAPResultCode.OPERATIONS_ERROR, msg);
    } catch (ManagedObjectDecodingException e) {
      // FIXME: should not abort here. Instead, display the errors (if
      // verbose) and apply the changes to the partial managed object.
      Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_GET_CHILD_MODE.get(ufn);
      throw new ClientException(LDAPResultCode.OPERATIONS_ERROR, msg);
    } catch (CommunicationException e) {
      Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_MODIFY_CE.get(ufn, e.getMessage());
      throw new ClientException(LDAPResultCode.OPERATIONS_ERROR, msg);
    } catch (ConcurrentModificationException e) {
      Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_MODIFY_CME.get(ufn);
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION,
          msg);
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
    } catch (ManagedObjectNotFoundException e) {
      Message ufn = path.getManagedObjectDefinition().getUserFriendlyName();
      Message msg = ERR_DSCFG_ERROR_GET_CHILD_MONFE.get(ufn);
      throw new ClientException(LDAPResultCode.NO_SUCH_OBJECT, msg);
    }
    if (result.isQuit()) {
      if (!app.isMenuDrivenMode()) {
        // User chose to quit.
        Message msg = INFO_DSCFG_CONFIRM_MODIFY_FAIL.get(ufn);
        app.printVerboseMessage(msg);
      }
      return MenuResult.quit();
    } else if (result.isCancel()) {
      return MenuResult.cancel();
    }
    ManagedObject<?> child = result.getValue();
    ManagedObjectDefinition<?, ?> d = child.getManagedObjectDefinition();
    Map<String, ModificationType> lastModTypes =
      new HashMap<String, ModificationType>();
@@ -442,8 +459,8 @@
        }
      } else {
        lastModTypes.put(propertyName, ModificationType.REMOVE);
        modifyPropertyValues(child, pd, changes,
            ModificationType.REMOVE, value);
        modifyPropertyValues(child, pd, changes, ModificationType.REMOVE,
            value);
      }
    }
@@ -495,7 +512,7 @@
    }
    // Interactively set properties if applicable.
    if (getConsoleApplication().isInteractive()) {
    if (app.isInteractive()) {
      SortedSet<PropertyDefinition<?>> properties =
        new TreeSet<PropertyDefinition<?>>();
@@ -504,14 +521,6 @@
          continue;
        }
        if (pd.hasOption(PropertyOption.READ_ONLY)) {
          continue;
        }
        if (pd.hasOption(PropertyOption.MONITORING)) {
          continue;
        }
        if (!isAdvancedMode() && pd.hasOption(PropertyOption.ADVANCED)) {
          continue;
        }
@@ -519,54 +528,47 @@
        properties.add(pd);
      }
      PropertyValueReader reader =
        new PropertyValueReader(getConsoleApplication());
      reader.readAll(child, properties);
      PropertyValueEditor editor = new PropertyValueEditor(app);
      MenuResult<Void> result2 = editor.edit(child, properties, true);
      if (result2.isQuit()) {
        if (!app.isMenuDrivenMode()) {
          // User chose to cancel any changes.
          Message msg = INFO_DSCFG_CONFIRM_MODIFY_FAIL.get(ufn);
          app.printVerboseMessage(msg);
        }
        return MenuResult.quit();
      } else if (result2.isCancel()) {
        return MenuResult.cancel();
      }
    }
    try {
      // Confirm commit.
      Message prompt = INFO_DSCFG_CONFIRM_MODIFY.get(d.getUserFriendlyName());
      if (!getConsoleApplication().confirmAction(prompt)) {
        // Output failure message.
        Message msg =
            INFO_DSCFG_CONFIRM_MODIFY_FAIL.get(d.getUserFriendlyName());
        getConsoleApplication().printVerboseMessage(msg);
        return 1;
      }
      child.commit();
      // Output success message.
      Message msg =
          INFO_DSCFG_CONFIRM_MODIFY_SUCCESS.get(d.getUserFriendlyName());
      getConsoleApplication().printVerboseMessage(msg);
      Message msg = INFO_DSCFG_CONFIRM_MODIFY_SUCCESS.get(ufn);
      app.printVerboseMessage(msg);
    } catch (MissingMandatoryPropertiesException e) {
      throw ArgumentExceptionFactory.adaptMissingMandatoryPropertiesException(
          e, d);
    } catch (AuthorizationException e) {
      Message msg = ERR_DSCFG_ERROR_MODIFY_AUTHZ.get(d.getUserFriendlyName());
      throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS,
          msg);
      Message msg = ERR_DSCFG_ERROR_MODIFY_AUTHZ.get(ufn);
      throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS, msg);
    } catch (ConcurrentModificationException e) {
      Message msg = ERR_DSCFG_ERROR_MODIFY_CME.get(d.getUserFriendlyName());
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION,
          msg);
      Message msg = ERR_DSCFG_ERROR_MODIFY_CME.get(ufn);
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
    } catch (OperationRejectedException e) {
      Message msg = ERR_DSCFG_ERROR_MODIFY_ORE.get(
          d.getUserFriendlyName(), e.getMessage());
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION,
          msg);
      Message msg = ERR_DSCFG_ERROR_MODIFY_ORE.get(ufn, e.getMessage());
      throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
    } catch (CommunicationException e) {
      Message msg = ERR_DSCFG_ERROR_MODIFY_CE.get(
          d.getUserFriendlyName(), e.getMessage());
      Message msg = ERR_DSCFG_ERROR_MODIFY_CE.get(ufn, e.getMessage());
      throw new ClientException(LDAPResultCode.OPERATIONS_ERROR, msg);
    } catch (ManagedObjectAlreadyExistsException e) {
      // Should never happen.
      throw new IllegalStateException(e);
    }
    return 0;
    return MenuResult.success(0);
  }
opends/src/server/org/opends/server/tools/dsconfig/SubCommandBuilder.java
File was deleted
opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandler.java
@@ -25,11 +25,10 @@
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
import org.opends.messages.Message;
import static org.opends.messages.ToolMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import java.io.PrintStream;
import java.util.ArrayList;
@@ -42,6 +41,7 @@
import java.util.List;
import java.util.Set;
import org.opends.messages.Message;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.ConfigurationClient;
@@ -69,6 +69,11 @@
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.cli.CLIException;
import org.opends.server.util.cli.ConsoleApplication;
import org.opends.server.util.cli.Menu;
import org.opends.server.util.cli.MenuBuilder;
import org.opends.server.util.cli.MenuResult;
import org.opends.server.util.table.TabSeparatedTablePrinter;
import org.opends.server.util.table.TablePrinter;
@@ -77,7 +82,7 @@
/**
 * An interface for sub-command implementations.
 */
abstract class SubCommandHandler {
abstract class SubCommandHandler implements Comparable<SubCommandHandler> {
  /**
   * A path serializer which is used to retrieve a managed object
@@ -85,9 +90,8 @@
   */
  private class ManagedObjectFinder implements ManagedObjectPathSerializer {
    // Any argument exception that was caught when attempting to find
    // the managed object.
    private ArgumentException ae;
    // The console application.
    private ConsoleApplication app;
    // The index of the next path argument to be retrieved.
    private int argIndex;
@@ -99,23 +103,23 @@
    private CommunicationException ce;
    // Any CLI exception that was caught when attempting to find
    // the managed object.
    private CLIException clie;
    private ConcurrentModificationException cme;
    // Any operation exception that was caught when attempting to find
    // the managed object.
    private DefinitionDecodingException dde;
    // Flag indicating whether or not an exception occurred during
    // retrieval.
    private boolean gotException;
    // The last managed object retrieved.
    private ManagedObject<?> managedObject;
    private ManagedObjectDecodingException mode;
    private ManagedObjectNotFoundException monfe;
    // The current result.
    private MenuResult<ManagedObject<?>> result;
    /**
@@ -125,7 +129,7 @@
        void appendManagedObjectPathElement(
        InstantiableRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d, String name) {
      if (!gotException) {
      if (result.isSuccess()) {
        // We should ignore the "template" name here and use a path
        // argument.
        String childName = args.get(argIndex++);
@@ -135,50 +139,61 @@
          // the user choose.
          if (childName == null) {
            try {
              childName = readChildName(managedObject, r, d);
            } catch (ArgumentException e) {
              ae = e;
              gotException = true;
              MenuResult<String> sresult = readChildName(app,
                  result.getValue(), r, d);
              if (sresult.isCancel()) {
                result = MenuResult.cancel();
                return;
              } else if (sresult.isQuit()) {
                result = MenuResult.quit();
                return;
              } else {
                childName = sresult.getValue();
              }
            } catch (CLIException e) {
              clie = e;
              result = MenuResult.quit();
              return;
            }
          } else if (childName.trim().length() == 0) {
            IllegalManagedObjectNameException e =
              new IllegalManagedObjectNameException(childName);
            ae = ArgumentExceptionFactory
            clie = ArgumentExceptionFactory
                .adaptIllegalManagedObjectNameException(e, d);
            gotException = true;
            result = MenuResult.quit();
            return;
          }
          ManagedObject<?> child = managedObject.getChild(r, childName);
          ManagedObject<?> child = result.getValue().getChild(r, childName);
          // Check that child is a sub-type of the specified
          // definition.
          if (!child.getManagedObjectDefinition().isChildOf(d)) {
            ae = ArgumentExceptionFactory.wrongManagedObjectType(r, child
            clie = ArgumentExceptionFactory.wrongManagedObjectType(r, child
                .getManagedObjectDefinition());
            gotException = true;
            result = MenuResult.quit();
          } else {
            managedObject = child;
            result = MenuResult.<ManagedObject<?>>success(child);
          }
        } catch (DefinitionDecodingException e) {
          dde = e;
          gotException = true;
          result = MenuResult.quit();
        } catch (ManagedObjectDecodingException e) {
          mode = e;
          gotException = true;
          result = MenuResult.quit();
        } catch (AuthorizationException e) {
          authze = e;
          gotException = true;
          result = MenuResult.quit();
        } catch (ManagedObjectNotFoundException e) {
          monfe = e;
          gotException = true;
          result = MenuResult.quit();
        } catch (ConcurrentModificationException e) {
          cme = e;
          gotException = true;
          result = MenuResult.quit();
        } catch (CommunicationException e) {
          ce = e;
          gotException = true;
          result = MenuResult.quit();
        }
      }
    }
@@ -192,37 +207,37 @@
        void appendManagedObjectPathElement(
        OptionalRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      if (!gotException) {
      if (result.isSuccess()) {
        try {
          ManagedObject<?> child = managedObject.getChild(r);
          ManagedObject<?> child = result.getValue().getChild(r);
          // Check that child is a sub-type of the specified
          // definition.
          if (!child.getManagedObjectDefinition().isChildOf(d)) {
            ae = ArgumentExceptionFactory.wrongManagedObjectType(r, child
            clie = ArgumentExceptionFactory.wrongManagedObjectType(r, child
                .getManagedObjectDefinition());
            gotException = true;
            result = MenuResult.quit();
          } else {
            managedObject = child;
            result = MenuResult.<ManagedObject<?>>success(child);
          }
        } catch (DefinitionDecodingException e) {
          dde = e;
          gotException = true;
          result = MenuResult.quit();
        } catch (ManagedObjectDecodingException e) {
          mode = e;
          gotException = true;
          result = MenuResult.quit();
        } catch (AuthorizationException e) {
          authze = e;
          gotException = true;
          result = MenuResult.quit();
        } catch (ManagedObjectNotFoundException e) {
          monfe = e;
          gotException = true;
          result = MenuResult.quit();
        } catch (ConcurrentModificationException e) {
          cme = e;
          gotException = true;
          result = MenuResult.quit();
        } catch (CommunicationException e) {
          ce = e;
          gotException = true;
          result = MenuResult.quit();
        }
      }
    }
@@ -236,37 +251,37 @@
        void appendManagedObjectPathElement(
        SingletonRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      if (!gotException) {
      if (result.isSuccess()) {
        try {
          ManagedObject<?> child = managedObject.getChild(r);
          ManagedObject<?> child = result.getValue().getChild(r);
          // Check that child is a sub-type of the specified
          // definition.
          if (!child.getManagedObjectDefinition().isChildOf(d)) {
            ae = ArgumentExceptionFactory.wrongManagedObjectType(r, child
            clie = ArgumentExceptionFactory.wrongManagedObjectType(r, child
                .getManagedObjectDefinition());
            gotException = true;
            result = MenuResult.quit();
          } else {
            managedObject = child;
            result = MenuResult.<ManagedObject<?>>success(child);
          }
        } catch (DefinitionDecodingException e) {
          dde = e;
          gotException = true;
          result = MenuResult.quit();
        } catch (ManagedObjectDecodingException e) {
          mode = e;
          gotException = true;
          result = MenuResult.quit();
        } catch (AuthorizationException e) {
          authze = e;
          gotException = true;
          result = MenuResult.quit();
        } catch (ManagedObjectNotFoundException e) {
          monfe = e;
          gotException = true;
          result = MenuResult.quit();
        } catch (ConcurrentModificationException e) {
          cme = e;
          gotException = true;
          result = MenuResult.quit();
        } catch (CommunicationException e) {
          ce = e;
          gotException = true;
          result = MenuResult.quit();
        }
      }
    }
@@ -276,14 +291,20 @@
    /**
     * Finds the named managed object.
     *
     * @param app
     *          The console application.
     * @param context
     *          The management context.
     * @param path
     *          The managed object path.
     * @param args
     *          The managed object path arguments.
     * @return Returns the named managed object.
     * @throws ArgumentException
     * @return Returns a {@link MenuResult#success()} containing the
     *         managed object referenced by the provided managed
     *         object path, or {@link MenuResult#quit()}, or
     *         {@link MenuResult#cancel()}, if the sub-command was
     *         run interactively and the user chose to quit or cancel.
     * @throws CLIException
     *           If one of the naming arguments referenced a managed
     *           object of the wrong type.
     * @throws DefinitionDecodingException
@@ -306,18 +327,19 @@
     *           If the client cannot contact the server due to an
     *           underlying communication problem.
     */
    public ManagedObject<?> find(ManagementContext context,
        ManagedObjectPath<?, ?> path, List<String> args)
        throws ArgumentException, CommunicationException,
    public MenuResult<ManagedObject<?>> find(ConsoleApplication app,
        ManagementContext context, ManagedObjectPath<?, ?> path,
        List<String> args) throws CLIException, CommunicationException,
        AuthorizationException, ConcurrentModificationException,
        DefinitionDecodingException, ManagedObjectDecodingException,
        ManagedObjectNotFoundException {
      this.managedObject = context.getRootConfigurationManagedObject();
      this.result = MenuResult.<ManagedObject<?>> success(context
          .getRootConfigurationManagedObject());
      this.app = app;
      this.args = args;
      this.argIndex = 0;
      this.gotException = false;
      this.ae = null;
      this.clie = null;
      this.authze = null;
      this.ce = null;
      this.cme = null;
@@ -327,8 +349,10 @@
      path.serialize(this);
      if (ae != null) {
        throw ae;
      if (result.isSuccess()) {
        return result;
      } else if (clie != null) {
        throw clie;
      } else if (authze != null) {
        throw authze;
      } else if (ce != null) {
@@ -342,7 +366,8 @@
      } else if (monfe != null) {
        throw monfe;
      } else {
        return managedObject;
        // User requested termination interactively.
        return result;
      }
    }
  }
@@ -500,6 +525,12 @@
  }
  /**
   * The threshold above which choice menus should be displayed in
   * multiple columns.
   */
  public static final int MULTI_COLUMN_THRESHOLD = 8;
  /**
   * The value for the long option advanced.
   */
  private static final String OPTION_DSCFG_LONG_ADVANCED = "advanced";
@@ -552,9 +583,6 @@
  // The argument which should be used to request advanced mode.
  private BooleanArgument advancedModeArgument;
  // The application instance.
  private final ConsoleApplication app;
  // The argument which should be used to specify zero or more
  // property names.
  private StringArgument propertyArgument;
@@ -575,12 +603,41 @@
  /**
   * Create a new sub-command handler.
   *
   * @param app
   *          The application instance.
   */
  protected SubCommandHandler(ConsoleApplication app) {
    this.app = app;
  protected SubCommandHandler() {
    // No implementation required.
  }
  /**
   * {@inheritDoc}
   */
  public final int compareTo(SubCommandHandler o) {
    String s1 = getSubCommand().getName();
    String s2 = o.getSubCommand().getName();
    return s1.compareTo(s2);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public final boolean equals(Object obj) {
    if (this == obj) {
      return true;
    } else if (obj instanceof SubCommandHandler) {
      SubCommandHandler other = (SubCommandHandler) obj;
      String s1 = getSubCommand().getName();
      String s2 = other.getSubCommand().getName();
      return s1.equals(s2);
    } else {
      return false;
    }
  }
@@ -607,17 +664,38 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public final int hashCode() {
    return getSubCommand().getName().hashCode();
  }
  /**
   * Run this sub-command handler.
   *
   * @return Returns zero if the sub-command completed successfully or
   *         non-zero if it did not.
   * @param app
   *          The console application.
   * @param factory
   *          The management context factory.
   * @return Returns a {@link MenuResult#success()} containing zero if
   *         the sub-command completed successfully or non-zero if it
   *         did not, or {@link MenuResult#quit()}, or
   *         {@link MenuResult#cancel()}, if the sub-command was run
   *         interactively and the user chose to quit or cancel.
   * @throws ArgumentException
   *           If an argument required by the sub-command could not be
   *           parsed successfully.
   * @throws ClientException
   *           If the management context could not be created.
   * @throws CLIException
   *           If a CLI exception occurred.
   */
  public abstract int run() throws ArgumentException, ClientException;
  public abstract MenuResult<Integer> run(ConsoleApplication app,
      ManagementContextFactory factory) throws ArgumentException,
      ClientException, CLIException;
@@ -702,26 +780,22 @@
  /**
   * Gets the console application instance.
   *
   * @return Returns the console application instance.
   */
  protected final ConsoleApplication getConsoleApplication() {
    return app;
  }
  /**
   * Get the managed object referenced by the provided managed object
   * path.
   *
   * @param app
   *          The console application.
   * @param context
   *          The management context.
   * @param path
   *          The managed object path.
   * @param args
   *          The list of managed object names required by the path.
   * @return Returns the managed object referenced by the provided
   *         managed object path.
   * @return Returns a {@link MenuResult#success()} containing the
   *         managed object referenced by the provided managed object
   *         path, or {@link MenuResult#quit()}, or
   *         {@link MenuResult#cancel()}, if the sub-command was run
   *         interactively and the user chose to quit or cancel.
   * @throws DefinitionDecodingException
   *           If the managed object was found but its type could not
   *           be determined.
@@ -741,20 +815,21 @@
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   * @throws ArgumentException
   * @throws CLIException
   *           If one of the naming arguments referenced a managed
   *           object of the wrong type.
   * @throws ClientException
   *           If the management context could not be created.
   */
  protected final ManagedObject<?> getManagedObject(
      ManagedObjectPath<?, ?> path, List<String> args)
      throws ArgumentException, AuthorizationException,
      DefinitionDecodingException, ManagedObjectDecodingException,
      CommunicationException, ConcurrentModificationException,
      ManagedObjectNotFoundException, ClientException {
  protected final MenuResult<ManagedObject<?>> getManagedObject(
      ConsoleApplication app, ManagementContext context,
      ManagedObjectPath<?, ?> path, List<String> args) throws CLIException,
      AuthorizationException, DefinitionDecodingException,
      ManagedObjectDecodingException, CommunicationException,
      ConcurrentModificationException, ManagedObjectNotFoundException,
      ClientException {
    ManagedObjectFinder finder = new ManagedObjectFinder();
    return finder.find(app.getManagementContext(), path, args);
    return finder.find(app, context, path, args);
  }
@@ -762,6 +837,8 @@
  /**
   * Gets the values of the naming arguments.
   *
   * @param app
   *          The console application.
   * @param namingArgs
   *          The naming arguments.
   * @return Returns the values of the naming arguments.
@@ -769,7 +846,7 @@
   *           If one of the naming arguments is missing and the
   *           application is non-interactive.
   */
  protected final List<String> getNamingArgValues(
  protected final List<String> getNamingArgValues(ConsoleApplication app,
      List<StringArgument> namingArgs) throws ArgumentException {
    ArrayList<String> values = new ArrayList<String>(namingArgs.size());
    for (StringArgument arg : namingArgs) {
@@ -902,14 +979,19 @@
   *          The type of child client configuration.
   * @param <S>
   *          The type of child server configuration.
   * @param app
   *          The console application.
   * @param parent
   *          The parent managed object.
   * @param r
   *          The relation between the parent and the children.
   * @param d
   *          The type of child managed object to choose from.
   * @return Returns the name of the managed object that the user
   *         selected.
   * @return Returns a {@link MenuResult#success()} containing the
   *         name of the managed object that the user selected, or
   *         {@link MenuResult#quit()}, or
   *         {@link MenuResult#cancel()}, if the sub-command was run
   *         interactive and the user chose to quit or cancel.
   * @throws CommunicationException
   *           If the server cannot be contacted.
   * @throws ConcurrentModificationException
@@ -917,52 +999,62 @@
   * @throws AuthorizationException
   *           If the children cannot be listed due to an
   *           authorization failure.
   * @throws ArgumentException
   * @throws CLIException
   *           If the user input can be read from the console or if
   *           there are no children.
   */
  protected final <C extends ConfigurationClient, S extends Configuration>
      String readChildName(ManagedObject<?> parent,
      MenuResult<String> readChildName(
      ConsoleApplication app, ManagedObject<?> parent,
      InstantiableRelationDefinition<C, S> r,
      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
      throws AuthorizationException, ConcurrentModificationException,
      CommunicationException, ArgumentException {
      CommunicationException, CLIException {
    if (d == null) {
      d = r.getChildDefinition();
    }
    app.println();
    app.println();
    String[] children = parent.listChildren(r, d);
    switch (children.length) {
    case 0: {
      // No options available - abort.
      Message msg =
          ERR_DSCFG_ERROR_FINDER_NO_CHILDREN.get(d.getUserFriendlyPluralName());
      throw new ArgumentException(msg);
      throw new CLIException(msg);
    }
    case 1: {
      // Only one option available so confirm that the user wishes to
      // access it.
      Message msg = INFO_DSCFG_FINDER_PROMPT_SINGLE.get(
              d.getUserFriendlyName(), children[0]);
      if (getConsoleApplication().confirmAction(msg)) {
        return children[0];
      if (app.confirmAction(msg, true)) {
        return MenuResult.success(children[0]);
      } else {
        msg = ERR_DSCFG_ERROR_FINDER_SINGLE_CHILD_REJECTED.get(
            d.getUserFriendlyName());
        throw new ArgumentException(msg);
        return MenuResult.cancel();
      }
    }
    default: {
      // Display a menu.
      MenuBuilder<String> builder = new MenuBuilder<String>(app);
      builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD);
      builder.setPrompt(INFO_DSCFG_FINDER_PROMPT_MANY.get(d
          .getUserFriendlyName()));
      Arrays.sort(children, String.CASE_INSENSITIVE_ORDER);
      ArrayList<Message> desc = new ArrayList<Message>();
      for (String s : Arrays.asList(children)) {
        desc.add(Message.raw(s));
      for (String child : children) {
        Message option = Message.raw("%s", child);
        builder.addNumberedOption(option, MenuResult.success(child));
      }
      Message prompt = INFO_DSCFG_FINDER_PROMPT_MANY.get(
              d.getUserFriendlyName());
      return getConsoleApplication().readChoice(
              prompt, desc, Arrays.asList(children), null);
      if (app.isMenuDrivenMode()) {
        builder.addCancelOption(true);
      }
      builder.addQuitOption();
      Menu<String> menu = builder.toMenu();
      return menu.run();
    }
    }
  }
@@ -1062,4 +1154,7 @@
    subCommand.addArgument(unitTimeArgument);
  }
}
opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandlerFactory.java
New file
@@ -0,0 +1,357 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
import java.util.SortedSet;
import java.util.TreeSet;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.AggregationRelationDefinition;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.ConfigurationClient;
import org.opends.server.admin.InstantiableRelationDefinition;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.OptionalRelationDefinition;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.RelationDefinitionVisitor;
import org.opends.server.admin.RelationOption;
import org.opends.server.admin.SingletonRelationDefinition;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.args.SubCommandArgumentParser;
/**
 * Uses the administration framework introspection API to construct
 * the dsconfig sub-command handlers.
 */
final class SubCommandHandlerFactory {
  /**
   * A relation definition visitor used to recursively determine the
   * set of available sub-commands.
   */
  private final class Visitor implements
      RelationDefinitionVisitor<Void, ManagedObjectPath<?, ?>> {
    /**
     * {@inheritDoc}
     */
    public Void visitAggregation(AggregationRelationDefinition<?, ?> r,
        ManagedObjectPath<?, ?> p) {
      // Do not create sub-commands for aggregations.
      return null;
    }
    /**
     * {@inheritDoc}
     */
    public Void visitInstantiable(InstantiableRelationDefinition<?, ?> r,
        ManagedObjectPath<?, ?> p) {
      try {
        // Create the sub-commands.
        createHandlers.add(CreateSubCommandHandler.create(parser, p, r));
        deleteHandlers.add(DeleteSubCommandHandler.create(parser, p, r));
        listHandlers.add(ListSubCommandHandler.create(parser, p, r));
        getPropHandlers.add(GetPropSubCommandHandler.create(parser, p, r));
        setPropHandlers.add(SetPropSubCommandHandler.create(parser, p, r));
        // Process the referenced managed object definition and its
        // sub-types.
        processRelation(p, r);
      } catch (ArgumentException e) {
        exception = e;
      }
      return null;
    }
    /**
     * {@inheritDoc}
     */
    public Void visitOptional(OptionalRelationDefinition<?, ?> r,
        ManagedObjectPath<?, ?> p) {
      try {
        // Create the sub-commands.
        createHandlers.add(CreateSubCommandHandler.create(parser, p, r));
        deleteHandlers.add(DeleteSubCommandHandler.create(parser, p, r));
        listHandlers.add(ListSubCommandHandler.create(parser, p, r));
        getPropHandlers.add(GetPropSubCommandHandler.create(parser, p, r));
        setPropHandlers.add(SetPropSubCommandHandler.create(parser, p, r));
        // Process the referenced managed object definition and its
        // sub-types.
        processRelation(p, r);
      } catch (ArgumentException e) {
        exception = e;
      }
      return null;
    }
    /**
     * {@inheritDoc}
     */
    public Void visitSingleton(SingletonRelationDefinition<?, ?> r,
        ManagedObjectPath<?, ?> p) {
      try {
        // Create the sub-commands.
        getPropHandlers.add(GetPropSubCommandHandler.create(parser, p, r));
        setPropHandlers.add(SetPropSubCommandHandler.create(parser, p, r));
        // Process the referenced managed object definition and its
        // sub-types.
        processRelation(p, r);
      } catch (ArgumentException e) {
        exception = e;
      }
      return null;
    }
  }
  // The set of all available sub-commands.
  private SortedSet<SubCommandHandler> allHandlers =
    new TreeSet<SubCommandHandler>();
  // The set of create-xxx available sub-commands.
  private SortedSet<CreateSubCommandHandler<?, ?>> createHandlers =
    new TreeSet<CreateSubCommandHandler<?, ?>>();
  // The set of delete-xxx available sub-commands.
  private SortedSet<DeleteSubCommandHandler> deleteHandlers =
    new TreeSet<DeleteSubCommandHandler>();
  // Any exception that occurred whilst creating the sub-commands.
  private ArgumentException exception = null;
  // The set of get-xxx-prop available sub-commands.
  private SortedSet<GetPropSubCommandHandler> getPropHandlers =
    new TreeSet<GetPropSubCommandHandler>();
  // The help sub-command handler.
  private HelpSubCommandHandler helpHandler = null;
  // The set of list-xxx available sub-commands.
  private SortedSet<ListSubCommandHandler> listHandlers =
    new TreeSet<ListSubCommandHandler>();
  // The sub-command argument parser.
  private final SubCommandArgumentParser parser;
  // The set of set-xxx-prop available sub-commands.
  private SortedSet<SetPropSubCommandHandler> setPropHandlers =
    new TreeSet<SetPropSubCommandHandler>();
  // The relation visitor.
  private final Visitor visitor = new Visitor();
  /**
   * Create a new sub-command builder.
   *
   * @param parser
   *          The sub-command argument parser.
   * @throws ArgumentException
   *           If a sub-command could not be created successfully.
   */
  public SubCommandHandlerFactory(
      SubCommandArgumentParser parser) throws ArgumentException {
    this.parser = parser;
    // We always need a help properties sub-command handler.
    helpHandler = HelpSubCommandHandler.create(parser);
    processPath(ManagedObjectPath.emptyPath());
    allHandlers.add(helpHandler);
    allHandlers.addAll(createHandlers);
    allHandlers.addAll(deleteHandlers);
    allHandlers.addAll(listHandlers);
    allHandlers.addAll(getPropHandlers);
    allHandlers.addAll(setPropHandlers);
    if (exception != null) {
      throw exception;
    }
  }
  /**
   * Gets all the sub-command handlers.
   *
   * @return Returns all the sub-command handlers.
   */
  public SortedSet<SubCommandHandler> getAllSubCommandHandlers() {
    return allHandlers;
  }
  /**
   * Gets all the create-xxx sub-command handlers.
   *
   * @return Returns all the create-xxx sub-command handlers.
   */
  public SortedSet<CreateSubCommandHandler<?, ?>>
      getCreateSubCommandHandlers() {
    return createHandlers;
  }
  /**
   * Gets all the delete-xxx sub-command handlers.
   *
   * @return Returns all the delete-xxx sub-command handlers.
   */
  public SortedSet<DeleteSubCommandHandler> getDeleteSubCommandHandlers() {
    return deleteHandlers;
  }
  /**
   * Gets all the get-xxx-prop sub-command handlers.
   *
   * @return Returns all the get-xxx-prop sub-command handlers.
   */
  public SortedSet<GetPropSubCommandHandler> getGetPropSubCommandHandlers() {
    return getPropHandlers;
  }
  /**
   * Gets all the list-xxx sub-command handlers.
   *
   * @return Returns all the list-xxx sub-command handlers.
   */
  public SortedSet<ListSubCommandHandler> getListSubCommandHandlers() {
    return listHandlers;
  }
  /**
   * Gets all the set-xxx-prop sub-command handlers.
   *
   * @return Returns all the set-xxx-prop sub-command handlers.
   */
  public SortedSet<SetPropSubCommandHandler> getSetPropSubCommandHandlers() {
    return setPropHandlers;
  }
  // Process the relations associated with the managed object
  // definition identified by the provided path.
  private void processPath(ManagedObjectPath<?, ?> path) {
    AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
    // Do not process inherited relation definitions.
    for (RelationDefinition<?, ?> r : d.getRelationDefinitions()) {
      if (!r.hasOption(RelationOption.HIDDEN)) {
        r.accept(visitor, path);
      }
    }
  }
  // Process an instantiable relation.
  private <C extends ConfigurationClient, S extends Configuration>
      void processRelation(
      ManagedObjectPath<?, ?> path, InstantiableRelationDefinition<C, S> r) {
    AbstractManagedObjectDefinition<C, S> d = r.getChildDefinition();
    // Process all relations associated directly with this
    // definition.
    helpHandler.registerManagedObjectDefinition(d);
    processPath(path.child(r, d, "DUMMY"));
    // Now process relations associated with derived definitions.
    for (AbstractManagedObjectDefinition<? extends C, ? extends S> c : d
        .getAllChildren()) {
      helpHandler.registerManagedObjectDefinition(c);
      processPath(path.child(r, c, "DUMMY"));
    }
  }
  // Process an optional relation.
  private <C extends ConfigurationClient, S extends Configuration>
      void processRelation(
      ManagedObjectPath<?, ?> path, OptionalRelationDefinition<C, S> r) {
    AbstractManagedObjectDefinition<C, S> d = r.getChildDefinition();
    // Process all relations associated directly with this
    // definition.
    helpHandler.registerManagedObjectDefinition(d);
    processPath(path.child(r, d));
    // Now process relations associated with derived definitions.
    for (AbstractManagedObjectDefinition<? extends C, ? extends S> c : d
        .getAllChildren()) {
      helpHandler.registerManagedObjectDefinition(c);
      processPath(path.child(r, c));
    }
  }
  // Process a singleton relation.
  private <C extends ConfigurationClient, S extends Configuration>
      void processRelation(
      ManagedObjectPath<?, ?> path, SingletonRelationDefinition<C, S> r) {
    AbstractManagedObjectDefinition<C, S> d = r.getChildDefinition();
    // Process all relations associated directly with this
    // definition.
    helpHandler.registerManagedObjectDefinition(d);
    processPath(path.child(r, d));
    // Now process relations associated with derived definitions.
    for (AbstractManagedObjectDefinition<? extends C, ? extends S> c : d
        .getAllChildren()) {
      helpHandler.registerManagedObjectDefinition(c);
      processPath(path.child(r, c));
    }
  }
}
opends/src/server/org/opends/server/util/cli/CLIException.java
New file
@@ -0,0 +1,92 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.util.cli;
import org.opends.messages.Message;
import org.opends.messages.UtilityMessages;
import org.opends.server.types.IdentifiedException;
/**
 * Thrown to indicate that a problem occurred when interacting with
 * the client. For example, if input provided by the client was
 * invalid.
 */
public class CLIException extends IdentifiedException {
  /**
   * Serialization ID.
   */
  private static final long serialVersionUID = -8182075627986981748L;
  /**
   * Adapts any exception that may have occurred whilst reading input
   * from the console.
   *
   * @param cause
   *          The exception that occurred whilst reading input from
   *          the console.
   * @return Returns a new CLI exception describing a problem that
   *         occurred whilst reading input from the console.
   */
  public static CLIException adaptInputException(Throwable cause) {
    return new CLIException(UtilityMessages.ERR_CONSOLE_INPUT_ERROR.get(cause
        .getMessage()), cause);
  }
  /**
   * Creates a new CLI exception with the provided message.
   *
   * @param message
   *          The message explaining the problem that occurred.
   */
  public CLIException(Message message) {
    super(message);
  }
  /**
   * Creates a new CLI exception with the provided message and cause.
   *
   * @param message
   *          The message explaining the problem that occurred.
   * @param cause
   *          The cause of this exception.
   */
  public CLIException(Message message, Throwable cause) {
    super(message, cause);
  }
}
opends/src/server/org/opends/server/util/cli/ConsoleApplication.java
New file
@@ -0,0 +1,453 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.util.cli;
import static org.opends.messages.UtilityMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import org.opends.messages.Message;
import org.opends.server.types.NullOutputStream;
import org.opends.server.util.PasswordReader;
/**
 * This class provides an abstract base class which can be used as the
 * basis of a console-based application.
 */
public abstract class ConsoleApplication {
  /**
   * A null reader.
   */
  private static final class NullReader extends Reader {
    /**
     * {@inheritDoc}
     */
    @Override
    public void close() throws IOException {
      // Do nothing.
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
      return -1;
    }
  }
  // The error stream which this application should use.
  private final PrintStream err;
  // The input stream reader which this application should use.
  private final BufferedReader in;
  // The output stream which this application should use.
  private final PrintStream out;
  /**
   * Creates a new console application instance.
   *
   * @param in
   *          The application input stream.
   * @param out
   *          The application output stream.
   * @param err
   *          The application error stream.
   */
  protected ConsoleApplication(BufferedReader in, PrintStream out,
      PrintStream err) {
    if (in != null) {
      this.in = in;
    } else {
      this.in = new BufferedReader(new NullReader());
    }
    if (out != null) {
      this.out = out;
    } else {
      this.out = NullOutputStream.printStream();
    }
    if (err != null) {
      this.err = out;
    } else {
      this.err = NullOutputStream.printStream();
    }
  }
  /**
   * Creates a new console application instance.
   *
   * @param in
   *          The application input stream.
   * @param out
   *          The application output stream.
   * @param err
   *          The application error stream.
   */
  protected ConsoleApplication(InputStream in, OutputStream out,
      OutputStream err) {
    if (in != null) {
      this.in = new BufferedReader(new InputStreamReader(in));
    } else {
      this.in = new BufferedReader(new NullReader());
    }
    if (out != null) {
      this.out = new PrintStream(out);
    } else {
      this.out = NullOutputStream.printStream();
    }
    if (err != null) {
      this.err = new PrintStream(err);
    } else {
      this.err = NullOutputStream.printStream();
    }
  }
  /**
   * Interactively confirms whether a user wishes to perform an
   * action. If the application is non-interactive, then the provided
   * default is returned automatically.
   *
   * @param prompt
   *          The prompt describing the action.
   * @param defaultValue
   *          The default value for the confirmation message. This
   *          will be returned if the application is non-interactive
   *          or if the user just presses return.
   * @return Returns <code>true</code> if the user wishes the action
   *         to be performed, or <code>false</code> if they refused,
   *         or if an exception occurred.
   * @throws CLIException
   *           If the user's response could not be read from the
   *           console for some reason.
   */
  public final boolean confirmAction(Message prompt, final boolean defaultValue)
      throws CLIException {
    if (!isInteractive()) {
      return defaultValue;
    }
    final Message yes = INFO_GENERAL_YES.get();
    final Message no = INFO_GENERAL_NO.get();
    final Message errMsg = ERR_CONSOLE_APP_CONFIRM.get(yes, no);
    prompt = INFO_MENU_PROMPT_CONFIRM.get(prompt, yes, no, defaultValue ? yes
        : no);
    ValidationCallback<Boolean> validator = new ValidationCallback<Boolean>() {
      public Boolean validate(ConsoleApplication app, String input) {
        String ninput = input.toLowerCase().trim();
        if (ninput.length() == 0) {
          return defaultValue;
        } else if (no.toString().startsWith(ninput)) {
          return false;
        } else if (yes.toString().startsWith(ninput)) {
          return true;
        } else {
          // Try again...
          app.println();
          app.println(errMsg);
          app.println();
        }
        return null;
      }
    };
    try {
      return readValidatedInput(prompt, validator);
    } catch (CLIException e) {
      // Should never happen.
      throw new RuntimeException(e);
    }
  }
  /**
   * Gets the application error stream.
   *
   * @return Returns the application error stream.
   */
  public final PrintStream getErrorStream() {
    return err;
  }
  /**
   * Gets the application input stream.
   *
   * @return Returns the application input stream.
   */
  public final BufferedReader getInputStream() {
    return in;
  }
  /**
   * Gets the application output stream.
   *
   * @return Returns the application output stream.
   */
  public final PrintStream getOutputStream() {
    return out;
  }
  /**
   * Indicates whether or not the user has requested interactive
   * behavior.
   *
   * @return Returns <code>true</code> if the user has requested
   *         interactive behavior.
   */
  public abstract boolean isInteractive();
  /**
   * Indicates whether or not this console application is running in
   * its menu-driven mode. This can be used to dictate whether output
   * should go to the error stream or not. In addition, it may also
   * dictate whether or not sub-menus should display a cancel option
   * as well as a quit option.
   *
   * @return Returns <code>true</code> if this console application
   *         is running in its menu-driven mode.
   */
  public abstract boolean isMenuDrivenMode();
  /**
   * Indicates whether or not the user has requested quiet output.
   *
   * @return Returns <code>true</code> if the user has requested
   *         quiet output.
   */
  public abstract boolean isQuiet();
  /**
   * Indicates whether or not the user has requested script-friendly
   * output.
   *
   * @return Returns <code>true</code> if the user has requested
   *         script-friendly output.
   */
  public abstract boolean isScriptFriendly();
  /**
   * Indicates whether or not the user has requested verbose output.
   *
   * @return Returns <code>true</code> if the user has requested
   *         verbose output.
   */
  public abstract boolean isVerbose();
  /**
   * Interactively prompts the user to press return to continue. This
   * method should be called in situations where a user needs to be
   * given a chance to read some documentation before continuing
   * (continuing may cause the documentation to be scrolled out of
   * view).
   */
  public final void pressReturnToContinue() {
    Message msg = INFO_MENU_PROMPT_RETURN_TO_CONTINUE.get();
    try {
      readLineOfInput(msg);
    } catch (CLIException e) {
      // Ignore the exception - applications don't care.
    }
  }
  /**
   * Displays a blank line to the error stream.
   */
  public final void println() {
    err.println();
  }
  /**
   * Displays a message to the error stream.
   *
   * @param msg
   *          The message.
   */
  public final void println(Message msg) {
    err.println(wrapText(msg, MAX_LINE_WIDTH));
  }
  /**
   * Displays a message to the error stream indented by the specified
   * number of columns.
   *
   * @param msg
   *          The message.
   * @param indent
   *          The number of columns to indent.
   */
  public final void println(Message msg, int indent) {
    err.println(wrapText(msg, MAX_LINE_WIDTH, indent));
  }
  /**
   * Displays a message to the error stream if verbose mode is
   * enabled.
   *
   * @param msg
   *          The verbose message.
   */
  public final void printVerboseMessage(Message msg) {
    if (isVerbose() || isInteractive()) {
      err.println(wrapText(msg, MAX_LINE_WIDTH));
    }
  }
  /**
   * Interactively retrieves a line of input from the console.
   *
   * @param prompt
   *          The prompt.
   * @return Returns the line of input, or <code>null</code> if the
   *         end of input has been reached.
   * @throws CLIException
   *           If the line of input could not be retrieved for some
   *           reason.
   */
  public final String readLineOfInput(Message prompt) throws CLIException {
    err.print(wrapText(prompt + " ", MAX_LINE_WIDTH));
    try {
      String s = in.readLine();
      if (s == null) {
        throw CLIException
            .adaptInputException(new EOFException("End of input"));
      } else {
        return s;
      }
    } catch (IOException e) {
      throw CLIException.adaptInputException(e);
    }
  }
  /**
   * Interactively retrieves a password from the console.
   *
   * @param prompt
   *          The password prompt.
   * @return Returns the password.
   * @throws CLIException
   *           If the password could not be retrieved for some reason.
   */
  public final String readPassword(Message prompt) throws CLIException {
    err.print(wrapText(prompt + " ", MAX_LINE_WIDTH));
    char[] pwChars;
    try {
      pwChars = PasswordReader.readPassword();
    } catch (Exception e) {
      throw CLIException.adaptInputException(e);
    }
    return new String(pwChars);
  }
  /**
   * Interactively prompts for user input and continues until valid
   * input is provided.
   *
   * @param <T>
   *          The type of decoded user input.
   * @param prompt
   *          The interactive prompt which should be displayed on each
   *          input attempt.
   * @param validator
   *          An input validator responsible for validating and
   *          decoding the user's response.
   * @return Returns the decoded user's response.
   * @throws CLIException
   *           If an unexpected error occurred which prevented
   *           validation.
   */
  public final <T> T readValidatedInput(Message prompt,
      ValidationCallback<T> validator) throws CLIException {
    while (true) {
      String response = readLineOfInput(prompt);
      T value = validator.validate(this, response);
      if (value != null) {
        return value;
      }
    }
  }
}
opends/src/server/org/opends/server/util/cli/ErrorStreamConsoleApplication.java
New file
@@ -0,0 +1,105 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.util.cli;
/**
 * A console application decorator which redirects all output to the
 * underlying application's error stream.
 */
public class ErrorStreamConsoleApplication extends ConsoleApplication {
  // The underlying console application.
  private final ConsoleApplication app;
  /**
   * Creates a new console application instance which redirects all
   * output to the underlying application's error stream.
   *
   * @param app
   *          The underlying application console.
   */
  public ErrorStreamConsoleApplication(ConsoleApplication app) {
    super(app.getInputStream(), app.getErrorStream(), app.getErrorStream());
    this.app = app;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isInteractive() {
    return app.isInteractive();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isMenuDrivenMode() {
    return app.isMenuDrivenMode();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isQuiet() {
    return app.isQuiet();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isScriptFriendly() {
    return app.isScriptFriendly();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isVerbose() {
    return app.isVerbose();
  }
}
opends/src/server/org/opends/server/util/cli/HelpCallback.java
File was renamed from opends/src/server/org/opends/server/tools/dsconfig/HelpCallback.java
@@ -24,14 +24,14 @@
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
package org.opends.server.util.cli;
/**
 * An interface for displaying help interactively.
 */
interface HelpCallback {
public interface HelpCallback {
  /**
   * Displays help to the provided application console.
opends/src/server/org/opends/server/util/cli/Menu.java
New file
@@ -0,0 +1,52 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.util.cli;
/**
 * An interactive console-based menu.
 *
 * @param <T>
 *          The type of success result value(s) returned by the
 *          call-back. Use <code>Void</code> if the call-backs do
 *          not return any values.
 */
public interface Menu<T> {
  /**
   * Displays the menu and waits for the user to select a valid
   * option. When the user selects an option, the call-back associated
   * with the option will be invoked and its result returned.
   *
   * @return Returns the result of invoking the chosen menu call-back.
   * @throws CLIException
   *           If an I/O exception occurred or if one of the menu
   *           option call-backs failed for some reason.
   */
  MenuResult<T> run() throws CLIException;
}
opends/src/server/org/opends/server/util/cli/MenuBuilder.java
New file
@@ -0,0 +1,818 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.util.cli;
import static org.opends.messages.UtilityMessages.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.opends.messages.Message;
import org.opends.server.util.table.TableBuilder;
import org.opends.server.util.table.TablePrinter;
import org.opends.server.util.table.TextTablePrinter;
/**
 * An interface for incrementally building a command-line menu.
 *
 * @param <T>
 *          The type of value returned by the call-backs. Use
 *          <code>Void</code> if the call-backs do not return a
 *          value.
 */
public final class MenuBuilder<T> {
  /**
   * A simple menu option call-back which is a composite of zero or
   * more underlying call-backs.
   *
   * @param <T>
   *          The type of value returned by the call-back.
   */
  private static final class CompositeCallback<T> implements MenuCallback<T> {
    // The list of underlying call-backs.
    private final Collection<MenuCallback<T>> callbacks;
    /**
     * Creates a new composite call-back with the specified set of
     * call-backs.
     *
     * @param callbacks
     *          The set of call-backs.
     */
    public CompositeCallback(Collection<MenuCallback<T>> callbacks) {
      this.callbacks = callbacks;
    }
    /**
     * {@inheritDoc}
     */
    public MenuResult<T> invoke(ConsoleApplication app) throws CLIException {
      List<T> values = new ArrayList<T>();
      for (MenuCallback<T> callback : callbacks) {
        MenuResult<T> result = callback.invoke(app);
        if (!result.isSuccess()) {
          // Throw away all the other results.
          return result;
        } else {
          values.addAll(result.getValues());
        }
      }
      return MenuResult.success(values);
    }
  }
  /**
   * Underlying menu implementation generated by this menu builder.
   *
   * @param <T>
   *          The type of value returned by the call-backs. Use
   *          <code>Void</code> if the call-backs do not return a
   *          value.
   */
  private static final class MenuImpl<T> implements Menu<T> {
    // Indicates whether the menu will allow selection of multiple
    // numeric options.
    private final boolean allowMultiSelect;
    // The application console.
    private final ConsoleApplication app;
    // The call-back lookup table.
    private final Map<String, MenuCallback<T>> callbacks;
    // The char options table builder.
    private final TableBuilder cbuilder;
    // The call-back for the optional default action.
    private final MenuCallback<T> defaultCallback;
    // The description of the optional default action.
    private final Message defaultDescription;
    // The numeric options table builder.
    private final TableBuilder nbuilder;
    // The table printer.
    private final TablePrinter printer;
    // The menu prompt.
    private final Message prompt;
    // The menu title.
    private final Message title;
    // Private constructor.
    private MenuImpl(ConsoleApplication app, Message title, Message prompt,
        TableBuilder ntable, TableBuilder ctable, TablePrinter printer,
        Map<String, MenuCallback<T>> callbacks, boolean allowMultiSelect,
        MenuCallback<T> defaultCallback, Message defaultDescription) {
      this.app = app;
      this.title = title;
      this.prompt = prompt;
      this.nbuilder = ntable;
      this.cbuilder = ctable;
      this.printer = printer;
      this.callbacks = callbacks;
      this.allowMultiSelect = allowMultiSelect;
      this.defaultCallback = defaultCallback;
      this.defaultDescription = defaultDescription;
    }
    /**
     * {@inheritDoc}
     */
    public MenuResult<T> run() throws CLIException {
      // The validation call-back which will be used to determine the
      // action call-back.
      ValidationCallback<MenuCallback<T>> validator =
        new ValidationCallback<MenuCallback<T>>() {
        public MenuCallback<T> validate(ConsoleApplication app, String input) {
          String ninput = input.trim();
          if (ninput.length() == 0) {
            if (defaultCallback != null) {
              return defaultCallback;
            } else if (allowMultiSelect) {
              app.println();
              app.println(ERR_MENU_BAD_CHOICE_MULTI.get());
              app.println();
              return null;
            } else {
              app.println();
              app.println(ERR_MENU_BAD_CHOICE_SINGLE.get());
              app.println();
              return null;
            }
          } else if (allowMultiSelect) {
            // Use a composite call-back to collect all the results.
            List<MenuCallback<T>> cl = new ArrayList<MenuCallback<T>>();
            for (String value : ninput.split(",")) {
              // Make sure that there are no duplicates.
              String nvalue = value.trim();
              Set<String> choices = new HashSet<String>();
              if (choices.contains(nvalue)) {
                app.println();
                app.println(ERR_MENU_BAD_CHOICE_MULTI_DUPE.get(value));
                app.println();
                return null;
              } else if (!callbacks.containsKey(nvalue)) {
                app.println();
                app.println(ERR_MENU_BAD_CHOICE_MULTI.get());
                app.println();
                return null;
              } else {
                cl.add(callbacks.get(nvalue));
                choices.add(nvalue);
              }
            }
            return new CompositeCallback<T>(cl);
          } else if (!callbacks.containsKey(ninput)) {
            app.println();
            app.println(ERR_MENU_BAD_CHOICE_SINGLE.get());
            app.println();
            return null;
          } else {
            return callbacks.get(ninput);
          }
        }
      };
      // Determine the correct choice prompt.
      Message promptMsg;
      if (allowMultiSelect) {
        if (defaultDescription != null) {
          promptMsg = INFO_MENU_PROMPT_MULTI_DEFAULT.get(defaultDescription);
        } else {
          promptMsg = INFO_MENU_PROMPT_MULTI.get();
        }
      } else {
        if (defaultDescription != null) {
          promptMsg = INFO_MENU_PROMPT_SINGLE_DEFAULT.get(defaultDescription);
        } else {
          promptMsg = INFO_MENU_PROMPT_SINGLE.get();
        }
      }
      // If the user selects help then we need to loop around and
      // display the menu again.
      while (true) {
        // Display the menu.
        if (title != null) {
          app.println(title);
          app.println();
        }
        if (prompt != null) {
          app.println(prompt);
          app.println();
        }
        if (nbuilder.getTableHeight() > 0) {
          nbuilder.print(printer);
          app.println();
        }
        if (cbuilder.getTableHeight() > 0) {
          TextTablePrinter cprinter =
            new TextTablePrinter(app.getErrorStream());
          cprinter.setDisplayHeadings(false);
          int sz = String.valueOf(nbuilder.getTableHeight()).length() + 1;
          cprinter.setColumnWidth(0, 2);
          cprinter.setColumnWidth(1, sz);
          cprinter.setColumnWidth(2, 0);
          cbuilder.print(cprinter);
          app.println();
        }
        // Get the user's choice.
        MenuCallback<T> choice;
        try {
          choice = app.readValidatedInput(promptMsg, validator);
        } catch (CLIException e) {
          // Should never happen.
          throw new RuntimeException(e);
        }
        // Invoke the user's selected choice.
        MenuResult<T> result = choice.invoke(app);
        // Determine if the help needs to be displayed, display it and
        // start again.
        if (!result.isAgain()) {
          return result;
        } else {
          app.println();
          app.println();
        }
      }
    }
  }
  /**
   * A simple menu option call-back which does nothing but return the
   * provided menu result.
   *
   * @param <T>
   *          The type of result returned by the call-back.
   */
  private static final class ResultCallback<T> implements MenuCallback<T> {
    // The result to be returned by this call-back.
    private final MenuResult<T> result;
    // Private constructor.
    private ResultCallback(MenuResult<T> result) {
      this.result = result;
    }
    /**
     * {@inheritDoc}
     */
    public MenuResult<T> invoke(ConsoleApplication app) throws CLIException {
      return result;
    }
  }
  // The multiple column display threshold.
  private int threshold = -1;
  // Indicates whether the menu will allow selection of multiple
  // numeric options.
  private boolean allowMultiSelect = false;
  // The application console.
  private final ConsoleApplication app;
  // The char option call-backs.
  private final List<MenuCallback<T>> charCallbacks =
    new ArrayList<MenuCallback<T>>();
  // The char option keys (must be single-character messages).
  private final List<Message> charKeys = new ArrayList<Message>();
  // The synopsis of char options.
  private final List<Message> charSynopsis = new ArrayList<Message>();
  // Optional column headings.
  private final List<Message> columnHeadings = new ArrayList<Message>();
  // Optional column widths.
  private final List<Integer> columnWidths = new ArrayList<Integer>();
  // The call-back for the optional default action.
  private MenuCallback<T> defaultCallback = null;
  // The description of the optional default action.
  private Message defaultDescription = null;
  // The numeric option call-backs.
  private final List<MenuCallback<T>> numericCallbacks =
    new ArrayList<MenuCallback<T>>();
  // The numeric option fields.
  private final List<List<Message>> numericFields =
    new ArrayList<List<Message>>();
  // The menu title.
  private Message title = null;
  // The menu prompt.
  private Message prompt = null;
  /**
   * Creates a new menu.
   *
   * @param app
   *          The application console.
   */
  public MenuBuilder(ConsoleApplication app) {
    this.app = app;
  }
  /**
   * Creates a "back" menu option. When invoked, this option will
   * return a {@link MenuResult#cancel()} result.
   *
   * @param isDefault
   *          Indicates whether this option should be made the menu
   *          default.
   */
  public void addBackOption(boolean isDefault) {
    addCharOption(INFO_MENU_OPTION_BACK_KEY.get(), INFO_MENU_OPTION_BACK.get(),
        MenuResult.<T> cancel());
    if (isDefault) {
      setDefault(INFO_MENU_OPTION_BACK_KEY.get(), MenuResult.<T> cancel());
    }
  }
  /**
   * Creates a "cancel" menu option. When invoked, this option will
   * return a {@link MenuResult#cancel()} result.
   *
   * @param isDefault
   *          Indicates whether this option should be made the menu
   *          default.
   */
  public void addCancelOption(boolean isDefault) {
    addCharOption(INFO_MENU_OPTION_CANCEL_KEY.get(), INFO_MENU_OPTION_CANCEL
        .get(), MenuResult.<T> cancel());
    if (isDefault) {
      setDefault(INFO_MENU_OPTION_CANCEL_KEY.get(), MenuResult.<T> cancel());
    }
  }
  /**
   * Adds a menu choice to the menu which will have a single letter as
   * its key.
   *
   * @param c
   *          The single-letter message which will be used as the key
   *          for this option.
   * @param description
   *          The menu option description.
   * @param callback
   *          The call-back associated with this option.
   */
  public void addCharOption(Message c, Message description,
      MenuCallback<T> callback) {
    charKeys.add(c);
    charSynopsis.add(description);
    charCallbacks.add(callback);
  }
  /**
   * Adds a menu choice to the menu which will have a single letter as
   * its key and which returns the provided result.
   *
   * @param c
   *          The single-letter message which will be used as the key
   *          for this option.
   * @param description
   *          The menu option description.
   * @param result
   *          The menu result which should be returned by this menu
   *          choice.
   */
  public void addCharOption(Message c, Message description,
      MenuResult<T> result) {
    addCharOption(c, description, new ResultCallback<T>(result));
  }
  /**
   * Creates a "help" menu option which will use the provided help
   * call-back to display help relating to the other menu options.
   * When the help menu option is selected help will be displayed and
   * then the user will be shown the menu again and prompted to enter
   * a choice.
   *
   * @param callback
   *          The help call-back.
   */
  public void addHelpOption(final HelpCallback callback) {
    MenuCallback<T> wrapper = new MenuCallback<T>() {
      public MenuResult<T> invoke(ConsoleApplication app) throws CLIException {
        app.println();
        callback.display(app);
        return MenuResult.again();
      }
    };
    addCharOption(INFO_MENU_OPTION_HELP_KEY.get(), INFO_MENU_OPTION_HELP.get(),
        wrapper);
  }
  /**
   * Adds a menu choice to the menu which will have a numeric key.
   *
   * @param description
   *          The menu option description.
   * @param callback
   *          The call-back associated with this option.
   * @param extraFields
   *          Any additional fields associated with this menu option.
   * @return Returns the number associated with menu choice.
   */
  public int addNumberedOption(Message description, MenuCallback<T> callback,
      Message... extraFields) {
    List<Message> fields = new ArrayList<Message>();
    fields.add(description);
    if (extraFields != null) {
      fields.addAll(Arrays.asList(extraFields));
    }
    numericFields.add(fields);
    numericCallbacks.add(callback);
    return numericCallbacks.size();
  }
  /**
   * Adds a menu choice to the menu which will have a numeric key and
   * which returns the provided result.
   *
   * @param description
   *          The menu option description.
   * @param result
   *          The menu result which should be returned by this menu
   *          choice.
   * @param extraFields
   *          Any additional fields associated with this menu option.
   * @return Returns the number associated with menu choice.
   */
  public int addNumberedOption(Message description, MenuResult<T> result,
      Message... extraFields) {
    return addNumberedOption(description, new ResultCallback<T>(result),
        extraFields);
  }
  /**
   * Creates a "quit" menu option. When invoked, this option will
   * return a {@link MenuResult#quit()} result.
   */
  public void addQuitOption() {
    addCharOption(INFO_MENU_OPTION_QUIT_KEY.get(), INFO_MENU_OPTION_QUIT.get(),
        MenuResult.<T> quit());
  }
  /**
   * Sets the flag which indicates whether or not the menu will permit
   * multiple numeric options to be selected at once. Users specify
   * multiple choices by separating them with a comma. The default is
   * <code>false</code>.
   *
   * @param allowMultiSelect
   *          Indicates whether or not the menu will permit multiple
   *          numeric options to be selected at once.
   */
  public void setAllowMultiSelect(boolean allowMultiSelect) {
    this.allowMultiSelect = allowMultiSelect;
  }
  /**
   * Sets the optional column headings. The column headings will be
   * displayed above the menu options.
   *
   * @param headings
   *          The optional column headings.
   */
  public void setColumnHeadings(Message... headings) {
    this.columnHeadings.clear();
    if (headings != null) {
      this.columnHeadings.addAll(Arrays.asList(headings));
    }
  }
  /**
   * Sets the optional column widths. A value of zero indicates that
   * the column should be expandable, a value of <code>null</code>
   * indicates that the column should use its default width.
   *
   * @param widths
   *          The optional column widths.
   */
  public void setColumnWidths(Integer... widths) {
    this.columnWidths.clear();
    if (widths != null) {
      this.columnWidths.addAll(Arrays.asList(widths));
    }
  }
  /**
   * Sets the optional default action for this menu. The default
   * action call-back will be invoked if the user does not specify an
   * option and just presses enter.
   *
   * @param description
   *          A short description of the default action.
   * @param callback
   *          The call-back associated with the default action.
   */
  public void setDefault(Message description, MenuCallback<T> callback) {
    defaultCallback = callback;
    defaultDescription = description;
  }
  /**
   * Sets the optional default action for this menu. The default
   * action call-back will be invoked if the user does not specify an
   * option and just presses enter.
   *
   * @param description
   *          A short description of the default action.
   * @param result
   *          The menu result which should be returned by default.
   */
  public void setDefault(Message description, MenuResult<T> result) {
    setDefault(description, new ResultCallback<T>(result));
  }
  /**
   * Sets the number of numeric options required to trigger
   * multiple-column display. A negative value (the default) indicates
   * that the numeric options will always be displayed in a single
   * column. A value of 0 indicates that numeric options will always
   * be displayed in multiple columns.
   *
   * @param threshold
   *          The number of numeric options required to trigger
   *          multiple-column display.
   */
  public void setMultipleColumnThreshold(int threshold) {
    this.threshold = threshold;
  }
  /**
   * Sets the optional menu prompt. The prompt will be displayed above
   * the menu. Menus do not have a prompt by default.
   *
   * @param prompt
   *          The menu prompt, or <code>null</code> if there is not
   *          prompt.
   */
  public void setPrompt(Message prompt) {
    this.prompt = prompt;
  }
  /**
   * Sets the optional menu title. The title will be displayed above
   * the menu prompt. Menus do not have a title by default.
   *
   * @param title
   *          The menu title, or <code>null</code> if there is not
   *          title.
   */
  public void setTitle(Message title) {
    this.title = title;
  }
  /**
   * Creates a menu from this menu builder.
   *
   * @return Returns the new menu.
   */
  public Menu<T> toMenu() {
    TableBuilder nbuilder = new TableBuilder();
    Map<String, MenuCallback<T>> callbacks =
      new HashMap<String, MenuCallback<T>>();
    // Determine whether multiple columns should be used for numeric
    // options.
    boolean useMultipleColumns = false;
    if (threshold >= 0 && numericCallbacks.size() >= threshold) {
      useMultipleColumns = true;
    }
    // Create optional column headers.
    if (!columnHeadings.isEmpty()) {
      nbuilder.appendHeading();
      nbuilder.appendHeading();
      for (Message heading : columnHeadings) {
        if (heading != null) {
          nbuilder.appendHeading(heading);
        } else {
          nbuilder.appendHeading();
        }
      }
      if (useMultipleColumns) {
        nbuilder.appendHeading();
        for (Message heading : columnHeadings) {
          if (heading != null) {
            nbuilder.appendHeading(heading);
          } else {
            nbuilder.appendHeading();
          }
        }
      }
    }
    // Add the numeric options first.
    int sz = numericCallbacks.size();
    int rows = sz;
    if (useMultipleColumns) {
      // Display in two columns the first column should contain half
      // the options. If there are an odd number of columns then the
      // first column should contain an additional option (e.g. if
      // there are 23 options, the first column should contain 12
      // options and the second column 11 options).
      rows /= 2;
      rows += sz % 2;
    }
    for (int i = 0, j = rows; i < rows; i++, j++) {
      nbuilder.startRow();
      nbuilder.appendCell();
      nbuilder.appendCell(INFO_MENU_NUMERIC_OPTION.get(i + 1));
      for (Message field : numericFields.get(i)) {
        if (field != null) {
          nbuilder.appendCell(field);
        } else {
          nbuilder.appendCell();
        }
      }
      callbacks.put(String.valueOf(i + 1), numericCallbacks.get(i));
      // Second column.
      if (useMultipleColumns && (j < sz)) {
        nbuilder.appendCell(INFO_MENU_NUMERIC_OPTION.get(j + 1));
        for (Message field : numericFields.get(j)) {
          if (field != null) {
            nbuilder.appendCell(field);
          } else {
            nbuilder.appendCell();
          }
        }
        callbacks.put(String.valueOf(j + 1), numericCallbacks.get(j));
      }
    }
    // Add the char options last.
    TableBuilder cbuilder = new TableBuilder();
    for (int i = 0; i < charCallbacks.size(); i++) {
      char c = charKeys.get(i).charAt(0);
      Message option = INFO_MENU_CHAR_OPTION.get(c);
      cbuilder.startRow();
      cbuilder.appendCell();
      cbuilder.appendCell(option);
      cbuilder.appendCell(charSynopsis.get(i));
      callbacks.put(String.valueOf(c), charCallbacks.get(i));
    }
    // Configure the table printer.
    TextTablePrinter printer = new TextTablePrinter(app.getErrorStream());
    if (columnHeadings.isEmpty()) {
      printer.setDisplayHeadings(false);
    } else {
      printer.setDisplayHeadings(true);
    }
    printer.setColumnWidth(0, 2);
    if (columnWidths.isEmpty()) {
      printer.setColumnWidth(2, 0);
      if (useMultipleColumns) {
        printer.setColumnWidth(4, 0);
      }
    } else {
      for (int i = 0; i < columnWidths.size(); i++) {
        Integer j = columnWidths.get(i);
        if (j != null) {
          // Skip the option key column.
          printer.setColumnWidth(i + 2, j);
          if (useMultipleColumns) {
            printer.setColumnWidth(i + 3 + columnWidths.size(), j);
          }
        }
      }
    }
    return new MenuImpl<T>(app, title, prompt, nbuilder, cbuilder, printer,
        callbacks, allowMultiSelect, defaultCallback, defaultDescription);
  }
}
opends/src/server/org/opends/server/util/cli/MenuCallback.java
copy from opends/src/server/org/opends/server/tools/dsconfig/HelpCallback.java copy to opends/src/server/org/opends/server/util/cli/MenuCallback.java
File was copied from opends/src/server/org/opends/server/tools/dsconfig/HelpCallback.java
@@ -24,20 +24,29 @@
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
package org.opends.server.util.cli;
/**
 * An interface for displaying help interactively.
 * A menu call-back which should be associated with each menu option.
 * When an option is selected the call-back is invoked.
 *
 * @param <T>
 *          The type of success result value(s) returned by the
 *          call-back. Use <code>Void</code> if the call-backs do
 *          not return any values.
 */
interface HelpCallback {
public interface MenuCallback<T> {
  /**
   * Displays help to the provided application console.
   * Invoke the menu call-back.
   *
   * @param app
   *          The console application.
   *          The application console.
   * @return Returns the result of invoking the menu call-back.
   * @throws CLIException
   *           If the menu call-back fails for some reason.
   */
  void display(ConsoleApplication app);
  MenuResult<T> invoke(ConsoleApplication app) throws CLIException;
}
opends/src/server/org/opends/server/util/cli/MenuResult.java
New file
@@ -0,0 +1,324 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.util.cli;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
/**
 * The result of running a {@link Menu}. The result indicates to the
 * application how it should proceed:
 * <ul>
 * <li>{@link #again()} - the menu should be displayed again. A good
 * example of this is when a user chooses to view some help. Normally,
 * after the help is displayed, the user is allowed to select another
 * option
 * <li>{@link #cancel()} - the user chose to cancel any task
 * currently in progress and go back to the previous main menu if
 * applicable
 * <li>{@link #success()} - the user chose to apply any task
 * currently in progress and go back to the previous menu if
 * applicable. Any result values applicable to the chosen option can
 * be retrieved using {@link #getValue()} or {@link #getValues()}
 * <li>{@link #quit()} - the user chose to quit the application and
 * cancel all outstanding tasks.
 * </ul>
 *
 * @param <T>
 *          The type of result value(s) contained in success results.
 *          Use <code>Void</code> if success results should not
 *          contain values.
 */
public final class MenuResult<T> {
  /**
   * The type of result returned from the menu.
   */
  private static enum Type {
    /**
     * The user selected an option which did not return a result, so
     * the menu should be displayed again.
     */
    AGAIN,
    /**
     * The user did not select an option and instead chose to cancel
     * the current task.
     */
    CANCEL,
    /**
     * The user did not select an option and instead chose to quit the
     * entire application.
     */
    QUIT,
    /**
     * The user selected an option which succeeded and returned one or
     * more result values.
     */
    SUCCESS
  }
  /**
   * Creates a new menu result indicating that the menu should be
   * displayed again. A good example of this is when a user chooses to
   * view some help. Normally, after the help is displayed, the user
   * is allowed to select another option.
   *
   * @param <T>
   *          The type of result value(s) contained in success
   *          results. Use <code>Void</code> if success results
   *          should not contain values.
   * @return Returns a new menu result indicating that the menu should
   *         be displayed again.
   */
  public static <T> MenuResult<T> again() {
    return new MenuResult<T>(Type.AGAIN, Collections.<T> emptyList());
  }
  /**
   * Creates a new menu result indicating that the user chose to
   * cancel any task currently in progress and go back to the previous
   * main menu if applicable.
   *
   * @param <T>
   *          The type of result value(s) contained in success
   *          results. Use <code>Void</code> if success results
   *          should not contain values.
   * @return Returns a new menu result indicating that the user chose
   *         to cancel any task currently in progress and go back to
   *         the previous main menu if applicable.
   */
  public static <T> MenuResult<T> cancel() {
    return new MenuResult<T>(Type.CANCEL, Collections.<T> emptyList());
  }
  /**
   * Creates a new menu result indicating that the user chose to quit
   * the application and cancel all outstanding tasks.
   *
   * @param <T>
   *          The type of result value(s) contained in success
   *          results. Use <code>Void</code> if success results
   *          should not contain values.
   * @return Returns a new menu result indicating that the user chose
   *         to quit the application and cancel all outstanding tasks.
   */
  public static <T> MenuResult<T> quit() {
    return new MenuResult<T>(Type.QUIT, Collections.<T> emptyList());
  }
  /**
   * Creates a new menu result indicating that the user chose to apply
   * any task currently in progress and go back to the previous menu
   * if applicable. The menu result will not contain any result
   * values.
   *
   * @param <T>
   *          The type of result value(s) contained in success
   *          results. Use <code>Void</code> if success results
   *          should not contain values.
   * @return Returns a new menu result indicating that the user chose
   *         to apply any task currently in progress and go back to
   *         the previous menu if applicable.The menu result will not
   *         contain any result values.
   */
  public static <T> MenuResult<T> success() {
    return success(Collections.<T> emptySet());
  }
  /**
   * Creates a new menu result indicating that the user chose to apply
   * any task currently in progress and go back to the previous menu
   * if applicable. The menu result will contain the provided values,
   * which can be retrieved using {@link #getValue()} or
   * {@link #getValues()}.
   *
   * @param <T>
   *          The type of the result values.
   * @param values
   *          The result values.
   * @return Returns a new menu result indicating that the user chose
   *         to apply any task currently in progress and go back to
   *         the previous menu if applicable. The menu result will
   *         contain the provided values, which can be retrieved using
   *         {@link #getValue()} or {@link #getValues()}.
   */
  public static <T> MenuResult<T> success(Collection<T> values) {
    return new MenuResult<T>(Type.SUCCESS, new ArrayList<T>(values));
  }
  /**
   * Creates a new menu result indicating that the user chose to apply
   * any task currently in progress and go back to the previous menu
   * if applicable. The menu result will contain the provided value,
   * which can be retrieved using {@link #getValue()} or
   * {@link #getValues()}.
   *
   * @param <T>
   *          The type of the result value.
   * @param value
   *          The result value.
   * @return Returns a new menu result indicating that the user chose
   *         to apply any task currently in progress and go back to
   *         the previous menu if applicable. The menu result will
   *         contain the provided value, which can be retrieved using
   *         {@link #getValue()} or {@link #getValues()}.
   */
  public static <T> MenuResult<T> success(T value) {
    return success(Collections.singleton(value));
  }
  // The type of result returned from the menu.
  private final Type type;
  // The menu result value(s).
  private final Collection<T> values;
  // Private constructor.
  private MenuResult(Type type, Collection<T> values) {
    this.type = type;
    this.values = values;
  }
  /**
   * Gets the menu result value if this is a menu result indicating
   * success.
   *
   * @return Returns the menu result value, or <code>null</code> if
   *         there was no result value or if this is not a success
   *         menu result.
   * @see #isSuccess()
   */
  public T getValue() {
    if (values.isEmpty()) {
      return null;
    } else {
      return values.iterator().next();
    }
  }
  /**
   * Gets the menu result values if this is a menu result indicating
   * success.
   *
   * @return Returns the menu result values, which may be empty if
   *         there were no result values or if this is not a success
   *         menu result.
   * @see #isSuccess()
   */
  public Collection<T> getValues() {
    return new ArrayList<T>(values);
  }
  /**
   * Determines if this menu result indicates that the menu should be
   * displayed again. A good example of this is when a user chooses to
   * view some help. Normally, after the help is displayed, the user
   * is allowed to select another option.
   *
   * @return Returns <code>true</code> if this menu result indicates
   *         that the menu should be displayed again.
   */
  public boolean isAgain() {
    return type == Type.AGAIN;
  }
  /**
   * Determines if this menu result indicates that the user chose to
   * cancel any task currently in progress and go back to the previous
   * main menu if applicable.
   *
   * @return Returns <code>true</code> if this menu result indicates
   *         that the user chose to cancel any task currently in
   *         progress and go back to the previous main menu if
   *         applicable.
   */
  public boolean isCancel() {
    return type == Type.CANCEL;
  }
  /**
   * Determines if this menu result indicates that the user chose to
   * quit the application and cancel all outstanding tasks.
   *
   * @return Returns <code>true</code> if this menu result indicates
   *         that the user chose to quit the application and cancel
   *         all outstanding tasks.
   */
  public boolean isQuit() {
    return type == Type.QUIT;
  }
  /**
   * Determines if this menu result indicates that the user chose to
   * apply any task currently in progress and go back to the previous
   * menu if applicable. Any result values can be retrieved using the
   * {@link #getValue()} or {@link #getValues()} methods.
   *
   * @return Returns <code>true</code> if this menu result indicates
   *         that the user chose to apply any task currently in
   *         progress and go back to the previous menu if applicable.
   * @see #getValue()
   * @see #getValues()
   */
  public boolean isSuccess() {
    return type == Type.SUCCESS;
  }
}
opends/src/server/org/opends/server/util/cli/OutputStreamConsoleApplication.java
New file
@@ -0,0 +1,105 @@
/*
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * 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
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.util.cli;
/**
 * A console application decorator which redirects all output to the
 * underlying application's output stream.
 */
public class OutputStreamConsoleApplication extends ConsoleApplication {
  // The underlying console application.
  private final ConsoleApplication app;
  /**
   * Creates a new console application instance which redirects all
   * output to the underlying application's output stream.
   *
   * @param app
   *          The underlying application console.
   */
  public OutputStreamConsoleApplication(ConsoleApplication app) {
    super(app.getInputStream(), app.getOutputStream(), app.getOutputStream());
    this.app = app;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isInteractive() {
    return app.isInteractive();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isMenuDrivenMode() {
    return app.isMenuDrivenMode();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isQuiet() {
    return app.isQuiet();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isScriptFriendly() {
    return app.isScriptFriendly();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isVerbose() {
    return app.isVerbose();
  }
}
opends/src/server/org/opends/server/util/cli/ValidationCallback.java
File was renamed from opends/src/server/org/opends/server/tools/dsconfig/ValidationCallback.java
@@ -24,9 +24,7 @@
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
import org.opends.server.tools.ClientException;
package org.opends.server.util.cli;
@@ -36,7 +34,7 @@
 * @param <T>
 *          The type of the decoded input.
 */
interface ValidationCallback<T> {
public interface ValidationCallback<T> {
  /**
   * Validates and decodes the user-provided input. Implementations
@@ -51,9 +49,9 @@
   *          The user input to be validated.
   * @return Returns the decoded input if the input is valid, or
   *         <code>null</code> if it is not.
   * @throws ClientException
   * @throws CLIException
   *           If an unexpected error occurred which prevented
   *           validation.
   */
  T validate(ConsoleApplication app, String input) throws ClientException;
  T validate(ConsoleApplication app, String input) throws CLIException;
}
opends/src/server/org/opends/server/util/cli/package-info.java
copy from opends/src/server/org/opends/server/tools/dsconfig/HelpCallback.java copy to opends/src/server/org/opends/server/util/cli/package-info.java
File was copied from opends/src/server/org/opends/server/tools/dsconfig/HelpCallback.java
@@ -22,22 +22,23 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
/**
 * An interface for displaying help interactively.
 * Provides a high-level framework for implementing command-line
 * tools.
 * <p>
 * The {@link ConsoleApplication} interface can be used as a basis for
 * console based applications. It includes common utility methods for
 * interacting with the console.
 * <p>
 * The {@link MenuBuilder} and associated classes and interfaces can
 * be used to implement text based menu driven applications.
 */
interface HelpCallback {
@org.opends.server.types.PublicAPI(
    stability = org.opends.server.types.StabilityLevel.PRIVATE)
package org.opends.server.util.cli;
  /**
   * Displays help to the provided application console.
   *
   * @param app
   *          The console application.
   */
  void display(ConsoleApplication app);
}