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

Violette Roche-Montane
29.25.2014 4f50cdb97c5bc421bd1dccada157d45c3bdb5fc1
OPENDJ-1303 Split out CLI support from opendj-ldap-toolkit into a separate Maven module, "opendj-cli"
Part I. Added required classes.

15 files added
1 files renamed
2 files modified
6098 ■■■■■ changed files
opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java 651 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentException.java 73 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentGroup.java 170 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java 1596 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/BooleanArgument.java 108 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/CLIException.java 2 ●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/CliConstants.java 625 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/CommonArguments.java 120 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/ConsoleApplication.java 328 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/FileBasedArgument.java 242 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/IntegerArgument.java 483 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiChoiceArgument.java 239 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiColumnPrinter.java 483 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/StringArgument.java 136 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java 336 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/package-info.java 29 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/org/forgerock/opendj/cli/package-info.java 1 ●●●● patch | view | raw | blame | history
opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties 476 ●●●●● patch | view | raw | blame | history
opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java
New file
@@ -0,0 +1,651 @@
/*
 * 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.cli.CliMessages.*;
import static com.forgerock.opendj.util.StaticUtils.toLowerCase;
import java.util.Iterator;
import java.util.LinkedList;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
/**
 * This class defines a generic argument that may be used in the argument list
 * for an application. This is an abstract class that must be subclassed in
 * order to provide specific functionality.
 */
abstract class Argument {
    // Indicates whether this argument should be hidden in the usage
    // information.
    private boolean isHidden;
    // Indicates whether this argument may be specified more than once for
    // multiple values.
    private boolean isMultiValued;
    // Indicates whether this argument was provided in the set of
    // command-line
    // arguments.
    private boolean isPresent;
    // Indicates whether this argument is required to have a value.
    private boolean isRequired;
    // Indicates whether this argument requires a value.
    private boolean needsValue;
    // The single-character identifier for this argument.
    private final Character shortIdentifier;
    // The unique ID of the description for this argument.
    private final LocalizableMessage description;
    // The set of values for this argument.
    private final LinkedList<String> values;
    // The default value for the argument if none other is provided.
    private String defaultValue;
    // The long identifier for this argument.
    private final String longIdentifier;
    // The generic name that will be used to refer to this argument.
    private final String name;
    // The name of the property that can be used to set the default value.
    private String propertyName;
    // The value placeholder for this argument, which will be used in
    // usage
    // information.
    private LocalizableMessage valuePlaceholder;
    // Indicates whether this argument was provided in the set of
    // properties
    // found is a properties file.
    private boolean isValueSetByProperty;
    /**
     * Creates a new 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.
     */
    protected Argument(final String name, final Character shortIdentifier,
            final String longIdentifier, final boolean isRequired, final boolean isMultiValued,
            final boolean needsValue, final LocalizableMessage valuePlaceholder,
            final String defaultValue, final String propertyName,
            final LocalizableMessage description) throws ArgumentException {
        this.name = name;
        this.shortIdentifier = shortIdentifier;
        this.longIdentifier = longIdentifier;
        this.isRequired = isRequired;
        this.isMultiValued = isMultiValued;
        this.needsValue = needsValue;
        this.valuePlaceholder = valuePlaceholder;
        this.defaultValue = defaultValue;
        this.propertyName = propertyName;
        this.description = description;
        this.isValueSetByProperty = false;
        if ((shortIdentifier == null) && (longIdentifier == null)) {
            final LocalizableMessage message = ERR_ARG_NO_IDENTIFIER.get(name);
            throw new ArgumentException(message);
        }
        if (needsValue && (valuePlaceholder == null)) {
            final LocalizableMessage message = ERR_ARG_NO_VALUE_PLACEHOLDER.get(name);
            throw new ArgumentException(message);
        }
        values = new LinkedList<String>();
        isPresent = false;
        isHidden = false;
    }
    /**
     * Adds a value to the set of values for this argument. This should only be
     * called if the value is allowed by the <CODE>valueIsAcceptable</CODE>
     * method.
     *
     * @param valueString
     *            The string representation of the value to add to this
     *            argument.
     */
    public void addValue(final String valueString) {
        values.add(valueString);
    }
    /**
     * Clears the set of values assigned to this argument.
     */
    public void clearValues() {
        values.clear();
    }
    /**
     * Retrieves the value of this argument as a <CODE>Boolean</CODE>.
     *
     * @return The value of this argument as a <CODE>Boolean</CODE>.
     * @throws ArgumentException
     *             If this argument cannot be interpreted as a Boolean value.
     */
    public boolean getBooleanValue() throws ArgumentException {
        if (values.isEmpty()) {
            final LocalizableMessage message = ERR_ARG_NO_BOOLEAN_VALUE.get(name);
            throw new ArgumentException(message);
        }
        final Iterator<String> iterator = values.iterator();
        final String valueString = toLowerCase(iterator.next());
        boolean booleanValue;
        if (valueString.equals("true") || valueString.equals("yes") || valueString.equals("on")
                || valueString.equals("1")) {
            booleanValue = true;
        } else if (valueString.equals("false") || valueString.equals("no")
                || valueString.equals("off") || valueString.equals("0")) {
            booleanValue = false;
        } else {
            final LocalizableMessage message =
                    ERR_ARG_CANNOT_DECODE_AS_BOOLEAN.get(valueString, name);
            throw new ArgumentException(message);
        }
        if (iterator.hasNext()) {
            final LocalizableMessage message = ERR_ARG_BOOLEAN_MULTIPLE_VALUES.get(name);
            throw new ArgumentException(message);
        } else {
            return booleanValue;
        }
    }
    /**
     * Retrieves the default value that will be used for this argument if it is
     * not specified on the command line and it is not set from a properties
     * file.
     *
     * @return The default value that will be used for this argument if it is
     *         not specified on the command line and it is not set from a
     *         properties file, or <CODE>null</CODE> if there is no default
     *         value.
     */
    public String getDefaultValue() {
        return defaultValue;
    }
    /**
     * Retrieves the human-readable description for this argument.
     *
     * @return The human-readable description for this argument.
     */
    public LocalizableMessage getDescription() {
        return description != null ? description : LocalizableMessage.EMPTY;
    }
    /**
     * Retrieves the value of this argument as an integer.
     *
     * @return The value of this argument as an integer.
     * @throws ArgumentException
     *             If there are multiple values, or the value cannot be parsed
     *             as an integer.
     */
    public double getDoubleValue() throws ArgumentException {
        if (values.isEmpty()) {
            final LocalizableMessage message = ERR_ARG_NO_INT_VALUE.get(name);
            throw new ArgumentException(message);
        }
        final Iterator<String> iterator = values.iterator();
        final String valueString = iterator.next();
        double intValue;
        try {
            intValue = Double.parseDouble(valueString);
        } catch (final Exception e) {
            final LocalizableMessage message = ERR_ARG_CANNOT_DECODE_AS_INT.get(valueString, name);
            throw new ArgumentException(message, e);
        }
        if (iterator.hasNext()) {
            final LocalizableMessage message = ERR_ARG_INT_MULTIPLE_VALUES.get(name);
            throw new ArgumentException(message);
        } else {
            return intValue;
        }
    }
    /**
     * Retrieves the set of values for this argument as a list of integers.
     *
     * @return A list of the integer representations of the values for this
     *         argument.
     * @throws ArgumentException
     *             If any of the values cannot be parsed as an integer.
     */
    public LinkedList<Double> getDoubleValues() throws ArgumentException {
        final LinkedList<Double> intList = new LinkedList<Double>();
        final Iterator<String> iterator = values.iterator();
        while (iterator.hasNext()) {
            final String valueString = iterator.next();
            try {
                intList.add(Double.valueOf(valueString));
            } catch (final Exception e) {
                final LocalizableMessage message =
                        ERR_ARG_CANNOT_DECODE_AS_INT.get(valueString, name);
                throw new ArgumentException(message, e);
            }
        }
        return intList;
    }
    /**
     * Retrieves the value of this argument as an integer.
     *
     * @return The value of this argument as an integer.
     * @throws ArgumentException
     *             If there are multiple values, or the value cannot be parsed
     *             as an integer.
     */
    public int getIntValue() throws ArgumentException {
        if (values.isEmpty()) {
            final LocalizableMessage message = ERR_ARG_NO_INT_VALUE.get(name);
            throw new ArgumentException(message);
        }
        final Iterator<String> iterator = values.iterator();
        final String valueString = iterator.next();
        int intValue;
        try {
            intValue = Integer.parseInt(valueString);
        } catch (final Exception e) {
            final LocalizableMessage message = ERR_ARG_CANNOT_DECODE_AS_INT.get(valueString, name);
            throw new ArgumentException(message, e);
        }
        if (iterator.hasNext()) {
            final LocalizableMessage message = ERR_ARG_INT_MULTIPLE_VALUES.get(name);
            throw new ArgumentException(message);
        } else {
            return intValue;
        }
    }
    /**
     * Retrieves the set of values for this argument as a list of integers.
     *
     * @return A list of the integer representations of the values for this
     *         argument.
     * @throws ArgumentException
     *             If any of the values cannot be parsed as an integer.
     */
    public LinkedList<Integer> getIntValues() throws ArgumentException {
        final LinkedList<Integer> intList = new LinkedList<Integer>();
        final Iterator<String> iterator = values.iterator();
        while (iterator.hasNext()) {
            final String valueString = iterator.next();
            try {
                intList.add(Integer.valueOf(valueString));
            } catch (final Exception e) {
                final LocalizableMessage message =
                        ERR_ARG_CANNOT_DECODE_AS_INT.get(valueString, name);
                throw new ArgumentException(message, e);
            }
        }
        return intList;
    }
    /**
     * Retrieves the long (multi-character) identifier that may be used to
     * specify the value of this argument.
     *
     * @return The long (multi-character) identifier that may be used to specify
     *         the value of this argument.
     */
    public String getLongIdentifier() {
        return longIdentifier;
    }
    /**
     * Retrieves the generic name that will be used to refer to this argument.
     *
     * @return The generic name that will be used to refer to this argument.
     */
    public String getName() {
        return name;
    }
    /**
     * Retrieves the name of a property in a properties file that may be used to
     * set the default value for this argument if it is present. A value read
     * from a properties file will override the default value returned from the
     * <CODE>getDefaultValue</CODE>, but the properties file value will be
     * overridden by a value supplied on the command line.
     *
     * @return The name of a property in a properties file that may be used to
     *         set the default value for this argument if it is present.
     */
    public String getPropertyName() {
        return propertyName;
    }
    /**
     * Retrieves the single-character identifier that may be used to specify the
     * value of this argument.
     *
     * @return The single-character identifier that may be used to specify the
     *         value of this argument, or <CODE>null</CODE> if there is none.
     */
    public Character getShortIdentifier() {
        return shortIdentifier;
    }
    /**
     * Retrieves the string vale for this argument. If it has multiple values,
     * then the first will be returned. If it does not have any values, then the
     * default value will be returned.
     *
     * @return The string value for this argument, or <CODE>null</CODE> if there
     *         are no values and no default value has been given.
     */
    public String getValue() {
        if (values.isEmpty()) {
            return defaultValue;
        }
        return values.getFirst();
    }
    /**
     * Retrieves the value placeholder that will be displayed for this argument
     * in the generated usage information.
     *
     * @return The value placeholder that will be displayed for this argument in
     *         the generated usage information, or <CODE>null</CODE> if there is
     *         none.
     */
    public LocalizableMessage getValuePlaceholder() {
        return valuePlaceholder;
    }
    /**
     * Retrieves the set of string values for this argument.
     *
     * @return The set of string values for this argument.
     */
    public LinkedList<String> getValues() {
        return values;
    }
    /**
     * Indicates whether this argument has at least one value.
     *
     * @return <CODE>true</CODE> if this argument has at least one value, or
     *         <CODE>false</CODE> if it does not have any values.
     */
    public boolean hasValue() {
        return (!values.isEmpty());
    }
    /**
     * Indicates whether this argument should be hidden from the usage
     * information.
     *
     * @return <CODE>true</CODE> if this argument should be hidden from the
     *         usage information, or <CODE>false</CODE> if not.
     */
    public boolean isHidden() {
        return isHidden;
    }
    /**
     * Indicates whether this argument may be provided more than once on the
     * command line to specify multiple values.
     *
     * @return <CODE>true</CODE> if this argument may be provided more than once
     *         on the command line to specify multiple values, or
     *         <CODE>false</CODE> if it may have at most one value.
     */
    public boolean isMultiValued() {
        return isMultiValued;
    }
    /**
     * Indicates whether this argument is present in the parsed set of
     * command-line arguments.
     *
     * @return <CODE>true</CODE> if this argument is present in the parsed set
     *         of command-line arguments, or <CODE>false</CODE> if not.
     */
    public boolean isPresent() {
        return isPresent;
    }
    /**
     * Indicates whether this argument is required to have at least one value.
     *
     * @return <CODE>true</CODE> if this argument is required to have at least
     *         one value, or <CODE>false</CODE> if it does not need to have a
     *         value.
     */
    public boolean isRequired() {
        return isRequired;
    }
    /**
     * Indicates whether this argument was provided in the set of properties
     * found is a properties file.
     *
     * @return <CODE>true</CODE> if this argument was provided in the set of
     *         properties found is a properties file, or <CODE>false</CODE> if
     *         not.
     */
    public boolean isValueSetByProperty() {
        return isValueSetByProperty;
    }
    /**
     * Indicates whether a value must be provided with this argument if it is
     * present.
     *
     * @return <CODE>true</CODE> if a value must be provided with the argument
     *         if it is present, or <CODE>false</CODE> if the argument does not
     *         take a value and the presence of the argument identifier itself
     *         is sufficient to convey the necessary information.
     */
    public boolean needsValue() {
        return needsValue;
    }
    /**
     * Specifies the default value that will be used for this argument if it is
     * not specified on the command line and it is not set from a properties
     * file.
     *
     * @param defaultValue
     *            The default value that will be used for this argument if it is
     *            not specified on the command line and it is not set from a
     *            properties file.
     */
    public void setDefaultValue(final String defaultValue) {
        this.defaultValue = defaultValue;
    }
    /**
     * Specifies whether this argument should be hidden from the usage
     * information.
     *
     * @param isHidden
     *            Indicates whether this argument should be hidden from the
     *            usage information.
     */
    public void setHidden(final boolean isHidden) {
        this.isHidden = isHidden;
    }
    /**
     * Specifies whether this argument may be provided more than once on the
     * command line to specify multiple values.
     *
     * @param isMultiValued
     *            Indicates whether this argument may be provided more than once
     *            on the command line to specify multiple values.
     */
    public void setMultiValued(final boolean isMultiValued) {
        this.isMultiValued = isMultiValued;
    }
    /**
     * Specifies whether a value must be provided with this argument if it is
     * present. If this is changed from <CODE>false</CODE> to <CODE>true</CODE>,
     * then a value placeholder must also be provided.
     *
     * @param needsValue
     *            Indicates whether a value must be provided with this argument
     *            if it is present.
     */
    public void setNeedsValue(final boolean needsValue) {
        this.needsValue = needsValue;
    }
    /**
     * Specifies whether this argument is present in the parsed set of
     * command-line arguments.
     *
     * @param isPresent
     *            Indicates whether this argument is present in the set of
     *            command-line arguments.
     */
    public void setPresent(final boolean isPresent) {
        this.isPresent = isPresent;
    }
    /**
     * Specifies the name of a property in a properties file that may be used to
     * set the default value for this argument if it is present.
     *
     * @param propertyName
     *            The name of a property in a properties file that may be used
     *            to set the default value for this argument if it is present.
     */
    public void setPropertyName(final String propertyName) {
        this.propertyName = propertyName;
    }
    /**
     * Specifies whether this argument is required to have at least one value.
     *
     * @param isRequired
     *            Indicates whether this argument is required to have at least
     *            one value.
     */
    public void setRequired(final boolean isRequired) {
        this.isRequired = isRequired;
    }
    /**
     * Specifies the value placeholder that will be displayed for this argument
     * in the generated usage information. It may be <CODE>null</CODE> only if
     * <CODE>needsValue()</CODE> returns <CODE>false</CODE>.
     *
     * @param valuePlaceholder
     *            The value placeholder that will be displayed for this argument
     *            in the generated usage information.
     */
    public void setValuePlaceholder(final LocalizableMessage valuePlaceholder) {
        this.valuePlaceholder = valuePlaceholder;
    }
    /**
     * Specifies whether this argument was provided in the set of properties
     * found is a properties file.
     *
     * @param isValueSetByProperty
     *            Specify whether this argument was provided in the set of
     *            properties found is a properties file.
     */
    public void setValueSetByProperty(final boolean isValueSetByProperty) {
        this.isValueSetByProperty = isValueSetByProperty;
    }
    /**
     * Indicates whether the provided value is acceptable for use in this
     * argument.
     *
     * @param valueString
     *            The value for which to make the determination.
     * @param invalidReason
     *            A buffer into which the invalid reason may be written if the
     *            value is not acceptable.
     * @return <CODE>true</CODE> if the value is acceptable, or
     *         <CODE>false</CODE> if it is not.
     */
    public abstract boolean valueIsAcceptable(String valueString,
            LocalizableMessageBuilder invalidReason);
}
opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentException.java
New file
@@ -0,0 +1,73 @@
/*
 * 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 org.forgerock.i18n.LocalizableException;
import org.forgerock.i18n.LocalizableMessage;
/**
 * This class defines an exception that may be thrown if there is a problem with
 * an argument definition.
 */
@SuppressWarnings("serial")
public final class ArgumentException extends Exception implements LocalizableException {
    // The I18N message associated with this exception.
    private final LocalizableMessage message;
    /**
     * Creates a new argument exception with the provided message.
     *
     * @param message
     *            The message that explains the problem that occurred.
     */
    public ArgumentException(final LocalizableMessage message) {
        super(String.valueOf(message));
        this.message = message;
    }
    /**
     * Creates a new argument exception with the provided message and root
     * cause.
     *
     * @param message
     *            The message that explains the problem that occurred.
     * @param cause
     *            The exception that was caught to trigger this exception.
     */
    public ArgumentException(final LocalizableMessage message, final Throwable cause) {
        super(String.valueOf(message), cause);
        this.message = message;
    }
    /**
     * {@inheritDoc}
     */
    public LocalizableMessage getMessageObject() {
        return this.message;
    }
}
opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentGroup.java
New file
@@ -0,0 +1,170 @@
/*
 * 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 2008 Sun Microsystems, Inc.
 *      Portions copyright 2012-2014 ForgeRock AS.
 */
package com.forgerock.opendj.cli;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.forgerock.i18n.LocalizableMessage;
/**
 * Class for organizing options into logical groups when argument usage is
 * printed. To use an argument group, create an instance and use
 * {@code ArgumentParser#addArgument(Argument, ArgumentGroup)}
 * when adding arguments for to the parser.
 */
