Skip to content

Commit 44a4fa2

Browse files
committed
Fix for CLI-340
1 parent b6ca226 commit 44a4fa2

3 files changed

Lines changed: 329 additions & 1 deletion

File tree

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

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
1818
package org.apache.commons.cli;
1919

2020
import java.io.Serializable;
21+
import java.lang.reflect.Array;
2122
import java.util.ArrayList;
2223
import java.util.Iterator;
2324
import java.util.LinkedList;
@@ -691,6 +692,203 @@ public <T> T getParsedOptionValue(final String opt, final T defaultValue) throws
691692
return getParsedOptionValue(resolveOption(opt), defaultValue);
692693
}
693694

695+
/**
696+
* Gets a version of this {@code Option} converted to an array of a particular type.
697+
*
698+
* @param opt the name of the option.
699+
* @param <T> The array type for the return value.
700+
* @return the values parsed into an array of objects.
701+
* @throws ParseException if there are problems turning the option value into the desired type
702+
* @see PatternOptionBuilder
703+
* @since 1.10.0
704+
*/
705+
public <T> T[] getParsedOptionValues(final char opt) throws ParseException {
706+
return getParsedOptionValues(String.valueOf(opt));
707+
}
708+
709+
/**
710+
* Gets a version of this {@code Option} converted to an array of a particular type.
711+
*
712+
* @param opt the name of the option.
713+
* @param defaultValue the default value to return if opt is not set.
714+
* @param <T> The array type for the return value.
715+
* @return the values parsed into an array of objects.
716+
* @throws ParseException if there are problems turning the option value into the desired type
717+
* @see PatternOptionBuilder
718+
* @since 1.10.0
719+
*/
720+
public <T> T[] getParsedOptionValues(final char opt, final Supplier<T[]> defaultValue) throws ParseException {
721+
return getParsedOptionValues(String.valueOf(opt), defaultValue);
722+
}
723+
724+
/**
725+
* Gets a version of this {@code Option} converted to an array of a particular type.
726+
*
727+
* @param opt the name of the option.
728+
* @param defaultValue the default value to return if opt is not set.
729+
* @param <T> The array type for the return value.
730+
* @return the values parsed into an array of objects.
731+
* @throws ParseException if there are problems turning the option value into the desired type
732+
* @see PatternOptionBuilder
733+
* @since 1.10.0
734+
*/
735+
public <T> T[] getParsedOptionValues(final char opt, final T[] defaultValue) throws ParseException {
736+
return getParsedOptionValues(String.valueOf(opt), defaultValue);
737+
}
738+
739+
/**
740+
* Gets a version of this {@code Option} converted to an array of a particular type.
741+
*
742+
* @param option the option.
743+
* @param <T> The array type for the return value.
744+
* @return the values parsed into an array of objects.
745+
* @throws ParseException if there are problems turning the option value into the desired type
746+
* @see PatternOptionBuilder
747+
* @since 1.10.0
748+
*/
749+
public <T> T[] getParsedOptionValues(final Option option) throws ParseException {
750+
return getParsedOptionValues(option, () -> null);
751+
}
752+
753+
/**
754+
* Gets a version of this {@code Option} converted to an array of a particular type.
755+
*
756+
* @param option the option.
757+
* @param defaultValue the default value to return if opt is not set.
758+
* @param <T> The array type for the return value.
759+
* @return the values parsed into an array of objects.
760+
* @throws ParseException if there are problems turning the option value into the desired type
761+
* @see PatternOptionBuilder
762+
* @since 1.10.0
763+
*/
764+
@SuppressWarnings("unchecked")
765+
public <T> T[] getParsedOptionValues(final Option option, final Supplier<T[]> defaultValue) throws ParseException {
766+
if (option == null) {
767+
return get(defaultValue);
768+
}
769+
Class<? extends T> clazz = (Class<? extends T>) option.getType();
770+
String[] values = getOptionValues(option);
771+
if (values == null) {
772+
return get(defaultValue);
773+
}
774+
T[] result = (T[]) Array.newInstance(clazz, values.length);
775+
try {
776+
for (int i = 0; i < values.length; i++) {
777+
result[i] = clazz.cast(option.getConverter().apply(values[i]));
778+
}
779+
return result;
780+
} catch (Throwable t) {
781+
throw ParseException.wrap(t);
782+
}
783+
784+
}
785+
786+
/**
787+
* Gets a version of this {@code Option} converted to an array of a particular type.
788+
*
789+
* @param option the option.
790+
* @param defaultValue the default value to return if opt is not set.
791+
* @param <T> The array type for the return value.
792+
* @return the values parsed into an array of objects.
793+
* @throws ParseException if there are problems turning the option value into the desired type
794+
* @see PatternOptionBuilder
795+
* @since 1.10.0
796+
*/
797+
public <T> T[] getParsedOptionValues(final Option option, final T[] defaultValue) throws ParseException {
798+
return getParsedOptionValues(option, () -> defaultValue);
799+
}
800+
801+
/**
802+
* Gets a version of this {@code OptionGroup} converted to an array of a particular type.
803+
*
804+
* @param optionGroup the option group.
805+
* @param <T> The array type for the return value.
806+
* @return the values parsed into an array of objects.
807+
* @throws ParseException if there are problems turning the selected option value into the desired type
808+
* @see PatternOptionBuilder
809+
* @since 1.10.0
810+
*/
811+
public <T> T[] getParsedOptionValues(final OptionGroup optionGroup) throws ParseException {
812+
return getParsedOptionValues(optionGroup, () -> null);
813+
}
814+
815+
/**
816+
* Gets a version of this {@code OptionGroup} converted to an array of a particular type.
817+
*
818+
* @param optionGroup the option group.
819+
* @param defaultValue the default value to return if opt is not set.
820+
* @param <T> The array type for the return value.
821+
* @return the values parsed into an array of objects.
822+
* @throws ParseException if there are problems turning the selected option value into the desired type
823+
* @see PatternOptionBuilder
824+
* @since 1.10.0
825+
*/
826+
public <T> T[] getParsedOptionValues(final OptionGroup optionGroup, final Supplier<T[]> defaultValue) throws ParseException {
827+
if (optionGroup == null || !optionGroup.isSelected()) {
828+
return get(defaultValue);
829+
}
830+
return getParsedOptionValues(optionGroup.getSelected(), defaultValue);
831+
}
832+
833+
/**
834+
* Gets a version of this {@code OptionGroup} converted to an array of a particular type.
835+
*
836+
* @param optionGroup the option group.
837+
* @param defaultValue the default value to return if an option is not selected.
838+
* @param <T> The array type for the return value.
839+
* @return the values parsed into an array of objects.
840+
* @throws ParseException if there are problems turning the option value into the desired type
841+
* @see PatternOptionBuilder
842+
* @since 1.10.0
843+
*/
844+
public <T> T[] getParsedOptionValues(final OptionGroup optionGroup, final T[] defaultValue) throws ParseException {
845+
return getParsedOptionValues(optionGroup, () -> defaultValue);
846+
}
847+
848+
/**
849+
* Gets a version of this {@code Option} converted to an array of a particular type.
850+
*
851+
* @param opt the name of the option.
852+
* @param <T> The array type for the return value.
853+
* @return the values parsed into an array of objects.
854+
* @throws ParseException if there are problems turning the option value into the desired type
855+
* @see PatternOptionBuilder
856+
* @since 1.10.0
857+
*/
858+
public <T> T[] getParsedOptionValues(final String opt) throws ParseException {
859+
return getParsedOptionValues(resolveOption(opt));
860+
}
861+
862+
/**
863+
* Gets a version of this {@code Option} converted to an array of a particular type.
864+
*
865+
* @param opt the name of the option.
866+
* @param defaultValue the default value to return if opt is not set.
867+
* @param <T> The array type for the return value.
868+
* @return the values parsed into an array of objects.
869+
* @throws ParseException if there are problems turning the option value into the desired type
870+
* @see PatternOptionBuilder
871+
* @since 1.10.0
872+
*/
873+
public <T> T[] getParsedOptionValues(final String opt, final Supplier<T[]> defaultValue) throws ParseException {
874+
return getParsedOptionValues(resolveOption(opt), defaultValue);
875+
}
876+
877+
/**
878+
* Gets a version of this {@code Option} converted to an array of a particular type.
879+
*
880+
* @param opt the name of the option.
881+
* @param defaultValue the default value to return if opt is not set.
882+
* @param <T> The array type for the return value.
883+
* @return the values parsed into an array of objects.
884+
* @throws ParseException if there are problems turning the option value into the desired type
885+
* @see PatternOptionBuilder
886+
* @since 1.10.0
887+
*/
888+
public <T> T[] getParsedOptionValues(final String opt, final T[] defaultValue) throws ParseException {
889+
return getParsedOptionValues(resolveOption(opt), defaultValue);
890+
}
891+
694892
/**
695893
* Handles deprecated options.
696894
*

src/main/java/org/apache/commons/cli/help/HelpFormatter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ protected HelpFormatter(final Builder builder) {
147147
*/
148148
@Override
149149
public TableDefinition getTableDefinition(final Iterable<Option> options) {
150-
// set up the base TextStyle for the columns configured for the Option opt and arg values..
150+
// set up the base TextStyle for the columns configured for the Option opt and arg values.
151151
final TextStyle.Builder builder = TextStyle.builder().setAlignment(TextStyle.Alignment.LEFT).setIndent(DEFAULT_LEFT_PAD).setScalable(false);
152152
final List<TextStyle> styles = new ArrayList<>();
153153
styles.add(builder.get());

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

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
2727
import java.io.ByteArrayOutputStream;
2828
import java.io.PrintStream;
2929
import java.util.ArrayList;
30+
import java.util.Arrays;
3031
import java.util.List;
3132
import java.util.Properties;
3233
import java.util.function.Supplier;
@@ -905,4 +906,133 @@ public void testNullOption() throws Exception {
905906
assertNull(cmd.getOptionValue((OptionGroup) null));
906907
assertNull(cmd.getParsedOptionValue((OptionGroup) null));
907908
}
909+
910+
@ParameterizedTest(name = "{0}, {1}")
911+
@MethodSource("createParsedOptionValuesParameters")
912+
public void testGetParsedOptionValues(final String[] args, final Option opt, final OptionGroup optionGroup, final boolean optDep,
913+
final Integer[] optValue, final boolean grpDep, final Integer[] grpValue, final Option grpOpt) throws ParseException {
914+
final Options options = new Options().addOptionGroup(optionGroup);
915+
final List<Option> handler = new ArrayList<>();
916+
final CommandLine commandLine = DefaultParser.builder().setDeprecatedHandler(handler::add).get().parse(options, args);
917+
final Supplier<Integer[]> thinger = () -> new Integer[]{2, 3};
918+
final OptionGroup otherGroup = new OptionGroup().addOption(Option.builder("o").longOpt("other").hasArg().build())
919+
.addOption(Option.builder().option("p").longOpt("part").hasArg().build());
920+
final OptionGroup nullGroup = null;
921+
final Integer[] thing = {2, 3};
922+
923+
// test char option arg
924+
assertArrayEquals(optValue, commandLine.getParsedOptionValues(asChar(opt)));
925+
checkHandler(optDep, handler, opt);
926+
927+
assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(asChar(opt), thing));
928+
checkHandler(optDep, handler, opt);
929+
930+
assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(asChar(opt), thinger));
931+
checkHandler(optDep, handler, opt);
932+
933+
// test short option arg
934+
assertArrayEquals(optValue, commandLine.getParsedOptionValues(opt.getOpt()));
935+
checkHandler(optDep, handler, opt);
936+
937+
assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(opt.getOpt(), thing));
938+
checkHandler(optDep, handler, opt);
939+
940+
assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(opt.getOpt(), thinger));
941+
checkHandler(optDep, handler, opt);
942+
943+
// test long option arg
944+
assertArrayEquals(optValue, commandLine.getParsedOptionValues(opt.getLongOpt()));
945+
checkHandler(optDep, handler, opt);
946+
947+
assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(opt.getLongOpt(), thing));
948+
checkHandler(optDep, handler, opt);
949+
950+
assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(opt.getLongOpt(), thinger));
951+
checkHandler(optDep, handler, opt);
952+
953+
954+
// test Option arg
955+
assertArrayEquals(optValue, commandLine.getParsedOptionValues(opt));
956+
checkHandler(optDep, handler, opt);
957+
958+
assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(opt, thing));
959+
checkHandler(optDep, handler, opt);
960+
961+
assertArrayEquals(optValue == null ? thing : optValue, commandLine.getParsedOptionValues(opt, thinger));
962+
checkHandler(optDep, handler, opt);
963+
964+
// test OptionGroup arg
965+
assertArrayEquals(grpValue, commandLine.getParsedOptionValues(optionGroup));
966+
checkHandler(grpDep, handler, grpOpt);
967+
968+
assertArrayEquals(grpValue == null ? thing : grpValue, commandLine.getParsedOptionValues(optionGroup, thing));
969+
checkHandler(grpDep, handler, grpOpt);
970+
971+
assertArrayEquals(grpValue == null ? thing : grpValue, commandLine.getParsedOptionValues(optionGroup, thinger));
972+
checkHandler(grpDep, handler, grpOpt);
973+
974+
// test other Group arg
975+
assertNull(commandLine.getParsedOptionValues(otherGroup));
976+
checkHandler(false, handler, grpOpt);
977+
978+
assertArrayEquals(thing, commandLine.getParsedOptionValues(otherGroup, thing));
979+
checkHandler(false, handler, grpOpt);
980+
981+
assertArrayEquals(thing, commandLine.getParsedOptionValues(otherGroup, thinger));
982+
checkHandler(false, handler, grpOpt);
983+
984+
// test null Group arg
985+
assertNull(commandLine.getParsedOptionValues(nullGroup));
986+
checkHandler(false, handler, grpOpt);
987+
988+
assertArrayEquals(thing, commandLine.getParsedOptionValues(nullGroup, thing));
989+
checkHandler(false, handler, grpOpt);
990+
991+
assertArrayEquals(thing, commandLine.getParsedOptionValues(nullGroup, thinger));
992+
checkHandler(false, handler, grpOpt);
993+
994+
995+
// test not an option
996+
assertNull(commandLine.getParsedOptionValues("Nope"));
997+
checkHandler(false, handler, opt);
998+
999+
assertArrayEquals(thing, commandLine.getParsedOptionValues("Nope", thing));
1000+
checkHandler(false, handler, opt);
1001+
1002+
assertArrayEquals(thing, commandLine.getParsedOptionValues("Nope", thinger));
1003+
checkHandler(false, handler, opt);
1004+
}
1005+
1006+
private static Stream<Arguments> createParsedOptionValuesParameters() throws ParseException {
1007+
final List<Arguments> lst = new ArrayList<>();
1008+
final Option optT = Option.builder().option("T").longOpt("tee").deprecated().type(Integer.class).optionalArg(true).hasArgs().build();
1009+
final Option optU = Option.builder("U").longOpt("you").type(Integer.class).optionalArg(true).hasArgs().build();
1010+
final OptionGroup optionGroup = new OptionGroup().addOption(optT).addOption(optU);
1011+
final Integer[] expected = new Integer[]{1, 2};
1012+
1013+
// T set
1014+
lst.add(Arguments.of(new String[] {"-T"}, optT, optionGroup, true, null, true, null, optT));
1015+
lst.add(Arguments.of(new String[] {"-T", "1", "2"}, optT, optionGroup, true, expected, true, expected, optT));
1016+
lst.add(Arguments.of(new String[] {"--tee"}, optT, optionGroup, true, null, true, null, optT));
1017+
lst.add(Arguments.of(new String[] {"--tee", "1", "2"}, optT, optionGroup, true, expected, true, expected, optT));
1018+
1019+
lst.add(Arguments.of(new String[] {"-U"}, optT, optionGroup, false, null, false, null, optU));
1020+
lst.add(Arguments.of(new String[] {"-U", "1", "2"}, optT, optionGroup, false, null, false, expected, optU));
1021+
lst.add(Arguments.of(new String[] {"--you"}, optT, optionGroup, false, null, false, null, optU));
1022+
lst.add(Arguments.of(new String[] {"--you", "1", "2"}, optT, optionGroup, false, null, false, expected, optU));
1023+
1024+
1025+
// U set
1026+
lst.add(Arguments.of(new String[] {"-T"}, optU, optionGroup, false, null, true, null, optT));
1027+
lst.add(Arguments.of(new String[] {"-T", "1", "2"}, optU, optionGroup, false, null, true, expected, optT));
1028+
lst.add(Arguments.of(new String[] {"--tee"}, optU, optionGroup, false, null, true, null, optT));
1029+
lst.add(Arguments.of(new String[] {"--tee", "1", "2"}, optU, optionGroup, false, null, true, expected, optT));
1030+
1031+
lst.add(Arguments.of(new String[] {"-U"}, optU, optionGroup, false, null, false, null, optU));
1032+
lst.add(Arguments.of(new String[] {"-U", "1", "2"}, optU, optionGroup, false, expected, false, expected, optU));
1033+
lst.add(Arguments.of(new String[] {"--you"}, optU, optionGroup, false, null, false, null, optU));
1034+
lst.add(Arguments.of(new String[] {"--you", "1", "2"}, optU, optionGroup, false, expected, false, expected, optU));
1035+
1036+
return lst.stream();
1037+
}
9081038
}

0 commit comments

Comments
 (0)