opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java
@@ -40,7 +40,7 @@ * for an application. This is an abstract class that must be subclassed in * order to provide specific functionality. */ abstract class Argument { public abstract class Argument { // Indicates whether this argument should be hidden in the usage // information. private boolean isHidden; opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java
@@ -44,6 +44,7 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Properties; import java.util.Set; import java.util.SortedSet; @@ -62,7 +63,7 @@ * file to obtain default values for arguments there if they are not specified * on the command-line. */ public final class ArgumentParser { public class ArgumentParser { /** * The argument that will be used to indicate the file properties. */ @@ -143,7 +144,7 @@ private String[] rawArguments; /** Set of argument groups. */ private Set<ArgumentGroup> argumentGroups; protected Set<ArgumentGroup> argumentGroups; /** * Group for arguments that have not been explicitly grouped. These will @@ -1593,4 +1594,41 @@ buffer.append(EOL); } } void normalizeArguments(final Properties argumentProperties, final List<Argument> arguments) throws ArgumentException { for (final Argument a : arguments) { if (!a.isPresent() // See if there is a value in the properties that can be used && argumentProperties != null && a.getPropertyName() != null) { final String value = argumentProperties.getProperty(a.getPropertyName().toLowerCase()); final LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder(); if (value != null) { Boolean addValue = true; if (!(a instanceof BooleanArgument)) { addValue = a.valueIsAcceptable(value, invalidReason); } if (addValue) { a.addValue(value); if (a.needsValue()) { a.setPresent(true); } a.setValueSetByProperty(true); } } } if (!a.isPresent() && a.needsValue()) { // See if the argument defines a default. if (a.getDefaultValue() != null) { a.addValue(a.getDefaultValue()); } // If there is still no value and the argument is required, then that's a problem. if (!a.hasValue() && a.isRequired()) { throw new ArgumentException(ERR_ARGPARSER_NO_VALUE_FOR_REQUIRED_ARG.get(a.getName())); } } } } } opendj-cli/src/main/java/com/forgerock/opendj/cli/CliConstants.java
@@ -617,6 +617,22 @@ */ public static final String OPTION_LONG_OUTPUT_LDIF_FILENAME = "outputLDIF"; /** * The value for the long option to automatically accept the license * if present. */ public static final String OPTION_LONG_ACCEPT_LICENSE = "acceptLicense"; /** * The value for the short option rootUserDN. */ public static final char OPTION_SHORT_ROOT_USER_DN = 'D'; /** * The value for the long option rootUserDN. */ public static final String OPTION_LONG_ROOT_USER_DN = "rootUserDN"; // Prevent instantiation. private CliConstants() { opendj-cli/src/main/java/com/forgerock/opendj/cli/CommonArguments.java
@@ -46,8 +46,7 @@ * If there is a problem with any of the parameters used to create this argument. */ public static final BooleanArgument getShowUsage() throws ArgumentException { return new BooleanArgument("showUsage", OPTION_SHORT_HELP, OPTION_LONG_HELP, INFO_DESCRIPTION_SHOWUSAGE.get()); return new BooleanArgument("showUsage", OPTION_SHORT_HELP, OPTION_LONG_HELP, INFO_DESCRIPTION_SHOWUSAGE.get()); } /** @@ -71,9 +70,8 @@ * If there is a problem with any of the parameters used to create this argument. */ public static final StringArgument getPropertiesFileArgument() throws ArgumentException { return new StringArgument("propertiesFilePath", null, OPTION_LONG_PROP_FILE_PATH, false, false, true, INFO_PROP_FILE_PATH_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_PROP_FILE_PATH.get()); return new StringArgument("propertiesFilePath", null, OPTION_LONG_PROP_FILE_PATH, false, false, true, INFO_PROP_FILE_PATH_PLACEHOLDER.get(), null, null, INFO_DESCRIPTION_PROP_FILE_PATH.get()); } /** @@ -117,4 +115,423 @@ return version; } /** * Returns the "quiet" boolean argument. * * @return The "quiet" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final BooleanArgument getQuiet() throws ArgumentException { return new BooleanArgument(OPTION_LONG_QUIET, OPTION_SHORT_QUIET, OPTION_LONG_QUIET, INFO_DESCRIPTION_QUIET.get()); } /** * Returns the "no-prompt" boolean argument. * * @return The "no-prompt" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final BooleanArgument getNoPrompt() throws ArgumentException { return new BooleanArgument(OPTION_LONG_NO_PROMPT, OPTION_SHORT_NO_PROMPT, OPTION_LONG_NO_PROMPT, INFO_DESCRIPTION_NO_PROMPT.get()); } /** * Returns the "acceptLicense" boolean argument. * * @return The "acceptLicense" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final BooleanArgument getAcceptLicense() throws ArgumentException { return new BooleanArgument(OPTION_LONG_ACCEPT_LICENSE, null, OPTION_LONG_ACCEPT_LICENSE, INFO_OPTION_ACCEPT_LICENSE.get()); } /** * Returns the "test only" boolean argument. * * @return The "test only" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final BooleanArgument getTestOnly() throws ArgumentException { final BooleanArgument testOnly = new BooleanArgument("testOnly".toLowerCase(), 't', "testOnly", INFO_ARGUMENT_DESCRIPTION_TESTONLY.get()); testOnly.setHidden(true); testOnly.setPropertyName("testOnly"); return testOnly; } /** * Returns the "CLI" boolean argument. * * @return The "CLI" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final BooleanArgument getCLI() throws ArgumentException { final BooleanArgument cli = new BooleanArgument(OPTION_LONG_CLI.toLowerCase(), OPTION_SHORT_CLI, OPTION_LONG_CLI, INFO_ARGUMENT_DESCRIPTION_CLI.get()); cli.setPropertyName(OPTION_LONG_CLI); return cli; } /** * Returns the "baseDN" string argument. * * @return The "baseDN" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final StringArgument getBaseDN() throws ArgumentException { return new StringArgument(OPTION_LONG_BASEDN.toLowerCase(), OPTION_SHORT_BASEDN, OPTION_LONG_BASEDN, false, true, true, INFO_BASEDN_PLACEHOLDER.get(), null, OPTION_LONG_BASEDN, INFO_ARGUMENT_DESCRIPTION_BASEDN.get()); } /** * Returns the "add base entry" boolean argument. * * @return The "addBaseEntry" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final BooleanArgument getAddBaseEntry() throws ArgumentException { final BooleanArgument addBaseEntryArg = new BooleanArgument("addBaseEntry".toLowerCase(), 'a', "addBaseEntry", INFO_ARGUMENT_DESCRIPTION_ADDBASE.get()); addBaseEntryArg.setPropertyName("addBaseEntry"); return addBaseEntryArg; } /** * Returns the "import LDIF" string argument. * * @return The "import LDIF" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final StringArgument getImportLDIF() throws ArgumentException { return new StringArgument(OPTION_LONG_LDIF_FILE.toLowerCase(), OPTION_SHORT_LDIF_FILE, OPTION_LONG_LDIF_FILE, false, true, true, INFO_LDIFFILE_PLACEHOLDER.get(), null, OPTION_LONG_LDIF_FILE, INFO_ARGUMENT_DESCRIPTION_IMPORTLDIF.get()); } /** * Returns the "rejected import ldif file" string argument. * * @return The "rejectFile" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final StringArgument getRejectedImportLdif() throws ArgumentException { return new StringArgument("rejectFile".toLowerCase(), 'R', "rejectFile", false, false, true, INFO_REJECT_FILE_PLACEHOLDER.get(), null, "rejectFile", INFO_GENERAL_DESCRIPTION_REJECTED_FILE.get()); } /** * Returns the "skip file" string argument. * * @return The "skipFile" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final StringArgument getSkippedImportFile() throws ArgumentException { return new StringArgument("skipFile".toLowerCase(), null, "skipFile", false, false, true, INFO_SKIP_FILE_PLACEHOLDER.get(), null, "skipFile", INFO_GENERAL_DESCRIPTION_SKIPPED_FILE.get()); } /** * Returns the "sample data" integer argument. * * @return The "sampleData" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final IntegerArgument getSampleData() throws ArgumentException { return new IntegerArgument("sampleData".toLowerCase(), 'd', "sampleData", false, false, true, INFO_NUM_ENTRIES_PLACEHOLDER.get(), 0, "sampleData", true, 0, false, 0, INFO_SETUP_DESCRIPTION_SAMPLE_DATA.get()); } /** * Returns the "LDAP port" integer argument. * * @param defaultLdapPort * Default LDAP Connector port. * @return The "ldapPort" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final IntegerArgument getLDAPPort(final int defaultLdapPort) throws ArgumentException { return new IntegerArgument("ldapPort".toLowerCase(), OPTION_SHORT_PORT, "ldapPort", false, false, true, INFO_PORT_PLACEHOLDER.get(), defaultLdapPort, "ldapPort", true, 1, true, 65535, INFO_ARGUMENT_DESCRIPTION_LDAPPORT.get()); } /** * Returns the "Admin port" integer argument. * * @param defaultAdminPort * Default Administration Connector port. * @return The "adminConnectorPort" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final IntegerArgument getAdminLDAPPort(final int defaultAdminPort) throws ArgumentException { return new IntegerArgument("adminConnectorPort".toLowerCase(), null, "adminConnectorPort", false, false, true, INFO_PORT_PLACEHOLDER.get(), defaultAdminPort, "adminConnectorPort", true, 1, true, 65535, INFO_ARGUMENT_DESCRIPTION_ADMINCONNECTORPORT.get()); } /** * Returns the "JMX port" integer argument. * * @param defaultJMXPort * Default JMX port. * @return The "jmxPort" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final IntegerArgument getJMXPort(final int defaultJMXPort) throws ArgumentException { return new IntegerArgument("jmxPort".toLowerCase(), 'x', "jmxPort", false, false, true, INFO_JMXPORT_PLACEHOLDER.get(), defaultJMXPort, "jmxPort", true, 1, true, 65535, INFO_ARGUMENT_DESCRIPTION_SKIPPORT.get()); } /** * Returns the "skip port check" boolean argument. * * @return The "getSkipPortCheck" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final BooleanArgument getSkipPortCheck() throws ArgumentException { final BooleanArgument skipPortCheck = new BooleanArgument("skipPortCheck".toLowerCase(), 'S', "skipPortCheck", INFO_ARGUMENT_DESCRIPTION_SKIPPORT.get()); skipPortCheck.setPropertyName("skipPortCheck"); return skipPortCheck; } /** * Returns the "directory manager DN" string argument. * * @return The "rootUserDN" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final StringArgument getRootDN() throws ArgumentException { return new StringArgument(OPTION_LONG_ROOT_USER_DN.toLowerCase(), OPTION_SHORT_ROOT_USER_DN, OPTION_LONG_ROOT_USER_DN, false, false, true, INFO_ROOT_USER_DN_PLACEHOLDER.get(), "cn=Directory Manager", OPTION_LONG_ROOT_USER_DN, INFO_ARGUMENT_DESCRIPTION_ROOTDN.get()); } /** * Returns the "directory manager DN password" string argument. * * @return The "rootUserPassword" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final StringArgument getRootDNPwd() throws ArgumentException { return new StringArgument("rootUserPassword".toLowerCase(), OPTION_SHORT_BINDPWD, "rootUserPassword", false, false, true, INFO_ROOT_USER_PWD_PLACEHOLDER.get(), null, "rootUserPassword", INFO_ROOT_USER_PWD_PLACEHOLDER.get()); } /** * Returns the "directory manager DN password file" file argument. * * @return The "rootUserPasswordFile" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final FileBasedArgument getRootDNPwdFile() throws ArgumentException { return new FileBasedArgument("rootUserPasswordFile".toLowerCase(), OPTION_SHORT_BINDPWD_FILE, "rootUserPasswordFile", false, false, INFO_ROOT_USER_PWD_FILE_PLACEHOLDER.get(), null, "rootUserPasswordFile", INFO_ARGUMENT_DESCRIPTION_ROOTPWFILE.get()); } /** * Returns the "enable window service" integer argument. * * @return The "enableWindowsService" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final BooleanArgument getEnableWindowsService() throws ArgumentException { final BooleanArgument enableWindowsServiceArg = new BooleanArgument("enableWindowsService".toLowerCase(), 'e', "enableWindowsService", INFO_ARGUMENT_DESCRIPTION_ENABLE_WINDOWS_SERVICE.get()); enableWindowsServiceArg.setPropertyName("enableWindowsService"); return enableWindowsServiceArg; } /** * Returns the "do not start" boolean argument. * * @return The "doNotStart" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final BooleanArgument getDoNotStart() throws ArgumentException { final BooleanArgument doNotStartArg = new BooleanArgument("doNotStart".toLowerCase(), 'O', "doNotStart", INFO_SETUP_DESCRIPTION_DO_NOT_START.get()); doNotStartArg.setPropertyName("doNotStart"); return doNotStartArg; } /** * Returns the "enable start TLS" boolean argument. * * @return The "enableStartTLS" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final BooleanArgument getEnableTLS() throws ArgumentException { final BooleanArgument enableStartTLS = new BooleanArgument("enableStartTLS".toLowerCase(), OPTION_SHORT_START_TLS, "enableStartTLS", INFO_SETUP_DESCRIPTION_ENABLE_STARTTLS.get()); enableStartTLS.setPropertyName("enableStartTLS"); return enableStartTLS; } /** * Returns the "ldaps port" integer argument. * * @param defaultSecurePort * Default value for the LDAPS port. * @return The "ldapsPort" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final IntegerArgument getLDAPSPort(final int defaultSecurePort) throws ArgumentException { return new IntegerArgument("ldapsPort".toLowerCase(), OPTION_SHORT_USE_SSL, "ldapsPort", false, false, true, INFO_PORT_PLACEHOLDER.get(), defaultSecurePort, "ldapsPort", true, 1, true, 65535, INFO_ARGUMENT_DESCRIPTION_LDAPSPORT.get()); } /** * Returns the "generate self certificate" boolean argument. * * @return The "generateSelfSignedCertificate" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final BooleanArgument getGenerateSelfSigned() throws ArgumentException { final BooleanArgument generateSelfSigned = new BooleanArgument("generateSelfSignedCertificate".toLowerCase(), null, "generateSelfSignedCertificate", INFO_ARGUMENT_DESCRIPTION_USE_SELF_SIGNED_CERTIFICATE.get()); generateSelfSigned.setPropertyName("generateSelfSignedCertificate"); return generateSelfSigned; } /** * Returns the "host name" string argument. * * @param defaultHostName * The default host name value. * @return The "hostname" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final StringArgument getHostName(final String defaultHostName) throws ArgumentException { final StringArgument hostName = new StringArgument(OPTION_LONG_HOST.toLowerCase(), OPTION_SHORT_HOST, OPTION_LONG_HOST, false, false, true, INFO_HOST_PLACEHOLDER.get(), defaultHostName, null, INFO_ARGUMENT_DESCRIPTION_HOST_NAME.get()); hostName.setPropertyName(OPTION_LONG_HOST); return hostName; } /** * Returns the "use PKCS11 key store" boolean argument. * * @return The "usePkcs11Keystore" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final BooleanArgument getUsePKCS11Keystore() throws ArgumentException { final BooleanArgument usePkcs11 = new BooleanArgument("usePkcs11Keystore".toLowerCase(), null, "usePkcs11Keystore", INFO_ARGUMENT_DESCRIPTION_USE_PKCS11.get()); usePkcs11.setPropertyName("usePkcs11Keystore"); return usePkcs11; } /** * Returns the "use java key store" string argument. * * @return The "useJavaKeystore" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final StringArgument getUseJavaKeyStore() throws ArgumentException { return new StringArgument("useJavaKeystore".toLowerCase(), null, "useJavaKeystore", false, false, true, INFO_KEYSTOREPATH_PLACEHOLDER.get(), null, "useJavaKeystore", INFO_ARGUMENT_DESCRIPTION_USE_JAVAKEYSTORE.get()); } /** * Returns the "use JCEKS" string argument. * * @return The "useJCEKS" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final StringArgument getUseJCEKS() throws ArgumentException { return new StringArgument("useJCEKS".toLowerCase(), null, "useJCEKS", false, false, true, INFO_KEYSTOREPATH_PLACEHOLDER.get(), null, "useJCEKS", INFO_ARGUMENT_DESCRIPTION_USE_JCEKS.get()); } /** * Returns the "use PKCS12 key store" string argument. * * @return The "usePkcs12keyStore" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final StringArgument getUsePKCS12KeyStore() throws ArgumentException { return new StringArgument("usePkcs12keyStore".toLowerCase(), null, "usePkcs12keyStore", false, false, true, INFO_KEYSTOREPATH_PLACEHOLDER.get(), null, "usePkcs12keyStore", INFO_ARGUMENT_DESCRIPTION_USE_PKCS12.get()); } /** * Returns the "key store password" string argument. * * @return The "keyStorePassword" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final StringArgument getKeyStorePassword() throws ArgumentException { return new StringArgument(OPTION_LONG_KEYSTORE_PWD.toLowerCase(), OPTION_SHORT_KEYSTORE_PWD, OPTION_LONG_KEYSTORE_PWD, false, false, true, INFO_KEYSTORE_PWD_PLACEHOLDER.get(), null, OPTION_LONG_KEYSTORE_PWD, INFO_ARGUMENT_DESCRIPTION_KEYSTOREPASSWORD.get()); } /** * Returns the "key store password file" file argument. * * @return The "keyStorePassword" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final FileBasedArgument getKeyStorePasswordFile() throws ArgumentException { return new FileBasedArgument(OPTION_LONG_KEYSTORE_PWD_FILE.toLowerCase(), OPTION_SHORT_KEYSTORE_PWD_FILE, OPTION_LONG_KEYSTORE_PWD_FILE, false, false, INFO_KEYSTORE_PWD_FILE_PLACEHOLDER.get(), null, OPTION_LONG_KEYSTORE_PWD_FILE, INFO_ARGUMENT_DESCRIPTION_KEYSTOREPASSWORD_FILE.get()); } /** * Returns the "key store password file" string argument. * * @return The "keyStorePassword" argument. * @throws ArgumentException * If there is a problem with any of the parameters used to create this argument. */ public static final StringArgument getCertNickName() throws ArgumentException { return new StringArgument(OPTION_LONG_CERT_NICKNAME.toLowerCase(), OPTION_SHORT_CERT_NICKNAME, OPTION_LONG_CERT_NICKNAME, false, false, true, INFO_NICKNAME_PLACEHOLDER.get(), null, OPTION_LONG_CERT_NICKNAME, INFO_ARGUMENT_DESCRIPTION_CERT_NICKNAME.get()); } } opendj-cli/src/main/java/com/forgerock/opendj/cli/IntegerArgument.java
@@ -44,133 +44,10 @@ private final boolean hasUpperBound; // The lower bound that will be enforced for this argument. private final double lowerBound; private final int lowerBound; // The upper bound that will be enforced for this argument. private final double upperBound; /** * Creates a new integer argument with the provided information. * * @param name * The generic name that should be used to refer to this * argument. * @param shortIdentifier * The single-character identifier for this argument, or * <CODE>null</CODE> if there is none. * @param longIdentifier * The long identifier for this argument, or <CODE>null</CODE> if * there is none. * @param isRequired * Indicates whether this argument must be specified on the * command line. * @param isMultiValued * Indicates whether this argument may be specified more than * once to provide multiple values. * @param needsValue * Indicates whether this argument requires a value. * @param valuePlaceholder * The placeholder for the argument value that will be displayed * in usage information, or <CODE>null</CODE> if this argument * does not require a value. * @param defaultValue * The default value that should be used for this argument if * none is provided in a properties file or on the command line. * This may be <CODE>null</CODE> if there is no generic default. * @param propertyName * The name of the property in a property file that may be used * to override the default value but will be overridden by a * command-line argument. * @param hasLowerBound * Indicates whether a lower bound should be enforced for values * of this argument. * @param lowerBound * The lower bound that should be enforced for values of this * argument. * @param hasUpperBound * Indicates whether an upperbound should be enforced for values * of this argument. * @param upperBound * The upper bound that should be enforced for values of this * argument. * @param description * LocalizableMessage for the description of this argument. * @throws ArgumentException * If there is a problem with any of the parameters used to * create this argument. */ public IntegerArgument(final String name, final Character shortIdentifier, final String longIdentifier, final boolean isRequired, final boolean isMultiValued, final boolean needsValue, final LocalizableMessage valuePlaceholder, final double defaultValue, final String propertyName, final boolean hasLowerBound, final double lowerBound, final boolean hasUpperBound, final double upperBound, final LocalizableMessage description) throws ArgumentException { super(name, shortIdentifier, longIdentifier, isRequired, isMultiValued, needsValue, valuePlaceholder, String.valueOf(defaultValue), propertyName, description); this.hasLowerBound = hasLowerBound; this.hasUpperBound = hasUpperBound; this.lowerBound = lowerBound; this.upperBound = upperBound; if (hasLowerBound && hasUpperBound && (lowerBound > upperBound)) { final LocalizableMessage message = ERR_INTARG_LOWER_BOUND_ABOVE_UPPER_BOUND.get(name, lowerBound, upperBound); throw new ArgumentException(message); } } /** * Creates a new integer argument with the provided information. * * @param name * The generic name that should be used to refer to this * argument. * @param shortIdentifier * The single-character identifier for this argument, or * <CODE>null</CODE> if there is none. * @param longIdentifier * The long identifier for this argument, or <CODE>null</CODE> if * there is none. * @param isRequired * Indicates whether this argument must be specified on the * command line. * @param isMultiValued * Indicates whether this argument may be specified more than * once to provide multiple values. * @param needsValue * Indicates whether this argument requires a value. * @param valuePlaceholder * The placeholder for the argument value that will be displayed * in usage information, or <CODE>null</CODE> if this argument * does not require a value. * @param defaultValue * The default value that should be used for this argument if * none is provided in a properties file or on the command line. * This may be <CODE>null</CODE> if there is no generic default. * @param propertyName * The name of the property in a property file that may be used * to override the default value but will be overridden by a * command-line argument. * @param description * LocalizableMessage for the description of this argument. * @throws ArgumentException * If there is a problem with any of the parameters used to * create this argument. */ public IntegerArgument(final String name, final Character shortIdentifier, final String longIdentifier, final boolean isRequired, final boolean isMultiValued, final boolean needsValue, final LocalizableMessage valuePlaceholder, final double defaultValue, final String propertyName, final LocalizableMessage description) throws ArgumentException { super(name, shortIdentifier, longIdentifier, isRequired, isMultiValued, needsValue, valuePlaceholder, String.format("%f", defaultValue), propertyName, description); hasLowerBound = false; hasUpperBound = false; lowerBound = Integer.MIN_VALUE; upperBound = Integer.MAX_VALUE; } private final int upperBound; /** * Creates a new integer argument with the provided information. @@ -226,7 +103,7 @@ final String longIdentifier, final boolean isRequired, final boolean isMultiValued, final boolean needsValue, final LocalizableMessage valuePlaceholder, final int defaultValue, final String propertyName, final boolean hasLowerBound, final double lowerBound, final boolean hasUpperBound, final double upperBound, final int lowerBound, final boolean hasUpperBound, final int upperBound, final LocalizableMessage description) throws ArgumentException { super(name, shortIdentifier, longIdentifier, isRequired, isMultiValued, needsValue, valuePlaceholder, String.valueOf(defaultValue), propertyName, description); @@ -337,7 +214,7 @@ public IntegerArgument(final String name, final Character shortIdentifier, final String longIdentifier, final boolean isRequired, final boolean needsValue, final LocalizableMessage valuePlaceholder, final boolean hasLowerBound, final double lowerBound, final boolean hasUpperBound, final double upperBound, final int lowerBound, final boolean hasUpperBound, final int upperBound, final LocalizableMessage description) throws ArgumentException { super(name, shortIdentifier, longIdentifier, isRequired, false, needsValue, valuePlaceholder, null, null, description); @@ -390,8 +267,8 @@ hasLowerBound = false; hasUpperBound = false; lowerBound = Double.MIN_VALUE; upperBound = Double.MAX_VALUE; lowerBound = Integer.MIN_VALUE; upperBound = Integer.MAX_VALUE; } /** @@ -400,7 +277,7 @@ * * @return The lower bound that may be enforced for values of this argument. */ public double getLowerBound() { public int getLowerBound() { return lowerBound; } @@ -410,7 +287,7 @@ * * @return The upper bound that may be enforced for values of this argument. */ public double getUpperBound() { public int getUpperBound() { return upperBound; } @@ -452,18 +329,18 @@ public boolean valueIsAcceptable(final String valueString, final LocalizableMessageBuilder invalidReason) { // First, the value must be decodable as an integer. double intValue; int intValue; try { intValue = Double.parseDouble(valueString); intValue = Integer.parseInt(valueString); } catch (final Exception e) { invalidReason.append(ERR_ARG_CANNOT_DECODE_AS_INT.get(valueString, getName())); invalidReason.append(ERR_ARG_CANNOT_DECODE_AS_INT.get(valueString, getPropertyName())); return false; } // If there is a lower bound, then the value must be greater than or // equal to it. if (hasLowerBound && (intValue < lowerBound)) { invalidReason.append(ERR_INTARG_VALUE_BELOW_LOWER_BOUND.get(getName(), intValue, invalidReason.append(ERR_INTARG_VALUE_BELOW_LOWER_BOUND.get(getPropertyName(), intValue, lowerBound)); return false; } @@ -472,7 +349,7 @@ // equal to it. if (hasUpperBound && (intValue > upperBound)) { invalidReason.append(ERR_INTARG_VALUE_ABOVE_UPPER_BOUND.get(getName(), intValue, invalidReason.append(ERR_INTARG_VALUE_ABOVE_UPPER_BOUND.get(getPropertyName(), intValue, upperBound)); return false; } opendj-cli/src/main/java/com/forgerock/opendj/cli/SubCommand.java
New file @@ -0,0 +1,359 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2008 Sun Microsystems, Inc. * Portions Copyright 2014 ForgeRock AS */ package com.forgerock.opendj.cli; import static com.forgerock.opendj.util.StaticUtils.toLowerCase; import static com.forgerock.opendj.cli.CliMessages.*; import org.forgerock.i18n.LocalizableMessage; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; /** * This class defines a data structure for holding information about a subcommand that may be used with the subcommand * argument parser. The subcommand has a name, a description, and a set of arguments. */ public class SubCommand { // Indicates whether this subCommand should be hidden in the usage // information. private boolean isHidden; // The mapping between the short argument IDs and the arguments for this // subcommand. private HashMap<Character, Argument> shortIDMap; // The mapping between the long argument IDs and the arguments for this // subcommand. private HashMap<String, Argument> longIDMap; // The list of arguments associated with this subcommand. private LinkedList<Argument> arguments; // The description for this subcommand. private LocalizableMessage description; // The name of this subcommand. private String name; // The argument parser with which this subcommand is associated. private SubCommandArgumentParser parser; // Indicates whether this parser will allow additional unnamed // arguments at the end of the list. private boolean allowsTrailingArguments; // The maximum number of unnamed trailing arguments that may be // provided. private int maxTrailingArguments; // The minimum number of unnamed trailing arguments that may be // provided. private int minTrailingArguments; // The display name that will be used for the trailing arguments in // the usage information. private String trailingArgsDisplayName; /** * Creates a new subcommand with the provided information. The subcommand will be automatically registered with the * associated parser. * * @param parser * The argument parser with which this subcommand is associated. * @param name * The name of this subcommand. * @param description * The description of this subcommand. * @throws ArgumentException * If the associated argument parser already has a subcommand with the same name. */ public SubCommand(SubCommandArgumentParser parser, String name, LocalizableMessage description) throws ArgumentException { this(parser, name, false, 0, 0, null, description); } /** * Creates a new subcommand with the provided information. The subcommand will be automatically registered with the * associated parser. * * @param parser * The argument parser with which this subcommand is associated. * @param name * The name of this subcommand. * @param allowsTrailingArguments * Indicates whether this parser allows unnamed trailing arguments to be provided. * @param minTrailingArguments * The minimum number of unnamed trailing arguments that must be provided. A value less than or equal to * zero indicates that no minimum will be enforced. * @param maxTrailingArguments * The maximum number of unnamed trailing arguments that may be provided. A value less than or equal to * zero indicates that no maximum will be enforced. * @param trailingArgsDisplayName * The display name that should be used as a placeholder for unnamed trailing arguments in the generated * usage information. * @param description * The description of this subcommand. * @throws ArgumentException * If the associated argument parser already has a subcommand with the same name. */ public SubCommand(SubCommandArgumentParser parser, String name, boolean allowsTrailingArguments, int minTrailingArguments, int maxTrailingArguments, String trailingArgsDisplayName, LocalizableMessage description) throws ArgumentException { this.parser = parser; this.name = name; this.description = description; this.allowsTrailingArguments = allowsTrailingArguments; this.minTrailingArguments = minTrailingArguments; this.maxTrailingArguments = maxTrailingArguments; this.trailingArgsDisplayName = trailingArgsDisplayName; this.isHidden = false; String nameToCheck = name; if (parser.longArgumentsCaseSensitive()) { nameToCheck = toLowerCase(name); } if (parser.hasSubCommand(nameToCheck)) { LocalizableMessage message = ERR_ARG_SUBCOMMAND_DUPLICATE_SUBCOMMAND.get(name); throw new ArgumentException(message); } parser.addSubCommand(this); shortIDMap = new HashMap<Character, Argument>(); longIDMap = new HashMap<String, Argument>(); arguments = new LinkedList<Argument>(); } /** * Retrieves the name of this subcommand. * * @return The name of this subcommand. */ public String getName() { return name; } /** * Retrieves the description for this subcommand. * * @return The description for this subcommand. */ public LocalizableMessage getDescription() { return description; } /** * Retrieves the set of arguments for this subcommand. * * @return The set of arguments for this subcommand. */ public LinkedList<Argument> getArguments() { return arguments; } /** * Retrieves the subcommand argument with the specified short identifier. * * @param shortID * The short identifier of the argument to retrieve. * @return The subcommand argument with the specified short identifier, or <CODE>null</CODE> if there is none. */ public Argument getArgument(Character shortID) { return shortIDMap.get(shortID); } /** * Retrieves the subcommand argument with the specified long identifier. * * @param longID * The long identifier of the argument to retrieve. * @return The subcommand argument with the specified long identifier, or <CODE>null</CODE> if there is none. */ public Argument getArgument(String longID) { return longIDMap.get(longID); } /** * Retrieves the subcommand argument with the specified name. * * @param name * The name of the argument to retrieve. * @return The subcommand argument with the specified name, or <CODE>null</CODE> if there is no such argument. */ public Argument getArgumentForName(String name) { for (Argument a : arguments) { if (a.getName().equals(name)) { return a; } } return null; } /** * Adds the provided argument for use with this subcommand. * * @param argument * The argument to add for use with this subcommand. * @throws ArgumentException * If either the short ID or long ID for the argument conflicts with that of another argument already * associated with this subcommand. */ public void addArgument(Argument argument) throws ArgumentException { String argumentName = argument.getName(); for (Argument a : arguments) { if (argumentName.equals(a.getName())) { LocalizableMessage message = ERR_ARG_SUBCOMMAND_DUPLICATE_ARGUMENT_NAME.get(name, argumentName); throw new ArgumentException(message); } } if (parser.hasGlobalArgument(argumentName)) { LocalizableMessage message = ERR_ARG_SUBCOMMAND_ARGUMENT_GLOBAL_CONFLICT.get(argumentName, name); throw new ArgumentException(message); } Character shortID = argument.getShortIdentifier(); if (shortID != null) { if (shortIDMap.containsKey(shortID)) { LocalizableMessage message = ERR_ARG_SUBCOMMAND_DUPLICATE_SHORT_ID.get(argumentName, name, String.valueOf(shortID), shortIDMap.get(shortID).getName()); throw new ArgumentException(message); } Argument arg = parser.getGlobalArgumentForShortID(shortID); if (arg != null) { LocalizableMessage message = ERR_ARG_SUBCOMMAND_ARGUMENT_SHORT_ID_GLOBAL_CONFLICT.get(argumentName, name, String.valueOf(shortID), arg.getName()); throw new ArgumentException(message); } } String longID = argument.getLongIdentifier(); if (longID != null) { if (!parser.longArgumentsCaseSensitive()) { longID = toLowerCase(longID); } if (longIDMap.containsKey(longID)) { LocalizableMessage message = ERR_ARG_SUBCOMMAND_DUPLICATE_LONG_ID.get(argumentName, name, argument.getLongIdentifier(), longIDMap.get(longID).getName()); throw new ArgumentException(message); } Argument arg = parser.getGlobalArgumentForLongID(longID); if (arg != null) { LocalizableMessage message = ERR_ARG_SUBCOMMAND_ARGUMENT_LONG_ID_GLOBAL_CONFLICT.get(argumentName, name, argument.getLongIdentifier(), arg.getName()); throw new ArgumentException(message); } } arguments.add(argument); if (shortID != null) { shortIDMap.put(shortID, argument); } if (longID != null) { longIDMap.put(longID, argument); } } /** * Indicates whether this sub-command will allow unnamed trailing arguments. These will be arguments at the end of * the list that are not preceded by either a long or short identifier and will need to be manually parsed by the * application using this parser. Note that once an unnamed trailing argument has been identified, all remaining * arguments will be classified as such. * * @return <CODE>true</CODE> if this sub-command allows unnamed trailing arguments, or <CODE>false</CODE> if it does * not. */ public boolean allowsTrailingArguments() { return allowsTrailingArguments; } /** * Retrieves the minimum number of unnamed trailing arguments that must be provided. * * @return The minimum number of unnamed trailing arguments that must be provided, or a value less than or equal to * zero if no minimum will be enforced. */ public int getMinTrailingArguments() { return minTrailingArguments; } /** * Retrieves the maximum number of unnamed trailing arguments that may be provided. * * @return The maximum number of unnamed trailing arguments that may be provided, or a value less than or equal to * zero if no maximum will be enforced. */ public int getMaxTrailingArguments() { return maxTrailingArguments; } /** * Retrieves the trailing arguments display name. * * @return Returns the trailing arguments display name. */ public String getTrailingArgumentsDisplayName() { return trailingArgsDisplayName; } /** * Retrieves the set of unnamed trailing arguments that were provided on the command line. * * @return The set of unnamed trailing arguments that were provided on the command line. */ public ArrayList<String> getTrailingArguments() { return parser.getTrailingArguments(); } /** * Indicates whether this subcommand should be hidden from the usage information. * * @return <CODE>true</CODE> if this subcommand should be hidden from the usage information, or <CODE>false</CODE> * if not. */ public boolean isHidden() { return isHidden; } /** * Specifies whether this subcommand should be hidden from the usage information. * * @param isHidden * Indicates whether this subcommand should be hidden from the usage information. */ public void setHidden(boolean isHidden) { this.isHidden = isHidden; } } opendj-cli/src/main/java/com/forgerock/opendj/cli/SubCommandArgumentParser.java
New file @@ -0,0 +1,1531 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2014 ForgeRock AS */ package com.forgerock.opendj.cli; import static com.forgerock.opendj.util.StaticUtils.toLowerCase; import static com.forgerock.opendj.util.StaticUtils.EOL; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.SortedMap; import java.util.TreeMap; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.LocalizableMessageBuilder; import static com.forgerock.opendj.cli.CliMessages.*; import static com.forgerock.opendj.cli.CliConstants.*; import static com.forgerock.opendj.cli.Utils.*; /** * This class defines a variant of the argument parser that can be used with applications that use subcommands to * customize their behavior and that have a different set of options per subcommand (e.g, "cvs checkout" takes different * options than "cvs commit"). This parser also has the ability to use global options that will always be applicable * regardless of the subcommand in addition to the subcommand-specific arguments. There must not be any conflicts * between the global options and the option for any subcommand, but it is allowed to re-use subcommand-specific options * for different purposes between different subcommands. */ public class SubCommandArgumentParser extends ArgumentParser { /** * The argument that will be used to trigger the display of usage information. */ private Argument usageArgument; /** * The arguments that will be used to trigger the display of usage information for groups of sub-commands. */ private final Map<Argument, Collection<SubCommand>> usageGroupArguments; /** * The set of unnamed trailing arguments that were provided for this parser. */ private ArrayList<String> trailingArguments; /** * Indicates whether subcommand and long argument names should be treated in a case-sensitive manner. */ private final boolean longArgumentsCaseSensitive; /** Indicates whether the usage information has been displayed. */ private boolean usageOrVersionDisplayed; /** * The set of global arguments defined for this parser, referenced by short ID. */ private final Map<Character, Argument> globalShortIDMap; /** * The set of global arguments defined for this parser, referenced by argument name. */ private final Map<String, Argument> globalArgumentMap; /** * The set of global arguments defined for this parser, referenced by long ID. */ private final Map<String, Argument> globalLongIDMap; /** * The set of subcommands defined for this parser, referenced by subcommand name. */ private final SortedMap<String, SubCommand> subCommands; /** The total set of global arguments defined for this parser. */ private final List<Argument> globalArgumentList; /** The output stream to which usage information should be printed. */ private OutputStream usageOutputStream; /** * The fully-qualified name of the Java class that should be invoked to launch the program with which this argument * parser is associated. */ private final String mainClassName; /** * A human-readable description for the tool, which will be included when displaying usage information. */ private final LocalizableMessage toolDescription; /** The raw set of command-line arguments that were provided. */ private String[] rawArguments; /** * The subcommand requested by the user as part of the command-line arguments. */ private SubCommand subCommand; /** Indicates whether the version argument was provided. */ private boolean versionPresent; private static final String INDENT = " "; private final static int MAX_LENGTH = 80; /** * Creates a new instance of this subcommand argument parser with no arguments. * * @param mainClassName * The fully-qualified name of the Java class that should be invoked to launch the program with which * this argument parser is associated. * @param toolDescription * A human-readable description for the tool, which will be included when displaying usage information. * @param longArgumentsCaseSensitive * Indicates whether subcommand and long argument names should be treated in a case-sensitive manner. */ public SubCommandArgumentParser(String mainClassName, LocalizableMessage toolDescription, boolean longArgumentsCaseSensitive) { super(mainClassName, toolDescription, longArgumentsCaseSensitive); this.mainClassName = mainClassName; this.toolDescription = toolDescription; this.longArgumentsCaseSensitive = longArgumentsCaseSensitive; trailingArguments = new ArrayList<String>(); globalArgumentList = new LinkedList<Argument>(); globalArgumentMap = new HashMap<String, Argument>(); globalShortIDMap = new HashMap<Character, Argument>(); globalLongIDMap = new HashMap<String, Argument>(); usageGroupArguments = new HashMap<Argument, Collection<SubCommand>>(); subCommands = new TreeMap<String, SubCommand>(); usageOrVersionDisplayed = false; rawArguments = null; subCommand = null; usageArgument = null; usageOutputStream = null; } /** * Retrieves the fully-qualified name of the Java class that should be invoked to launch the program with which this * argument parser is associated. * * @return The fully-qualified name of the Java class that should be invoked to launch the program with which this * argument parser is associated. */ @Override public String getMainClassName() { return mainClassName; } /** * Retrieves a human-readable description for this tool, which should be included at the top of the command-line * usage information. * * @return A human-readable description for this tool, or {@code null} if none is available. */ @Override public LocalizableMessage getToolDescription() { return toolDescription; } /** * Indicates whether subcommand names and long argument strings should be treated in a case-sensitive manner. * * @return <CODE>true</CODE> if subcommand names and long argument strings should be treated in a case-sensitive * manner, or <CODE>false</CODE> if they should not. */ public boolean longArgumentsCaseSensitive() { return longArgumentsCaseSensitive; } /** * Retrieves the list of all global arguments that have been defined for this argument parser. * * @return The list of all global arguments that have been defined for this argument parser. */ public List<Argument> getGlobalArgumentList() { return globalArgumentList; } /** * Indicates whether this argument parser contains a global argument with the specified name. * * @param argumentName * The name for which to make the determination. * @return <CODE>true</CODE> if a global argument exists with the specified name, or <CODE>false</CODE> if not. */ public boolean hasGlobalArgument(String argumentName) { return globalArgumentMap.containsKey(argumentName); } /** * Retrieves the global argument with the specified name. * * @param name * The name of the global argument to retrieve. * @return The global argument with the specified name, or <CODE>null</CODE> if there is no such argument. */ public Argument getGlobalArgument(String name) { return globalArgumentMap.get(name); } /** * Retrieves the set of global arguments mapped by the short identifier that may be used to reference them. Note * that arguments that do not have a short identifier will not be present in this list. * * @return The set of global arguments mapped by the short identifier that may be used to reference them. */ public Map<Character, Argument> getGlobalArgumentsByShortID() { return globalShortIDMap; } /** * Indicates whether this argument parser has a global argument with the specified short ID. * * @param shortID * The short ID character for which to make the determination. * @return <CODE>true</CODE> if a global argument exists with the specified short ID, or <CODE>false</CODE> if not. */ public boolean hasGlobalArgumentWithShortID(Character shortID) { return globalShortIDMap.containsKey(shortID); } /** * Retrieves the global argument with the specified short identifier. * * @param shortID * The short identifier for the global argument to retrieve. * @return The global argument with the specified short identifier, or <CODE>null</CODE> if there is no such * argument. */ public Argument getGlobalArgumentForShortID(Character shortID) { return globalShortIDMap.get(shortID); } /** * Retrieves the set of global arguments mapped by the long identifier that may be used to reference them. Note that * arguments that do not have a long identifier will not be present in this list. * * @return The set of global arguments mapped by the long identifier that may be used to reference them. */ public Map<String, Argument> getGlobalArgumentsByLongID() { return globalLongIDMap; } /** * Indicates whether this argument parser has a global argument with the specified long ID. * * @param longID * The long ID string for which to make the determination. * @return <CODE>true</CODE> if a global argument exists with the specified long ID, or <CODE>false</CODE> if not. */ public boolean hasGlobalArgumentWithLongID(String longID) { return globalLongIDMap.containsKey(longID); } /** * Retrieves the global argument with the specified long identifier. * * @param longID * The long identifier for the global argument to retrieve. * @return The global argument with the specified long identifier, or <CODE>null</CODE> if there is no such * argument. */ public Argument getGlobalArgumentForLongID(String longID) { return globalLongIDMap.get(longID); } /** * Retrieves the set of subcommands defined for this argument parser, referenced by subcommand name. * * @return The set of subcommands defined for this argument parser, referenced by subcommand name. */ public SortedMap<String, SubCommand> getSubCommands() { return subCommands; } /** * Indicates whether this argument parser has a subcommand with the specified name. * * @param name * The subcommand name for which to make the determination. * @return <CODE>true</CODE> if this argument parser has a subcommand with the specified name, or <CODE>false</CODE> * if it does not. */ public boolean hasSubCommand(String name) { return subCommands.containsKey(name); } /** * Retrieves the subcommand with the specified name. * * @param name * The name of the subcommand to retrieve. * @return The subcommand with the specified name, or <CODE>null</CODE> if no such subcommand is defined. */ public SubCommand getSubCommand(String name) { return subCommands.get(name); } /** * Retrieves the subcommand that was selected in the set of command-line arguments. * * @return The subcommand that was selected in the set of command-line arguments, or <CODE>null</CODE> if none was * selected. */ public SubCommand getSubCommand() { return subCommand; } /** * Retrieves the raw set of arguments that were provided. * * @return The raw set of arguments that were provided, or <CODE>null</CODE> if the argument list has not yet been * parsed. */ @Override public String[] getRawArguments() { return rawArguments; } /** * Adds the provided argument to the set of global arguments handled by this parser. * * @param argument * The argument to be added. * @throws ArgumentException * If the provided argument conflicts with another global or subcommand argument that has already been * defined. */ public void addGlobalArgument(Argument argument) throws ArgumentException { addGlobalArgument(argument, null); } /** * Adds the provided argument to the set of global arguments handled by this parser. * * @param argument * The argument to be added. * @param group * The argument group to which the argument belongs. * @throws ArgumentException * If the provided argument conflicts with another global or subcommand argument that has already been * defined. */ public void addGlobalArgument(Argument argument, ArgumentGroup group) throws ArgumentException { String argumentName = argument.getName(); if (globalArgumentMap.containsKey(argumentName)) { LocalizableMessage message = ERR_SUBCMDPARSER_DUPLICATE_GLOBAL_ARG_NAME.get(argumentName); throw new ArgumentException(message); } for (SubCommand s : subCommands.values()) { if (s.getArgumentForName(argumentName) != null) { LocalizableMessage message = ERR_SUBCMDPARSER_GLOBAL_ARG_NAME_SUBCMD_CONFLICT.get(argumentName, s.getName()); throw new ArgumentException(message); } } Character shortID = argument.getShortIdentifier(); if (shortID != null) { if (globalShortIDMap.containsKey(shortID)) { String name = globalShortIDMap.get(shortID).getName(); LocalizableMessage message = ERR_SUBCMDPARSER_DUPLICATE_GLOBAL_ARG_SHORT_ID.get( String.valueOf(shortID), argumentName, name); throw new ArgumentException(message); } for (SubCommand s : subCommands.values()) { if (s.getArgument(shortID) != null) { String cmdName = s.getName(); String name = s.getArgument(shortID).getName(); LocalizableMessage message = ERR_SUBCMDPARSER_GLOBAL_ARG_SHORT_ID_CONFLICT.get( String.valueOf(shortID), argumentName, name, cmdName); throw new ArgumentException(message); } } } String longID = argument.getLongIdentifier(); if (longID != null) { if (!longArgumentsCaseSensitive) { longID = toLowerCase(longID); } if (globalLongIDMap.containsKey(longID)) { String name = globalLongIDMap.get(longID).getName(); LocalizableMessage message = ERR_SUBCMDPARSER_DUPLICATE_GLOBAL_ARG_LONG_ID.get( argument.getLongIdentifier(), argumentName, name); throw new ArgumentException(message); } for (SubCommand s : subCommands.values()) { if (s.getArgument(longID) != null) { String cmdName = s.getName(); String name = s.getArgument(longID).getName(); LocalizableMessage message = ERR_SUBCMDPARSER_GLOBAL_ARG_LONG_ID_CONFLICT.get( argument.getLongIdentifier(), argumentName, name, cmdName); throw new ArgumentException(message); } } } if (shortID != null) { globalShortIDMap.put(shortID, argument); } if (longID != null) { globalLongIDMap.put(longID, argument); } globalArgumentList.add(argument); if (group == null) { group = getStandardGroup(argument); } group.addArgument(argument); argumentGroups.add(group); } /** * Removes the provided argument from the set of global arguments handled by this parser. * * @param argument * The argument to be removed. */ protected void removeGlobalArgument(Argument argument) { String argumentName = argument.getName(); globalArgumentMap.remove(argumentName); Character shortID = argument.getShortIdentifier(); if (shortID != null) { globalShortIDMap.remove(shortID); } String longID = argument.getLongIdentifier(); if (longID != null) { if (!longArgumentsCaseSensitive) { longID = toLowerCase(longID); } globalLongIDMap.remove(longID); } globalArgumentList.remove(argument); } /** * Sets the provided argument as one which will automatically trigger the output of full usage information if it is * provided on the command line and no further argument validation will be performed. * <p> * If sub-command groups are defined using the {@link #setUsageGroupArgument(Argument, Collection)} method, then * this usage argument, when specified, will result in usage information being displayed which does not include * information on sub-commands. * <p> * Note that the caller will still need to add this argument to the parser with the * {@link #addGlobalArgument(Argument)} method, and the argument should not be required and should not take a value. * Also, the caller will still need to check for the presence of the usage argument after calling * {@link #parseArguments(String[])} to know that no further processing will be required. * * @param argument * The argument whose presence should automatically trigger the display of full usage information. * @param outputStream * The output stream to which the usage information should be written. */ @Override public void setUsageArgument(Argument argument, OutputStream outputStream) { usageArgument = argument; usageOutputStream = outputStream; usageGroupArguments.put(argument, Collections.<SubCommand> emptySet()); } /** * Sets the provided argument as one which will automatically trigger the output of partial usage information if it * is provided on the command line and no further argument validation will be performed. * <p> * Partial usage information will include a usage synopsis, a summary of each of the sub-commands listed in the * provided sub-commands collection, and a summary of the global options. * <p> * Note that the caller will still need to add this argument to the parser with the * {@link #addGlobalArgument(Argument)} method, and the argument should not be required and should not take a value. * Also, the caller will still need to check for the presence of the usage argument after calling * {@link #parseArguments(String[])} to know that no further processing will be required. * * @param argument * The argument whose presence should automatically trigger the display of partial usage information. * @param subCommands * The list of sub-commands which should have their usage displayed. */ public void setUsageGroupArgument(Argument argument, Collection<SubCommand> subCommands) { usageGroupArguments.put(argument, subCommands); } /** * Parses the provided set of arguments and updates the information associated with this parser accordingly. * * @param rawArguments * The raw set of arguments to parse. * @throws ArgumentException * If a problem was encountered while parsing the provided arguments. */ @Override public void parseArguments(String[] rawArguments) throws ArgumentException { parseArguments(rawArguments, null); } /** * Parses the provided set of arguments and updates the information associated with this parser accordingly. Default * values for unspecified arguments may be read from the specified properties if any are provided. * * @param rawArguments * The set of raw arguments to parse. * @param argumentProperties * A set of properties that may be used to provide default values for arguments not included in the given * raw arguments. * @throws ArgumentException * If a problem was encountered while parsing the provided arguments. */ @Override public void parseArguments(String[] rawArguments, Properties argumentProperties) throws ArgumentException { this.rawArguments = rawArguments; this.subCommand = null; this.trailingArguments = new ArrayList<String>(); this.usageOrVersionDisplayed = false; boolean inTrailingArgs = false; int numArguments = rawArguments.length; for (int i = 0; i < numArguments; i++) { final String arg = rawArguments[i]; if (inTrailingArgs) { trailingArguments.add(arg); if (subCommand == null) { throw new ArgumentException(ERR_ARG_SUBCOMMAND_INVALID.get()); } if (subCommand.getMaxTrailingArguments() > 0 && trailingArguments.size() > subCommand.getMaxTrailingArguments()) { LocalizableMessage message = ERR_ARGPARSER_TOO_MANY_TRAILING_ARGS.get(subCommand .getMaxTrailingArguments()); throw new ArgumentException(message); } continue; } if (arg.equals("--")) { inTrailingArgs = true; } else if (arg.startsWith("--")) { // This indicates that we are using the long name to reference the // argument. It may be in any of the following forms: // --name // --name value // --name=value String argName = arg.substring(2); String argValue = null; int equalPos = argName.indexOf('='); if (equalPos < 0) { // This is fine. The value is not part of the argument name token. } else if (equalPos == 0) { // The argument starts with "--=", which is not acceptable. LocalizableMessage message = ERR_SUBCMDPARSER_LONG_ARG_WITHOUT_NAME.get(arg); throw new ArgumentException(message); } else { // The argument is in the form --name=value, so parse them both out. argValue = argName.substring(equalPos + 1); argName = argName.substring(0, equalPos); } // If we're not case-sensitive, then convert the name to lowercase. String origArgName = argName; if (!longArgumentsCaseSensitive) { argName = toLowerCase(argName); } // See if the specified name references a global argument. If not, then // see if it references a subcommand argument. Argument a = globalLongIDMap.get(argName); if (a == null) { if (subCommand == null) { if (argName.equals(OPTION_LONG_HELP)) { // "--help" will always be interpreted as requesting usage // information. getUsage(usageOutputStream); return; } else if (argName.equals(OPTION_LONG_PRODUCT_VERSION)) { // "--version" will always be interpreted as requesting usage // information. versionPresent = true; usageOrVersionDisplayed = true; printVersion(); return; } else { // There is no such global argument. LocalizableMessage message = ERR_SUBCMDPARSER_NO_GLOBAL_ARGUMENT_FOR_LONG_ID .get(origArgName); throw new ArgumentException(message); } } else { a = subCommand.getArgument(argName); if (a == null) { if (argName.equals(OPTION_LONG_HELP)) { // "--help" will always be interpreted as requesting usage // information. getUsage(usageOutputStream); return; } else if (argName.equals(OPTION_LONG_PRODUCT_VERSION)) { // "--version" will always be interpreted as requesting usage // information. versionPresent = true; usageOrVersionDisplayed = true; printVersion(); return; } else { // There is no such global or subcommand argument. LocalizableMessage message = ERR_SUBCMDPARSER_NO_ARGUMENT_FOR_LONG_ID.get(origArgName); throw new ArgumentException(message); } } } } a.setPresent(true); // If this is a usage argument, then immediately stop and print // usage information. if (usageGroupArguments.containsKey(a)) { getUsage(a, usageOutputStream); return; } // See if the argument takes a value. If so, then make sure one was // provided. If not, then make sure none was provided. if (a.needsValue()) { if (argValue == null) { if ((i + 1) == numArguments) { LocalizableMessage message = ERR_SUBCMDPARSER_NO_VALUE_FOR_ARGUMENT_WITH_LONG_ID .get(argName); throw new ArgumentException(message); } argValue = rawArguments[++i]; } LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder(); if (!a.valueIsAcceptable(argValue, invalidReason)) { LocalizableMessage message = ERR_SUBCMDPARSER_VALUE_UNACCEPTABLE_FOR_LONG_ID.get(argValue, argName, invalidReason.toString()); throw new ArgumentException(message); } // If the argument already has a value, then make sure it is // acceptable to have more than one. if (a.hasValue() && !a.isMultiValued()) { LocalizableMessage message = ERR_SUBCMDPARSER_NOT_MULTIVALUED_FOR_LONG_ID.get(origArgName); throw new ArgumentException(message); } a.addValue(argValue); } else { if (argValue != null) { LocalizableMessage message = ERR_SUBCMDPARSER_ARG_FOR_LONG_ID_DOESNT_TAKE_VALUE .get(origArgName); throw new ArgumentException(message); } } } else if (arg.startsWith("-")) { // This indicates that we are using the 1-character name to reference // the argument. It may be in any of the following forms: // -n // -nvalue // -n value if (arg.equals("-")) { LocalizableMessage message = ERR_SUBCMDPARSER_INVALID_DASH_AS_ARGUMENT.get(); throw new ArgumentException(message); } char argCharacter = arg.charAt(1); String argValue; if (arg.length() > 2) { argValue = arg.substring(2); } else { argValue = null; } // Get the argument with the specified short ID. It may be either a // global argument or a subcommand-specific argument. Argument a = globalShortIDMap.get(argCharacter); if (a == null) { if (subCommand == null) { if (argCharacter == '?') { // "-?" will always be interpreted as requesting usage. getUsage(usageOutputStream); if (usageArgument != null) { usageArgument.setPresent(true); } return; } else if (argCharacter == OPTION_SHORT_PRODUCT_VERSION) { // "-V" will always be interpreted as requesting // version information except if it's already defined. if (dashVAccepted()) { usageOrVersionDisplayed = true; versionPresent = true; printVersion(); return; } else { // -V is defined in another suncommand, so we can // accepted it as the version information argument LocalizableMessage message = ERR_SUBCMDPARSER_NO_GLOBAL_ARGUMENT_FOR_SHORT_ID .get(String.valueOf(argCharacter)); throw new ArgumentException(message); } } else { // There is no such argument registered. LocalizableMessage message = ERR_SUBCMDPARSER_NO_GLOBAL_ARGUMENT_FOR_SHORT_ID.get(String .valueOf(argCharacter)); throw new ArgumentException(message); } } else { a = subCommand.getArgument(argCharacter); if (a == null) { if (argCharacter == '?') { // "-?" will always be interpreted as requesting usage. getUsage(usageOutputStream); return; } else if (argCharacter == OPTION_SHORT_PRODUCT_VERSION) { if (dashVAccepted()) { usageOrVersionDisplayed = true; versionPresent = true; printVersion(); return; } } else { // There is no such argument registered. LocalizableMessage message = ERR_SUBCMDPARSER_NO_ARGUMENT_FOR_SHORT_ID.get(String .valueOf(argCharacter)); throw new ArgumentException(message); } } } } a.setPresent(true); // If this is the usage argument, then immediately stop and print // usage information. if (usageGroupArguments.containsKey(a)) { getUsage(a, usageOutputStream); return; } // See if the argument takes a value. If so, then make sure one was // provided. If not, then make sure none was provided. if (a.needsValue()) { if (argValue == null) { if ((i + 1) == numArguments) { LocalizableMessage message = ERR_SUBCMDPARSER_NO_VALUE_FOR_ARGUMENT_WITH_SHORT_ID .get(String.valueOf(argCharacter)); throw new ArgumentException(message); } argValue = rawArguments[++i]; } LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder(); if (!a.valueIsAcceptable(argValue, invalidReason)) { LocalizableMessage message = ERR_SUBCMDPARSER_VALUE_UNACCEPTABLE_FOR_SHORT_ID.get(argValue, String.valueOf(argCharacter), invalidReason.toString()); throw new ArgumentException(message); } // If the argument already has a value, then make sure it is // acceptable to have more than one. if (a.hasValue() && !a.isMultiValued()) { LocalizableMessage message = ERR_SUBCMDPARSER_NOT_MULTIVALUED_FOR_SHORT_ID.get(String .valueOf(argCharacter)); throw new ArgumentException(message); } a.addValue(argValue); } else { if (argValue != null) { // If we've gotten here, then it means that we're in a scenario like // "-abc" where "a" is a valid argument that doesn't take a value. // However, this could still be valid if all remaining characters in // the value are also valid argument characters that don't take // values. int valueLength = argValue.length(); for (int j = 0; j < valueLength; j++) { char c = argValue.charAt(j); Argument b = globalShortIDMap.get(c); if (b == null) { if (subCommand == null) { LocalizableMessage message = ERR_SUBCMDPARSER_NO_GLOBAL_ARGUMENT_FOR_SHORT_ID .get(String.valueOf(argCharacter)); throw new ArgumentException(message); } else { b = subCommand.getArgument(c); if (b == null) { LocalizableMessage message = ERR_SUBCMDPARSER_NO_ARGUMENT_FOR_SHORT_ID .get(String.valueOf(argCharacter)); throw new ArgumentException(message); } } } if (b.needsValue()) { // This means we're in a scenario like "-abc" where b is a // valid argument that takes a value. We don't support that. LocalizableMessage message = ERR_SUBCMDPARSER_CANT_MIX_ARGS_WITH_VALUES.get( String.valueOf(argCharacter), argValue, String.valueOf(c)); throw new ArgumentException(message); } else { b.setPresent(true); // If this is the usage argument, then immediately stop and // print usage information. if (usageGroupArguments.containsKey(b)) { getUsage(b, usageOutputStream); return; } } } } } } else if (subCommand != null) { // It's not a short or long identifier and the sub-command has // already been specified, so it must be the first trailing argument. if (subCommand.allowsTrailingArguments()) { trailingArguments.add(arg); inTrailingArgs = true; } else { // Trailing arguments are not allowed for this sub-command. LocalizableMessage message = ERR_ARGPARSER_DISALLOWED_TRAILING_ARGUMENT.get(arg); throw new ArgumentException(message); } } else { // It must be the sub-command. String nameToCheck = arg; if (!longArgumentsCaseSensitive) { nameToCheck = toLowerCase(arg); } SubCommand sc = subCommands.get(nameToCheck); if (sc == null) { LocalizableMessage message = ERR_SUBCMDPARSER_INVALID_ARGUMENT.get(arg); throw new ArgumentException(message); } else { subCommand = sc; } } } // If we have a sub-command and it allows trailing arguments and // there is a minimum number, then make sure at least that many // were provided. if (subCommand != null) { int minTrailingArguments = subCommand.getMinTrailingArguments(); if (subCommand.allowsTrailingArguments() && minTrailingArguments > 0 && trailingArguments.size() < minTrailingArguments) { LocalizableMessage message = ERR_ARGPARSER_TOO_FEW_TRAILING_ARGUMENTS.get(minTrailingArguments); throw new ArgumentException(message); } } // If we don't have the argumentProperties, try to load a properties file. if (argumentProperties == null) { argumentProperties = checkExternalProperties(); } // Iterate through all the global arguments normalizeArguments(argumentProperties, globalArgumentList); // Iterate through all the subcommand-specific arguments if (subCommand != null) { normalizeArguments(argumentProperties, subCommand.getArguments()); } } private boolean dashVAccepted() { if (globalShortIDMap.containsKey(OPTION_SHORT_PRODUCT_VERSION)) { return false; } for (SubCommand subCmd : subCommands.values()) { if (subCmd.getArgument(OPTION_SHORT_PRODUCT_VERSION) != null) { return false; } } return true; } /** * Appends usage information for the specified subcommand to the provided buffer. * * @param buffer * The buffer to which the usage information should be appended. * @param subCommand * The subcommand for which to display the usage information. */ public void getSubCommandUsage(LocalizableMessageBuilder buffer, SubCommand subCommand) { usageOrVersionDisplayed = true; String scriptName = System.getProperty(PROPERTY_SCRIPT_NAME); if (scriptName == null || scriptName.length() == 0) { scriptName = "java " + mainClassName; } buffer.append(INFO_ARGPARSER_USAGE_JAVA_SCRIPTNAME.get(scriptName)); buffer.append(" "); buffer.append(scriptName); buffer.append(" "); buffer.append(subCommand.getName()); buffer.append(" ").append(INFO_SUBCMDPARSER_OPTIONS.get()); if (subCommand.allowsTrailingArguments()) { buffer.append(' '); buffer.append(subCommand.getTrailingArgumentsDisplayName()); } buffer.append(EOL); buffer.append(subCommand.getDescription()); buffer.append(EOL); if (!globalArgumentList.isEmpty()) { buffer.append(EOL); buffer.append(INFO_GLOBAL_OPTIONS.get()); buffer.append(EOL); buffer.append(" "); buffer.append(INFO_GLOBAL_OPTIONS_REFERENCE.get(scriptName)); buffer.append(EOL); } if (!subCommand.getArguments().isEmpty()) { buffer.append(EOL); buffer.append(INFO_SUBCMD_OPTIONS.get()); buffer.append(EOL); } for (Argument a : subCommand.getArguments()) { // If this argument is hidden, then skip it. if (a.isHidden()) { continue; } // Write a line with the short and/or long identifiers that may be used // for the argument. Character shortID = a.getShortIdentifier(); String longID = a.getLongIdentifier(); if (shortID != null) { int currentLength = buffer.length(); if (a.equals(usageArgument)) { buffer.append("-?, "); } buffer.append("-"); buffer.append(shortID.charValue()); if (a.needsValue() && longID == null) { buffer.append(" "); buffer.append(a.getValuePlaceholder()); } if (longID != null) { StringBuilder newBuffer = new StringBuilder(); newBuffer.append(", --"); newBuffer.append(longID); if (a.needsValue()) { newBuffer.append(" "); newBuffer.append(a.getValuePlaceholder()); } int lineLength = (buffer.length() - currentLength) + newBuffer.length(); if (lineLength > MAX_LENGTH) { buffer.append(EOL); } buffer.append(newBuffer.toString()); } buffer.append(EOL); } else { if (longID != null) { if (a.equals(usageArgument)) { buffer.append("-?, "); } buffer.append("--"); buffer.append(longID); if (a.needsValue()) { buffer.append(" "); buffer.append(a.getValuePlaceholder()); } buffer.append(EOL); } } indentAndWrap2(INDENT, a.getDescription(), buffer); if (a.needsValue() && a.getDefaultValue() != null && a.getDefaultValue().length() > 0) { indentAndWrap2(INDENT, INFO_ARGPARSER_USAGE_DEFAULT_VALUE.get(a.getDefaultValue()), buffer); } } } /** * Write one or more lines with the description of the argument. We will indent the description five characters and * try our best to wrap at or before column 79 so it will be friendly to 80-column displays. * <p> * FIXME Try to merge with #indentAndWrap(LocalizableMessage, LocalizableMessage, LocalizableMessageBuilder). */ private void indentAndWrap2(String indent, LocalizableMessage text, LocalizableMessageBuilder buffer) { int actualSize = MAX_LENGTH - indent.length() - 1; if (text.length() <= actualSize) { buffer.append(indent); buffer.append(text); buffer.append(EOL); } else { String s = text.toString(); while (s.length() > actualSize) { int spacePos = s.lastIndexOf(' ', actualSize); if (spacePos > 0) { buffer.append(indent); buffer.append(s.substring(0, spacePos).trim()); s = s.substring(spacePos + 1).trim(); buffer.append(EOL); } else { // There are no spaces in the first 74 columns. // See if there is one after that point. // If so, then break there. If not, then don't break at all. spacePos = s.indexOf(' '); if (spacePos > 0) { buffer.append(indent); buffer.append(s.substring(0, spacePos).trim()); s = s.substring(spacePos + 1).trim(); buffer.append(EOL); } else { buffer.append(indent); buffer.append(s); s = ""; buffer.append(EOL); } } } if (s.length() > 0) { buffer.append(indent); buffer.append(s); buffer.append(EOL); } } } /** * Retrieves a string containing usage information based on the defined arguments. * * @return A string containing usage information based on the defined arguments. */ @Override public String getUsage() { LocalizableMessageBuilder buffer = new LocalizableMessageBuilder(); if (subCommand == null) { if (System.getProperty("org.forgerock.opendj.gendoc") != null) { // Generate reference documentation for dsconfig subcommands for (SubCommand s : subCommands.values()) { buffer.append(toRefSect2(s)); } } else if (usageGroupArguments.size() > 1) { // We have sub-command groups, so don't display any // sub-commands by default. getFullUsage(Collections.<SubCommand> emptySet(), true, buffer); } else { // No grouping, so display all sub-commands. getFullUsage(subCommands.values(), true, buffer); } } else { getSubCommandUsage(buffer, subCommand); } return buffer.toMessage().toString(); } /** * Retrieves a string describing how the user can get more help. * * @return A string describing how the user can get more help. */ public LocalizableMessage getHelpUsageReference() { usageOrVersionDisplayed = true; String scriptName = System.getProperty(PROPERTY_SCRIPT_NAME); if (scriptName == null || scriptName.length() == 0) { scriptName = "java " + mainClassName; } LocalizableMessageBuilder buffer = new LocalizableMessageBuilder(); buffer.append(INFO_GLOBAL_HELP_REFERENCE.get(scriptName)); buffer.append(EOL); return buffer.toMessage(); } /** * Retrieves the set of unnamed trailing arguments that were provided on the command line. * * @return The set of unnamed trailing arguments that were provided on the command line. */ @Override public ArrayList<String> getTrailingArguments() { return trailingArguments; } /** * Indicates whether the usage information has been displayed to the end user either by an explicit argument like * "-H" or "--help", or by a built-in argument like "-?". * * @return {@code true} if the usage information has been displayed, or {@code false} if not. */ @Override public boolean usageOrVersionDisplayed() { return usageOrVersionDisplayed; } /** * Adds the provided subcommand to this argument parser. This is only intended for use by the * <CODE>SubCommand</CODE> constructor and does not do any validation of its own to ensure that there are no * conflicts with the subcommand or any of its arguments. * * @param subCommand * The subcommand to add to this argument parser. */ void addSubCommand(SubCommand subCommand) { subCommands.put(toLowerCase(subCommand.getName()), subCommand); } /** Get usage for a specific usage argument. */ private void getUsage(Argument a, OutputStream outputStream) { LocalizableMessageBuilder buffer = new LocalizableMessageBuilder(); if (a.equals(usageArgument) && subCommand != null) { getSubCommandUsage(buffer, subCommand); } else if (a.equals(usageArgument) && usageGroupArguments.size() <= 1) { // No groups - so display all sub-commands. getFullUsage(subCommands.values(), true, buffer); } else if (a.equals(usageArgument)) { // Using groups - so display all sub-commands group help. getFullUsage(Collections.<SubCommand> emptySet(), true, buffer); } else { // Requested help on specific group - don't display global // options. getFullUsage(usageGroupArguments.get(a), false, buffer); } try { outputStream.write(buffer.toString().getBytes()); } catch (Exception e) { // TODO empty catch } } /** {@inheritDoc} */ @Override public void getUsage(OutputStream outputStream) { try { outputStream.write(getUsage().getBytes()); } catch (IOException e) { // TODO empty catch } } /** * Appends complete usage information for the specified set of sub-commands. */ private void getFullUsage(Collection<SubCommand> c, boolean showGlobalOptions, LocalizableMessageBuilder buffer) { usageOrVersionDisplayed = true; if (toolDescription != null && toolDescription.length() > 0) { buffer.append(wrapText(toolDescription, MAX_LENGTH - 1)); buffer.append(EOL); buffer.append(EOL); } String scriptName = System.getProperty(PROPERTY_SCRIPT_NAME); if (scriptName == null || scriptName.length() == 0) { scriptName = "java " + mainClassName; } buffer.append(INFO_ARGPARSER_USAGE.get()); buffer.append(" "); buffer.append(scriptName); if (subCommands.isEmpty()) { buffer.append(" ").append(INFO_SUBCMDPARSER_OPTIONS.get()); } else { buffer.append(" ").append(INFO_SUBCMDPARSER_SUBCMD_AND_OPTIONS.get()); } if (!subCommands.isEmpty()) { buffer.append(EOL); buffer.append(EOL); if (c.isEmpty()) { buffer.append(INFO_SUBCMDPARSER_SUBCMD_HELP_HEADING.get()); } else { buffer.append(INFO_SUBCMDPARSER_SUBCMD_HEADING.get()); } buffer.append(EOL); } if (c.isEmpty()) { // Display usage arguments (except the default one). for (Argument a : globalArgumentList) { if (a.isHidden()) { continue; } if (usageGroupArguments.containsKey(a) && !a.equals(usageArgument)) { printArgumentUsage(a, buffer); } } } else { boolean isFirst = true; for (SubCommand sc : c) { if (sc.isHidden()) { continue; } if (isFirst) { buffer.append(EOL); } buffer.append(sc.getName()); buffer.append(EOL); indentAndWrap(LocalizableMessage.raw(INDENT), sc.getDescription(), buffer); buffer.append(EOL); isFirst = false; } } buffer.append(EOL); if (showGlobalOptions) { if (subCommands.isEmpty()) { buffer.append(INFO_SUBCMDPARSER_WHERE_OPTIONS_INCLUDE.get()); } else { buffer.append(INFO_SUBCMDPARSER_GLOBAL_HEADING.get()); } buffer.append(EOL); buffer.append(EOL); boolean printGroupHeaders = printUsageGroupHeaders(); // Display non-usage arguments. for (ArgumentGroup argGroup : argumentGroups) { if (argGroup.containsArguments() && printGroupHeaders) { // Print the groups description if any LocalizableMessage groupDesc = argGroup.getDescription(); if (groupDesc != null && !LocalizableMessage.EMPTY.equals(groupDesc)) { buffer.append(EOL); buffer.append(wrapText(groupDesc.toString(), MAX_LENGTH - 1)); buffer.append(EOL); buffer.append(EOL); } } for (Argument a : argGroup.getArguments()) { if (a.isHidden()) { continue; } if (!usageGroupArguments.containsKey(a)) { printArgumentUsage(a, buffer); } } } // Finally print default usage argument. if (usageArgument != null) { printArgumentUsage(usageArgument, buffer); } else { buffer.append("-?"); } buffer.append(EOL); } } /** * Appends argument usage information to the provided buffer. * * @param a * The argument to handle. * @param buffer * The buffer to which the usage information should be appended. */ private void printArgumentUsage(Argument a, LocalizableMessageBuilder buffer) { String value; if (a.needsValue()) { LocalizableMessage pHolder = a.getValuePlaceholder(); if (pHolder == null) { value = " {value}"; } else { value = " " + pHolder; } } else { value = ""; } Character shortIDChar = a.getShortIdentifier(); if (shortIDChar != null) { if (a.equals(usageArgument)) { buffer.append("-?, "); } buffer.append("-"); buffer.append(shortIDChar); String longIDString = a.getLongIdentifier(); if (longIDString != null) { buffer.append(", --"); buffer.append(longIDString); } buffer.append(value); } else { String longIDString = a.getLongIdentifier(); if (longIDString != null) { if (a.equals(usageArgument)) { buffer.append("-?, "); } buffer.append("--"); buffer.append(longIDString); buffer.append(value); } } buffer.append(EOL); indentAndWrap(LocalizableMessage.raw(INDENT), a.getDescription(), buffer); if (a.needsValue() && a.getDefaultValue() != null && a.getDefaultValue().length() > 0) { indentAndWrap(LocalizableMessage.raw(INDENT), INFO_ARGPARSER_USAGE_DEFAULT_VALUE.get(a.getDefaultValue()), buffer); } } /** * Write one or more lines with the description of the argument. We will indent the description five characters and * try our best to wrap at or before column 79 so it will be friendly to 80-column displays. */ private void indentAndWrap(LocalizableMessage indent, LocalizableMessage text, LocalizableMessageBuilder buffer) { int actualSize = MAX_LENGTH - indent.length(); if (text.length() <= actualSize) { buffer.append(indent); buffer.append(text); buffer.append(EOL); } else { String s = text.toString(); while (s.length() > actualSize) { int spacePos = s.lastIndexOf(' ', actualSize); if (spacePos > 0) { buffer.append(indent); buffer.append(s.substring(0, spacePos).trim()); s = s.substring(spacePos + 1).trim(); buffer.append(EOL); } else { // There are no spaces in the first actualSize -1 columns. // See if there is one after that point. // If so, then break there. If not, then don't break at all. spacePos = s.indexOf(' '); if (spacePos > 0) { buffer.append(indent); buffer.append(s.substring(0, spacePos).trim()); s = s.substring(spacePos + 1).trim(); buffer.append(EOL); } else { buffer.append(indent); buffer.append(s); s = ""; buffer.append(EOL); } } } if (s.length() > 0) { buffer.append(indent); buffer.append(s); buffer.append(EOL); } } } /** * Returns whether the usage argument was provided or not. This method should be called after a call to * parseArguments. * * @return <CODE>true</CODE> if the usage argument was provided and <CODE>false</CODE> otherwise. */ @Override public boolean isUsageArgumentPresent() { return usageArgument != null && usageArgument.isPresent(); } /** * Returns whether the version argument was provided or not. This method should be called after a call to * parseArguments. * * @return <CODE>true</CODE> if the version argument was provided and <CODE>false</CODE> otherwise. */ @Override public boolean isVersionArgumentPresent() { return super.isVersionArgumentPresent() && !versionPresent; } /** * Generate reference documentation for dsconfig subcommands in DocBook 5 XML format. As the number of categories is * large, the subcommand entries are sorted here by name for inclusion in a <refsect1> covering all dsconfig * Subcommands as part of the <refentry> for dsconfig (in man-dsconfig.xml). * <p> * Although it would be possible to categorize subcommands in the same way as they are categorized in dsconfig * interactive mode, this generator does not use the categories. * <p> * It would also be possible to generate the sort of information provided by the configuration reference, such that * this reference would not stop at simply listing an option like --set {PROP:VAL}, but instead would also provide * the list of PROPs and their possible VALs. A future improvement could no doubt merge the configuration reference * with this content, though perhaps the problem calls for hypertext rather than the linear structure of a * <refentry>. * <p> * Each individual subcommand results in a <refsect2> element similar to the following. * * <pre> * <refsect2 xml:id="dsconfig-create-local-db-index"> * <title>dsconfig create-local-db-index</title> * <para>Creates Local DB Indexes</para> * <variablelist> * <varlistentry> * <term><option>--backend-name {name} * </option></term> * <listitem> * <para>The name of the Local DB Backend</para> * </listitem> * </varlistentry> * <varlistentry> * <term><option>--index-name {OID}</option></term> * <listitem> * <para>The name of the new Local DB Index which will also be used * as the value of the "attribute" property: Specifies the name * of the attribute for which the index is to be maintained.</para> * </listitem> * </varlistentry> * <varlistentry> * <term><option>--set {PROP:VALUE}</option></term> * <listitem> * <para>Assigns a value to a property where PROP is the name of the * property and VALUE is the single value to be assigned. Specify the same * property multiple times in order to assign more than one value to * it</para> * </listitem> * </varlistentry> * </variablelist> * </refsect2> * </pre> * * @param sc * The SubCommand containing reference information. * @return Refsect2 representation of the subcommand. */ private String toRefSect2(SubCommand sc) { final StringBuilder options = new StringBuilder(); if (!sc.getArguments().isEmpty()) { options.append(" <variablelist>").append(EOL); for (Argument a : sc.getArguments()) { options.append(" <varlistentry>").append(EOL); options.append(" <term><option>"); Character shortID = a.getShortIdentifier(); if (shortID != null) { options.append("-").append(shortID.charValue()); } String longID = a.getLongIdentifier(); if (shortID != null && longID != null) { options.append(" | "); } if (longID != null) { options.append("--").append(longID); } if (a.needsValue()) { options.append(" ").append(a.getValuePlaceholder()); } options.append("</option></term>").append(EOL); options.append(" <listitem>").append(EOL); options.append(" <para>"); options.append(a.getDescription()); options.append("</para>").append(EOL); options.append(" </listitem>").append(EOL); options.append(" </varlistentry>").append(EOL); } options.append(" </variablelist>").append(EOL); } return "<refsect2 xml:id=\"dsconfig-" + sc.getName() + "\">" + EOL + " <title>dsconfig " + sc.getName() + "</title>" + EOL + " <para>" + sc.getDescription() + "</para>" + EOL + options + "</refsect2>" + EOL; } void printVersion() { // TODO // DirectoryServer.printVersion(usageOutputStream); } } opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java
@@ -40,6 +40,9 @@ * This class provides utility functions for all the client side tools. */ final public class Utils { /** Platform appropriate line separator. */ static public final String LINE_SEPARATOR = System.getProperty("line.separator"); /** * The name of a command-line script used to launch a tool. */ opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties
@@ -137,6 +137,17 @@ INFO_ARGPARSER_USAGE_JAVA_SCRIPTNAME=Usage: %s {options} INFO_ARGPARSER_USAGE_TRAILINGARGS={trailing-arguments} INFO_ARGPARSER_USAGE_DEFAULT_VALUE=Default value: %s INFO_SUBCMDPARSER_OPTIONS={options} INFO_GLOBAL_OPTIONS=Global Options: INFO_GLOBAL_OPTIONS_REFERENCE=See "%s --help" INFO_GLOBAL_HELP_REFERENCE=See "%s --help" to get more usage help INFO_SUBCMD_OPTIONS=SubCommand Options: INFO_ARGPARSER_USAGE=Usage: INFO_SUBCMDPARSER_SUBCMD_AND_OPTIONS={subcommand} {options} INFO_SUBCMDPARSER_SUBCMD_HELP_HEADING=To get the list of subcommands use: INFO_SUBCMDPARSER_SUBCMD_HEADING=Available subcommands: ERR_ARG_SUBCOMMAND_INVALID=Invalid subcommand INFO_SUBCMDPARSER_GLOBAL_HEADING=The global options are: # # Extension messages # @@ -146,7 +157,7 @@ ERR_CANNOT_INITIALIZE_ARGS=An unexpected error occurred while \ attempting to initialize the command-line arguments: %s ERR_ERROR_PARSING_ARGS=An error occurred while parsing the \ command-line arguments: %s command-line arguments: %s INFO_PROCESSING_OPERATION=Processing %s request for %s INFO_OPERATION_FAILED=%s operation failed INFO_OPERATION_SUCCESSFUL=%s operation successful for DN %s @@ -501,3 +512,188 @@ processing : %s ERR_CONSTANT_ARG_CANNOT_DECODE=Unable to parse a constant argument, \ expecting name=value but got %s INFO_DESCRIPTION_QUIET=Use quiet mode INFO_DESCRIPTION_NO_PROMPT=Use non-interactive mode. If data in \ the command is missing, the user is not prompted and the tool will fail INFO_OPTION_ACCEPT_LICENSE=Automatically accepts the product license \ (if present) # # Setup messages # INFO_SETUP_TITLE=OPENDJ3 Setup tool INFO_SETUP_DESCRIPTION=This utility can be used to setup the Directory Server INFO_ARGUMENT_DESCRIPTION_TESTONLY=Just verify that the JVM can be \ started properly INFO_ARGUMENT_DESCRIPTION_CLI=Use the command line install. \ If not specified the graphical interface will be launched. The rest of the \ options (excluding help and version) will only be taken into account if this \ option is specified INFO_ARGUMENT_DESCRIPTION_BASEDN=Base DN for user \ information in the Directory Server. Multiple base DNs may be provided by \ using this option multiple times INFO_ARGUMENT_DESCRIPTION_ADDBASE=Indicates whether to create the base \ entry in the Directory Server database INFO_ARGUMENT_DESCRIPTION_IMPORTLDIF=Path to an LDIF file \ containing data that should be added to the Directory Server database. \ Multiple LDIF files may be provided by using this option multiple times INFO_LDIFFILE_PLACEHOLDER={ldifFile} INFO_REJECT_FILE_PLACEHOLDER={rejectFile} INFO_SKIP_FILE_PLACEHOLDER={skipFile} INFO_JMXPORT_PLACEHOLDER={jmxPort} INFO_ROOT_USER_DN_PLACEHOLDER={rootUserDN} INFO_ROOT_USER_PWD_PLACEHOLDER={rootUserPassword} INFO_ROOT_USER_PWD_FILE_PLACEHOLDER={rootUserPasswordFile} INFO_HOST_PLACEHOLDER={host} INFO_GENERAL_DESCRIPTION_REJECTED_FILE=Write rejected entries to the \ specified file INFO_GENERAL_DESCRIPTION_SKIPPED_FILE=Write skipped entries to the \ specified file INFO_SETUP_DESCRIPTION_SAMPLE_DATA=Specifies that the database should \ be populated with the specified number of sample entries INFO_ARGUMENT_DESCRIPTION_LDAPPORT=Port on which the \ Directory Server should listen for LDAP communication INFO_ARGUMENT_DESCRIPTION_ADMINCONNECTORPORT=Port on which the \ Administration Connector should listen for communication INFO_ARGUMENT_DESCRIPTION_JMXPORT=Port on which the \ Directory Server should listen for JMX communication INFO_ARGUMENT_DESCRIPTION_SKIPPORT=Skip the check to determine whether \ the specified ports are usable INFO_ARGUMENT_DESCRIPTION_ROOTDN=DN for the initial root \ user for the Directory Server INFO_ARGUMENT_DESCRIPTION_ROOTPW=Password for the initial \ root user for the Directory Server INFO_ARGUMENT_DESCRIPTION_ROOTPWFILE=Path to a file \ containing the password for the initial root user for the Directory Server INFO_ARGUMENT_DESCRIPTION_ENABLE_WINDOWS_SERVICE=Enable the server to run \ as a Windows Service INFO_ARGUMENT_DESCRIPTION_LDAPSPORT=Port on which the \ Directory Server should listen for LDAPS communication. The LDAPS port will \ be configured and SSL will be enabled only if this argument is explicitly \ specified INFO_ARGUMENT_DESCRIPTION_HOST_NAME=The fully-qualified directory server \ host name that will be used when generating self-signed \ certificates for LDAP SSL/StartTLS, the administration connector, and \ replication INFO_ARGUMENT_DESCRIPTION_USE_SELF_SIGNED_CERTIFICATE=Generate a \ self-signed certificate that the server should use when accepting SSL-based \ connections or performing StartTLS negotiation INFO_ARGUMENT_DESCRIPTION_USE_PKCS11=Use a certificate in a \ PKCS#11 token that the server should use when accepting SSL-based \ connections or performing StartTLS negotiation INFO_ARGUMENT_DESCRIPTION_USE_JAVAKEYSTORE=Path of a Java \ Key Store (JKS) containing a certificate to be used as the server certificate INFO_ARGUMENT_DESCRIPTION_USE_JCEKS=Path of a JCEKS containing a \ certificate to be used as the server certificate INFO_ARGUMENT_DESCRIPTION_USE_PKCS12=Path of a PKCS#12 key \ store containing the certificate that the server should use when accepting \ SSL-based connections or performing StartTLS negotiation INFO_ARGUMENT_CERT_OPTION_JCEKS=Use an existing certificate located on a \ JCEKS key store INFO_ARGUMENT_DESCRIPTION_CERT_NICKNAME=Nickname of the \ certificate that the server should use when accepting SSL-based \ connections or performing StartTLS negotiation INFO_ARGUMENT_DESCRIPTION_KEYSTOREPASSWORD=Certificate key store PIN. \ A PIN is required when you specify to use an existing certificate (JKS, \ JCEKS, PKCS#12 or PKCS#11) as server certificate INFO_ARGUMENT_DESCRIPTION_KEYSTOREPASSWORD_FILE=Certificate key store \ PIN file. A PIN is required when you specify to use an existing certificate \ (JKS, JCEKS, PKCS#12 or PKCS#11) as server certificate INFO_SETUP_DESCRIPTION_DO_NOT_START=Do not start the server when the \ configuration is completed INFO_SETUP_DESCRIPTION_ENABLE_STARTTLS=Enable StartTLS to allow \ secure communication with the server using the LDAP port # # SubCommandes messages # ERR_ARG_SUBCOMMAND_DUPLICATE_SUBCOMMAND=The argument parser already \ has a %s subcommand ERR_ARG_SUBCOMMAND_DUPLICATE_ARGUMENT_NAME=There are multiple \ arguments for subcommand %s with name %s ERR_ARG_SUBCOMMAND_ARGUMENT_GLOBAL_CONFLICT=Argument %s for \ subcommand %s conflicts with a global argument with the same name ERR_ARG_SUBCOMMAND_DUPLICATE_SHORT_ID=Argument %s for subcommand %s \ has a short identifier -%s that conflicts with that of argument %s ERR_ARG_SUBCOMMAND_ARGUMENT_SHORT_ID_GLOBAL_CONFLICT=Argument %s \ for subcommand %s has a short ID -%s that conflicts with that of global \ argument %s ERR_ARG_SUBCOMMAND_DUPLICATE_LONG_ID=Argument %s for subcommand %s \ has a long identifier --%s that conflicts with that of argument %s ERR_ARG_SUBCOMMAND_ARGUMENT_LONG_ID_GLOBAL_CONFLICT=Argument %s for \ subcommand %s has a long ID --%s that conflicts with that of global argument \ %s ERR_SUBCMDPARSER_DUPLICATE_GLOBAL_ARG_NAME=There is already another \ global argument named "%s" ERR_SUBCMDPARSER_GLOBAL_ARG_NAME_SUBCMD_CONFLICT=The argument name \ %s conflicts with the name of another argument associated with the %s \ subcommand ERR_SUBCMDPARSER_DUPLICATE_GLOBAL_ARG_SHORT_ID=Short ID -%s for \ global argument %s conflicts with the short ID of another global argument %s ERR_SUBCMDPARSER_GLOBAL_ARG_SHORT_ID_CONFLICT=Short ID -%s for \ global argument %s conflicts with the short ID for the %s argument associated \ with subcommand %s ERR_SUBCMDPARSER_DUPLICATE_GLOBAL_ARG_LONG_ID=Long ID --%s for \ global argument %s conflicts with the long ID of another global argument %s ERR_SUBCMDPARSER_GLOBAL_ARG_LONG_ID_CONFLICT=Long ID --%s for \ global argument %s conflicts with the long ID for the %s argument associated \ with subcommand %s ERR_SUBCMDPARSER_CANNOT_READ_PROPERTIES_FILE=An error occurred \ while attempting to read the contents of the argument properties file %s: %s ERR_SUBCMDPARSER_LONG_ARG_WITHOUT_NAME=The provided command-line \ argument %s does not contain an argument name ERR_SUBCMDPARSER_NO_GLOBAL_ARGUMENT_FOR_LONG_ID=The provided \ argument --%s is not a valid global argument identifier ERR_SUBCMDPARSER_NO_ARGUMENT_FOR_LONG_ID=The provided argument --%s \ is not a valid global or subcommand argument identifier ERR_SUBCMDPARSER_NO_VALUE_FOR_ARGUMENT_WITH_LONG_ID=Command-line \ argument --%s requires a value but none was given ERR_SUBCMDPARSER_VALUE_UNACCEPTABLE_FOR_LONG_ID=The provided value \ "%s" for argument --%s is not acceptable: %s ERR_SUBCMDPARSER_NOT_MULTIVALUED_FOR_LONG_ID=The argument --%s was \ included multiple times in the provided set of arguments but it does not \ allow multiple values ERR_SUBCMDPARSER_ARG_FOR_LONG_ID_DOESNT_TAKE_VALUE=A value was \ provided for argument --%s but that argument does not take a value ERR_SUBCMDPARSER_INVALID_DASH_AS_ARGUMENT=The dash character by \ itself is invalid for use as an argument name ERR_SUBCMDPARSER_NO_GLOBAL_ARGUMENT_FOR_SHORT_ID=The provided \ argument -%s is not a valid global argument identifier ERR_SUBCMDPARSER_NO_ARGUMENT_FOR_SHORT_ID=The provided argument \ -%s is not a valid global or subcommand argument identifier ERR_SUBCMDPARSER_NO_VALUE_FOR_ARGUMENT_WITH_SHORT_ID=Argument -%s \ requires a value but none was provided ERR_SUBCMDPARSER_VALUE_UNACCEPTABLE_FOR_SHORT_ID=The provided \ value "%s" for argument -%s is not acceptable: %s ERR_SUBCMDPARSER_NOT_MULTIVALUED_FOR_SHORT_ID=The argument -%s was \ included multiple times in the provided set of arguments but it does not \ allow multiple values ERR_SUBCMDPARSER_CANT_MIX_ARGS_WITH_VALUES=The provided argument \ block '-%s%s' is illegal because the '%s' argument requires a value but is in \ the same block as at least one other argument that doesn't require a value ERR_SUBCMDPARSER_INVALID_ARGUMENT=The provided argument "%s" is \ not recognized ERR_SUBCMDPARSER_NO_VALUE_FOR_REQUIRED_ARG=The argument %s is \ required to have a value but none was provided in the argument list and no \ default value is available ERR_ARGUMENT_NO_BASE_DN_SPECIFIED=You have specified \ not to create a base DN. If no base DN is to be created you cannot specify \ argument '%s' ERR_PORT_ALREADY_SPECIFIED=ERROR: You have specified \ the value %s for different ports ERR_SEVERAL_CERTIFICATE_TYPE_SPECIFIED=You have \ specified several certificate types to be used. Only one certificate type \ (self-signed, JKS, JCEKS, PKCS#12 or PCKS#11) is allowed ERR_CERTIFICATE_REQUIRED_FOR_SSL_OR_STARTTLS=You have \ chosen to enable SSL or StartTLS. You must specify which type of certificate \ you want the server to use ERR_TWO_CONFLICTING_ARGUMENTS=ERROR: You may not \ provide both the %s and the %s arguments at the same time ERR_NO_KEYSTORE_PASSWORD=You must provide the PIN of the \ keystore to retrieve the certificate to be used by the server. You can use \ {%s} or {%s} ERR_SSL_OR_STARTTLS_REQUIRED=You have specified to use a \ certificate as server certificate. You must enable SSL (using option {%s}) \ or Start TLS (using option %s) ERR_NO_ROOT_PASSWORD=ERROR: No password was provided \ for the initial root user. When performing a non-interactive installation, \ this must be provided using either the %s or the %s argument