final class ArgumentGroup implements Comparable<ArgumentGroup> {
    // Description for this group of arguments
    private LocalizableMessage description = null;
    // List of arguments belonging to this group
    private List<Argument> args = null;
    // Governs groups position within usage statement
    private final Integer priority;
    /**
     * Creates a parameterized instance.
     *
     * @param description
     *            for options in this group that is printed before argument
     *            descriptions in usage output
     * @param priority
     *            number governing the position of this group within the usage
     *            statement. Groups with higher priority values appear before
     *            groups with lower priority.
     */
    ArgumentGroup(final LocalizableMessage description, final int priority) {
        this.description = description;
        this.priority = priority;
        this.args = new LinkedList<Argument>();
    }
    /**
     * {@inheritDoc}
     */
    public int compareTo(final ArgumentGroup o) {
        // Groups with higher priority numbers appear before
        // those with lower priority in the usage output
        return -1 * priority.compareTo(o.priority);
    }
    /**
     * Adds an argument to this group.
     *
     * @param arg
     *            to add
     * @return boolean where true indicates the add was successful
     */
    boolean addArgument(final Argument arg) {
        boolean success = false;
        if (arg != null) {
            final Character newShort = arg.getShortIdentifier();
            final String newLong = arg.getLongIdentifier();
            // See if there is already an argument in this group that the
            // new argument should replace
            for (final Iterator<Argument> it = this.args.iterator(); it.hasNext();) {
                final Argument a = it.next();
                if (newShort != null && newShort.equals(a.getShortIdentifier()) || newLong != null
                        && newLong.equals(a.getLongIdentifier())) {
                    it.remove();
                    break;
                }
            }
            success = this.args.add(arg);
        }
        return success;
    }
    /**
     * Indicates whether this group contains any members.
     *
     * @return boolean where true means this group contains members
     */
    boolean containsArguments() {
        return this.args.size() > 0;
    }
    /**
     * Indicates whether this group contains any non-hidden members.
     *
     * @return boolean where true means this group contains non-hidden members
     */
    boolean containsNonHiddenArguments() {
        for (final Argument arg : args) {
            if (!arg.isHidden()) {
                return true;
            }
        }
        return false;
    }
    /**
     * Gets the list of arguments associated with this group.
     *
     * @return list of associated arguments
     */
    List<Argument> getArguments() {
        return Collections.unmodifiableList(args);
    }
    /**
     * Gets the description for this group of arguments.
     *
     * @return description for this argument group
     */
    LocalizableMessage getDescription() {
        return this.description;
    }
    /**
     * Removes an argument from this group.
     *
     * @param arg
     *            to remove
     * @return boolean where true indicates the remove was successful
     */
    boolean removeArgument(final Argument arg) {
        return this.args.remove(arg);
    }
    /**
     * Sets the description for this group of arguments.
     *
     * @param description
     *            for this argument group
     */
    void setDescription(final LocalizableMessage description) {
        this.description = description;
    }
}
opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java
New file
@@ -0,0 +1,1596 @@
/*
 * 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 2012-2014 ForgeRock AS.
 */
package com.forgerock.opendj.cli;
import static com.forgerock.opendj.cli.CliMessages.*;
import static com.forgerock.opendj.cli.CliConstants.*;
import static com.forgerock.opendj.cli.Utils.PROPERTY_SCRIPT_NAME;
import static com.forgerock.opendj.cli.Utils.wrapText;
import static com.forgerock.opendj.util.StaticUtils.EOL;
import static com.forgerock.opendj.util.StaticUtils.getBytes;
import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage;
import static com.forgerock.opendj.util.StaticUtils.toLowerCase;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
/**
 * This class defines a utility that can be used to deal with command-line
 * arguments for applications in a CLIP-compliant manner using either short
 * one-character or longer word-based arguments. It is also integrated with the
 * Directory Server message catalog so that it can display messages in an
 * internationalizable format, can automatically generate usage information,
 * can detect conflicts between arguments, and can interact with a properties
 * file to obtain default values for arguments there if they are not specified
 * on the command-line.
 */
