Skip to content

Commit df52eee

Browse files
committed
[CLI-224] Added new fluent builder API to Option, deprecating OptionBuilder, adapting the PatternOptionBuilder to use the new API, thanks to Duncan Jones.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/cli/trunk@1444720 13f79535-47bb-0310-9956-ffa450edef68
1 parent e89475c commit df52eee

5 files changed

Lines changed: 262 additions & 11 deletions

File tree

src/changes/changes.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
<body>
2424

2525
<release version="1.3" date="in SVN">
26+
<action type="add" dev="tn" issue="CLI-224" due-to="Duncan Jones, Brian Blount">
27+
Added new fluent API to create Option instances via builder class Option.Builder.
28+
This replaces the now deprecated OptionBuilder.
29+
</action>
2630
<action type="fix" dev="tn" issue="CLI-218" due-to="Sven">
2731
Clarify javadoc for CommandLine.getOptionValue() that the first specified
2832
argument will be returned.

src/main/java/org/apache/commons/cli/Option.java

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,24 @@ public class Option implements Cloneable, Serializable
7676
/** the character that is the value separator */
7777
private char valuesep;
7878

79+
/**
80+
* Private constructor used by the nested Builder class.
81+
*
82+
* @param builder builder used to create this option
83+
*/
84+
private Option(final Builder builder)
85+
{
86+
this.argName = builder.argName;
87+
this.description = builder.description;
88+
this.longOpt = builder.longOpt;
89+
this.numberOfArgs = builder.numberOfArgs;
90+
this.opt = builder.opt;
91+
this.optionalArg = builder.optionalArg;
92+
this.required = builder.required;
93+
this.type = builder.type;
94+
this.valuesep = builder.valuesep;
95+
}
96+
7997
/**
8098
* Creates an Option using the specified parameters.
8199
* The option does not take an argument.
@@ -727,4 +745,174 @@ boolean requiresArg()
727745
return acceptsArg();
728746
}
729747
}
748+
749+
/**
750+
* A nested builder class to create <code>Option</code> instances
751+
* using descriptive methods.
752+
* <p>
753+
* Example usage:
754+
* <pre>
755+
* Option option = new Option.Builder("a", "Long description")
756+
* .required(true)
757+
* .longOpt("arg-name")
758+
* .build();
759+
* </pre>
760+
*
761+
* @since 1.3
762+
*/
763+
public static class Builder
764+
{
765+
/** the name of the option */
766+
private final String opt;
767+
768+
/** description of the option */
769+
private final String description;
770+
771+
/** the long representation of the option */
772+
private String longOpt;
773+
774+
/** the name of the argument for this option */
775+
private String argName;
776+
777+
/** specifies whether this option is required to be present */
778+
private boolean required;
779+
780+
/** specifies whether the argument value of this Option is optional */
781+
private boolean optionalArg;
782+
783+
/** the number of argument values this option can have */
784+
private int numberOfArgs = UNINITIALIZED;
785+
786+
/** the type of this Option */
787+
private Class<?> type = String.class;
788+
789+
/** the character that is the value separator */
790+
private char valuesep;
791+
792+
/**
793+
* Constructs a new <code>Builder</code> with the minimum
794+
* required parameters for an <code>Option</code> instance.
795+
*
796+
* @param opt short representation of the option
797+
* @param description describes the function of the option
798+
* @throws IllegalArgumentException if there are any non valid
799+
* Option characters in <code>opt</code>.
800+
*/
801+
public Builder(final String opt, final String description)
802+
throws IllegalArgumentException
803+
{
804+
OptionValidator.validateOption(opt);
805+
this.opt = opt;
806+
this.description = description;
807+
}
808+
809+
/**
810+
* Sets the display name for the argument value.
811+
*
812+
* @param argName the display name for the argument value.
813+
* @return this builder, to allow method chaining
814+
*/
815+
public Builder argName(final String argName)
816+
{
817+
this.argName = argName;
818+
return this;
819+
}
820+
821+
/**
822+
* Sets the long name of the Option.
823+
*
824+
* @param longOpt the long name of the Option
825+
* @return this builder, to allow method chaining
826+
*/
827+
public Builder longOpt(final String longOpt)
828+
{
829+
this.longOpt = longOpt;
830+
return this;
831+
}
832+
833+
/**
834+
* Sets the number of argument values the Option can take.
835+
*
836+
* @param numberOfArgs the number of argument values
837+
* @return this builder, to allow method chaining
838+
*/
839+
public Builder numberOfArgs(final int numberOfArgs)
840+
{
841+
this.numberOfArgs = numberOfArgs;
842+
return this;
843+
}
844+
845+
/**
846+
* Sets whether the Option can have an optional argument.
847+
*
848+
* @param isOptional specifies whether the Option can have
849+
* an optional argument.
850+
* @return this builder, to allow method chaining
851+
*/
852+
public Builder optionalArg(final boolean isOptional)
853+
{
854+
this.optionalArg = isOptional;
855+
return this;
856+
}
857+
858+
/**
859+
* Sets whether the Option is mandatory.
860+
*
861+
* @param required specifies whether the Option is mandatory
862+
* @return this builder, to allow method chaining
863+
*/
864+
public Builder required(final boolean required)
865+
{
866+
this.required = required;
867+
return this;
868+
}
869+
870+
/**
871+
* Sets the type of the Option.
872+
*
873+
* @param type the type of the Option
874+
* @return this builder, to allow method chaining
875+
*/
876+
public Builder type(final Class<?> type)
877+
{
878+
this.type = type;
879+
return this;
880+
}
881+
882+
/**
883+
* Sets the value separator. For example if the argument value
884+
* was a Java property, the value separator would be '='.
885+
*
886+
* @param sep The value separator.
887+
* @return this builder, to allow method chaining
888+
*/
889+
public Builder valueSeparator(final char sep)
890+
{
891+
valuesep = sep;
892+
return this;
893+
}
894+
895+
/**
896+
* Indicates if the Option has an argument or not.
897+
*
898+
* @param hasArg specifies whether the Option takes an argument or not
899+
* @return this builder, to allow method chaining
900+
*/
901+
public Builder hasArg(final boolean hasArg)
902+
{
903+
// set to UNINITIALIZED when no arg is specified to be compatible with OptionBuilder
904+
numberOfArgs = hasArg ? 1 : Option.UNINITIALIZED;
905+
return this;
906+
}
907+
908+
/**
909+
* Constructs an Option.
910+
*
911+
* @return the new Option
912+
*/
913+
public Option build()
914+
{
915+
return new Option(this);
916+
}
917+
}
730918
}

