From 9b1384eb5e70df3e6bc1fec5aed5c841adbd094b Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Thu, 11 Feb 2016 13:46:57 +0000
Subject: [PATCH] OPENDJSDK-42 Cli arguments fluent builder

---
 opendj-sdk/opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java |  406 +++++++++++++++++++++++++++++----------------------------
 1 files changed, 208 insertions(+), 198 deletions(-)

diff --git a/opendj-sdk/opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java b/opendj-sdk/opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java
index 8044d1d..9f0d878 100644
--- a/opendj-sdk/opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java
+++ b/opendj-sdk/opendj-cli/src/main/java/com/forgerock/opendj/cli/Argument.java
@@ -34,6 +34,7 @@
 
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizableMessageBuilder;
+import org.forgerock.util.Reject;
 
 /**
  * This class defines a generic argument that may be used in the argument list
@@ -41,38 +42,194 @@
  * order to provide specific functionality.
  */
 public abstract class Argument implements DocDescriptionSupplement {
-    /** 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 default value for the argument if none other is provided. */
-    private String defaultValue;
+
+    /**
+     * An abstract base class to build a generic {@link Argument}.
+     *
+     * @param <B>
+     *         The concrete {@link ArgumentBuilder} subclass.
+     * @param <T>
+     *         The default value type of the {@link Argument}.
+     * @param <A>
+     *         The concrete {@link Argument} type to build.
+     */
+    static abstract class ArgumentBuilder<B extends ArgumentBuilder<B, T, A>, T, A extends Argument> {
+        T defaultValue;
+        LocalizableMessage description;
+        LocalizableMessage docDescriptionSupplement;
+        boolean hidden;
+        final String longIdentifier;
+        boolean multiValued;
+        boolean needsValue = true;
+        boolean required;
+        Character shortIdentifier;
+        LocalizableMessage valuePlaceholder;
+
+        ArgumentBuilder(final String longIdentifier) {
+            Reject.ifNull(longIdentifier, "An argument must have a long identifier");
+            this.longIdentifier = longIdentifier;
+        }
+
+        abstract B getThis();
+
+        /**
+         * Build the argument.
+         *
+         * @return The argument built.
+         * @throws ArgumentException
+         *         If there is a problem with any of the parameters used to
+         *         create this argument.
+         */
+        public abstract A buildArgument() throws ArgumentException;
+
+        /**
+         * Build the argument and add it to the provided {@link ArgumentParser}.
+         *
+         * @param parser
+         *         The argument parser.
+         * @return The argument built.
+         * @throws ArgumentException
+         *         If there is a problem with any of the parameters used to
+         *         create this argument.
+         */
+        public A buildAndAddToParser(final ArgumentParser parser) throws ArgumentException {
+            final A arg = buildArgument();
+            parser.addArgument(arg);
+            return arg;
+        }
+
+        /**
+         * Build the argument and add it to the provided {@link SubCommand}.
+         *
+         * @param subCommand
+         *         The sub command.
+         * @return The argument built.
+         * @throws ArgumentException
+         *         If there is a problem with any of the parameters used to
+         *         create this argument.
+         */
+        public A buildAndAddToSubCommand(final SubCommand subCommand) throws ArgumentException {
+            final A arg = buildArgument();
+            subCommand.addArgument(arg);
+            return arg;
+        }
+
+        /**
+         * Sets this argument default value.
+         *
+         * @param defaultValue
+         *         The default value.
+         * @return This builder.
+         */
+        public B defaultValue(final T defaultValue) {
+            this.defaultValue = defaultValue;
+            return getThis();
+        }
+
+        /**
+         * Sets this argument description.
+         *
+         * @param description
+         *         The localized description.
+         * @return This builder.
+         */
+        public B description(final LocalizableMessage description) {
+            this.description = description;
+            return getThis();
+        }
+
+        /**
+         * Sets a supplement to the description intended for use in generated reference documentation.
+         *
+         * @param docDescriptionSupplement
+         *         The supplement to the description for use in generated reference documentation.
+         * @return This builder.
+         */
+        public B docDescriptionSupplement(final LocalizableMessage docDescriptionSupplement) {
+            this.docDescriptionSupplement = docDescriptionSupplement;
+            return getThis();
+        }
+
+        /**
+         * Specifies that this argument is hidden.
+         *
+         * @return This builder.
+         */
+        public B hidden() {
+            this.hidden = true;
+            return getThis();
+        }
+
+        /**
+         * Specifies that this argument may have multiple values.
+         *
+         * @return This builder.
+         */
+        public B multiValued() {
+            this.multiValued = true;
+            return getThis();
+        }
+
+        /**
+         * Specifies that this argument is required.
+         *
+         * @return This builder.
+         */
+        public B required() {
+            this.required = true;
+            return getThis();
+        }
+
+        /**
+         * Sets this argument single-character identifier.
+         *
+         * @param shortIdentifier
+         *         The single-character identifier.
+         * @return This builder.
+         */
+        public B shortIdentifier(final Character shortIdentifier) {
+            this.shortIdentifier = shortIdentifier;
+            return getThis();
+        }
+
+        /**
+         * Sets this argument value placeholder, which will be used in usage information.
+         *
+         * @param valuePlaceholder
+         *         The localized value placeholder.
+         * @return This builder.
+         */
+        public B valuePlaceholder(final LocalizableMessage valuePlaceholder) {
+            this.valuePlaceholder = valuePlaceholder;
+            return getThis();
+        }
+    }
+
+    /** The long identifier for this argument. */
+    final String longIdentifier;
 
     /** The single-character identifier for this argument. */
     private final Character shortIdentifier;
-    /** The long identifier for this argument. */
-    private final String longIdentifier;
-
     /** The unique ID of the description for this argument. */
     private final LocalizableMessage description;
+    /** Indicates whether this argument should be hidden in the usage information. */
+    private final boolean isHidden;
+    /** Indicates whether this argument may be specified more than once for multiple values. */
+    private final boolean isMultiValued;
+    /** Indicates whether this argument is required to have a value. */
+    private final boolean isRequired;
+    /** Indicates whether this argument requires a value. */
+    private final boolean needsValue;
+    /** The default value for the argument if none other is provided. */
+    private final String defaultValue;
+    /** The value placeholder for this argument, which will be used in usage information. */
+    private final LocalizableMessage valuePlaceholder;
 
     /** The set of values for this argument. */
     private final LinkedList<String> values = new LinkedList<>();
 
-    /** 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 command-line arguments. */
+    private boolean isPresent;
 
     /**
      * Indicates whether this argument was provided in the set of
@@ -80,71 +237,24 @@
      */
     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) {
-            throw new ArgumentException(ERR_ARG_NO_IDENTIFIER.get(name));
-        }
+    <B extends ArgumentBuilder<B, T, A>, T, A extends Argument> Argument(final ArgumentBuilder<B, T, A> builder)
+            throws ArgumentException {
+        this.shortIdentifier = builder.shortIdentifier;
+        this.longIdentifier = builder.longIdentifier;
+        this.isRequired = builder.required;
+        this.isMultiValued = builder.multiValued;
+        this.needsValue = builder.needsValue;
+        this.valuePlaceholder = builder.valuePlaceholder;
+        this.defaultValue = builder.defaultValue != null ? String.valueOf(builder.defaultValue) : null;
+        this.description = builder.description;
+        this.isHidden = builder.hidden;
+        this.docDescriptionSupplement = builder.docDescriptionSupplement;
 
         if (needsValue && valuePlaceholder == null) {
-            throw new ArgumentException(ERR_ARG_NO_VALUE_PLACEHOLDER.get(name));
+            throw new ArgumentException(ERR_ARG_NO_VALUE_PLACEHOLDER.get(longIdentifier));
         }
 
         isPresent = false;
-        isHidden = false;
     }
 
     /**
@@ -201,11 +311,6 @@
         return docDescriptionSupplement != null ? docDescriptionSupplement : LocalizableMessage.EMPTY;
     }
 
-    @Override
-    public void setDocDescriptionSupplement(final LocalizableMessage docDescriptionSupplement) {
-        this.docDescriptionSupplement = docDescriptionSupplement;
-    }
-
     /**
      * Retrieves the value of this argument as an integer.
      *
@@ -216,19 +321,19 @@
      */
     public int getIntValue() throws ArgumentException {
         if (values.isEmpty()) {
-            throw new ArgumentException(ERR_ARG_NO_INT_VALUE.get(name));
+            throw new ArgumentException(ERR_ARG_NO_INT_VALUE.get(longIdentifier));
         }
 
         final Iterator<String> iterator = values.iterator();
         final String valueString = iterator.next();
         if (iterator.hasNext()) {
-            throw new ArgumentException(ERR_ARG_INT_MULTIPLE_VALUES.get(name));
+            throw new ArgumentException(ERR_ARG_INT_MULTIPLE_VALUES.get(longIdentifier));
         }
 
         try {
             return Integer.parseInt(valueString);
         } catch (final Exception e) {
-            throw new ArgumentException(ERR_ARG_CANNOT_DECODE_AS_INT.get(valueString, name), e);
+            throw new ArgumentException(ERR_ARG_CANNOT_DECODE_AS_INT.get(valueString, longIdentifier), e);
         }
     }
 
@@ -244,29 +349,6 @@
     }
 
     /**
-     * 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.
      *
@@ -388,44 +470,6 @@
     }
 
     /**
-     * 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 this argument is present in the parsed set of
      * command-line arguments.
      *
@@ -437,52 +481,8 @@
         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;
+    void valueSetByProperty() {
+        isValueSetByProperty = true;
     }
 
     /**
@@ -519,4 +519,14 @@
         sb.append(")");
         return sb.toString();
     }
+
+    @Override
+    public boolean equals(final Object arg) {
+        return this == arg || (arg instanceof Argument && ((Argument) arg).longIdentifier.equals(this.longIdentifier));
+    }
+
+    @Override
+    public int hashCode() {
+        return longIdentifier.hashCode();
+    }
 }

--
Gitblit v1.10.0