public final class ArgumentParser {
    /**
     * The argument that will be used to indicate the file properties.
     */
    private StringArgument filePropertiesPathArgument;
    /**
     * The argument that will be used to indicate that we'll not look for
     * default properties file.
     */
    private BooleanArgument noPropertiesFileArgument;
    // The argument that will be used to trigger the display of usage
    // information.
    private Argument usageArgument;
    // The argument that will be used to trigger the display of the OpenDJ
    // version.
    private Argument versionArgument;
    // The set of unnamed trailing arguments that were provided for this
    // parser.
    private final ArrayList<String> trailingArguments;
    // Indicates whether this parser will allow additional unnamed
    // arguments at the end of the list.
    private final boolean allowsTrailingArguments;
    // Indicates whether long arguments should be treated in a
    // case-sensitive manner.
    private final boolean longArgumentsCaseSensitive;
    // Indicates whether the usage or version information has been
    // displayed.
    private boolean usageOrVersionDisplayed;
    // Indicates whether the version argument was provided.
    private boolean versionPresent;
    // The set of arguments defined for this parser, referenced by short
    // ID.
    private final HashMap<Character, Argument> shortIDMap;
    // The set of arguments defined for this parser, referenced by
    // argument name.
    private final HashMap<String, Argument> argumentMap;
    // The set of arguments defined for this parser, referenced by long
    // ID.
    private final HashMap<String, Argument> longIDMap;
    // The maximum number of unnamed trailing arguments that may be
    // provided.
    private final int maxTrailingArguments;
    // The minimum number of unnamed trailing arguments that may be
    // provided.
    private final int minTrailingArguments;
    // The total set of arguments defined for this parser.
    private final LinkedList<Argument> argumentList;
    // 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 display name that will be used for the trailing arguments in
    // the usage information.
    private final String trailingArgsDisplayName;
    // The raw set of command-line arguments that were provided.
    private String[] rawArguments;
    /** Set of argument groups. */
    private Set<ArgumentGroup> argumentGroups;
    /**
     * Group for arguments that have not been explicitly grouped. These will
     * appear at the top of the usage statement without a header.
     */
    private final ArgumentGroup defaultArgGroup = new ArgumentGroup(LocalizableMessage.EMPTY,
            Integer.MAX_VALUE);
    /**
     * Group for arguments that are related to connection through LDAP. This
     * includes options like the bind DN, the port, etc.
     */
    private final ArgumentGroup ldapArgGroup = new ArgumentGroup(
            INFO_DESCRIPTION_LDAP_CONNECTION_ARGS.get(), Integer.MIN_VALUE + 2);
    /**
     * Group for arguments that are related to utility input/output like
     * properties file, no-prompt etc. These will appear toward the bottom of
     * the usage statement.
     */
    private final ArgumentGroup ioArgGroup = new ArgumentGroup(INFO_DESCRIPTION_IO_ARGS.get(),
            Integer.MIN_VALUE + 1);
    /**
     * Group for arguments that are general like help, version etc. These will
     * appear at the end of the usage statement.
     */
    private final ArgumentGroup generalArgGroup = new ArgumentGroup(INFO_DESCRIPTION_GENERAL_ARGS
            .get(), Integer.MIN_VALUE);
    private final static String INDENT = "    ";
    private final static int MAX_LENGTH = 80;
    /**
     * Creates a new instance of this argument parser with no arguments. Unnamed
     * trailing arguments will not be allowed.
     *
     * @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 long arguments should be treated in a
     *            case-sensitive manner.
     */
    public ArgumentParser(final String mainClassName, final LocalizableMessage toolDescription,
            final boolean longArgumentsCaseSensitive) {
        this.mainClassName = mainClassName;
        this.toolDescription = toolDescription;
        this.longArgumentsCaseSensitive = longArgumentsCaseSensitive;
        argumentList = new LinkedList<Argument>();
        argumentMap = new HashMap<String, Argument>();
        shortIDMap = new HashMap<Character, Argument>();
        longIDMap = new HashMap<String, Argument>();
        allowsTrailingArguments = false;
        usageOrVersionDisplayed = false;
        versionPresent = false;
        trailingArgsDisplayName = null;
        maxTrailingArguments = 0;
        minTrailingArguments = 0;
        trailingArguments = new ArrayList<String>();
        rawArguments = null;
        usageArgument = null;
        filePropertiesPathArgument = null;
        noPropertiesFileArgument = null;
        usageOutputStream = System.out;
        initGroups();
    }
    /**
     * Creates a new instance of this argument parser with no arguments that may
     * or may not be allowed to have unnamed trailing 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 long arguments should be treated in a
     *            case-sensitive manner.
     * @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.
     */
    public ArgumentParser(final String mainClassName, final LocalizableMessage toolDescription,
            final boolean longArgumentsCaseSensitive, final boolean allowsTrailingArguments,
            final int minTrailingArguments, final int maxTrailingArguments,
            final String trailingArgsDisplayName) {
        this.mainClassName = mainClassName;
        this.toolDescription = toolDescription;
        this.longArgumentsCaseSensitive = longArgumentsCaseSensitive;
        this.allowsTrailingArguments = allowsTrailingArguments;
        this.minTrailingArguments = minTrailingArguments;
        this.maxTrailingArguments = maxTrailingArguments;
        this.trailingArgsDisplayName = trailingArgsDisplayName;
        argumentList = new LinkedList<Argument>();
        argumentMap = new HashMap<String, Argument>();
        shortIDMap = new HashMap<Character, Argument>();
        longIDMap = new HashMap<String, Argument>();
        trailingArguments = new ArrayList<String>();
        usageOrVersionDisplayed = false;
        versionPresent = false;
        rawArguments = null;
        usageArgument = null;
        usageOutputStream = System.out;
        initGroups();
    }
    /**
     * Adds the provided argument to the set of arguments handled by this
     * parser.
     *
     * @param argument
     *            The argument to be added.
     * @throws ArgumentException
     *             If the provided argument conflicts with another argument that
     *             has already been defined.
     */
    public void addArgument(final Argument argument) throws ArgumentException {
        addArgument(argument, null);
    }
    /**
     * Adds the provided argument to the set of 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 argument that
     *             has already been defined.
     */
    public void addArgument(final Argument argument, ArgumentGroup group) throws ArgumentException {
        final Character shortID = argument.getShortIdentifier();
        if ((shortID != null) && shortIDMap.containsKey(shortID)) {
            final String conflictingName = shortIDMap.get(shortID).getName();
            final LocalizableMessage message =
                    ERR_ARGPARSER_DUPLICATE_SHORT_ID.get(argument.getName(), String
                            .valueOf(shortID), conflictingName);
            throw new ArgumentException(message);
        }
        if (versionArgument != null) {
            if (shortID != null && shortID.equals(versionArgument.getShortIdentifier())) {
                // Update the version argument to not display its short
                // identifier.
                try {
                    versionArgument =
                            new BooleanArgument(OPTION_LONG_PRODUCT_VERSION, null,
                                    OPTION_LONG_PRODUCT_VERSION, INFO_DESCRIPTION_PRODUCT_VERSION
                                            .get());
                    this.generalArgGroup.addArgument(versionArgument);
                } catch (final ArgumentException e) {
                    // ignore
                }
            }
        }
        String longID = argument.getLongIdentifier();
        if (longID != null) {
            if (!longArgumentsCaseSensitive) {
                longID = toLowerCase(longID);
            }
            if (longIDMap.containsKey(longID)) {
                final String conflictingName = longIDMap.get(longID).getName();
                final LocalizableMessage message =
                        ERR_ARGPARSER_DUPLICATE_LONG_ID.get(argument.getName(), argument
                                .getLongIdentifier(), conflictingName);
                throw new ArgumentException(message);
            }
        }
        if (shortID != null) {
            shortIDMap.put(shortID, argument);
        }
        if (longID != null) {
            longIDMap.put(longID, argument);
        }
        argumentList.add(argument);
        if (group == null) {
            group = getStandardGroup(argument);
        }
        group.addArgument(argument);
        argumentGroups.add(group);
    }
    /**
     * Adds the provided argument to the set of arguments handled by this parser
     * and puts the arguement in the default group.
     *
     * @param argument
     *            The argument to be added.
     * @throws ArgumentException
     *             If the provided argument conflicts with another argument that
     *             has already been defined.
     */
    void addDefaultArgument(final Argument argument) throws ArgumentException {
        addArgument(argument, defaultArgGroup);
    }
    /**
     * Adds the provided argument to the set of arguments handled by this parser
     * and puts the arguement in the general group.
     *
     * @param argument
     *            The argument to be added.
     * @throws ArgumentException
     *             If the provided argument conflicts with another argument that
     *             has already been defined.
     */
    void addGeneralArgument(final Argument argument) throws ArgumentException {
        addArgument(argument, generalArgGroup);
    }
    /**
     * Adds the provided argument to the set of arguments handled by this parser
     * and puts the argument in the input/output group.
     *
     * @param argument
     *            The argument to be added.
     * @throws ArgumentException
     *             If the provided argument conflicts with another argument that
     *             has already been defined.
     */
    void addInputOutputArgument(final Argument argument) throws ArgumentException {
        addArgument(argument, ioArgGroup);
    }
    /**
     * Adds the provided argument to the set of arguments handled by this parser
     * and puts the argument in the LDAP connection group.
     *
     * @param argument
     *            The argument to be added.
     * @throws ArgumentException
     *             If the provided argument conflicts with another argument that
     *             has already been defined.
     */
    public void addLdapConnectionArgument(final Argument argument) throws ArgumentException {
        addArgument(argument, ldapArgGroup);
    }
    /**
     * Indicates whether this parser 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 parser allows unnamed trailing
     *         arguments, or <CODE>false</CODE> if it does not.
     */
    boolean allowsTrailingArguments() {
        return allowsTrailingArguments;
    }
    /**
     * Check if we have a properties file.
     *
     * @return The properties found in the properties file or null.
     * @throws ArgumentException
     *             If a problem was encountered while parsing the provided
     *             arguments.
     */
    Properties checkExternalProperties() throws ArgumentException {
        // We don't look for properties file.
        if ((noPropertiesFileArgument != null) && (noPropertiesFileArgument.isPresent())) {
            return null;
        }
        // Check if we have a properties file argument
        if (filePropertiesPathArgument == null) {
            return null;
        }
        // check if the properties file argument has been set. If not
        // look for default location.
        String propertiesFilePath = null;
        if (filePropertiesPathArgument.isPresent()) {
            propertiesFilePath = filePropertiesPathArgument.getValue();
        } else {
            // Check in "user home"/.opendj directory
            final String userDir = System.getProperty("user.home");
            propertiesFilePath =
                    findPropertiesFile(userDir + File.separator + DEFAULT_OPENDJ_CONFIG_DIR);
        }
        // We don't have a properties file location
        if (propertiesFilePath == null) {
            return null;
        }
        // We have a location for the properties file.
        final Properties argumentProperties = new Properties();
        final String scriptName = System.getProperty(PROPERTY_SCRIPT_NAME);
        try {
            final Properties p = new Properties();
            final FileInputStream fis = new FileInputStream(propertiesFilePath);
            p.load(fis);
            fis.close();
            for (final Enumeration<?> e = p.propertyNames(); e.hasMoreElements();) {
                final String currentPropertyName = (String) e.nextElement();
                String propertyName = currentPropertyName;
                // Property name form <script name>.<property name> has the
                // precedence to <property name>
                if (scriptName != null) {
                    if (currentPropertyName.startsWith(scriptName)) {
                        propertyName = currentPropertyName.substring(scriptName.length() + 1);
                    } else {
                        if (p.containsKey(scriptName + "." + currentPropertyName)) {
                            continue;
                        }
                    }
                }
                argumentProperties.setProperty(propertyName.toLowerCase(), p
                        .getProperty(currentPropertyName));
            }
        } catch (final Exception e) {
            final LocalizableMessage message =
                    ERR_ARGPARSER_CANNOT_READ_PROPERTIES_FILE.get(String
                            .valueOf(propertiesFilePath), getExceptionMessage(e));
            throw new ArgumentException(message, e);
        }
        return argumentProperties;
    }
    /**
     * Retrieves the argument with the specified name.
     *
     * @param name
     *            The name of the argument to retrieve.
     * @return The argument with the specified name, or <CODE>null</CODE> if
     *         there is no such argument.
     */
    Argument getArgument(final String name) {
        return argumentMap.get(name);
    }
    /**
     * Retrieves the argument with the specified long identifier.
     *
     * @param longID
     *            The long identifier of the argument to retrieve.
     * @return The argument with the specified long identifier, or
     *         <CODE>null</CODE> if there is no such argument.
     */
    Argument getArgumentForLongID(final String longID) {
        return longIDMap.get(longID);
    }
    /**
     * Retrieves the argument with the specified short identifier.
     *
     * @param shortID
     *            The short ID for the argument to retrieve.
     * @return The argument with the specified short identifier, or
     *         <CODE>null</CODE> if there is no such argument.
     */
    Argument getArgumentForShortID(final Character shortID) {
        return shortIDMap.get(shortID);
    }
    /**
     * Retrieves the list of all arguments that have been defined for this
     * argument parser.
     *
     * @return The list of all arguments that have been defined for this
     *         argument parser.
     */
    LinkedList<Argument> getArgumentList() {
        return argumentList;
    }
    /**
     * Retrieves the set of 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 arguments mapped by the long identifier that may be
     *         used to reference them.
     */
    HashMap<String, Argument> getArgumentsByLongID() {
        return longIDMap;
    }
    /**
     * Retrieves the set of 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 arguments mapped by the short identifier that may be
     *         used to reference them.
     */
    HashMap<Character, Argument> getArgumentsByShortID() {
        return shortIDMap;
    }
    /**
     * 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.
     */
    String getMainClassName() {
        return mainClassName;
    }
    /**
     * 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.
     */
    int getMaxTrailingArguments() {
        return maxTrailingArguments;
    }
    /**
     * 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.
     */
    int getMinTrailingArguments() {
        return minTrailingArguments;
    }
    /**
     * 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.
     */
    String[] getRawArguments() {
        return rawArguments;
    }
    /**
     * Given an argument, returns an appropriate group. Arguments may be part of
     * one of the special groups or the default group.
     *
     * @param argument
     *            for which a group is requested
     * @return argument group appropriate for <code>argument</code>
     */
    ArgumentGroup getStandardGroup(final Argument argument) {
        ArgumentGroup group;
        if (isInputOutputArgument(argument)) {
            group = ioArgGroup;
        } else if (isGeneralArgument(argument)) {
            group = generalArgGroup;
        } else if (isLdapConnectionArgument(argument)) {
            group = ldapArgGroup;
        } else {
            group = defaultArgGroup;
        }
        return group;
    }
    /**
     * 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.
     */
    LocalizableMessage getToolDescription() {
        return toolDescription;
    }
    /**
     * 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 trailingArguments;
    }
    /**
     * Retrieves a string containing usage information based on the defined
     * arguments.
     *
     * @return A string containing usage information based on the defined
     *         arguments.
     */
    String getUsage() {
        final StringBuilder buffer = new StringBuilder();
        getUsage(buffer);
        return buffer.toString();
    }
    /**
     * Writes usage information based on the defined arguments to the provided
     * output stream.
     *
     * @param outputStream
     *            The output stream to which the usage information should be
     *            written.
     * @throws IOException
     *             If a problem occurs while attempting to write the usage
     *             information to the provided output stream.
     */
    void getUsage(final OutputStream outputStream) throws IOException {
        final StringBuilder buffer = new StringBuilder();
        getUsage(buffer);
        outputStream.write(getBytes(buffer.toString()));
    }
    /**
     * Appends usage information based on the defined arguments to the provided
     * buffer.
     *
     * @param buffer
     *            The buffer to which the usage information should be appended.
     */
    void getUsage(final StringBuilder buffer) {
        usageOrVersionDisplayed = true;
        final String scriptName = System.getProperty(PROPERTY_SCRIPT_NAME);
        if ((scriptName == null) || (scriptName.length() == 0)) {
            buffer.append(INFO_ARGPARSER_USAGE_JAVA_CLASSNAME.get(mainClassName));
        } else {
            buffer.append(INFO_ARGPARSER_USAGE_JAVA_SCRIPTNAME.get(scriptName));
        }
        if (allowsTrailingArguments) {
            if (trailingArgsDisplayName == null) {
                buffer.append(" " + INFO_ARGPARSER_USAGE_TRAILINGARGS.get());
            } else {
                buffer.append(" ");
                buffer.append(trailingArgsDisplayName);
            }
        }
        buffer.append(EOL);
        buffer.append(EOL);
        if ((toolDescription != null) && (toolDescription.length() > 0)) {
            buffer.append(wrapText(toolDescription.toString(), MAX_LENGTH - 1));
            buffer.append(EOL);
            buffer.append(EOL);
        }
        buffer.append(INFO_SUBCMDPARSER_WHERE_OPTIONS_INCLUDE.get());
        buffer.append(EOL);
        buffer.append(EOL);
        Argument helpArgument = null;
        final boolean printHeaders = printUsageGroupHeaders();
        for (final ArgumentGroup argGroup : argumentGroups) {
            if (argGroup.containsArguments() && printHeaders) {
                // Print the groups description if any
                final 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);
                }
            }
            final SortedSet<Argument> args = new TreeSet<Argument>(new Comparator<Argument>() {
                /**
                 * {@inheritDoc}
                 */
                public int compare(final Argument o1, final Argument o2) {
                    final String s1;
                    final String s2;
                    if (o1.getShortIdentifier() != null) {
                        s1 = o1.getShortIdentifier().toString();
                    } else {
                        s1 = o1.getLongIdentifier();
                    }
                    if (o2.getShortIdentifier() != null) {
                        s2 = o2.getShortIdentifier().toString();
                    } else {
                        s2 = o2.getLongIdentifier();
                    }
                    final int res = s1.compareToIgnoreCase(s2);
                    if (res != 0) {
                        return res;
                    } else {
                        // Lowercase options first then uppercase.
                        return -s1.compareTo(s2);
                    }
                }
            });
            args.addAll(argGroup.getArguments());
            for (final Argument a : args) {
                // If this argument is hidden, then skip it.
                if (a.isHidden()) {
                    continue;
                }
                // Help argument should be printed at the end
                if ((usageArgument != null) && usageArgument.getName().equals(a.getName())) {
                    helpArgument = a;
                    continue;
                }
                printArgumentUsage(a, buffer);
            }
        }
        if (helpArgument != null) {
            printArgumentUsage(helpArgument, buffer);
        } else {
            buffer.append(EOL);
            buffer.append("-?");
            buffer.append(EOL);
        }
    }
    /**
     * Retrieves a message containing usage information based on the defined
     * arguments.
     *
     * @return A string containing usage information based on the defined
     *         arguments.
     */
    public LocalizableMessage getUsageMessage() {
        final StringBuilder buffer = new StringBuilder();
        getUsage(buffer);
        // TODO: rework getUsage(OutputStream) to work with messages
        // framework
        return LocalizableMessage.raw(buffer.toString());
    }
    /**
     * 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.
     */
    boolean isUsageArgumentPresent() {
        boolean isUsageArgumentPresent = false;
        if (usageArgument != null) {
            isUsageArgumentPresent = usageArgument.isPresent();
        }
        return isUsageArgumentPresent;
    }
    /**
     * 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.
     */
    boolean isVersionArgumentPresent() {
        return versionPresent;
    }
    /**
     * 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.
     */
    public void parseArguments(final 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.
     */
    public void parseArguments(final String[] rawArguments, Properties argumentProperties)
            throws ArgumentException {
        this.rawArguments = rawArguments;
        boolean inTrailingArgs = false;
        final int numArguments = rawArguments.length;
        for (int i = 0; i < numArguments; i++) {
            final String arg = rawArguments[i];
            if (inTrailingArgs) {
                trailingArguments.add(arg);
                if ((maxTrailingArguments > 0) && (trailingArguments.size() > maxTrailingArguments)) {
                    final LocalizableMessage message =
                            ERR_ARGPARSER_TOO_MANY_TRAILING_ARGS.get(maxTrailingArguments);
                    throw new ArgumentException(message);
                }
                continue;
            }
            if (arg.equals("--")) {
                // This is a special indicator that we have reached the end of
                // the named arguments and that everything that follows after
                // this
                // should be considered trailing arguments.
                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;
                final 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.
                    final LocalizableMessage message = ERR_ARGPARSER_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.
                final String origArgName = argName;
                if (!longArgumentsCaseSensitive) {
                    argName = toLowerCase(argName);
                }
                // Get the argument with the specified name.
                final Argument a = longIDMap.get(argName);
                if (a == null) {
                    if (argName.equals(OPTION_LONG_HELP)) {
                        // "--help" will always be interpreted as requesting
                        // usage
                        // information.
                        try {
                            getUsage(usageOutputStream);
                        } catch (final Exception e) {
                            // Ignored.
                        }
                        return;
                    } else if (argName.equals(OPTION_LONG_PRODUCT_VERSION)) {
                        // "--version" will always be interpreted as requesting
                        // version information.
                        usageOrVersionDisplayed = true;
                        versionPresent = true;
                        try {
                            // TODO
                            // DirectoryServer.printVersion(usageOutputStream);
                        } catch (final Exception e) {
                            // Ignored.
                        }
                        return;
                    } else {
                        // There is no such argument registered.
                        final LocalizableMessage message =
                                ERR_ARGPARSER_NO_ARGUMENT_WITH_LONG_ID.get(origArgName);
                        throw new ArgumentException(message);
                    }
                } else {
                    a.setPresent(true);
                    // If this is the usage argument, then immediately stop and
                    // print usage information.
                    if ((usageArgument != null) && usageArgument.getName().equals(a.getName())) {
                        try {
                            getUsage(usageOutputStream);
                        } catch (final Exception e) {
                            // Ignored.
                        }
                        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) {
                            final LocalizableMessage message =
                                    ERR_ARGPARSER_NO_VALUE_FOR_ARGUMENT_WITH_LONG_ID
                                            .get(origArgName);
                            throw new ArgumentException(message);
                        }
                        argValue = rawArguments[++i];
                    }
                    final LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder();
                    if (!a.valueIsAcceptable(argValue, invalidReason)) {
                        final LocalizableMessage message =
                                ERR_ARGPARSER_VALUE_UNACCEPTABLE_FOR_LONG_ID.get(argValue,
                                        origArgName, 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())) {
                        final LocalizableMessage message =
                                ERR_ARGPARSER_NOT_MULTIVALUED_FOR_LONG_ID.get(origArgName);
                        throw new ArgumentException(message);
                    }
                    a.addValue(argValue);
                } else {
                    if (argValue != null) {
                        final LocalizableMessage message =
                                ERR_ARGPARSER_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("-")) {
                    final LocalizableMessage message = ERR_ARGPARSER_INVALID_DASH_AS_ARGUMENT.get();
                    throw new ArgumentException(message);
                }
                final 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.
                final Argument a = shortIDMap.get(argCharacter);
                if (a == null) {
                    if (argCharacter == '?') {
                        // "-?" will always be interpreted as requesting usage
                        // information.
                        try {
                            getUsage(usageOutputStream);
                        } catch (final Exception e) {
                            // Ignored.
                        }
                        return;
                    } else if ((argCharacter == OPTION_SHORT_PRODUCT_VERSION)
                            && (!shortIDMap.containsKey(OPTION_SHORT_PRODUCT_VERSION))) {
                        // "-V" will always be interpreted as requesting
                        // version information except if it's already defined
                        // (e.g
                        // in ldap tools).
                        usageOrVersionDisplayed = true;
                        versionPresent = true;
                        try {
                            // TODO
                            // DirectoryServer.printVersion(usageOutputStream);
                        } catch (final Exception e) {
                            // Ignored.
                        }
                        return;
                    } else {
                        // There is no such argument registered.
                        final LocalizableMessage message =
                                ERR_ARGPARSER_NO_ARGUMENT_WITH_SHORT_ID.get(String
                                        .valueOf(argCharacter));
                        throw new ArgumentException(message);
                    }
                } else {
                    a.setPresent(true);
                    // If this is the usage argument, then immediately stop and
                    // print usage information.
                    if ((usageArgument != null) && usageArgument.getName().equals(a.getName())) {
                        try {
                            getUsage(usageOutputStream);
                        } catch (final Exception e) {
                            // Ignored.
                        }
                        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) {
                            final LocalizableMessage message =
                                    ERR_ARGPARSER_NO_VALUE_FOR_ARGUMENT_WITH_SHORT_ID.get(String
                                            .valueOf(argCharacter));
                            throw new ArgumentException(message);
                        }
                        argValue = rawArguments[++i];
                    }
                    final LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder();
                    if (!a.valueIsAcceptable(argValue, invalidReason)) {
                        final LocalizableMessage message =
                                ERR_ARGPARSER_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())) {
                        final LocalizableMessage message =
                                ERR_ARGPARSER_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.
                        final int valueLength = argValue.length();
                        for (int j = 0; j < valueLength; j++) {
                            final char c = argValue.charAt(j);
                            final Argument b = shortIDMap.get(c);
                            if (b == null) {
                                // There is no such argument registered.
                                final LocalizableMessage message =
                                        ERR_ARGPARSER_NO_ARGUMENT_WITH_SHORT_ID.get(String
                                                .valueOf(argCharacter));
                                throw new ArgumentException(message);
                            } else 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.
                                final LocalizableMessage message =
                                        ERR_ARGPARSER_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 ((usageArgument != null)
                                        && usageArgument.getName().equals(b.getName())) {
                                    try {
                                        getUsage(usageOutputStream);
                                    } catch (final Exception e) {
                                        // Ignored.
                                    }
                                    return;
                                }
                            }
                        }
                    }
                }
            } else if (allowsTrailingArguments) {
                // It doesn't start with a dash, so it must be a trailing
                // argument if that is acceptable.
                inTrailingArgs = true;
                trailingArguments.add(arg);
            } else {
                // It doesn't start with a dash and we don't allow trailing
                // arguments, so this is illegal.
                final LocalizableMessage message =
                        ERR_ARGPARSER_DISALLOWED_TRAILING_ARGUMENT.get(arg);
                throw new ArgumentException(message);
            }
        }
        // If we allow trailing arguments and there is a minimum number,
        // then make sure at least that many were provided.
        if (allowsTrailingArguments && (minTrailingArguments > 0)) {
            if (trailingArguments.size() < minTrailingArguments) {
                final 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 of the arguments. For any that were not
        // provided on the command line, see if there is an alternate default
        // that
        // can be used. For cases where there is not, see that argument is
        // required.
        for (final Argument a : argumentList) {
            if (!a.isPresent()) {
                // See if there is a value in the properties that can be used
                if ((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()) {
                    final LocalizableMessage message =
                            ERR_ARGPARSER_NO_VALUE_FOR_REQUIRED_ARG.get(a.getName());
                    throw new ArgumentException(message);
                }
            }
        }
    }
    /**
     * 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 file.
     *
     * @param rawArguments
     *            The set of raw arguments to parse.
     * @param propertiesFile
     *            The path to the properties file to use to obtain default
     *            values for unspecified properties.
     * @param requirePropertiesFile
     *            Indicates whether the parsing should fail if the provided
     *            properties file does not exist or is not accessible.
     * @throws ArgumentException
     *             If a problem was encountered while parsing the provided
     *             arguments or interacting with the properties file.
     */
    public void parseArguments(final String[] rawArguments, final String propertiesFile,
            final boolean requirePropertiesFile) throws ArgumentException {
        this.rawArguments = rawArguments;
        Properties argumentProperties = null;
        try {
            final Properties p = new Properties();
            final FileInputStream fis = new FileInputStream(propertiesFile);
            p.load(fis);
            fis.close();
            argumentProperties = p;
        } catch (final Exception e) {
            if (requirePropertiesFile) {
                final LocalizableMessage message =
                        ERR_ARGPARSER_CANNOT_READ_PROPERTIES_FILE.get(String
                                .valueOf(propertiesFile), getExceptionMessage(e));
                throw new ArgumentException(message, e);
            }
        }
        parseArguments(rawArguments, argumentProperties);
    }
    /**
     * Indicates whether or not argument group description headers should be
     * printed.
     *
     * @return boolean where true means print the descriptions
     */
    boolean printUsageGroupHeaders() {
        // If there is only a single group then we won't print them.
        int groupsContainingArgs = 0;
        for (final ArgumentGroup argGroup : argumentGroups) {
            if (argGroup.containsNonHiddenArguments()) {
                groupsContainingArgs++;
            }
        }
        return groupsContainingArgs > 1;
    }
    /**
     * Sets the usage group description for the default argument group.
     *
     * @param description
     *            for the default group
     */
    void setDefaultArgumentGroupDescription(final LocalizableMessage description) {
        this.defaultArgGroup.setDescription(description);
    }
    /**
     * Sets the provided argument which will be used to identify the file
     * properties.
     *
     * @param argument
     *            The argument which will be used to identify the file
     *            properties.
     */
    public void setFilePropertiesArgument(final StringArgument argument) {
        filePropertiesPathArgument = argument;
    }
    /**
     * Sets the usage group description for the general argument group.
     *
     * @param description
     *            for the general group
     */
    void setGeneralArgumentGroupDescription(final LocalizableMessage description) {
        this.generalArgGroup.setDescription(description);
    }
    /**
     * Sets the usage group description for the input/output argument group.
     *
     * @param description
     *            for the input/output group
     */
    void setInputOutputArgumentGroupDescription(final LocalizableMessage description) {
        this.ioArgGroup.setDescription(description);
    }
    /**
     * Sets the usage group description for the LDAP argument group.
     *
     * @param description
     *            for the LDAP group
     */
    void setLdapArgumentGroupDescription(final LocalizableMessage description) {
        this.ldapArgGroup.setDescription(description);
    }
    /**
     * Sets the provided argument which will be used to identify the file
     * properties.
     *
     * @param argument
     *            The argument which will be used to indicate if we have to look
     *            for properties file.
     */
    public void setNoPropertiesFileArgument(final BooleanArgument argument) {
        noPropertiesFileArgument = argument;
    }
    /**
     * Sets the provided argument as one which will automatically trigger the
     * output of usage information if it is provided on the command line and no
     * further argument validation will be performed. Note that the caller will
     * still need to add this argument to the parser with the
     * <CODE>addArgument</CODE> 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
     * <CODE>parseArguments</CODE> to know that no further processing will be
     * required.
     *
     * @param argument
     *            The argument whose presence should automatically trigger the
     *            display of usage information.
     */
    public void setUsageArgument(final Argument argument) {
        usageArgument = argument;
        usageOutputStream = System.out;
    }
    /**
     * Sets the provided argument as one which will automatically trigger the
     * output of usage information if it is provided on the command line and no
     * further argument validation will be performed. Note that the caller will
     * still need to add this argument to the parser with the
     * <CODE>addArgument</CODE> 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
     * <CODE>parseArguments</CODE> to know that no further processing will be
     * required.
     *
     * @param argument
     *            The argument whose presence should automatically trigger the
     *            display of usage information.
     * @param outputStream
     *            The output stream to which the usage information should be
     *            written.
     */
    public void setUsageArgument(final Argument argument, final OutputStream outputStream) {
        usageArgument = argument;
        usageOutputStream = outputStream;
    }
    /**
     * Indicates whether the version or 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.
     */
    public boolean usageOrVersionDisplayed() {
        return usageOrVersionDisplayed;
    }
    /**
     * Get the absolute path of the properties file.
     *
     * @param directory
     *            The location in which we should look for properties file
     * @return The absolute path of the properties file or null
     */
    private String findPropertiesFile(final String directory) {
        // Look for the tools properties file
        final File f =
                new File(directory, DEFAULT_OPENDJ_PROPERTIES_FILE_NAME
                        + DEFAULT_OPENDJ_PROPERTIES_FILE_EXTENSION);
        if (f.exists() && f.canRead()) {
            return f.getAbsolutePath();
        } else {
            return null;
        }
    }
    private void initGroups() {
        this.argumentGroups = new TreeSet<ArgumentGroup>();
        this.argumentGroups.add(defaultArgGroup);
        this.argumentGroups.add(ldapArgGroup);
        this.argumentGroups.add(generalArgGroup);
        this.argumentGroups.add(ioArgGroup);
        try {
            versionArgument =
                    new BooleanArgument(OPTION_LONG_PRODUCT_VERSION, OPTION_SHORT_PRODUCT_VERSION,
                            OPTION_LONG_PRODUCT_VERSION, INFO_DESCRIPTION_PRODUCT_VERSION.get());
            this.generalArgGroup.addArgument(versionArgument);
        } catch (final ArgumentException e) {
            // ignore
        }
    }
    private boolean isGeneralArgument(final Argument arg) {
        boolean general = false;
        if (arg != null) {
            final String longId = arg.getLongIdentifier();
            general = OPTION_LONG_HELP.equals(longId) || OPTION_LONG_PRODUCT_VERSION.equals(longId);
        }
        return general;
    }
    private boolean isInputOutputArgument(final Argument arg) {
        boolean io = false;
        if (arg != null) {
            final String longId = arg.getLongIdentifier();
            io =
                    OPTION_LONG_VERBOSE.equals(longId) || OPTION_LONG_QUIET.equals(longId)
                            || OPTION_LONG_NO_PROMPT.equals(longId)
                            || OPTION_LONG_PROP_FILE_PATH.equals(longId)
                            || OPTION_LONG_NO_PROP_FILE.equals(longId)
                            || OPTION_LONG_SCRIPT_FRIENDLY.equals(longId)
                            || OPTION_LONG_DONT_WRAP.equals(longId)
                            || OPTION_LONG_ENCODING.equals(longId)
                            || OPTION_LONG_BATCH_FILE_PATH.equals(longId);
        }
        return io;
    }
    private boolean isLdapConnectionArgument(final Argument arg) {
        boolean ldap = false;
        if (arg != null) {
            final String longId = arg.getLongIdentifier();
            ldap =
                    OPTION_LONG_USE_SSL.equals(longId) || OPTION_LONG_START_TLS.equals(longId)
                            || OPTION_LONG_HOST.equals(longId) || OPTION_LONG_PORT.equals(longId)
                            || OPTION_LONG_BINDDN.equals(longId)
                            || OPTION_LONG_BINDPWD.equals(longId)
                            || OPTION_LONG_BINDPWD_FILE.equals(longId)
                            || OPTION_LONG_SASLOPTION.equals(longId)
                            || OPTION_LONG_TRUSTALL.equals(longId)
                            || OPTION_LONG_TRUSTSTOREPATH.equals(longId)
                            || OPTION_LONG_TRUSTSTORE_PWD.equals(longId)
                            || OPTION_LONG_TRUSTSTORE_PWD_FILE.equals(longId)
                            || OPTION_LONG_KEYSTOREPATH.equals(longId)
                            || OPTION_LONG_KEYSTORE_PWD.equals(longId)
                            || OPTION_LONG_KEYSTORE_PWD_FILE.equals(longId)
                            || OPTION_LONG_CERT_NICKNAME.equals(longId)
                            || OPTION_LONG_REFERENCED_HOST_NAME.equals(longId)
                            || OPTION_LONG_ADMIN_UID.equals(longId)
                            || OPTION_LONG_REPORT_AUTHZ_ID.equals(longId)
                            || OPTION_LONG_USE_PW_POLICY_CTL.equals(longId)
                            || OPTION_LONG_USE_SASL_EXTERNAL.equals(longId)
                            || OPTION_LONG_PROTOCOL_VERSION.equals(longId);
        }
        return ldap;
    }
    /**
     * 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(final Argument a, final StringBuilder buffer) {
        // Write a line with the short and/or long identifiers that may be
        // used
        // for the argument.
        final int indentLength = INDENT.length();
        final Character shortID = a.getShortIdentifier();
        final String longID = a.getLongIdentifier();
        if (shortID != null) {
            final int currentLength = buffer.length();
            if (usageArgument.getName().equals(a.getName())) {
                buffer.append("-?, ");
            }
            buffer.append("-");
            buffer.append(shortID.charValue());
            if (a.needsValue() && longID == null) {
                buffer.append(" ");
                buffer.append(a.getValuePlaceholder());
            }
            if (longID != null) {
                final StringBuilder newBuffer = new StringBuilder();
                newBuffer.append(", --");
                newBuffer.append(longID);
                if (a.needsValue()) {
                    newBuffer.append(" ");
                    newBuffer.append(a.getValuePlaceholder());
                }
                final int lineLength = (buffer.length() - currentLength) + newBuffer.length();
                if (lineLength > MAX_LENGTH) {
                    buffer.append(EOL);
                    buffer.append(newBuffer.toString());
                } else {
                    buffer.append(newBuffer.toString());
                }
            }
            buffer.append(EOL);
        } else {
            if (longID != null) {
                if (usageArgument.getName().equals(a.getName())) {
                    buffer.append("-?, ");
                }
                buffer.append("--");
                buffer.append(longID);
                if (a.needsValue()) {
                    buffer.append(" ");
                    buffer.append(a.getValuePlaceholder());
                }
                buffer.append(EOL);
            }
        }
        // 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.
        buffer.append(wrapText(a.getDescription(), MAX_LENGTH, indentLength));
        buffer.append(EOL);
        if (a.needsValue() && (a.getDefaultValue() != null) && (a.getDefaultValue().length() > 0)) {
            buffer.append(INDENT);
            buffer.append(INFO_ARGPARSER_USAGE_DEFAULT_VALUE.get(a.getDefaultValue()).toString());
            buffer.append(EOL);
        }
    }
}
opendj-cli/src/main/java/com/forgerock/opendj/cli/BooleanArgument.java
New file
@@ -0,0 +1,108 @@
/*
 * 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.cli.CliMessages.ERR_BOOLEANARG_NO_VALUE_ALLOWED;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
/**
 * This class defines an argument type that will be used to represent Boolean
 * values. These arguments will never take values from the command line but and
 * will never be required. If the argument is provided, then it will be
 * considered true, and if not then it will be considered false. As such, the
 * default value will always be "false".
 */
public final class BooleanArgument extends Argument {
    /**
     * Creates a new Boolean 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 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 BooleanArgument(final String name, final Character shortIdentifier,
            final String longIdentifier, final LocalizableMessage description)
            throws ArgumentException {
        super(name, shortIdentifier, longIdentifier, false, false, false, null, String
                .valueOf(false), null, description);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    final public void addValue(final String valueString) {
        if (valueString != null) {
            clearValues();
            super.addValue(valueString);
            super.setPresent(Boolean.valueOf(valueString));
        }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    final public void setPresent(final boolean isPresent) {
        addValue(String.valueOf(isPresent));
    }
    /**
     * Indicates whether the provided value is acceptable for use in this
     * argument.
     *
     * @param valueString
     *            The value for which to make the determination.
     * @param invalidReason
     *            A buffer into which the invalid reason may be written if the
     *            value is not acceptable.
     * @return <CODE>true</CODE> if the value is acceptable, or
     *         <CODE>false</CODE> if it is not.
     */
    @Override
    public boolean valueIsAcceptable(final String valueString,
            final LocalizableMessageBuilder invalidReason) {
        // This argument type should never have a value, so any value
        // provided will be unacceptable.
        invalidReason.append(ERR_BOOLEANARG_NO_VALUE_ALLOWED.get(getName()));
        return false;
    }
}
opendj-cli/src/main/java/com/forgerock/opendj/cli/CLIException.java
File was renamed from opendj-cli/src/main/java/org/forgerock/opendj/cli/CLIException.java
@@ -24,7 +24,7 @@
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Portions copyright 2014 ForgeRock AS
 */
package org.forgerock.opendj.cli;
package com.forgerock.opendj.cli;
import static com.forgerock.opendj.cli.CliMessages.ERR_CONSOLE_INPUT_ERROR;
opendj-cli/src/main/java/com/forgerock/opendj/cli/CliConstants.java
New file
@@ -0,0 +1,625 @@
/*
 * 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 2012-2014 ForgeRock AS.
 */
package com.forgerock.opendj.cli;
/**
 * This class defines a number of constants used in one or more Directory Server
 * tools.
 */
public final class CliConstants {
    /**
     * The name of the SASL property that can be used to provide the
     * authentication ID for the bind.
     */
    public static final String SASL_PROPERTY_AUTHID = "authid";
    /**
     * The name of the SASL property that can be used to provide the
     * authorization ID for the bind.
     */
    public static final String SASL_PROPERTY_AUTHZID = "authzid";
    /**
     * The name of the SASL property that can be used to provide the digest URI
     * for the bind.
     */
    public static final String SASL_PROPERTY_DIGEST_URI = "digest-uri";
    /**
     * The name of the SASL property that can be used to provide the KDC for use
     * in Kerberos authentication.
     */
    public static final String SASL_PROPERTY_KDC = "kdc";
    /**
     * The name of the SASL property that can be used to provide the quality of
     * protection for the bind.
     */
    public static final String SASL_PROPERTY_QOP = "qop";
    /**
     * The name of the SASL property that can be used to provide the realm for
     * the bind.
     */
    public static final String SASL_PROPERTY_REALM = "realm";
    /**
     * The name of the SASL property that can be used to provide trace
     * information for a SASL ANONYMOUS request.
     */
    public static final String SASL_PROPERTY_TRACE = "trace";
    /**
     * The name of the SASL property that can be used to provide the SASL
     * mechanism to use.
     */
    public static final String SASL_PROPERTY_MECH = "mech";
    /**
     * The name of the opendj configuration direction in the user home
     * directory.
     */
    public static final String DEFAULT_OPENDJ_CONFIG_DIR = ".opendj";
    /**
     * The default properties file name.
     */
    public static final String DEFAULT_OPENDJ_PROPERTIES_FILE_NAME = "tools";
    /**
     * The default properties file extension.
     */
    public static final String DEFAULT_OPENDJ_PROPERTIES_FILE_EXTENSION = ".properties";
    /**
     * The value for the short option batchFilePath.
     */
    public static final char OPTION_SHORT_BATCH_FILE_PATH = 'F';
    /**
     * The value for the long option batchFilePath .
     */
    public static final String OPTION_LONG_BATCH_FILE_PATH = "batchFilePath";
    /**
     * The value for the short option hostname.
     */
    public static final char OPTION_SHORT_HOST = 'h';
    /**
     * The value for the long option hostname.
     */
    public static final String OPTION_LONG_HOST = "hostname";
    /**
     * The value for the short option port.
     */
    public static final char OPTION_SHORT_PORT = 'p';
    /**
     * The value for the long option port.
     */
    public static final String OPTION_LONG_PORT = "port";
    /**
     * The value for the short option useSSL.
     */
    public static final char OPTION_SHORT_USE_SSL = 'Z';
    /**
     * The value for the long option useSSL.
     */
    public static final String OPTION_LONG_USE_SSL = "useSSL";
    /**
     * The value for the short option baseDN.
     */
    public static final char OPTION_SHORT_BASEDN = 'b';
    /**
     * The value for the long option baseDN.
     */
    public static final String OPTION_LONG_BASEDN = "baseDN";
    /**
     * The value for the short option bindDN.
     */
    public static final char OPTION_SHORT_BINDDN = 'D';
    /**
     * The value for the long option bindDN.
     */
    public static final String OPTION_LONG_BINDDN = "bindDN";
    /**
     * The value for the short option bindPassword.
     */
    public static final char OPTION_SHORT_BINDPWD = 'w';
    /**
     * The value for the long option bindPassword.
     */
    public static final String OPTION_LONG_BINDPWD = "bindPassword";
    /**
     * The value for the short option bindPasswordFile.
     */
    public static final char OPTION_SHORT_BINDPWD_FILE = 'j';
    /**
     * The value for the long option bindPasswordFile.
     */
    public static final String OPTION_LONG_BINDPWD_FILE = "bindPasswordFile";
    /**
     * The value for the short option compress.
     */
    public static final char OPTION_SHORT_COMPRESS = 'c';
    /**
     * The value for the long option compress.
     */
    public static final String OPTION_LONG_COMPRESS = "compress";
    /**
     * The value for the short option filename.
     */
    public static final char OPTION_SHORT_FILENAME = 'f';
    /**
     * The value for the long option filename.
     */
    public static final String OPTION_LONG_FILENAME = "filename";
    /**
     * The value for the short option ldifFile.
     */
    public static final char OPTION_SHORT_LDIF_FILE = 'l';
    /**
     * The value for the long option ldifFile.
     */
    public static final String OPTION_LONG_LDIF_FILE = "ldifFile";
    /**
     * The value for the short option useStartTLS.
     */
    public static final char OPTION_SHORT_START_TLS = 'q';
    /**
     * The value for the long option useStartTLS.
     */
    public static final String OPTION_LONG_START_TLS = "useStartTLS";
    /**
     * The value for the short option randomSeed.
     */
    public static final char OPTION_SHORT_RANDOM_SEED = 's';
    /**
     * The value for the long option randomSeed.
     */
    public static final String OPTION_LONG_RANDOM_SEED = "randomSeed";
    /**
     * The value for the short option keyStorePath.
     */
    public static final char OPTION_SHORT_KEYSTOREPATH = 'K';
    /**
     * The value for the long option keyStorePath.
     */
    public static final String OPTION_LONG_KEYSTOREPATH = "keyStorePath";
    /**
     * The value for the short option trustStorePath.
     */
    public static final char OPTION_SHORT_TRUSTSTOREPATH = 'P';
    /**
     * The value for the long option trustStorePath.
     */
    public static final String OPTION_LONG_TRUSTSTOREPATH = "trustStorePath";
    /**
     * The value for the short option keyStorePassword.
     */
    public static final char OPTION_SHORT_KEYSTORE_PWD = 'W';
    /**
     * The value for the long option keyStorePassword.
     */
    public static final String OPTION_LONG_KEYSTORE_PWD = "keyStorePassword";
    /**
     * The value for the short option trustStorePassword.
     */
    public static final char OPTION_SHORT_TRUSTSTORE_PWD = 'T';
    /**
     * The value for the long option trustStorePassword.
     */
    public static final String OPTION_LONG_TRUSTSTORE_PWD = "trustStorePassword";
    /**
     * The value for the short option keyStorePasswordFile .
     */
    public static final char OPTION_SHORT_KEYSTORE_PWD_FILE = 'u';
    /**
     * The value for the long option keyStorePasswordFile .
     */
    public static final String OPTION_LONG_KEYSTORE_PWD_FILE = "keyStorePasswordFile";
    /**
     * The value for the short option keyStorePasswordFile .
     */
    public static final char OPTION_SHORT_TRUSTSTORE_PWD_FILE = 'U';
    /**
     * The value for the long option keyStorePasswordFile .
     */
    public static final String OPTION_LONG_TRUSTSTORE_PWD_FILE = "trustStorePasswordFile";
    /**
     * The value for the short option trustAll .
     */
    public static final char OPTION_SHORT_TRUSTALL = 'X';
    /**
     * The value for the long option trustAll .
     */
    public static final String OPTION_LONG_TRUSTALL = "trustAll";
    /**
     * The value for the short option certNickname .
     */
    public static final char OPTION_SHORT_CERT_NICKNAME = 'N';
    /**
     * The value for the long option certNickname .
     */
    public static final String OPTION_LONG_CERT_NICKNAME = "certNickname";
    /**
     * The value for the long option assertionFilter .
     */
    public static final String OPTION_LONG_ASSERTION_FILE = "assertionFilter";
    /**
     * The value for the short option dry-run.
     */
    public static final char OPTION_SHORT_DRYRUN = 'n';
    /**
     * The value for the long option dry-run.
     */
    public static final String OPTION_LONG_DRYRUN = "dry-run";
    /**
     * The value for the short option help.
     */
    public static final char OPTION_SHORT_HELP = 'H';
    /**
     * The value for the long option help.
     */
    public static final String OPTION_LONG_HELP = "help";
    /**
     * The value for the long option cli.
     */
    public static final String OPTION_LONG_CLI = "cli";
    /**
     * The value for the short option cli.
     */
    public static final char OPTION_SHORT_CLI = 'i';
    /**
     * The value for the short option proxyAs.
     */
    public static final char OPTION_SHORT_PROXYAUTHID = 'Y';
    /**
     * The value for the long option proxyAs.
     */
    public static final String OPTION_LONG_PROXYAUTHID = "proxyAs";
    /**
     * The value for the short option saslOption.
     */
    public static final char OPTION_SHORT_SASLOPTION = 'o';
    /**
     * The value for the long option saslOption.
     */
    public static final String OPTION_LONG_SASLOPTION = "saslOption";
    /**
     * The value for the short option geteffectiverights control authzid.
     */
    public static final char OPTION_SHORT_EFFECTIVERIGHTSUSER = 'g';
    /**
     * The value for the long option geteffectiverights control authzid.
     */
    public static final String OPTION_LONG_EFFECTIVERIGHTSUSER = "getEffectiveRightsAuthzid";
    /**
     * The value for the short option geteffectiveights control attributes.
     */
    public static final char OPTION_SHORT_EFFECTIVERIGHTSATTR = 'e';
    /**
     * The value for the long option geteffectiverights control specific
     * attribute list.
     */
    public static final String OPTION_LONG_EFFECTIVERIGHTSATTR = "getEffectiveRightsAttribute";
    /**
     * The value for the short option protocol version attributes.
     */
    public static final char OPTION_SHORT_PROTOCOL_VERSION = 'V';
    /**
     * The value for the long option protocol version attribute.
     */
    public static final String OPTION_LONG_PROTOCOL_VERSION = "ldapVersion";
    /**
     * The value for the long option version.
     */
    public static final char OPTION_SHORT_PRODUCT_VERSION = 'V';
    /**
     * The value for the long option version.
     */
    public static final String OPTION_LONG_PRODUCT_VERSION = "version";
    /**
     * The value for the short option description attributes.
     */
    public static final char OPTION_SHORT_DESCRIPTION = 'd';
    /**
     * The value for the long option description attribute.
     */
    public static final String OPTION_LONG_DESCRIPTION = "description";
    /**
     * The value for the short option groupName attributes.
     */
    public static final char OPTION_SHORT_GROUPNAME = 'g';
    /**
     * The value for the long option groupName attribute.
     */
    public static final String OPTION_LONG_GROUPNAME = "groupName";
    /**
     * The value for the short option newGroupName attribute.
     */
    public static final char OPTION_SHORT_NEWGROUPNAME = 'n';
    /**
     * The value for the long option groupName attribute.
     */
    public static final String OPTION_LONG_NEWGROUPNAME = "newGroupName";
    /**
     * The value for the short option member-name attributes.
     */
    public static final char OPTION_SHORT_MEMBERNAME = 'm';
    /**
     * The value for the long member-name version attribute.
     */
    public static final String OPTION_LONG_MEMBERNAME = "memberName";
    /**
     * The value for the short option serverID attributes.
     */
    public static final String OPTION_SHORT_SERVERID = null;
    /**
     * The value for the long option serverID attribute.
     */
    public static final String OPTION_LONG_SERVERID = "serverID";
    /**
     * The value for the short option userID attributes.
     */
    public static final String OPTION_SHORT_USERID = null;
    /**
     * The value for the long option userID attribute.
     */
    public static final String OPTION_LONG_USERID = "userID";
    /**
     * The value for the short option set.
     */
    public static final Character OPTION_SHORT_SET = null;
    /**
     * The value for the long option set.
     */
    public static final String OPTION_LONG_SET = "set";
    /**
     * Value for the quiet option short form.
     */
    public static final Character OPTION_SHORT_QUIET = 'Q';
    /**
     * Value for the quiet option long form.
     */
    public static final String OPTION_LONG_QUIET = "quiet";
    /**
     * Value for non-interactive session short form.
     */
    public static final Character OPTION_SHORT_NO_PROMPT = 'n';
    /**
     * Value for non-interactive session long form.
     */
    public static final String OPTION_LONG_NO_PROMPT = "no-prompt";
    /**
     * Long form of script friendly option.
     */
    public static final String OPTION_LONG_SCRIPT_FRIENDLY = "script-friendly";
    /**
     * Short form of script friendly option.
     */
    public static final Character OPTION_SHORT_SCRIPT_FRIENDLY = 's';
    /**
     * Value for verbose option short form.
     */
    public static final Character OPTION_SHORT_VERBOSE = 'v';
    /**
     * Value for verbose option long form.
     */
    public static final String OPTION_LONG_VERBOSE = "verbose";
    /**
     * The value for the long option propertiesFilePAth .
     */
    public static final String OPTION_LONG_PROP_FILE_PATH = "propertiesFilePath";
    /**
     * The value for the long option propertiesFilePAth .
     */
    public static final String OPTION_LONG_NO_PROP_FILE = "noPropertiesFile";
    /**
     * Long form of referenced host name.
     */
    public static final String OPTION_LONG_REFERENCED_HOST_NAME = "referencedHostName";
    /**
     * Long form of admin UID.
     */
    public static final String OPTION_LONG_ADMIN_UID = "adminUID";
    /**
     * Long form of report authorization ID connection option.
     */
    public static final String OPTION_LONG_REPORT_AUTHZ_ID = "reportAuthzID";
    /**
     * Long form of use password policy control connection option.
     */
    public static final String OPTION_LONG_USE_PW_POLICY_CTL = "usePasswordPolicyControl";
    /**
     * Long form of use SASL external connection option.
     */
    public static final String OPTION_LONG_USE_SASL_EXTERNAL = "useSASLExternal";
    /**
     * Long form of option for the command-line encoding option.
     */
    public static final String OPTION_LONG_ENCODING = "encoding";
    /**
     * Long form of option specifying no wrapping of the command-line.
     */
    public static final String OPTION_LONG_DONT_WRAP = "dontWrap";
    /**
     * The value for the long option targetDN.
     */
    public static final String OPTION_LONG_TARGETDN = "targetDN";
    /**
     * Long form of email notification upon completion option.
     */
    public static final String OPTION_LONG_COMPLETION_NOTIFICATION_EMAIL = "completionNotify";
    /**
     * Short form of email notification upon completion option.
     */
    public static final Character OPTION_SHORT_COMPLETION_NOTIFICATION_EMAIL = null;
    /**
     * Long form of email notification upon error option.
     */
    public static final String OPTION_LONG_ERROR_NOTIFICATION_EMAIL = "errorNotify";
    /**
     * Short form of email notification upon error option.
     */
    public static final Character OPTION_SHORT_ERROR_NOTIFICATION_EMAIL = null;
    /**
     * Long form of dependency option.
     */
    public static final String OPTION_LONG_DEPENDENCY = "dependency";
    /**
     * Short form of dependency option.
     */
    public static final Character OPTION_SHORT_DEPENDENCY = null;
    /**
     * Long form of failed dependency action option.
     */
    public static final String OPTION_LONG_FAILED_DEPENDENCY_ACTION = "failedDependencyAction";
    /**
     * Short form of failed dependency action option.
     */
    public static final Character OPTION_SHORT_FAILED_DEPENDENCY_ACTION = null;
    /**
     * The default separator to be used in tables.
     */
    public static final String LIST_TABLE_SEPARATOR = ":";
    /**
     *
     * The value for the short option output LDIF filename.
     */
    public static final char OPTION_SHORT_OUTPUT_LDIF_FILENAME = 'o';
    /**
     * The value for the long option output LDIF filename.
     */
    public static final String OPTION_LONG_OUTPUT_LDIF_FILENAME = "outputLDIF";
    // Prevent instantiation.
    private CliConstants() {
    }
}
opendj-cli/src/main/java/com/forgerock/opendj/cli/CommonArguments.java
New file
@@ -0,0 +1,120 @@
/*
 * 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 2014 ForgeRock AS
 */
package com.forgerock.opendj.cli;
import static com.forgerock.opendj.cli.CliConstants.*;
import static com.forgerock.opendj.cli.CliMessages.*;
/**
 * This class regroup commons arguments used by the different CLI.
 */
public final class CommonArguments {
    // Prevent instantiation.
    private CommonArguments() {
        // Nothing to do.
    }
    /**
     * Returns the "show usage" boolean argument.
     *
     * @return The "show usage" argument.
     * @throws ArgumentException
     *             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());
    }
    /**
     * Returns the "verbose" boolean argument.
     *
     * @return The "verbose" argument.
     * @throws ArgumentException
     *             If there is a problem with any of the parameters used to create this argument.
     */
    public static final BooleanArgument getVerbose() throws ArgumentException {
        final BooleanArgument verbose = new BooleanArgument("verbose", 'v', "verbose", INFO_DESCRIPTION_VERBOSE.get());
        verbose.setPropertyName("verbose");
        return verbose;
    }
    /**
     * Returns the "get properties file" string argument.
     *
     * @return The "get properties file" argument.
     * @throws ArgumentException
     *             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());
    }
    /**
     * Returns the "No properties file" boolean argument.
     *
     * @return The "No properties file" argument.
     * @throws ArgumentException
     *             If there is a problem with any of the parameters used to create this argument.
     */
    public static final BooleanArgument getNoPropertiesFileArgument() throws ArgumentException {
        return new BooleanArgument("noPropertiesFileArgument", null, OPTION_LONG_NO_PROP_FILE,
                INFO_DESCRIPTION_NO_PROP_FILE.get());
    }
    /**
     * Returns the "Continue On Error" boolean argument.
     *
     * @return The "Continue On Error" argument.
     * @throws ArgumentException
     *             If there is a problem with any of the parameters used to create this argument.
     */
    public static final BooleanArgument getContinueOnErrorArgument() throws ArgumentException {
        final BooleanArgument continueOnError = new BooleanArgument("continueOnError", 'c', "continueOnError",
                INFO_DESCRIPTION_CONTINUE_ON_ERROR.get());
        continueOnError.setPropertyName("continueOnError");
        return continueOnError;
    }
    /**
     * Returns the "version" integer argument.
     *
     * @return The "version" argument.
     * @throws ArgumentException
     *             If there is a problem with any of the parameters used to create this argument.
     */
    public static final IntegerArgument getVersionArgument() throws ArgumentException {
        final IntegerArgument version = new IntegerArgument("version", OPTION_SHORT_PROTOCOL_VERSION,
                OPTION_LONG_PROTOCOL_VERSION, false, false, true, INFO_PROTOCOL_VERSION_PLACEHOLDER.get(), 3, null,
                INFO_DESCRIPTION_VERSION.get());
        version.setPropertyName(OPTION_LONG_PROTOCOL_VERSION);
        return version;
    }
}
opendj-cli/src/main/java/com/forgerock/opendj/cli/ConsoleApplication.java
New file
@@ -0,0 +1,328 @@
/*
 * 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 2008-2009 Sun Microsystems, Inc.
 *      Portions copyright 2011-2014 ForgeRock AS
 *      Portions copyright 2011 Nemanja Lukić
 */
package com.forgerock.opendj.cli;
import static com.forgerock.opendj.cli.CliMessages.INFO_ERROR_EMPTY_RESPONSE;
import static com.forgerock.opendj.cli.CliMessages.INFO_MENU_PROMPT_RETURN_TO_CONTINUE;
import static com.forgerock.opendj.cli.CliMessages.INFO_PROMPT_SINGLE_DEFAULT;
import static com.forgerock.opendj.cli.Utils.MAX_LINE_WIDTH;
import static com.forgerock.opendj.cli.Utils.wrapText;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.Console;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import org.forgerock.i18n.LocalizableMessage;
/**
 * This class provides an abstract base class which can be used as the basis of
 * a console-based application.
 */
public abstract class ConsoleApplication {
    private final PrintStream err;
    private final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    private final InputStream in = System.in;
    private final PrintStream out;
    private final Console console = System.console();
    /**
     * Creates a new console application instance.
     */
    public ConsoleApplication() {
        this(System.out, System.err);
    }
    /**
     * Creates a new console application instance with provided standard and error out streams.
     *
     * @param out
     *            The output stream.
     * @param err
     *            The error stream.
     */
    public ConsoleApplication(PrintStream out, PrintStream err) {
        this.out = out;
        this.err = err;
    }
    /**
     * Closes the provided {@code Closeable}s if they are not {@code null}.
     *
     * @param closeables
     *          The closeables to be closed.
     */
    public final void closeIfNotNull(Closeable... closeables) {
        if (closeables == null) {
            return;
        }
        for (Closeable closeable : closeables) {
            if (closeable != null) {
                try {
                    closeable.close();
                } catch (Exception ignored) {
                    // Do nothing.
                }
            }
        }
    }
    /**
     * Returns the application error stream.
     *
     * @return The application error stream.
     */
    public final PrintStream getErrorStream() {
        return err;
    }
    /**
     * Returns the application input stream.
     *
     * @return The application input stream.
     */
    public final InputStream getInputStream() {
        return in;
    }
    /**
     * Returns the application output stream.
     *
     * @return The application output stream.
     */
    public final PrintStream getOutputStream() {
        return out;
    }
    /**
     * Indicates whether or not the user has requested interactive behavior. The
     * default implementation returns {@code true}.
     *
     * @return {@code true} if the user has requested interactive behavior.
     */
    public boolean isInteractive() {
        return true;
    }
    /**
     * Indicates whether or not the user has requested quiet output. The default
     * implementation returns {@code false}.
     *
     * @return {@code true} if the user has requested quiet output.
     */
    public boolean isQuiet() {
        return false;
    }
    /**
     * Indicates whether or not the user has requested script-friendly output.
     * The default implementation returns {@code false}.
     *
     * @return {@code true} if the user has requested script-friendly output.
     */
    public boolean isScriptFriendly() {
        return false;
    }
    /**
     * Indicates whether or not the user has requested verbose output. The
     * default implementation returns {@code false}.
     *
     * @return {@code true} if the user has requested verbose output.
     */
    public boolean isVerbose() {
        return false;
    }
    /**
     * Interactively prompts the user to press return to continue. This method
     * should be called in situations where a user needs to be given a chance to
     * read some documentation before continuing (continuing may cause the
     * documentation to be scrolled out of view).
     */
    public final void pressReturnToContinue() {
        final LocalizableMessage msg = INFO_MENU_PROMPT_RETURN_TO_CONTINUE.get();
        try {
            readLineOfInput(msg);
        } catch (final CLIException e) {
            // Ignore the exception - applications don't care.
        }
    }
    /**
     * Displays a message to the error stream.
     *
     * @param msg
     *            The message.
     */
    public final void print(final LocalizableMessage msg) {
        err.print(wrapText(msg, MAX_LINE_WIDTH));
    }
    /**
     * Displays a blank line to the error stream.
     */
    public final void println() {
        err.println();
    }
    /**
     * Displays a message to the error stream.
     *
     * @param msg
     *            The message.
     */
    public final void println(final LocalizableMessage msg) {
        err.println(wrapText(msg, MAX_LINE_WIDTH));
    }
    /**
     * Displays a message to the error stream indented by the specified number
     * of columns.
     *
     * @param msg
     *            The message.
     * @param indent
     *            The number of columns to indent.
     */
    public final void println(final LocalizableMessage msg, final int indent) {
        err.println(wrapText(msg, MAX_LINE_WIDTH, indent));
    }
    /**
     * Displays a message to the error stream if verbose mode is enabled.
     *
     * @param msg
     *            The verbose message.
     */
    public final void printVerboseMessage(final LocalizableMessage msg) {
        if (isVerbose() || isInteractive()) {
            err.println(wrapText(msg, MAX_LINE_WIDTH));
        }
    }
    /**
     * Interactively prompts (on error output) the user to provide a string
     * value. Any non-empty string will be allowed (the empty string will
     * indicate that the default should be used, if there is one).
     *
     * @param prompt
     *            The prompt to present to the user.
     * @param defaultValue
     *            The default value to assume if the user presses ENTER without
     *            typing anything, or {@code null} if there should not be a
     *            default and the user must explicitly provide a value.
     * @throws CLIException
     *             If the line of input could not be retrieved for some reason.
     * @return The string value read from the user.
     */
    public final String readInput(LocalizableMessage prompt, final String defaultValue)
            throws CLIException {
        while (true) {
            if (defaultValue != null) {
                prompt = INFO_PROMPT_SINGLE_DEFAULT.get(prompt.toString(), defaultValue);
            }
            final String response = readLineOfInput(prompt);
            if ("".equals(response)) {
                if (defaultValue == null) {
                    print(INFO_ERROR_EMPTY_RESPONSE.get());
                } else {
                    return defaultValue;
                }
            } else {
                return response;
            }
        }
    }
    /**
     * Interactively reads a password from the console.
     *
     * @param prompt
     *            The password prompt.
     * @return The password.
     * @throws CLIException
     *             If the password could not be retrieved for some reason.
     */
    public final char[] readPassword(final LocalizableMessage prompt) throws CLIException {
        if (console != null) {
            if (prompt != null) {
                err.print(wrapText(prompt, MAX_LINE_WIDTH));
                err.print(" ");
            }
            try {
                final char[] password = console.readPassword();
                if (password == null) {
                    throw new EOFException("End of input");
                }
                return password;
            } catch (final Throwable e) {
                throw CLIException.adaptInputException(e);
            }
        } else {
            // FIXME: should go direct to char[] and avoid the String.
            return readLineOfInput(prompt).toCharArray();
        }
    }
    /**
     * Interactively retrieves a line of input from the console.
     *
     * @param prompt
     *            The prompt.
     * @return The line of input.
     * @throws CLIException
     *             If the line of input could not be retrieved for some reason.
     */
    private final String readLineOfInput(final LocalizableMessage prompt) throws CLIException {
        if (prompt != null) {
            err.print(wrapText(prompt, MAX_LINE_WIDTH));
            err.print(" ");
        }
        try {
            final String s = reader.readLine();
            if (s == null) {
                throw CLIException.adaptInputException(new EOFException("End of input"));
            } else {
                return s;
            }
        } catch (final IOException e) {
            throw CLIException.adaptInputException(e);
        }
    }
}
opendj-cli/src/main/java/com/forgerock/opendj/cli/FileBasedArgument.java
New file
@@ -0,0 +1,242 @@
/*
 * 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.cli.CliMessages.*;
import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.LinkedHashMap;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
/**
 * This class defines an argument whose value will be read from a file rather
 * than actually specified on the command-line. When a value is specified on the
 * command line, it will be treated as the path to the file containing the
 * actual value rather than the value itself. <BR>
 * <BR>
 * Note that if if no filename is provided on the command line but a default
 * value is specified programmatically or if the default value is read from a
 * specified property, then that default value will be taken as the actual value
 * rather than a filename. <BR>
 * <BR>
 * Also note that this argument type assumes that the entire value for the
 * argument is on a single line in the specified file. If the file contains
 * multiple lines, then only the first line will be read.
 */
public final class FileBasedArgument extends Argument {
    // The mapping between filenames specified and the first lines read
    // from those
    // files.
    private final LinkedHashMap<String, String> namesToValues;
    /**
     * Creates a new file-based 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 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 FileBasedArgument(final String name, final Character shortIdentifier,
            final String longIdentifier, final boolean isRequired, final boolean isMultiValued,
            final LocalizableMessage valuePlaceholder, final String defaultValue,
            final String propertyName, final LocalizableMessage description)
            throws ArgumentException {
        super(name, shortIdentifier, longIdentifier, isRequired, isMultiValued, true,
                valuePlaceholder, defaultValue, propertyName, description);
        namesToValues = new LinkedHashMap<String, String>();
    }
    /**
     * Creates a new file-based 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 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 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 FileBasedArgument(final String name, final Character shortIdentifier,
            final String longIdentifier, final boolean isRequired,
            final LocalizableMessage valuePlaceholder, final LocalizableMessage description)
            throws ArgumentException {
        super(name, shortIdentifier, longIdentifier, isRequired, false, true, valuePlaceholder,
                null, null, description);
        namesToValues = new LinkedHashMap<String, String>();
    }
    /**
     * Adds a value to the set of values for this argument. This should only be
     * called if the value is allowed by the <CODE>valueIsAcceptable</CODE>
     * method. Note that in this case, correct behavior depends on a previous
     * successful call to <CODE>valueIsAcceptable</CODE> so that the value read
     * from the file may be stored in the name-to-value hash and used in place
     * of the filename here.
     *
     * @param valueString
     *            The string representation of the value to add to this
     *            argument.
     */
    @Override
    public void addValue(final String valueString) {
        final String actualValue = namesToValues.get(valueString);
        if (actualValue != null) {
            super.addValue(actualValue);
        }
    }
    /**
     * Retrieves a map between the filenames specified on the command line and
     * the first lines read from those files.
     *
     * @return A map between the filenames specified on the command line and the
     *         first lines read from those files.
     */
    public LinkedHashMap<String, String> getNameToValueMap() {
        return namesToValues;
    }
    /**
     * Indicates whether the provided value is acceptable for use in this
     * argument.
     *
     * @param valueString
     *            The value for which to make the determination.
     * @param invalidReason
     *            A buffer into which the invalid reason may be written if the
     *            value is not acceptable.
     * @return <CODE>true</CODE> if the value is acceptable, or
     *         <CODE>false</CODE> if it is not.
     */
    @Override
    public boolean valueIsAcceptable(final String valueString,
            final LocalizableMessageBuilder invalidReason) {
        // First, make sure that the specified file exists.
        File valueFile;
        try {
            valueFile = new File(valueString);
            if (!valueFile.exists()) {
                invalidReason.append(ERR_FILEARG_NO_SUCH_FILE.get(valueString, getName()));
                return false;
            }
        } catch (final Exception e) {
            invalidReason.append(ERR_FILEARG_CANNOT_VERIFY_FILE_EXISTENCE.get(valueString,
                    getName(), getExceptionMessage(e)));
            return false;
        }
        // Open the file for reading.
        BufferedReader reader;
        try {
            reader = new BufferedReader(new FileReader(valueFile));
        } catch (final Exception e) {
            invalidReason.append(ERR_FILEARG_CANNOT_OPEN_FILE.get(valueString, getName(),
                    getExceptionMessage(e)));
            return false;
        }
        // Read the first line and close the file.
        String line;
        try {
            line = reader.readLine();
        } catch (final Exception e) {
            invalidReason.append(ERR_FILEARG_CANNOT_READ_FILE.get(valueString, getName(),
                    getExceptionMessage(e)));
            return false;
        } finally {
            try {
                reader.close();
            } catch (final Exception e) {
                // Ignored.
            }
        }
        // If the line read is null, then that means the file was empty.
        if (line == null) {
            invalidReason.append(ERR_FILEARG_EMPTY_FILE.get(valueString, getName()));
            return false;
        }
        // Store the value in the hash so it will be available for addValue.
        // We won't do any validation on the value itself, so anything that we
        // read will be considered acceptable.
        namesToValues.put(valueString, line);
        return true;
    }
}
opendj-cli/src/main/java/com/forgerock/opendj/cli/IntegerArgument.java
New file
@@ -0,0 +1,483 @@
/*
 * 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 2014 ForgeRock AS
 */
package com.forgerock.opendj.cli;
import static com.forgerock.opendj.cli.CliMessages.*;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
/**
 * This class defines an argument type that will only accept integer values, and
 * potentially only those in a given range.
 */
public final class IntegerArgument extends Argument {
    // Indicates whether a lower bound will be enforced for this argument.
    private final boolean hasLowerBound;
    // Indicates whether an upper bound will be enforced for this
    // argument.
    private final boolean hasUpperBound;
    // The lower bound that will be enforced for this argument.
    private final double 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;
    }
    /**
     * 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 int 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 int defaultValue, final String propertyName, final LocalizableMessage description)
            throws ArgumentException {
        super(name, shortIdentifier, longIdentifier, isRequired, isMultiValued, needsValue,
                valuePlaceholder, String.valueOf(defaultValue), propertyName, description);
        hasLowerBound = false;
        hasUpperBound = false;
        lowerBound = Integer.MIN_VALUE;
        upperBound = Integer.MAX_VALUE;
    }
    /**
     * 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 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 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 needsValue,
            final LocalizableMessage valuePlaceholder, final boolean hasLowerBound,
            final double lowerBound, final boolean hasUpperBound, final double upperBound,
            final LocalizableMessage description) throws ArgumentException {
        super(name, shortIdentifier, longIdentifier, isRequired, false, needsValue,
                valuePlaceholder, null, null, 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 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 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 needsValue,
            final LocalizableMessage valuePlaceholder, final LocalizableMessage description)
            throws ArgumentException {
        super(name, shortIdentifier, longIdentifier, isRequired, false, needsValue,
                valuePlaceholder, null, null, description);
        hasLowerBound = false;
        hasUpperBound = false;
        lowerBound = Double.MIN_VALUE;
        upperBound = Double.MAX_VALUE;
    }
    /**
     * Retrieves the lower bound that may be enforced for values of this
     * argument.
     *
     * @return The lower bound that may be enforced for values of this argument.
     */
    public double getLowerBound() {
        return lowerBound;
    }
    /**
     * Retrieves the upper bound that may be enforced for values of this
     * argument.
     *
     * @return The upper bound that may be enforced for values of this argument.
     */
    public double getUpperBound() {
        return upperBound;
    }
    /**
     * Indicates whether a lower bound should be enforced for values of this
     * argument.
     *
     * @return <CODE>true</CODE> if a lower bound should be enforced for values
     *         of this argument, or <CODE>false</CODE> if not.
     */
    public boolean hasLowerBound() {
        return hasLowerBound;
    }
    /**
     * Indicates whether a upper bound should be enforced for values of this
     * argument.
     *
     * @return <CODE>true</CODE> if a upper bound should be enforced for values
     *         of this argument, or <CODE>false</CODE> if not.
     */
    public boolean hasUpperBound() {
        return hasUpperBound;
    }
    /**
     * Indicates whether the provided value is acceptable for use in this
     * argument.
     *
     * @param valueString
     *            The value for which to make the determination.
     * @param invalidReason
     *            A buffer into which the invalid reason may be written if the
     *            value is not acceptable.
     * @return <CODE>true</CODE> if the value is acceptable, or
     *         <CODE>false</CODE> if it is not.
     */
    @Override
    public boolean valueIsAcceptable(final String valueString,
            final LocalizableMessageBuilder invalidReason) {
        // First, the value must be decodable as an integer.
        double intValue;
        try {
            intValue = Double.parseDouble(valueString);
        } catch (final Exception e) {
            invalidReason.append(ERR_ARG_CANNOT_DECODE_AS_INT.get(valueString, getName()));
            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,
                    lowerBound));
            return false;
        }
        // If there is an upper bound, then the value must be less than or
        // equal to it.
        if (hasUpperBound && (intValue > upperBound)) {
            invalidReason.append(ERR_INTARG_VALUE_ABOVE_UPPER_BOUND.get(getName(), intValue,
                    upperBound));
            return false;
        }
        // At this point, the value should be acceptable.
        return true;
    }
}
opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiChoiceArgument.java
New file
@@ -0,0 +1,239 @@
/*
 * 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.cli.CliMessages.ERR_MCARG_VALUE_NOT_ALLOWED;
import java.util.Collection;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
/**
 * This class defines an argument type that will only accept one or more of a
 * specific set of string values.
 *
 * @param <T>
 *            The type of values returned by this argument.
 */