src/main/java/org/apache/commons/cli/OptionBuilder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
*
2828
* @version $Id$
2929
* @since 1.0
30+
* @deprecated since 1.3, use {@link Option.Builder} instead
3031
*/
32+
@Deprecated
3133
public final class OptionBuilder
3234
{
3335
/** long option */

src/main/java/org/apache/commons/cli/PatternOptionBuilder.java

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public class PatternOptionBuilder
6464
public static final Class<Date> DATE_VALUE = Date.class;
6565

6666
/** Class class */
67-
public static final Class CLASS_VALUE = Class.class;
67+
public static final Class<?> CLASS_VALUE = Class.class;
6868

6969
/// can we do this one??
7070
// is meant to check that the file exists, else it errors.
@@ -160,12 +160,14 @@ public static Options parsePattern(String pattern)
160160
{
161161
if (opt != ' ')
162162
{
163-
OptionBuilder.hasArg(type != null);
164-
OptionBuilder.isRequired(required);
165-
OptionBuilder.withType(type);
166-
163+
final Option option = new Option.Builder(String.valueOf(opt), null)
164+
.hasArg(type != null)
165+
.required(required)
166+
.type(type)
167+
.build();
168+
167169
// we have a previous one to deal with
168-
options.addOption(OptionBuilder.create(opt));
170+
options.addOption(option);
169171
required = false;
170172
type = null;
171173
opt = ' ';
@@ -185,12 +187,14 @@ else if (ch == '!')
185187

186188
if (opt != ' ')
187189
{
188-
OptionBuilder.hasArg(type != null);
189-
OptionBuilder.isRequired(required);
190-
OptionBuilder.withType(type);
191-
190+
final Option option = new Option.Builder(String.valueOf(opt), null)
191+
.hasArg(type != null)
192+
.required(required)
193+
.type(type)
194+
.build();
195+
192196
// we have a final one to deal with
193-
options.addOption(OptionBuilder.create(opt));
197+
options.addOption(option);
194198
}
195199

196200
return options;

src/test/java/org/apache/commons/cli/OptionTest.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,57 @@ public void testGetValue()
147147
assertEquals("foo", option.getValue(0));
148148
assertEquals("foo", option.getValue("default"));
149149
}
150+
151+
@Test
152+
public void testBuilderMethods()
153+
{
154+
char defaultSeparator = (char) 0;
155+
156+
checkOption(new Option.Builder("a", "desc").build(),
157+
"a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, String.class);
158+
checkOption(new Option.Builder("a", "desc").build(),
159+
"a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, String.class);
160+
checkOption(new Option.Builder("a", "desc").longOpt("aaa").build(),
161+
"a", "desc", "aaa", Option.UNINITIALIZED, null, false, false, defaultSeparator, String.class);
162+
checkOption(new Option.Builder("a", "desc").hasArg(true).build(),
163+
"a", "desc", null, 1, null, false, false, defaultSeparator, String.class);
164+
checkOption(new Option.Builder("a", "desc").hasArg(false).build(),
165+
"a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, String.class);
166+
checkOption(new Option.Builder("a", "desc").hasArg(true).build(),
167+
"a", "desc", null, 1, null, false, false, defaultSeparator, String.class);
168+
checkOption(new Option.Builder("a", "desc").numberOfArgs(3).build(),
169+
"a", "desc", null, 3, null, false, false, defaultSeparator, String.class);
170+
checkOption(new Option.Builder("a", "desc").required(true).build(),
171+
"a", "desc", null, Option.UNINITIALIZED, null, true, false, defaultSeparator, String.class);
172+
checkOption(new Option.Builder("a", "desc").required(false).build(),
173+
"a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, String.class);
174+
175+
checkOption(new Option.Builder("a", "desc").argName("arg1").build(),
176+
"a", "desc", null, Option.UNINITIALIZED, "arg1", false, false, defaultSeparator, String.class);
177+
checkOption(new Option.Builder("a", "desc").optionalArg(false).build(),
178+
"a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, String.class);
179+
checkOption(new Option.Builder("a", "desc").optionalArg(true).build(),
180+
"a", "desc", null, Option.UNINITIALIZED, null, false, true, defaultSeparator, String.class);
181+
checkOption(new Option.Builder("a", "desc").valueSeparator(':').build(),
182+
"a", "desc", null, Option.UNINITIALIZED, null, false, false, ':', String.class);
183+
checkOption(new Option.Builder("a", "desc").type(Integer.class).build(),
184+
"a", "desc", null, Option.UNINITIALIZED, null, false, false, defaultSeparator, Integer.class);
185+
}
186+
187+
private static void checkOption(Option option, String opt, String description, String longOpt, int numArgs,
188+
String argName, boolean required, boolean optionalArg,
189+
char valueSeparator, Class<?> cls)
190+
{
191+
assertEquals(opt, option.getOpt());
192+
assertEquals(description, option.getDescription());
193+
assertEquals(longOpt, option.getLongOpt());
194+
assertEquals(numArgs, option.getArgs());
195+
assertEquals(argName, option.getArgName());
196+
assertEquals(required, option.isRequired());
197+
198+
assertEquals(optionalArg, option.hasOptionalArg());
199+
assertEquals(valueSeparator, option.getValueSeparator());
200+
assertEquals(cls, option.getType());
201+
}
202+
150203
}

0 commit comments

Comments
 (0)