From 4f50cdb97c5bc421bd1dccada157d45c3bdb5fc1 Mon Sep 17 00:00:00 2001
From: Violette Roche-Montane <violette.roche-montane@forgerock.com>
Date: Wed, 29 Jan 2014 08:25:04 +0000
Subject: [PATCH] OPENDJ-1303 Split out CLI support from opendj-ldap-toolkit into a separate Maven module, "opendj-cli" Part I. Added required classes.
---
opendj-cli/src/main/java/com/forgerock/opendj/cli/IntegerArgument.java | 483 ++++
opendj-cli/src/main/java/com/forgerock/opendj/cli/ConsoleApplication.java | 328 +++
opendj-cli/src/main/java/com/forgerock/opendj/cli/package-info.java | 29
opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentGroup.java | 170 +
opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentException.java | 73
opendj-cli/src/main/java/com/forgerock/opendj/cli/StringArgument.java | 136 +
opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiChoiceArgument.java | 239 ++
opendj-cli/src/main/java/com/forgerock/opendj/cli/CliConstants.java | 625 ++++++
opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java | 336 +++
opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java | 651 ++++++
opendj-cli/src/main/java/com/forgerock/opendj/cli/FileBasedArgument.java | 242 ++
opendj-cli/src/main/java/com/forgerock/opendj/cli/CommonArguments.java | 120 +
opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiColumnPrinter.java | 483 ++++
opendj-cli/src/main/java/com/forgerock/opendj/cli/BooleanArgument.java | 108 +
opendj-cli/src/main/java/com/forgerock/opendj/cli/CLIException.java | 2
opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties | 476 ++++
opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java | 1596 +++++++++++++++
opendj-cli/src/main/java/org/forgerock/opendj/cli/package-info.java | 1
18 files changed, 6,095 insertions(+), 3 deletions(-)
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java
new file mode 100644
index 0000000..a15be6d
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java
@@ -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);
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentException.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentException.java
new file mode 100644
index 0000000..61ba1f8
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentException.java
@@ -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;
+ }
+
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentGroup.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentGroup.java
new file mode 100644
index 0000000..d3d8c7c
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentGroup.java
@@ -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;
+ }
+
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java
new file mode 100644
index 0000000..0b1a392
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ArgumentParser.java
@@ -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);
+ }
+ }
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/BooleanArgument.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/BooleanArgument.java
new file mode 100644
index 0000000..c40d7bc
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/BooleanArgument.java
@@ -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;
+ }
+}
diff --git a/opendj-cli/src/main/java/org/forgerock/opendj/cli/CLIException.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/CLIException.java
similarity index 98%
rename from opendj-cli/src/main/java/org/forgerock/opendj/cli/CLIException.java
rename to opendj-cli/src/main/java/com/forgerock/opendj/cli/CLIException.java
index 3cd4fdb..b1f5344 100755
--- a/opendj-cli/src/main/java/org/forgerock/opendj/cli/CLIException.java
+++ b/opendj-cli/src/main/java/com/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;
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/CliConstants.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/CliConstants.java
new file mode 100755
index 0000000..92b3ff0
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/CliConstants.java
@@ -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() {
+
+ }
+
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/CommonArguments.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/CommonArguments.java
new file mode 100644
index 0000000..038ed2a
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/CommonArguments.java
@@ -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;
+ }
+
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/ConsoleApplication.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ConsoleApplication.java
new file mode 100755
index 0000000..fa3e472
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ConsoleApplication.java
@@ -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);
+ }
+ }
+
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/FileBasedArgument.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/FileBasedArgument.java
new file mode 100644
index 0000000..f621b9b
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/FileBasedArgument.java
@@ -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;
+ }
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/IntegerArgument.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/IntegerArgument.java
new file mode 100644
index 0000000..5ede248
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/IntegerArgument.java
@@ -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;
+ }
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiChoiceArgument.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiChoiceArgument.java
new file mode 100644
index 0000000..44d4632
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiChoiceArgument.java
@@ -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;
+ }
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiColumnPrinter.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiColumnPrinter.java
new file mode 100644
index 0000000..c25f19d
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiColumnPrinter.java
@@ -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, "-");
+ * String oneRow[] = new String[3];
+ * oneRow[0] = "User Name";
+ * oneRow[1] = "Email Address";
+ * oneRow[2] = "Phone Number";
+ * mp.addTitle(oneRow);
+ * oneRow[0] = "Bob";
+ * oneRow[1] = "bob@foo.com";
+ * oneRow[2] = "123-4567";
+ * mp.add(oneRow);
+ * oneRow[0] = "John";
+ * oneRow[1] = "john@foo.com";
+ * oneRow[2] = "456-7890";
+ * 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, "-");
+ * 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] = "Name";
+ * oneRow[1] = "";
+ * oneRow[2] = "Contact";
+ * oneRow[3] = "";
+ * tp.addTitle(oneRow, span);
+ * oneRow[0] = "First";
+ * oneRow[1] = "Last";
+ * oneRow[2] = "Email";
+ * oneRow[3] = "Phone";
+ * tp.addTitle(oneRow);
+ * oneRow[0] = "Bob";
+ * oneRow[1] = "Jones";
+ * oneRow[2] = "bob@foo.com";
+ * oneRow[3] = "123-4567";
+ * tp.add(oneRow);
+ * oneRow[0] = "John";
+ * oneRow[1] = "Doe";
+ * oneRow[2] = "john@foo.com";
+ * oneRow[3] = "456-7890";
+ * 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 < 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(" ");
+ }
+ }
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/StringArgument.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/StringArgument.java
new file mode 100644
index 0000000..7c5fb6d
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/StringArgument.java
@@ -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;
+ }
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java
new file mode 100644
index 0000000..a5c7012
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java
@@ -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.
+ }
+}
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/package-info.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/package-info.java
new file mode 100644
index 0000000..a6ff22a
--- /dev/null
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/package-info.java
@@ -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;
diff --git a/opendj-cli/src/main/java/org/forgerock/opendj/cli/package-info.java b/opendj-cli/src/main/java/org/forgerock/opendj/cli/package-info.java
index 53090ca..c6d2414 100644
--- a/opendj-cli/src/main/java/org/forgerock/opendj/cli/package-info.java
+++ b/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.
*/
diff --git a/opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties b/opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties
index d330757..bfb49e9 100755
--- a/opendj-cli/src/main/resources/com/forgerock/opendj/cli/cli.properties
+++ b/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
\ No newline at end of file
+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
--
Gitblit v1.10.0