public final class MultiChoiceArgument<T> extends Argument {
    // Indicates whether argument values should be treated in a
    // case-sensitive manner.
    private final boolean caseSensitive;
    // The set of values that will be allowed for use with this argument.
    private final Collection<T> allowedValues;
    /**
     * Creates a new string 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 allowedValues
     *            The set of values that are allowed for use for this argument.
     *            If they are not to be treated in a case-sensitive value then
     *            they should all be formatted in lowercase.
     * @param caseSensitive
     *            Indicates whether the set of allowed values should be treated
     *            in a case-sensitive manner.
     * @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 MultiChoiceArgument(final String name, final Character shortIdentifier,
            final String longIdentifier, final boolean isRequired, final boolean isMultiValued,
            final boolean needsValue, final LocalizableMessage valuePlaceholder,
            final String defaultValue, final String propertyName,
            final Collection<T> allowedValues, final boolean caseSensitive,
            final LocalizableMessage description) throws ArgumentException {
        super(name, shortIdentifier, longIdentifier, isRequired, isMultiValued, needsValue,
                valuePlaceholder, defaultValue, propertyName, description);
        this.allowedValues = allowedValues;
        this.caseSensitive = caseSensitive;
    }
    /**
     * Creates a new string 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 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 allowedValues
     *            The set of values that are allowed for use for this argument.
     *            If they are not to be treated in a case-sensitive value then
     *            they should all be formatted in lowercase.
     * @param caseSensitive
     *            Indicates whether the set of allowed values should be treated
     *            in a case-sensitive manner.
     * @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 MultiChoiceArgument(final String name, final Character shortIdentifier,
            final String longIdentifier, final boolean isRequired, final boolean needsValue,
            final LocalizableMessage valuePlaceholder, final Collection<T> allowedValues,
            final boolean caseSensitive, final LocalizableMessage description)
            throws ArgumentException {
        super(name, shortIdentifier, longIdentifier, isRequired, false, needsValue,
                valuePlaceholder, null, null, description);
        this.allowedValues = allowedValues;
        this.caseSensitive = caseSensitive;
    }
    /**
     * Retrieves the set of allowed values for this argument. The contents of
     * this set must not be altered by the caller.
     *
     * @return The set of allowed values for this argument.
     */
    public Collection<T> getAllowedValues() {
        return allowedValues;
    }
    /**
     * Retrieves the string vale for this argument. If it has multiple values,
     * then the first will be returned. If it does not have any values, then the
     * default value will be returned.
     *
     * @return The string value for this argument, or <CODE>null</CODE> if there
     *         are no values and no default value has been given.
     * @throws ArgumentException
     *             The value cannot be parsed.
     */
    public T getTypedValue() throws ArgumentException {
        final String v = super.getValue();
        if (v == null) {
            return null;
        }
        for (final T o : allowedValues) {
            if ((caseSensitive && o.toString().equals(v)) || o.toString().equalsIgnoreCase(v)) {
                return o;
            }
        }
        // TODO: Some message
        throw new ArgumentException(null);
    }
    /**
     * Indicates whether the set of allowed values for this argument should be
     * treated in a case-sensitive manner.
     *
     * @return <CODE>true</CODE> if the values are to be treated in a
     *         case-sensitive manner, or <CODE>false</CODE> if not.
     */
    public boolean isCaseSensitive() {
        return caseSensitive;
    }
    /**
     * Specifies the default value that will be used for this argument if it is
     * not specified on the command line and it is not set from a properties
     * file.
     *
     * @param defaultValue
     *            The default value that will be used for this argument if it is
     *            not specified on the command line and it is not set from a
     *            properties file.
     */
    public void setDefaultValue(final T defaultValue) {
        super.setDefaultValue(defaultValue.toString());
    }
    /**
     * Indicates whether the provided value is acceptable for use in this
     * argument.
     *
     * @param valueString
     *            The value for which to make the determination.
     * @param invalidReason
     *            A buffer into which the invalid reason may be written if the
     *            value is not acceptable.
     * @return <CODE>true</CODE> if the value is acceptable, or
     *         <CODE>false</CODE> if it is not.
     */
    @Override
    public boolean valueIsAcceptable(final String valueString,
            final LocalizableMessageBuilder invalidReason) {
        for (final T o : allowedValues) {
            if ((caseSensitive && o.toString().equals(valueString))
                    || o.toString().equalsIgnoreCase(valueString)) {
                return true;
            }
        }
        invalidReason.append(ERR_MCARG_VALUE_NOT_ALLOWED.get(getName(), valueString));
        return false;
    }
}
opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiColumnPrinter.java
New file
@@ -0,0 +1,483 @@
/*
 * 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 2010 Sun Microsystems, Inc.
 *      Portions copyright 2012-2014 ForgeRock AS.
 */
package com.forgerock.opendj.cli;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
/**
 * Utility class for printing aligned columns of text.
 * <P>
 * This class allows you to specify:
 * <UL>
 * <LI>The number of columns in the output. This will determine the dimension of
 * the string arrays passed to add(String[]) or addTitle(String[]).
 * <LI>spacing/gap between columns
 * <LI>character to use for title border (null means no border)
 * <LI>column alignment. Only LEFT/CENTER is supported for now.
 * </UL>
 * <P>
 * Example usage:
 *
 * <PRE>
 * MyPrinter mp = new MyPrinter(3, 2, &quot;-&quot;);
 * String oneRow[] = new String[3];
 * oneRow[0] = &quot;User Name&quot;;
 * oneRow[1] = &quot;Email Address&quot;;
 * oneRow[2] = &quot;Phone Number&quot;;
 * mp.addTitle(oneRow);
 * oneRow[0] = &quot;Bob&quot;;
 * oneRow[1] = &quot;bob@foo.com&quot;;
 * oneRow[2] = &quot;123-4567&quot;;
 * mp.add(oneRow);
 * oneRow[0] = &quot;John&quot;;
 * oneRow[1] = &quot;john@foo.com&quot;;
 * oneRow[2] = &quot;456-7890&quot;;
 * mp.add(oneRow);
 * mp.print();
 * </PRE>
 * <P>
 * The above would print:
 * <P>
 *
 * <PRE>
 *  --------------------------------------
 *  User Name  Email Address  Phone Number
 *  --------------------------------------
 *  Bob        bob@foo.com    123-4567
 *  John       john@foo.com   456-7890
 * </PRE>
 * <P>
 * This class also supports multi-row titles and having title strings spanning
 * multiple columns. Example usage:
 *
 * <PRE>
 * TestPrinter tp = new TestPrinter(4, 2, &quot;-&quot;);
 * String oneRow[] = new String[4];
 * int[] span = new int[4];
 * span[0] = 2; // spans 2 columns
 * span[1] = 0; // spans 0 columns
 * span[2] = 2; // spans 2 columns
 * span[3] = 0; // spans 0 columns
 * tp.setTitleAlign(CENTER);
 * oneRow[0] = &quot;Name&quot;;
 * oneRow[1] = &quot;&quot;;
 * oneRow[2] = &quot;Contact&quot;;
 * oneRow[3] = &quot;&quot;;
 * tp.addTitle(oneRow, span);
 * oneRow[0] = &quot;First&quot;;
 * oneRow[1] = &quot;Last&quot;;
 * oneRow[2] = &quot;Email&quot;;
 * oneRow[3] = &quot;Phone&quot;;
 * tp.addTitle(oneRow);
 * oneRow[0] = &quot;Bob&quot;;
 * oneRow[1] = &quot;Jones&quot;;
 * oneRow[2] = &quot;bob@foo.com&quot;;
 * oneRow[3] = &quot;123-4567&quot;;
 * tp.add(oneRow);
 * oneRow[0] = &quot;John&quot;;
 * oneRow[1] = &quot;Doe&quot;;
 * oneRow[2] = &quot;john@foo.com&quot;;
 * oneRow[3] = &quot;456-7890&quot;;
 * tp.add(oneRow);
 * tp.println();
 * </PRE>
 * <P>
 * The above would print:
 * <P>
 *
 * <PRE>
 *      ------------------------------------
 *          Name             Contact
 *      First  Last      Email       Phone
 *      ------------------------------------
 *      Bob    Jones  bob@foo.com   123-4567
 *      John   Doe    john@foo.com  456-7890
 * </PRE>
 */
public final class MultiColumnPrinter {
    /**
     * Left ID.
     */
    static public final int LEFT = 0;
    /**
     * Center ID.
     */
    static public final int CENTER = 1;
    /**
     * Right ID.
     */
    static public final int RIGHT = 2;
    private int numCol = 2;
    private int gap = 4;
    private int align = CENTER;
    private int titleAlign = CENTER;
    private String border = null;
    private List<String[]> titleTable = null;
    private List<int[]> titleSpanTable = null;
    private final int[] curLength;
    private final ConsoleApplication app;
    /**
     * Creates a sorted new MultiColumnPrinter class using LEFT alignment and
     * with no title border.
     *
     * @param numCol
     *            number of columns
     * @param gap
     *            gap between each column
     * @param app
     *            the console application to use for outputting data
     */
    public MultiColumnPrinter(final int numCol, final int gap, final ConsoleApplication app) {
        this(numCol, gap, null, LEFT, app);
    }
    /**
     * Creates a sorted new MultiColumnPrinter class using LEFT alignment.
     *
     * @param numCol
     *            number of columns
     * @param gap
     *            gap between each column
     * @param border
     *            character used to frame the titles
     * @param app
     *            the console application to use for outputting data
     */
    public MultiColumnPrinter(final int numCol, final int gap, final String border,
            final ConsoleApplication app) {
        this(numCol, gap, border, LEFT, app);
    }
    /**
     * Creates a new MultiColumnPrinter class.
     *
     * @param numCol
     *            number of columns
     * @param gap
     *            gap between each column
     * @param border
     *            character used to frame the titles
     * @param align
     *            type of alignment within columns
     * @param app
     *            the console application to use for outputting data
     */
    public MultiColumnPrinter(final int numCol, final int gap, final String border, final int align,
            final ConsoleApplication app) {
        titleTable = new Vector<String[]>();
        titleSpanTable = new Vector<int[]>();
        curLength = new int[numCol];
        this.numCol = numCol;
        this.gap = gap;
        this.border = border;
        this.align = align;
        this.titleAlign = LEFT;
        this.app = app;
    }
    /**
     * Adds to the row of strings to be used as the title for the table.
     *
     * @param row
     *            Array of strings to print in one row of title.
     */
    public void addTitle(final String[] row) {
        if (row == null) {
            return;
        }
        final int[] span = new int[row.length];
        for (int i = 0; i < row.length; i++) {
            span[i] = 1;
        }
        addTitle(row, span);
    }
    /**
     * Adds to the row of strings to be used as the title for the table. Also
     * allows for certain title strings to span multiple columns The span
     * parameter is an array of integers which indicate how many columns the
     * corresponding title string will occupy. For a row that is 4 columns
     * wide, it is possible to have some title strings in a row to 'span'
     * multiple columns:
     * <P>
     *
     * <PRE>
     * ------------------------------------
     *     Name             Contact
     * First  Last      Email       Phone
     * ------------------------------------
     * Bob    Jones  bob@foo.com   123-4567
     * John   Doe    john@foo.com  456-7890
     * </PRE>
     *
     * In the example above, the title row has a string 'Name' that spans 2
     * columns. The string 'Contact' also spans 2 columns. The above is done
     * by passing in to addTitle() an array that contains:
     *
     * <PRE>
     * span[0] = 2; // spans 2 columns
     * span[1] = 0; // spans 0 columns, ignore
     * span[2] = 2; // spans 2 columns
     * span[3] = 0; // spans 0 columns, ignore
     * </PRE>
     * <P>
     * A span value of 1 is the default. The method addTitle(String[] row)
     * basically does:
     *
     * <PRE>
     * int[] span = new int[row.length];
     * for (int i = 0; i &lt; row.length; i++) {
     *     span[i] = 1;
     * }
     * addTitle(row, span);
     * </PRE>
     *
     * @param row
     *            Array of strings to print in one row of title.
     * @param span
     *            Array of integers that reflect the number of columns the
     *            corresponding title string will occupy.
     */
    public void addTitle(final String[] row, final int[] span) {
        // Need to create a new instance of it, otherwise the new values
        // will always overwrite the old values.
        final String[] rowInstance = new String[row.length];
        for (int i = 0; i < row.length; i++) {
            rowInstance[i] = row[i];
        }
        titleTable.add(rowInstance);
        titleSpanTable.add(span);
    }
    /**
     * Clears title strings.
     */
    public void clearTitle() {
        titleTable.clear();
        titleSpanTable.clear();
    }
    /**
     * Adds one row of text to output.
     *
     * @param row
     *            Array of strings to print in one row.
     */
    public void printRow(final String... row) {
        for (int i = 0; i < numCol; i++) {
            if (titleAlign == RIGHT) {
                final int spaceBefore = curLength[i] - row[i].length();
                printSpaces(spaceBefore);
                app.getOutputStream().print(row[i]);
                if (i < numCol - 1) {
                    printSpaces(gap);
                }
            } else if (align == CENTER) {
                int space1, space2;
                space1 = (curLength[i] - row[i].length()) / 2;
                space2 = curLength[i] - row[i].length() - space1;
                printSpaces(space1);
                app.getOutputStream().print(row[i]);
                printSpaces(space2);
                if (i < numCol - 1) {
                    printSpaces(gap);
                }
            } else {
                app.getOutputStream().print(row[i]);
                if (i < numCol - 1) {
                    printSpaces(curLength[i] - row[i].length() + gap);
                }
            }
        }
        app.getOutputStream().println("");
    }
    /**
     * Prints the table title.
     */
    public void printTitle() {
        // Get the longest string for each column and store in curLength[]
        // Scan through title rows
        Iterator<int[]> spanEnum = titleSpanTable.iterator();
        for (String[] row : titleTable) {
            final int[] curSpan = spanEnum.next();
            for (int i = 0; i < numCol; i++) {
                // None of the fields should be null, but if it
                // happens to be so, replace it with "-".
                if (row[i] == null) {
                    row[i] = "-";
                }
                int len = row[i].length();
                /*
                 * If a title string spans multiple columns, then the space it
                 * occupies in each column is at most len/span (since we have
                 * gap to take into account as well).
                 */
                final int span = curSpan[i];
                int rem = 0;
                if (span > 1) {
                    rem = len % span;
                    len = len / span;
                }
                if (curLength[i] < len) {
                    curLength[i] = len;
                    if ((span > 1) && ((i + span) <= numCol)) {
                        for (int j = i + 1; j < (i + span); ++j) {
                            curLength[j] = len;
                        }
                        /*
                         * Add remainder to last column in span to avoid
                         * round-off errors.
                         */
                        curLength[(i + span) - 1] += rem;
                    }
                }
            }
        }
        printBorder();
        spanEnum = titleSpanTable.iterator();
        for (String[] row : titleTable) {
            final int[] curSpan = spanEnum.next();
            for (int i = 0; i < numCol; i++) {
                int availableSpace = 0;
                final int span = curSpan[i];
                if (span == 0) {
                    continue;
                }
                availableSpace = curLength[i];
                if ((span > 1) && ((i + span) <= numCol)) {
                    for (int j = i + 1; j < (i + span); ++j) {
                        availableSpace += gap;
                        availableSpace += curLength[j];
                    }
                }
                if (titleAlign == RIGHT) {
                    final int spaceBefore = availableSpace - row[i].length();
                    printSpaces(spaceBefore);
                    app.getOutputStream().print(row[i]);
                    if (i < numCol - 1) {
                        printSpaces(gap);
                    }
                } else if (titleAlign == CENTER) {
                    int spaceBefore, spaceAfter;
                    spaceBefore = (availableSpace - row[i].length()) / 2;
                    spaceAfter = availableSpace - row[i].length() - spaceBefore;
                    printSpaces(spaceBefore);
                    app.getOutputStream().print(row[i]);
                    printSpaces(spaceAfter);
                    if (i < numCol - 1) {
                        printSpaces(gap);
                    }
                } else {
                    app.getOutputStream().print(row[i]);
                    if (i < numCol - 1) {
                        printSpaces(availableSpace - row[i].length() + gap);
                    }
                }
            }
            app.getOutputStream().println("");
        }
        printBorder();
    }
    /**
     * Set alignment for title strings.
     *
     * @param titleAlign
     *            The alignment which should be one of {@code LEFT},
     *            {@code RIGHT}, or {@code CENTER}.
     */
    public void setTitleAlign(final int titleAlign) {
        this.titleAlign = titleAlign;
    }
    private void printBorder() {
        if (border == null) {
            return;
        }
        // For the value in each column
        for (int i = 0; i < numCol; i++) {
            for (int j = 0; j < curLength[i]; j++) {
                app.getOutputStream().print(border);
            }
        }
        // For the gap between each column
        for (int i = 0; i < numCol - 1; i++) {
            for (int j = 0; j < gap; j++) {
                app.getOutputStream().print(border);
            }
        }
        app.getOutputStream().println("");
    }
    private void printSpaces(final int count) {
        for (int i = 0; i < count; ++i) {
            app.getOutputStream().print(" ");
        }
    }
}
opendj-cli/src/main/java/com/forgerock/opendj/cli/StringArgument.java
New file
@@ -0,0 +1,136 @@
/*
 * 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 org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
/**
 * This class defines an argument type that will accept any string value.
 */
public final class StringArgument extends Argument {
    /**
     * Creates a new string 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 StringArgument(final String name, final Character shortIdentifier,
            final String longIdentifier, final boolean isRequired, final boolean isMultiValued,
            final boolean needsValue, final LocalizableMessage valuePlaceholder,
            final String defaultValue, final String propertyName,
            final LocalizableMessage description) throws ArgumentException {
        super(name, shortIdentifier, longIdentifier, isRequired, isMultiValued, needsValue,
                valuePlaceholder, defaultValue, propertyName, description);
    }
    /**
     * Creates a new string 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 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 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 StringArgument(final String name, final Character shortIdentifier,
            final String longIdentifier, final boolean isRequired, final boolean needsValue,
            final LocalizableMessage valuePlaceholder, final LocalizableMessage description)
            throws ArgumentException {
        super(name, shortIdentifier, longIdentifier, isRequired, false, needsValue,
                valuePlaceholder, null, null, description);
    }
    /**
     * Indicates whether the provided value is acceptable for use in this
     * argument.
     *
     * @param valueString
     *            The value for which to make the determination.
     * @param invalidReason
     *            A buffer into which the invalid reason may be written if the
     *            value is not acceptable.
     * @return <CODE>true</CODE> if the value is acceptable, or
     *         <CODE>false</CODE> if it is not.
     */
    @Override
    public boolean valueIsAcceptable(final String valueString,
            final LocalizableMessageBuilder invalidReason) {
        // All values will be acceptable for this argument.
        return true;
    }
}
opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java
New file
@@ -0,0 +1,336 @@
/*
 * 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 2014 ForgeRock AS
 */
package com.forgerock.opendj.cli;
import static com.forgerock.opendj.cli.CliMessages.*;
import static com.forgerock.opendj.util.StaticUtils.EOL;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.StringTokenizer;
import org.forgerock.i18n.LocalizableMessage;
/**
 * This class provides utility functions for all the client side tools.
 */
final public class Utils {
    /**
     * The name of a command-line script used to launch a tool.
     */
    public static final String PROPERTY_SCRIPT_NAME = "com.forgerock.opendj.ldap.tools.scriptName";
    /**
     * The column at which to wrap long lines of output in the command-line
     * tools.
     */
    static public final int MAX_LINE_WIDTH;
    static {
        int columns = 80;
        try {
            final String s = System.getenv("COLUMNS");
            if (s != null) {
                columns = Integer.parseInt(s);
            }
        } catch (final Exception e) {
            // Do nothing.
        }
        MAX_LINE_WIDTH = columns - 1;
    }
    /**
     * Filters the provided value to ensure that it is appropriate for use as an
     * exit code. Exit code values are generally only allowed to be between 0
     * and 255, so any value outside of this range will be converted to 255,
     * which is the typical exit code used to indicate an overflow value.
     *
     * @param exitCode
     *            The exit code value to be processed.
     * @return An integer value between 0 and 255, inclusive. If the provided
     *         exit code was already between 0 and 255, then the original value
     *         will be returned. If the provided value was out of this range,
     *         then 255 will be returned.
     */
    public static int filterExitCode(final int exitCode) {
        if (exitCode < 0) {
            return 255;
        } else if (exitCode > 255) {
            return 255;
        } else {
            return exitCode;
        }
    }
    /**
     * Read the data from the specified file and return it in a byte array.
     *
     * @param filePath
     *            The path to the file that should be read.
     * @return A byte array containing the contents of the requested file.
     * @throws IOException
     *             If a problem occurs while trying to read the specified file.
     */
    public static byte[] readBytesFromFile(final String filePath) throws IOException {
        byte[] val = null;
        FileInputStream fis = null;
        try {
            final File file = new File(filePath);
            fis = new FileInputStream(file);
            final long length = file.length();
            val = new byte[(int) length];
            // Read in the bytes
            int offset = 0;
            int numRead = 0;
            while (offset < val.length
                    && (numRead = fis.read(val, offset, val.length - offset)) >= 0) {
                offset += numRead;
            }
            // Ensure all the bytes have been read in
            if (offset < val.length) {
                throw new IOException("Could not completely read file " + filePath);
            }
            return val;
        } finally {
            if (fis != null) {
                fis.close();
            }
        }
    }
    /**
     * Retrieves a user-friendly string that indicates the length of time (in
     * days, hours, minutes, and seconds) in the specified number of seconds.
     *
     * @param numSeconds
     *            The number of seconds to be converted to a more user-friendly
     *            value.
     * @return The user-friendly representation of the specified number of
     *         seconds.
     */
    public static LocalizableMessage secondsToTimeString(final int numSeconds) {
        if (numSeconds < 60) {
            // We can express it in seconds.
            return INFO_TIME_IN_SECONDS.get(numSeconds);
        } else if (numSeconds < 3600) {
            // We can express it in minutes and seconds.
            final int m = numSeconds / 60;
            final int s = numSeconds % 60;
            return INFO_TIME_IN_MINUTES_SECONDS.get(m, s);
        } else if (numSeconds < 86400) {
            // We can express it in hours, minutes, and seconds.
            final int h = numSeconds / 3600;
            final int m = (numSeconds % 3600) / 60;
            final int s = numSeconds % 3600 % 60;
            return INFO_TIME_IN_HOURS_MINUTES_SECONDS.get(h, m, s);
        } else {
            // We can express it in days, hours, minutes, and seconds.
            final int d = numSeconds / 86400;
            final int h = (numSeconds % 86400) / 3600;
            final int m = (numSeconds % 86400 % 3600) / 60;
            final int s = numSeconds % 86400 % 3600 % 60;
            return INFO_TIME_IN_DAYS_HOURS_MINUTES_SECONDS.get(d, h, m, s);
        }
    }
    /**
     * Inserts line breaks into the provided buffer to wrap text at no more than
     * the specified column width. Wrapping will only be done at space
     * boundaries and if there are no spaces within the specified width, then
     * wrapping will be performed at the first space after the specified column.
     *
     * @param message
     *            The message to be wrapped.
     * @param width
     *            The maximum number of characters to allow on a line if there
     *            is a suitable breaking point.
     * @return The wrapped text.
     */
    public static String wrapText(final LocalizableMessage message, final int width) {
        return wrapText(message.toString(), width, 0);
    }
    /**
     * Inserts line breaks into the provided buffer to wrap text at no more than
     * the specified column width. Wrapping will only be done at space
     * boundaries and if there are no spaces within the specified width, then
     * wrapping will be performed at the first space after the specified column.
     * In addition each line will be indented by the specified amount.
     *
     * @param message
     *            The message to be wrapped.
     * @param width
     *            The maximum number of characters to allow on a line if there
     *            is a suitable breaking point (including any indentation).
     * @param indent
     *            The number of columns to indent each line.
     * @return The wrapped text.
     */
    public static String wrapText(final LocalizableMessage message, final int width, final int indent) {
        return wrapText(message.toString(), width, indent);
    }
    /**
     * Inserts line breaks into the provided buffer to wrap text at no more than
     * the specified column width. Wrapping will only be done at space
     * boundaries and if there are no spaces within the specified width, then
     * wrapping will be performed at the first space after the specified column.
     *
     * @param text
     *            The text to be wrapped.
     * @param width
     *            The maximum number of characters to allow on a line if there
     *            is a suitable breaking point.
     * @return The wrapped text.
     */
    public static String wrapText(final String text, final int width) {
        return wrapText(text, width, 0);
    }
    /**
     * Inserts line breaks into the provided buffer to wrap text at no more than
     * the specified column width. Wrapping will only be done at space
     * boundaries and if there are no spaces within the specified width, then
     * wrapping will be performed at the first space after the specified column.
     * In addition each line will be indented by the specified amount.
     *
     * @param text
     *            The text to be wrapped.
     * @param width
     *            The maximum number of characters to allow on a line if there
     *            is a suitable breaking point (including any indentation).
     * @param indent
     *            The number of columns to indent each line.
     * @return The wrapped text.
     */
    public static String wrapText(final String text, int width, final int indent) {
        // Calculate the real width and indentation padding.
        width -= indent;
        final StringBuilder pb = new StringBuilder();
        for (int i = 0; i < indent; i++) {
            pb.append(' ');
        }
        final String padding = pb.toString();
        final StringBuilder buffer = new StringBuilder();
        if (text != null) {
            final StringTokenizer lineTokenizer = new StringTokenizer(text, "\r\n", true);
            while (lineTokenizer.hasMoreTokens()) {
                final String line = lineTokenizer.nextToken();
                if (line.equals("\r") || line.equals("\n")) {
                    // It's an end-of-line character, so append it as-is.
                    buffer.append(line);
                } else if (line.length() <= width) {
                    // The line fits in the specified width, so append it as-is.
                    buffer.append(padding);
                    buffer.append(line);
                } else {
                    // The line doesn't fit in the specified width, so it needs
                    // to be wrapped. Do so at space boundaries.
                    StringBuilder lineBuffer = new StringBuilder();
                    StringBuilder delimBuffer = new StringBuilder();
                    final StringTokenizer wordTokenizer = new StringTokenizer(line, " ", true);
                    while (wordTokenizer.hasMoreTokens()) {
                        final String word = wordTokenizer.nextToken();
                        if (word.equals(" ")) {
                            // It's a space, so add it to the delim buffer only
                            // if the line buffer is not empty.
                            if (lineBuffer.length() > 0) {
                                delimBuffer.append(word);
                            }
                        } else if (word.length() > width) {
                            // This is a long word that can't be wrapped, so
                            // we'll just have to make do.
                            if (lineBuffer.length() > 0) {
                                buffer.append(padding);
                                buffer.append(lineBuffer);
                                buffer.append(EOL);
                                lineBuffer = new StringBuilder();
                            }
                            buffer.append(padding);
                            buffer.append(word);
                            if (wordTokenizer.hasMoreTokens()) {
                                // The next token must be a space, so remove it.
                                // If there are still more tokens after that, then append an
                                // EOL.
                                wordTokenizer.nextToken();
                                if (wordTokenizer.hasMoreTokens()) {
                                    buffer.append(EOL);
                                }
                            }
                            if (delimBuffer.length() > 0) {
                                delimBuffer = new StringBuilder();
                            }
                        } else {
                            // It's not a space, so see if we can fit it on the
                            // current line.
                            final int newLineLength =
                                    lineBuffer.length() + delimBuffer.length() + word.length();
                            if (newLineLength < width) {
                                // It does fit on the line, so add it.
                                lineBuffer.append(delimBuffer).append(word);
                                if (delimBuffer.length() > 0) {
                                    delimBuffer = new StringBuilder();
                                }
                            } else {
                                // It doesn't fit on the line, so end the
                                // current line and start a new one.
                                buffer.append(padding);
                                buffer.append(lineBuffer);
                                buffer.append(EOL);
                                lineBuffer = new StringBuilder();
                                lineBuffer.append(word);
                                if (delimBuffer.length() > 0) {
                                    delimBuffer = new StringBuilder();
                                }
                            }
                        }
                    }
                    // If there's anything left in the line buffer, then add it
                    // to the final buffer.
                    buffer.append(padding);
                    buffer.append(lineBuffer);
                }
            }
        }
        return buffer.toString();
    }
    // Prevent instantiation.
    private Utils() {
        // Do nothing.
    }
}
opendj-cli/src/main/java/com/forgerock/opendj/cli/package-info.java
New file
@@ -0,0 +1,29 @@
/*
 * 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 2014 ForgeRock AS.
 */
/**
 * Classes implementing the OpenDJ CLI shared APIs.
 */
package com.forgerock.opendj.cli;
opendj-cli/src/main/java/org/forgerock/opendj/cli/package-info.java
@@ -23,7 +23,6 @@
 *
 *      Copyright 2014 ForgeRock AS.
 */
/**
 * Classes implementing the OpenDJ CLI shared APIs.
 */
opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties
@@ -26,4 +26,478 @@
#
# CLI messages
#
ERR_CONSOLE_INPUT_ERROR=The response could not be read from the console due to the following error: %s
ERR_ARG_NO_IDENTIFIER=The %s argument does not have either a \
 single-character or a long identifier that may be used to specify it.  At \
 least one of these must be specified for each argument
ERR_ARG_NO_VALUE_PLACEHOLDER=The %s argument is configured to take \
 a value but no value placeholder has been defined for it
ERR_ARG_NO_INT_VALUE=The %s argument does not have any value that \
 may be retrieved as an integer
ERR_ARG_CANNOT_DECODE_AS_INT=The provided value "%s" for the %s \
 argument cannot be decoded as an integer
ERR_ARG_INT_MULTIPLE_VALUES=The %s argument has multiple values and \
 therefore cannot be decoded as a single integer value
ERR_ARG_NO_BOOLEAN_VALUE=The %s argument does not have any value \
 that may be retrieved as a Boolean
ERR_ARG_CANNOT_DECODE_AS_BOOLEAN=The provided value "%s" for the %s \
 argument cannot be decoded as a Boolean
ERR_ARG_BOOLEAN_MULTIPLE_VALUES=The %s argument has multiple values \
 and therefore cannot be decoded as a single Boolean value
ERR_INTARG_LOWER_BOUND_ABOVE_UPPER_BOUND=The %s argument \
 configuration is invalid because the lower bound of %d is greater than the \
 upper bound of %d
ERR_INTARG_VALUE_BELOW_LOWER_BOUND=The provided %s value %d is \
 unacceptable because it is below the lower bound of %d
ERR_INTARG_VALUE_ABOVE_UPPER_BOUND=The provided %s value %d is \
 unacceptable because it is above the upper bound of %d
ERR_BOOLEANARG_NO_VALUE_ALLOWED=The provided %s value is \
 unacceptable because Boolean arguments are never allowed to have values
ERR_MCARG_VALUE_NOT_ALLOWED=The provided %s value %s is \
 unacceptable because it is not included in the set of allowed values for that \
 argument
ERR_FILEARG_NO_SUCH_FILE=The file %s specified for argument %s does \
 not exist
ERR_FILEARG_CANNOT_VERIFY_FILE_EXISTENCE=An error occurred while \
 trying to verify the existence of file %s specified for argument %s:  %s
ERR_FILEARG_CANNOT_OPEN_FILE=An error occurred while trying to open \
 file %s specified for argument %s for reading:  %s
ERR_FILEARG_CANNOT_READ_FILE=An error occurred while trying to read \
 from file %s specified for argument %s:  %s
ERR_FILEARG_EMPTY_FILE=The file %s specified for argument %s exists \
 but is empty
ERR_ARGPARSER_DUPLICATE_SHORT_ID=Cannot add argument %s to the \
 argument list because its short identifier -%s conflicts with the %s argument \
 that has already been defined
ERR_ARGPARSER_DUPLICATE_LONG_ID=Cannot add argument %s to the \
 argument list because its long identifier --%s conflicts with the %s argument \
 that has already been defined
ERR_ARGPARSER_CANNOT_READ_PROPERTIES_FILE=An error occurred while \
 attempting to read the contents of the argument properties file %s:  %s
ERR_ARGPARSER_TOO_MANY_TRAILING_ARGS=The provided set of \
 command-line arguments contained too many unnamed trailing arguments.  The \
 maximum number of allowed trailing arguments is %d
ERR_ARGPARSER_LONG_ARG_WITHOUT_NAME=The provided argument "%s" is \
 invalid because it does not include the argument name
ERR_ARGPARSER_NO_ARGUMENT_WITH_LONG_ID=Argument --%s is not allowed \
 for use with this program
ERR_ARGPARSER_NO_VALUE_FOR_ARGUMENT_WITH_LONG_ID=Argument --%s \
 requires a value but none was provided
ERR_ARGPARSER_VALUE_UNACCEPTABLE_FOR_LONG_ID=The provided value \
 "%s" for argument --%s is not acceptable:  %s
ERR_ARGPARSER_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_ARGPARSER_ARG_FOR_LONG_ID_DOESNT_TAKE_VALUE=A value was \
 provided for argument --%s but that argument does not take a value
ERR_ARGPARSER_INVALID_DASH_AS_ARGUMENT=The dash character by itself \
 is invalid for use as an argument name
ERR_ARGPARSER_NO_ARGUMENT_WITH_SHORT_ID=Argument -%s is not allowed \
 for use with this program
ERR_ARGPARSER_NO_VALUE_FOR_ARGUMENT_WITH_SHORT_ID=Argument -%s \
 requires a value but none was provided
ERR_ARGPARSER_VALUE_UNACCEPTABLE_FOR_SHORT_ID=The provided value \
 "%s" for argument -%s is not acceptable:  %s
ERR_ARGPARSER_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_ARGPARSER_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_ARGPARSER_DISALLOWED_TRAILING_ARGUMENT=Argument "%s" does not \
 start with one or two dashes and unnamed trailing arguments are not allowed
ERR_ARGPARSER_TOO_FEW_TRAILING_ARGUMENTS=At least %d unnamed \
 trailing arguments are required in the argument list, but too few were \
 provided
ERR_ARGPARSER_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
INFO_TIME_IN_SECONDS=%d seconds
INFO_TIME_IN_MINUTES_SECONDS=%d minutes, %d seconds
INFO_TIME_IN_HOURS_MINUTES_SECONDS=%d hours, %d minutes, %d seconds
INFO_TIME_IN_DAYS_HOURS_MINUTES_SECONDS=%d days, %d hours, %d minutes, %d \
 seconds
INFO_SUBCMDPARSER_WHERE_OPTIONS_INCLUDE=Command options:
ERR_MENU_BAD_CHOICE_SINGLE=Invalid response. Please enter a valid \
menu option
INFO_MENU_PROMPT_SINGLE=Enter choice:
INFO_MENU_PROMPT_RETURN_TO_CONTINUE=Press RETURN to continue
ERR_CONSOLE_INPUT_ERROR=The response could not be read from the console due to the following error: %s
INFO_LDAP_CONN_PROMPT_SECURITY_SERVER_CERTIFICATE=Server Certificate:
INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE=%s
INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION=Do you trust this server certificate?
INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_NO=No
INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_SESSION=Yes, for this session only
INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_ALWAYS=Yes, also add it to a truststore
INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_DETAILS=View certificate details
INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_USER_DN=User DN  : %s
INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_VALIDITY=Validity : From '%s'%n             To '%s'
INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_ISSUER=Issuer   : %s
INFO_PROMPT_SINGLE_DEFAULT=%s [%s]:
INFO_ARGPARSER_USAGE_JAVA_CLASSNAME=Usage:  java %s  {options}
INFO_ARGPARSER_USAGE_JAVA_SCRIPTNAME=Usage:  %s  {options}
INFO_ARGPARSER_USAGE_TRAILINGARGS={trailing-arguments}
INFO_ARGPARSER_USAGE_DEFAULT_VALUE=Default value: %s
#
# Extension messages
#
#
# Tools messages
#
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
INFO_PROCESSING_OPERATION=Processing %s request for %s
INFO_OPERATION_FAILED=%s operation failed
INFO_OPERATION_SUCCESSFUL=%s operation successful for DN %s
INFO_PROCESSING_COMPARE_OPERATION=Comparing type %s with value %s in \
 entry %s
INFO_COMPARE_OPERATION_RESULT_FALSE=Compare operation returned false for \
 entry %s
INFO_COMPARE_OPERATION_RESULT_TRUE=Compare operation returned true for \
 entry %s
INFO_DESCRIPTION_TRUSTALL=Trust all server SSL certificates
INFO_DESCRIPTION_BINDDN=DN to use to bind to the server
INFO_DESCRIPTION_BINDPASSWORD=Password to use to bind to \
 the server
INFO_DESCRIPTION_BINDPASSWORDFILE=Bind password file
INFO_DESCRIPTION_ENCODING=Use the specified character set for \
 command-line input
INFO_DESCRIPTION_VERBOSE=Use verbose mode
INFO_DESCRIPTION_KEYSTOREPATH=Certificate key store path
INFO_DESCRIPTION_TRUSTSTOREPATH=Certificate trust store path
INFO_DESCRIPTION_KEYSTOREPASSWORD=Certificate key store PIN
INFO_DESCRIPTION_HOST=Directory server hostname or IP address
INFO_DESCRIPTION_PORT=Directory server port number
INFO_DESCRIPTION_SHOWUSAGE=Display this usage information
INFO_DESCRIPTION_CONTROLS=Use a request control with the provided \
 information
INFO_DESCRIPTION_CONTINUE_ON_ERROR=Continue processing even if there are \
 errors
INFO_DESCRIPTION_USE_SSL=Use SSL for secure communication with the server
INFO_DESCRIPTION_START_TLS=Use StartTLS to secure communication with the \
 server
INFO_MODIFY_DESCRIPTION_DEFAULT_ADD=Treat records with no changetype as \
 add operations
INFO_SEARCH_DESCRIPTION_BASEDN=Search base DN
INFO_SEARCH_DESCRIPTION_SIZE_LIMIT=Maximum number of entries to return \
 from the search
INFO_SEARCH_DESCRIPTION_TIME_LIMIT=Maximum length of time in seconds to \
 allow for the search
INFO_SEARCH_DESCRIPTION_SEARCH_SCOPE=Search scope ('base', 'one', 'sub', \
 or 'subordinate')
INFO_SEARCH_DESCRIPTION_DEREFERENCE_POLICY=Alias dereference policy \
 ('never', 'always', 'search', or 'find')
ERR_LDAPAUTH_UNSUPPORTED_SASL_MECHANISM=The requested SASL mechanism \
 "%s" is not supported by this client
ERR_LDAPAUTH_SASL_AUTHID_REQUIRED=The "authid" SASL property is \
 required for use with the %s mechanism
INFO_DESCRIPTION_VERSION=LDAP protocol version number
ERR_DESCRIPTION_INVALID_VERSION=Invalid LDAP version number '%s'. \
 Allowed values are 2 and 3
ERR_SEARCH_NO_FILTERS=No filters specified for the search request
INFO_DESCRIPTION_DONT_WRAP=Do not wrap long lines
INFO_DESCRIPTION_NOOP=Show what would be done but do not perform any \
 operation
INFO_DESCRIPTION_TYPES_ONLY=Only retrieve attribute names but not their \
 values
INFO_DESCRIPTION_ASSERTION_FILTER=Use the LDAP assertion control with the \
 provided filter
ERR_LDAP_ASSERTION_INVALID_FILTER=The search filter provided for the \
 LDAP assertion control was invalid:  %s
INFO_DESCRIPTION_PREREAD_ATTRS=Use the LDAP ReadEntry pre-read control
INFO_DESCRIPTION_POSTREAD_ATTRS=Use the LDAP ReadEntry post-read control
INFO_LDAPMODIFY_PREREAD_ENTRY=Target entry before the operation:
INFO_LDAPMODIFY_POSTREAD_ENTRY=Target entry after the operation:
INFO_DESCRIPTION_PROXY_AUTHZID=Use the proxied authorization control with \
 the given authorization ID
INFO_DESCRIPTION_PSEARCH_INFO=Use the persistent search control
ERR_PSEARCH_MISSING_DESCRIPTOR=The request to use the persistent \
 search control did not include a descriptor that indicates the options to use \
 with that control
ERR_PSEARCH_DOESNT_START_WITH_PS=The persistent search descriptor %s \
 did not start with the required 'ps' string
ERR_PSEARCH_INVALID_CHANGE_TYPE=The provided change type value %s is \
 invalid.  The recognized change types are add, delete, modify, modifydn, and \
 any
ERR_PSEARCH_INVALID_CHANGESONLY=The provided changesOnly value %s is \
 invalid.  Allowed values are 1 to only return matching entries that have \
 changed since the beginning of the search, or 0 to also include existing \
 entries that match the search criteria
ERR_PSEARCH_INVALID_RETURN_ECS=The provided returnECs value %s is \
 invalid.  Allowed values are 1 to request that the entry change notification \
 control be included in updated entries, or 0 to exclude the control from \
 matching entries
INFO_DESCRIPTION_REPORT_AUTHZID=Use the authorization identity control
INFO_BIND_AUTHZID_RETURNED=# Bound with authorization ID %s
INFO_SEARCH_DESCRIPTION_FILENAME=File containing a list of search filter \
 strings
INFO_DESCRIPTION_MATCHED_VALUES_FILTER=Use the LDAP matched values \
 control with the provided filter
ERR_LDAP_MATCHEDVALUES_INVALID_FILTER=The provided matched values \
 filter was invalid:  %s
ERR_LDIF_FILE_CANNOT_OPEN_FOR_READ=An error occurred while \
 attempting to open the LDIF file %s for reading:  %s
ERR_LDIF_FILE_CANNOT_OPEN_FOR_WRITE=An error occurred while \
 attempting to open the LDIF file %s for writing:  %s
ERR_LDIF_FILE_READ_ERROR=An error occurred while attempting to read \
 the contents of LDIF file %s:  %s
INFO_BIND_PASSWORD_EXPIRED=# Your password has expired
INFO_BIND_PASSWORD_EXPIRING=# Your password will expire in %s
INFO_BIND_ACCOUNT_LOCKED=# Your account has been locked
INFO_BIND_MUST_CHANGE_PASSWORD=# You must change your password before any \
 other operations will be allowed
INFO_BIND_GRACE_LOGINS_REMAINING=# You have %d grace logins remaining
INFO_DESCRIPTION_USE_PWP_CONTROL=Use the password policy request control
INFO_LDAPPWMOD_DESCRIPTION_AUTHZID=Authorization ID for the \
 user entry whose password should be changed
INFO_LDAPPWMOD_DESCRIPTION_NEWPW=New password to provide \
 for the target user
INFO_LDAPPWMOD_DESCRIPTION_NEWPWFILE=Path to a file \
 containing the new password to provide for the target user
INFO_LDAPPWMOD_DESCRIPTION_CURRENTPW=Current password for \
 the target user
INFO_LDAPPWMOD_DESCRIPTION_CURRENTPWFILE=Path to a file \
 containing the current password for the target user
ERR_LDAPPWMOD_CONFLICTING_ARGS=The %s and %s arguments may not be \
 provided together
ERR_LDAPPWMOD_FAILED=The LDAP password modify operation failed: \
 %d (%s)
ERR_LDAPPWMOD_FAILURE_ERROR_MESSAGE=Error Message:  %s
ERR_LDAPPWMOD_FAILURE_MATCHED_DN=Matched DN:  %s
INFO_LDAPPWMOD_SUCCESSFUL=The LDAP password modify operation was \
 successful
INFO_LDAPPWMOD_ADDITIONAL_INFO=Additional Info:  %s
INFO_LDAPPWMOD_GENERATED_PASSWORD=Generated Password:  %s
INFO_COMPARE_CANNOT_BASE64_DECODE_ASSERTION_VALUE=The assertion value was \
 indicated to be base64-encoded, but an error occurred while trying to decode \
 the value
INFO_COMPARE_CANNOT_READ_ASSERTION_VALUE_FROM_FILE=Unable to read the \
 assertion value from the specified file:  %s
ERR_LDAPCOMPARE_NO_DNS=No entry DNs provided for the compare \
 operation
INFO_LDAPCOMPARE_TOOL_DESCRIPTION=This utility can be used to perform \
 LDAP compare operations in the Directory Server
INFO_LDAPMODIFY_TOOL_DESCRIPTION=This utility can be used to perform LDAP \
 modify, add, delete, and modify DN operations in the Directory Server
INFO_LDAPPWMOD_TOOL_DESCRIPTION=This utility can be used to perform LDAP \
 password modify operations in the Directory Server
INFO_LDAPSEARCH_TOOL_DESCRIPTION=This utility can be used to perform LDAP \
 search operations in the Directory Server
ERR_TOOL_CONFLICTING_ARGS=You may not provide both the --%s and \
 the --%s arguments
ERR_LDAPCOMPARE_NO_ATTR=No attribute was specified to use as the \
 target for the comparison
ERR_LDAPCOMPARE_INVALID_ATTR_STRING=Invalid attribute string '%s'. \
 The attribute string must be in one of the following forms: \
 'attribute:value', 'attribute::base64value', or 'attribute:<valueFilePath'
ERR_TOOL_INVALID_CONTROL_STRING=Invalid control specification '%s'
ERR_TOOL_SASLEXTERNAL_NEEDS_SSL_OR_TLS=SASL EXTERNAL \
 authentication may only be requested if SSL or StartTLS is used
ERR_TOOL_SASLEXTERNAL_NEEDS_KEYSTORE=SASL EXTERNAL authentication \
 may only be used if a client certificate key store is specified
INFO_LDAPSEARCH_PSEARCH_CHANGE_TYPE=# Persistent search change type:  %s
INFO_LDAPSEARCH_PSEARCH_PREVIOUS_DN=# Persistent search previous entry \
 DN:  %s
INFO_LDAPSEARCH_ACCTUSABLE_HEADER=# Account Usability Response Control
INFO_LDAPSEARCH_ACCTUSABLE_IS_USABLE=#   The account is usable
INFO_LDAPSEARCH_ACCTUSABLE_TIME_UNTIL_EXPIRATION=#   Time until password \
 expiration:  %s
INFO_LDAPSEARCH_ACCTUSABLE_NOT_USABLE=#   The account is not usable
INFO_LDAPSEARCH_ACCTUSABLE_ACCT_INACTIVE=#   The account has been \
 deactivated
INFO_LDAPSEARCH_ACCTUSABLE_PW_RESET=#   The password has been reset
INFO_LDAPSEARCH_ACCTUSABLE_PW_EXPIRED=#   The password has expired
INFO_LDAPSEARCH_ACCTUSABLE_REMAINING_GRACE=#   Number of grace logins \
 remaining:  %d
INFO_LDAPSEARCH_ACCTUSABLE_LOCKED=#   The account is locked
INFO_LDAPSEARCH_ACCTUSABLE_TIME_UNTIL_UNLOCK=#   Time until the account \
 is unlocked:  %s
INFO_DESCRIPTION_KEYSTOREPASSWORD_FILE=Certificate key store PIN file
INFO_DESCRIPTION_TRUSTSTOREPASSWORD=Certificate trust store PIN
INFO_DESCRIPTION_TRUSTSTOREPASSWORD_FILE=Certificate trust store PIN file
INFO_DESCRIPTION_COUNT_ENTRIES=Count the number of entries returned by \
 the server
INFO_LDAPSEARCH_MATCHING_ENTRY_COUNT=# Total number of matching entries: \
 %d
INFO_DESCRIPTION_SIMPLE_PAGE_SIZE=Use the simple paged results control \
 with the given page size
ERR_PAGED_RESULTS_REQUIRES_SINGLE_FILTER=The simple paged results \
 control may only be used with a single search filter
INFO_DESCRIPTION_CERT_NICKNAME=Nickname of certificate for SSL client \
 authentication
ERR_TOOL_RESULT_CODE=Result Code:  %d (%s)
ERR_TOOL_ERROR_MESSAGE=Additional Information:  %s
ERR_TOOL_MATCHED_DN=Matched DN:  %s
INFO_LDAPMODIFY_DESCRIPTION_FILENAME=LDIF file containing \
 the changes to apply
INFO_DESCRIPTION_SORT_ORDER=Sort the results using the provided sort \
 order
ERR_LDAP_SORTCONTROL_INVALID_ORDER=The provided sort order was \
 invalid:  %s
INFO_DESCRIPTION_VLV=Use the virtual list view control to retrieve the \
 specified results page
ERR_LDAPSEARCH_VLV_REQUIRES_SORT=If the --%s argument is provided, \
 then the --%s argument must also be given
ERR_LDAPSEARCH_VLV_INVALID_DESCRIPTOR=The provided virtual list view \
 descriptor was invalid.  It must be a value in the form \
 'beforeCount:afterCount:offset:contentCount' (where offset specifies the \
 index of the target entry and contentCount specifies the estimated total \
 number of results or zero if it is not known), or \
 'beforeCount:afterCount:assertionValue' (where the entry should be the first \
 entry whose primary sort value is greater than or equal to the provided \
 assertionValue).  In either case, beforeCount is the number of entries to \
 return before the target value and afterCount is the number of entries to \
 return after the target value
WARN_LDAPSEARCH_SORT_ERROR=# Server-side sort failed:  %s
INFO_LDAPSEARCH_VLV_TARGET_OFFSET=# VLV Target Offset:  %d
INFO_LDAPSEARCH_VLV_CONTENT_COUNT=# VLV Content Count:  %d
WARN_LDAPSEARCH_VLV_ERROR=# Virtual list view processing failed: \
 %s
INFO_DESCRIPTION_EFFECTIVERIGHTS_USER=Use geteffectiverights control with \
 the provided authzid
INFO_DESCRIPTION_EFFECTIVERIGHTS_ATTR=Specifies geteffectiverights \
 control specific attribute list
ERR_EFFECTIVERIGHTS_INVALID_AUTHZID=The authorization ID "%s" \
 contained in the geteffectiverights control is invalid because it does not \
 start with "dn:" to indicate a user DN
INFO_DESCRIPTION_PRODUCT_VERSION=Display Directory Server version \
 information
INFO_DESCRIPTION_SCRIPT_FRIENDLY=Use script-friendly mode
ERR_LDAP_CONN_CANNOT_INITIALIZE_SSL=ERROR:  Unable to perform SSL \
 initialization:  %s
ERR_LDAP_CONN_CANNOT_PARSE_SASL_OPTION=ERROR:  The provided SASL \
 option string "%s" could not be parsed in the form "name=value"
INFO_LDAP_CONN_DESCRIPTION_SASLOPTIONS=SASL bind options
INFO_DESCRIPTION_PROP_FILE_PATH=Path to the file containing default \
  property values used for command line arguments
INFO_DESCRIPTION_NO_PROP_FILE=No properties file will be \
  used to get default command line argument values
INFO_DESCRIPTION_GENERAL_ARGS=General options:
INFO_DESCRIPTION_IO_ARGS=Utility input/output options:
INFO_DESCRIPTION_LDAP_CONNECTION_ARGS=LDAP connection options:
INFO_FILE_PLACEHOLDER={file}
INFO_KEYSTOREPATH_PLACEHOLDER={keyStorePath}
INFO_TRUSTSTOREPATH_PLACEHOLDER={trustStorePath}
INFO_BINDPWD_FILE_PLACEHOLDER={bindPasswordFile}
INFO_HOST_PLACEHOLDER={host}
INFO_PORT_PLACEHOLDER={port}
INFO_BASEDN_PLACEHOLDER={baseDN}
INFO_BINDDN_PLACEHOLDER={bindDN}
INFO_BINDPWD_PLACEHOLDER={bindPassword}
INFO_KEYSTORE_PWD_PLACEHOLDER={keyStorePassword}
INFO_TRUSTSTORE_PWD_FILE_PLACEHOLDER={path}
INFO_TRUSTSTORE_PWD_PLACEHOLDER={trustStorePassword}
INFO_NICKNAME_PLACEHOLDER={nickname}
INFO_ASSERTION_FILTER_PLACEHOLDER={filter}
INFO_FILTER_PLACEHOLDER={filter}
INFO_PROXYAUTHID_PLACEHOLDER={authzID}
INFO_SASL_OPTION_PLACEHOLDER={name=value}
INFO_PROTOCOL_VERSION_PLACEHOLDER={version}
INFO_PROP_FILE_PATH_PLACEHOLDER={propertiesFilePath}
INFO_ATTRIBUTE_PLACEHOLDER={attribute}
INFO_NUM_ENTRIES_PLACEHOLDER={numEntries}
INFO_LDAP_CONTROL_PLACEHOLDER={controloid[:criticality[:value|::b64value|:<filePath]]}
INFO_ENCODING_PLACEHOLDER={encoding}
INFO_ATTRIBUTE_LIST_PLACEHOLDER={attrList}
INFO_NEW_PASSWORD_PLACEHOLDER={newPassword}
INFO_CURRENT_PASSWORD_PLACEHOLDER={currentPassword}
INFO_SEARCH_SCOPE_PLACEHOLDER={searchScope}
INFO_SORT_ORDER_PLACEHOLDER={sortOrder}
INFO_VLV_PLACEHOLDER={before:after:index:count | before:after:value}
INFO_DEREFERENCE_POLICE_PLACEHOLDER={dereferencePolicy}
INFO_SIZE_LIMIT_PLACEHOLDER={sizeLimit}
INFO_TIME_LIMIT_PLACEHOLDER={timeLimit}
INFO_TARGETDN_PLACEHOLDER={targetDN}
INFO_KEYSTORE_PWD_FILE_PLACEHOLDER={keyStorePasswordFile}
INFO_PSEARCH_PLACEHOLDER=ps[:changetype[:changesonly[:entrychgcontrols]]]
ERR_CANNOT_READ_TRUSTSTORE=Cannot access trust store '%s'.  Verify \
 that the provided trust store exists and that you have read access rights to it
ERR_CANNOT_READ_KEYSTORE=Cannot access key store '%s'.  Verify \
 that the provided key store exists and that you have read access rights to it
INFO_DESCRIPTION_ADMIN_PORT=Directory server administration port number
ERR_LDAPCOMPARE_ERROR_READING_FILE=An error occurred reading file \
 '%s'.  Check that the file exists and that you have read access rights to \
 it.  Details: %s
ERR_LDAPCOMPARE_FILENAME_AND_DNS=Both entry DNs and a file name \
 were provided for the compare operation.  These arguments are not compatible
INFO_ERROR_EMPTY_RESPONSE=ERROR: a response must be provided in order to continue
ERR_DECODE_CONTROL_FAILURE=# %s
INFO_SEARCHRATE_TOOL_DESCRIPTION=This utility can be used to measure \
  search throughput and response time of a directory service using \
  user-defined searches.\n\n\
  Example:\n\n\ \ searchrate -p 1389 -D "cn=directory manager" -w password \\\n\
  \ \ \ \ -F -c 4 -t 4 -b "dc=example,dc=com" -g "rand(0,2000)" "(uid=user.%%d)"
INFO_SEARCHRATE_TOOL_DESCRIPTION_BASEDN=Base DN format string.
INFO_MODRATE_TOOL_DESCRIPTION=This utility can be used to measure \
  modify throughput and response time of a directory service using \
  user-defined modifications.\n\n\
  Example:\n\n\ \ modrate -p 1389 -D "cn=directory manager" -w password \\\n\
  \ \ \ \ -F -c 4 -t 4 -b "uid=user.%%d,ou=people,dc=example,dc=com" \\\n\
  \ \ \ \ -g "rand(0,2000)" -g "randstr(16)" 'description:%%2$s'
INFO_MODRATE_TOOL_DESCRIPTION_TARGETDN=Target entry DN format string
INFO_AUTHRATE_TOOL_DESCRIPTION=This utility can be used to measure \
  bind throughput and response time of a directory service using \
  user-defined bind or search-then-bind operations.\n\nFormat strings may be \
  used in the bind DN option as well as the authid and authzid SASL bind \
  options. A search operation may be used to retrieve the bind DN by \
  specifying the base DN and a filter. The retrieved entry DN will be appended \
  as the last argument in the argument list when evaluating format strings.\n\n\
  Example (bind only):\n\n\ \ authrate -p 1389 -D "uid=user.%%d,ou=people,dc=example,dc=com" \\\n\
  \ \ \ \  -w password -f -c 10 -g "rand(0,2000)"\n\n\
  Example (search then bind):\n\n\ \ authrate -p 1389 -D '%%2$s' -w password -f -c 10 \\\n\
  \ \ \ \ -b "ou=people,dc=example,dc=com" -s one -g "rand(0,2000)" "(uid=user.%%d)"
INFO_OUTPUT_LDIF_FILE_PLACEHOLDER={file}
INFO_LDIFMODIFY_DESCRIPTION_OUTPUT_FILENAME=Write updated entries to %s \
 instead of stdout
INFO_LDIFDIFF_DESCRIPTION_OUTPUT_FILENAME=Write differences to %s \
 instead of stdout
INFO_LDIFSEARCH_DESCRIPTION_OUTPUT_FILENAME=Write search results to %s \
 instead of stdout
ERR_LDIFMODIFY_MULTIPLE_USES_OF_STDIN=Unable to use stdin for both the source \
 LDIF and changes LDIF
ERR_LDIFDIFF_MULTIPLE_USES_OF_STDIN=Unable to use stdin for both the source \
 LDIF and target LDIF
ERR_LDIFMODIFY_PATCH_FAILED=The changes could not be applied for the following \
 reason: %s
ERR_LDIFDIFF_DIFF_FAILED=The differences could not be computed for the following \
 reason: %s
ERR_LDIFSEARCH_FAILED=The search could not be performed for the following \
 reason: %s
INFO_LDIFMODIFY_TOOL_DESCRIPTION=This utility can be used to apply a set of \
 modify, add, and delete operations to entries contained in an LDIF file
INFO_LDIFDIFF_TOOL_DESCRIPTION=This utility can be used to compare two LDIF \
 files and report the differences in LDIF format
INFO_LDIFSEARCH_TOOL_DESCRIPTION=This utility can be used to perform search \
 operations against entries contained in an LDIF file
 #
 # MakeLDIF tool
 #
INFO_MAKELDIF_TOOL_DESCRIPTION=This utility can be used to generate LDIF \
 data based on a definition in a template file
INFO_CONSTANT_PLACEHOLDER={name=value}
INFO_SEED_PLACEHOLDER={seed}
INFO_PATH_PLACEHOLDER={path}
INFO_MAKELDIF_DESCRIPTION_CONSTANT=A constant that overrides the value \
 set in the template file
INFO_MAKELDIF_DESCRIPTION_LDIF=The path to the LDIF file to be written
INFO_MAKELDIF_DESCRIPTION_SEED=The seed to use to initialize the random \
 number generator
INFO_MAKELDIF_DESCRIPTION_HELP=Show this usage information
INFO_MAKELDIF_DESCRIPTION_RESOURCE_PATH=Path to look for \
 MakeLDIF resources (e.g., data files)
ERR_MAKELDIF_NO_SUCH_RESOURCE_DIRECTORY=The specified resource \
 directory %s could not be found
INFO_MAKELDIF_PROCESSED_N_ENTRIES=Processed %d entries
INFO_MAKELDIF_PROCESSING_COMPLETE=LDIF processing complete. %d entries \
 written
ERR_MAKELDIF_EXCEPTION_DURING_PARSE=An error occurred while \
 parsing template file :  %s
ERR_MAKELDIF_UNABLE_TO_CREATE_LDIF=An error occurred while \
 attempting to open LDIF file %s for writing:  %s
ERR_MAKELDIF_ERROR_WRITING_LDIF=An error occurred while writing data \
 to LDIF file %s:  %s
ERR_MAKELDIF_EXCEPTION_DURING_PROCESSING=An error occurred while \
 processing :  %s
ERR_CONSTANT_ARG_CANNOT_DECODE=Unable to parse a constant argument, \
 expecting name=value but got %s