Second cut of adding support for argument groups:

* mutually exclusive options (#199)
* option that must co-occur (#295)
* option grouping in the usage help message (#450)
* repeating composite arguments (#358 and #635) (this should also cover the use cases presented in #454 and #434 requests for repeatable subcommands)
This commit is contained in:
Remko Popma
2019-03-18 22:32:03 +09:00
parent 3337db2a79
commit 27ab5552fb
19 changed files with 2084 additions and 1339 deletions

View File

@@ -9,11 +9,12 @@ buildscript {
dependencies {
classpath "org.asciidoctor:asciidoctor-gradle-plugin:$asciidoctorGradlePluginVersion"
classpath 'org.asciidoctor:asciidoctorj-pdf:1.5.0-alpha.15'
classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$gradleBintrayPluginVersion"
}
}
apply plugin: 'org.asciidoctor.convert'
apply plugin: 'org.asciidoctor.convert' // version '1.5.8.1'
apply plugin: 'distribution'
apply plugin: 'maven-publish'
apply plugin: 'com.jfrog.bintray'
@@ -158,10 +159,18 @@ jar {
javadoc.options.overview = "src/main/java/overview.html"
javadoc.dependsOn('asciidoctor')
asciidoctorj {
version = '1.5.5'
}
asciidoctor {
sourceDir = file('docs')
outputDir = file('build/docs')
logDocuments = true
// backends 'pdf', 'html'
// attributes 'sourcedir': file('docs') //project.sourceSets.main.java.srcDirs[0]
//// attributes 'pdf-stylesdir': 'theme',
//// 'pdf-style': 'custom',
//// 'sourcedir': file('docs') //project.sourceSets.main.java.srcDirs[0]
}
// jacoco 0.8.2 does not work with Java 13; gradle 4.x has no JavaVersion enum value for Java 12
if (org.gradle.api.JavaVersion.current().isJava11Compatible()) {

View File

@@ -14,7 +14,7 @@ jacocoTestCoverageVerification {
violationRules {
rule {
limit {
minimum = 0.98
minimum = 0.97
}
}
}

View File

@@ -8,6 +8,8 @@ plugins {
group 'info.picocli'
description 'Picocli Code Generation - Tools to generate documentation, configuration, source code and other files from a picocli model.'
version "$projectVersion"
sourceCompatibility = 1.6
targetCompatibility = 1.6
dependencies {
compile rootProject

View File

@@ -1,6 +1,7 @@
package picocli.codegen.annotation.processing;
import picocli.CommandLine;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command;
import picocli.CommandLine.IFactory;
import picocli.CommandLine.Mixin;
@@ -942,6 +943,7 @@ public abstract class AbstractCommandSpecProcessor extends AbstractProcessor {
return false
|| e.getAnnotation(Option.class) == null
|| e.getAnnotation(Parameters.class) == null
|| e.getAnnotation(ArgGroup.class) == null
|| e.getAnnotation(Unmatched.class) == null
|| e.getAnnotation(Mixin.class) == null
|| e.getAnnotation(Spec.class) == null
@@ -995,6 +997,7 @@ public abstract class AbstractCommandSpecProcessor extends AbstractProcessor {
public boolean isArgSpec() { return isOption() || isParameter() || isMethodParameter(); }
public boolean isOption() { return isAnnotationPresent(Option.class); }
public boolean isParameter() { return isAnnotationPresent(Parameters.class); }
public boolean isArgGroup() { return isAnnotationPresent(ArgGroup.class); }
public boolean isMixin() { return isAnnotationPresent(Mixin.class); }
public boolean isUnmatched() { return isAnnotationPresent(Unmatched.class); }
public boolean isInjectSpec() { return isAnnotationPresent(Spec.class); }
@@ -1011,6 +1014,12 @@ public abstract class AbstractCommandSpecProcessor extends AbstractProcessor {
public boolean hasInitialValue() { return hasInitialValue; }
public boolean isMethodParameter() { return position >= 0; }
public int getMethodParamPosition() { return position; }
@Override
public CommandLine.Model.IScope scope() {
return null; // FIXME
}
public String getMixinName() {
String annotationName = getAnnotation(Mixin.class).name();
return empty(annotationName) ? getName() : annotationName;

View File

@@ -319,6 +319,17 @@
{ "name" : "helpRequested" }
]
},
{
"name" : "picocli.CommandLine$Model$ObjectScope",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true,
"methods" : [
{ "name" : "setMinimum", "parameterTypes" : ["int"] },
{ "name" : "setOtherFiles", "parameterTypes" : ["java.util.List"] }
]
},
{
"name" : "picocli.codegen.aot.graalvm.Example",
"allDeclaredConstructors" : true,

View File

@@ -262,6 +262,13 @@
{ "name" : "out" }
]
},
{
"name" : "picocli.CommandLine$Model$ObjectScope",
"allDeclaredConstructors" : true,
"allPublicConstructors" : true,
"allDeclaredMethods" : true,
"allPublicMethods" : true
},
{
"name" : "picocli.codegen.aot.graalvm.Issue622AbstractCommand",
"allDeclaredConstructors" : true,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1593,4 +1593,17 @@ public class CommandLineArityTest {
assertEquals("positional parameter at index 0..* (<String=String>) requires at least 2 values, but only 1 were specified: [a=c]", ex.getMessage());
}
}
@Test
public void testOverlap() {
Range r1 = Range.valueOf("1..5");
assertTrue(r1.overlaps(Range.valueOf("1..1")));
assertTrue(r1.overlaps(Range.valueOf("2..2")));
assertTrue(r1.overlaps(Range.valueOf("2..5")));
assertTrue(r1.overlaps(Range.valueOf("5")));
assertTrue(r1.overlaps(Range.valueOf("0..6")));
assertTrue(r1.overlaps(Range.valueOf("0..*")));
assertFalse(r1.overlaps(Range.valueOf("6")));
assertFalse(r1.overlaps(Range.valueOf("0")));
}
}

View File

@@ -21,7 +21,9 @@ import org.junit.contrib.java.lang.system.SystemErrRule;
import org.junit.contrib.java.lang.system.SystemOutRule;
import picocli.CommandLine.*;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Model.IScope;
import picocli.CommandLine.Model.MethodParam;
import picocli.CommandLine.Model.ObjectScope;
import picocli.CommandLine.Model.TypedMember;
import picocli.CommandLineTest.CompactFields;
@@ -1044,21 +1046,21 @@ public class CommandLineCommandMethodTest {
@Test
public void testTypedMemberConstructorRejectsGetterNorSetter() throws Exception {
Constructor<TypedMember> constructor = TypedMember.class.getDeclaredConstructor(Method.class, Object.class, CommandSpec.class);
Constructor<TypedMember> constructor = TypedMember.class.getDeclaredConstructor(Method.class, IScope.class, CommandSpec.class);
constructor.setAccessible(true);
Method getterNorSetter1 = TypedMemberObj.class.getDeclaredMethod("getterNorSetter1");
Method getterNorSetter2 = TypedMemberObj.class.getDeclaredMethod("getterNorSetter2");
try {
constructor.newInstance(getterNorSetter1, new TypedMemberObj(), CommandSpec.create());
constructor.newInstance(getterNorSetter1, new ObjectScope(new TypedMemberObj()), CommandSpec.create());
fail("expect exception");
} catch (InvocationTargetException ex) {
InitializationException ex2 = (InitializationException) ex.getCause();
assertEquals("Invalid method, must be either getter or setter: void picocli.CommandLineCommandMethodTest$TypedMemberObj.getterNorSetter1()", ex2.getMessage());
}
try {
constructor.newInstance(getterNorSetter2, new TypedMemberObj(), CommandSpec.create());
constructor.newInstance(getterNorSetter2, new ObjectScope(new TypedMemberObj()), CommandSpec.create());
fail("expect exception");
} catch (InvocationTargetException ex) {
InitializationException ex2 = (InitializationException) ex.getCause();
@@ -1068,22 +1070,22 @@ public class CommandLineCommandMethodTest {
@Test
public void testTypedMemberConstructorNonProxyObject() throws Exception {
Constructor<TypedMember> constructor = TypedMember.class.getDeclaredConstructor(Method.class, Object.class, CommandSpec.class);
Constructor<TypedMember> constructor = TypedMember.class.getDeclaredConstructor(Method.class, IScope.class, CommandSpec.class);
constructor.setAccessible(true);
Method getter = TypedMemberObj.class.getDeclaredMethod("getter");
TypedMember typedMember = constructor.newInstance(getter, new TypedMemberObj(), CommandSpec.create());
TypedMember typedMember = constructor.newInstance(getter, new ObjectScope(new TypedMemberObj()), CommandSpec.create());
assertSame(typedMember.getter(), typedMember.setter());
assertTrue(typedMember.getter() instanceof Model.MethodBinding);
}
@Test
public void testTypedMemberInitializeInitialValue() throws Exception {
Constructor<TypedMember> constructor = TypedMember.class.getDeclaredConstructor(Method.class, Object.class, CommandSpec.class);
Constructor<TypedMember> constructor = TypedMember.class.getDeclaredConstructor(Method.class, IScope.class, CommandSpec.class);
constructor.setAccessible(true);
Method setter = TypedMemberObj.class.getDeclaredMethod("setter", String.class);
TypedMember typedMember = constructor.newInstance(setter, new TypedMemberObj(), CommandSpec.create());
TypedMember typedMember = constructor.newInstance(setter, new ObjectScope(new TypedMemberObj()), CommandSpec.create());
Method initializeInitialValue = TypedMember.class.getDeclaredMethod("initializeInitialValue", Object.class);
initializeInitialValue.setAccessible(true);

View File

@@ -848,7 +848,7 @@ public class CommandLineTest {
"[picocli DEBUG] Set initial value for field boolean picocli.CommandLineTest$CompactFields.recursive of type boolean to false.%n" +
"[picocli DEBUG] Set initial value for field java.io.File picocli.CommandLineTest$CompactFields.outputFile of type class java.io.File to null.%n" +
"[picocli DEBUG] Set initial value for field java.io.File[] picocli.CommandLineTest$CompactFields.inputFiles of type class [Ljava.io.File; to null.%n" +
"[picocli DEBUG] Initializing %1$s$CompactFields: 3 options, 1 positional parameters, 0 required, 0 subcommands.%n" +
"[picocli DEBUG] Initializing %1$s$CompactFields: 3 options, 1 positional parameters, 0 required, 0 groups, 0 subcommands.%n" +
"[picocli DEBUG] Processing argument '-oout'. Remainder=[--, -r, -v, p1, p2]%n" +
"[picocli DEBUG] '-oout' cannot be separated into <option>=<option-parameter>%n" +
"[picocli DEBUG] Trying to process '-oout' as clustered short options%n" +
@@ -857,22 +857,22 @@ public class CommandLineTest {
"[picocli INFO] Setting field java.io.File picocli.CommandLineTest$CompactFields.outputFile to 'out' (was 'null') for option -o%n" +
"[picocli DEBUG] Processing argument '--'. Remainder=[-r, -v, p1, p2]%n" +
"[picocli INFO] Found end-of-options delimiter '--'. Treating remainder as positional parameters.%n" +
"[picocli DEBUG] Processing next arg as a positional parameter at index=0. Remainder=[-r, -v, p1, p2]%n" +
"[picocli DEBUG] Position 0 is in index range 0..*. Trying to assign args to field java.io.File[] %1$s$CompactFields.inputFiles, arity=0..1%n" +
"[picocli DEBUG] Processing next arg as a positional parameter. Command-local position=0. Remainder=[-r, -v, p1, p2]%n" +
"[picocli DEBUG] Position 0 (command-local) is in index range 0..*. Trying to assign args to field java.io.File[] %1$s$CompactFields.inputFiles, arity=0..1%n" +
"[picocli INFO] Adding [-r] to field java.io.File[] picocli.CommandLineTest$CompactFields.inputFiles for args[0..*] at position 0%n" +
"[picocli DEBUG] Consumed 1 arguments and 0 interactive values, moving position to index 1.%n" +
"[picocli DEBUG] Processing next arg as a positional parameter at index=1. Remainder=[-v, p1, p2]%n" +
"[picocli DEBUG] Position 1 is in index range 0..*. Trying to assign args to field java.io.File[] %1$s$CompactFields.inputFiles, arity=0..1%n" +
"[picocli DEBUG] Consumed 1 arguments and 0 interactive values, moving command-local position to index 1.%n" +
"[picocli DEBUG] Processing next arg as a positional parameter. Command-local position=1. Remainder=[-v, p1, p2]%n" +
"[picocli DEBUG] Position 1 (command-local) is in index range 0..*. Trying to assign args to field java.io.File[] %1$s$CompactFields.inputFiles, arity=0..1%n" +
"[picocli INFO] Adding [-v] to field java.io.File[] picocli.CommandLineTest$CompactFields.inputFiles for args[0..*] at position 1%n" +
"[picocli DEBUG] Consumed 1 arguments and 0 interactive values, moving position to index 2.%n" +
"[picocli DEBUG] Processing next arg as a positional parameter at index=2. Remainder=[p1, p2]%n" +
"[picocli DEBUG] Position 2 is in index range 0..*. Trying to assign args to field java.io.File[] %1$s$CompactFields.inputFiles, arity=0..1%n" +
"[picocli DEBUG] Consumed 1 arguments and 0 interactive values, moving command-local position to index 2.%n" +
"[picocli DEBUG] Processing next arg as a positional parameter. Command-local position=2. Remainder=[p1, p2]%n" +
"[picocli DEBUG] Position 2 (command-local) is in index range 0..*. Trying to assign args to field java.io.File[] %1$s$CompactFields.inputFiles, arity=0..1%n" +
"[picocli INFO] Adding [p1] to field java.io.File[] picocli.CommandLineTest$CompactFields.inputFiles for args[0..*] at position 2%n" +
"[picocli DEBUG] Consumed 1 arguments and 0 interactive values, moving position to index 3.%n" +
"[picocli DEBUG] Processing next arg as a positional parameter at index=3. Remainder=[p2]%n" +
"[picocli DEBUG] Position 3 is in index range 0..*. Trying to assign args to field java.io.File[] %1$s$CompactFields.inputFiles, arity=0..1%n" +
"[picocli DEBUG] Consumed 1 arguments and 0 interactive values, moving command-local position to index 3.%n" +
"[picocli DEBUG] Processing next arg as a positional parameter. Command-local position=3. Remainder=[p2]%n" +
"[picocli DEBUG] Position 3 (command-local) is in index range 0..*. Trying to assign args to field java.io.File[] %1$s$CompactFields.inputFiles, arity=0..1%n" +
"[picocli INFO] Adding [p2] to field java.io.File[] picocli.CommandLineTest$CompactFields.inputFiles for args[0..*] at position 3%n" +
"[picocli DEBUG] Consumed 1 arguments and 0 interactive values, moving position to index 4.%n",
"[picocli DEBUG] Consumed 1 arguments and 0 interactive values, moving command-local position to index 4.%n",
CommandLineTest.class.getName(),
new File("/home/rpopma/picocli"),
CommandLine.versionString());
@@ -1766,7 +1766,7 @@ public class CommandLineTest {
"[picocli DEBUG] Set initial value for field java.io.File picocli.Demo$Git.gitDir of type class java.io.File to null.%n" +
"[picocli DEBUG] Set initial value for field boolean picocli.CommandLine$AutoHelpMixin.helpRequested of type boolean to false.%n" +
"[picocli DEBUG] Set initial value for field boolean picocli.CommandLine$AutoHelpMixin.versionRequested of type boolean to false.%n" +
"[picocli DEBUG] Initializing %1$s$Git: 3 options, 0 positional parameters, 0 required, 12 subcommands.%n" +
"[picocli DEBUG] Initializing %1$s$Git: 3 options, 0 positional parameters, 0 required, 0 groups, 12 subcommands.%n" +
"[picocli DEBUG] Processing argument '--git-dir=/home/rpopma/picocli'. Remainder=[commit, -m, \"Fixed typos\", --, src1.java, src2.java, src3.java]%n" +
"[picocli DEBUG] Separated '--git-dir' option from '/home/rpopma/picocli' option parameter%n" +
"[picocli DEBUG] Found option named '--git-dir': field java.io.File %1$s$Git.gitDir, arity=1%n" +
@@ -1782,25 +1782,25 @@ public class CommandLineTest {
"[picocli DEBUG] Set initial value for field java.io.File picocli.Demo$GitCommit.file of type class java.io.File to null.%n" +
"[picocli DEBUG] Set initial value for field java.util.List<String> picocli.Demo$GitCommit.message of type interface java.util.List to [].%n" +
"[picocli DEBUG] Set initial value for field java.util.List<java.io.File> picocli.Demo$GitCommit.files of type interface java.util.List to [].%n" +
"[picocli DEBUG] Initializing %1$s$GitCommit: 8 options, 1 positional parameters, 0 required, 0 subcommands.%n" +
"[picocli DEBUG] Initializing %1$s$GitCommit: 8 options, 1 positional parameters, 0 required, 0 groups, 0 subcommands.%n" +
"[picocli DEBUG] Processing argument '-m'. Remainder=[\"Fixed typos\", --, src1.java, src2.java, src3.java]%n" +
"[picocli DEBUG] '-m' cannot be separated into <option>=<option-parameter>%n" +
"[picocli DEBUG] Found option named '-m': field java.util.List<String> %1$s$GitCommit.message, arity=1%n" +
"[picocli INFO] Adding [\"Fixed typos\"] to field java.util.List<String> picocli.Demo$GitCommit.message for option -m%n" +
"[picocli DEBUG] Processing argument '--'. Remainder=[src1.java, src2.java, src3.java]%n" +
"[picocli INFO] Found end-of-options delimiter '--'. Treating remainder as positional parameters.%n" +
"[picocli DEBUG] Processing next arg as a positional parameter at index=0. Remainder=[src1.java, src2.java, src3.java]%n" +
"[picocli DEBUG] Position 0 is in index range 0..*. Trying to assign args to field java.util.List<java.io.File> %1$s$GitCommit.files, arity=0..1%n" +
"[picocli DEBUG] Processing next arg as a positional parameter. Command-local position=0. Remainder=[src1.java, src2.java, src3.java]%n" +
"[picocli DEBUG] Position 0 (command-local) is in index range 0..*. Trying to assign args to field java.util.List<java.io.File> %1$s$GitCommit.files, arity=0..1%n" +
"[picocli INFO] Adding [src1.java] to field java.util.List<java.io.File> picocli.Demo$GitCommit.files for args[0..*] at position 0%n" +
"[picocli DEBUG] Consumed 1 arguments and 0 interactive values, moving position to index 1.%n" +
"[picocli DEBUG] Processing next arg as a positional parameter at index=1. Remainder=[src2.java, src3.java]%n" +
"[picocli DEBUG] Position 1 is in index range 0..*. Trying to assign args to field java.util.List<java.io.File> %1$s$GitCommit.files, arity=0..1%n" +
"[picocli DEBUG] Consumed 1 arguments and 0 interactive values, moving command-local position to index 1.%n" +
"[picocli DEBUG] Processing next arg as a positional parameter. Command-local position=1. Remainder=[src2.java, src3.java]%n" +
"[picocli DEBUG] Position 1 (command-local) is in index range 0..*. Trying to assign args to field java.util.List<java.io.File> %1$s$GitCommit.files, arity=0..1%n" +
"[picocli INFO] Adding [src2.java] to field java.util.List<java.io.File> picocli.Demo$GitCommit.files for args[0..*] at position 1%n" +
"[picocli DEBUG] Consumed 1 arguments and 0 interactive values, moving position to index 2.%n" +
"[picocli DEBUG] Processing next arg as a positional parameter at index=2. Remainder=[src3.java]%n" +
"[picocli DEBUG] Position 2 is in index range 0..*. Trying to assign args to field java.util.List<java.io.File> %1$s$GitCommit.files, arity=0..1%n" +
"[picocli DEBUG] Consumed 1 arguments and 0 interactive values, moving command-local position to index 2.%n" +
"[picocli DEBUG] Processing next arg as a positional parameter. Command-local position=2. Remainder=[src3.java]%n" +
"[picocli DEBUG] Position 2 (command-local) is in index range 0..*. Trying to assign args to field java.util.List<java.io.File> %1$s$GitCommit.files, arity=0..1%n" +
"[picocli INFO] Adding [src3.java] to field java.util.List<java.io.File> picocli.Demo$GitCommit.files for args[0..*] at position 2%n" +
"[picocli DEBUG] Consumed 1 arguments and 0 interactive values, moving position to index 3.%n",
"[picocli DEBUG] Consumed 1 arguments and 0 interactive values, moving command-local position to index 3.%n",
Demo.class.getName(),
new File("/home/rpopma/picocli"),
CommandLine.versionString());
@@ -3506,14 +3506,9 @@ public class CommandLineTest {
@Command class App {
@Unmatched String unmatched;
}
try {
new CommandLine(new App());
fail("Expected exception");
} catch (InitializationException ex) {
String pattern = "Invalid type for %s: must be either String[] or List<String>";
Field f = App.class.getDeclaredField("unmatched");
assertEquals(format(pattern, f), ex.getMessage());
}
Object app = new App();
Field f = App.class.getDeclaredField("unmatched");
assertInvalidUnmatchedFieldType(app, f);
}
@Test
@@ -3521,12 +3516,17 @@ public class CommandLineTest {
@Command class App {
@Unmatched List<Object> unmatched;
}
Object app = new App();
Field f = App.class.getDeclaredField("unmatched");
assertInvalidUnmatchedFieldType(app, f);
}
private void assertInvalidUnmatchedFieldType(Object app, Field f) {
try {
new CommandLine(new App());
new CommandLine(app);
fail("Expected exception");
} catch (InitializationException ex) {
String pattern = "Invalid type for %s: must be either String[] or List<String>";
Field f = App.class.getDeclaredField("unmatched");
assertEquals(format(pattern, f), ex.getMessage());
}
}
@@ -3540,7 +3540,7 @@ public class CommandLineTest {
new CommandLine(new App());
fail("Expected exception");
} catch (InitializationException ex) {
String pattern = "A member cannot have both @Unmatched and @Option or @Parameters annotations, but '%s' has both.";
String pattern = "A member cannot have both @Unmatched and @Option annotations, but '%s' has both.";
Field f = App.class.getDeclaredField("unmatched");
assertEquals(format(pattern, f), ex.getMessage());
}
@@ -3555,7 +3555,7 @@ public class CommandLineTest {
new CommandLine(new App());
fail("Expected exception");
} catch (InitializationException ex) {
String pattern = "A member cannot have both @Unmatched and @Option or @Parameters annotations, but '%s' has both.";
String pattern = "A member cannot have both @Unmatched and @Parameters annotations, but '%s' has both.";
Field f = App.class.getDeclaredField("unmatched");
assertEquals(format(pattern, f), ex.getMessage());
}
@@ -3570,7 +3570,7 @@ public class CommandLineTest {
new CommandLine(new App());
fail("Expected exception");
} catch (InitializationException ex) {
String pattern = "A member cannot be both a @Mixin command and an @Unmatched but '%s' is both.";
String pattern = "A member cannot have both @Mixin and @Unmatched annotations, but '%s' has both.";
Field f = App.class.getDeclaredField("unmatched");
assertEquals(format(pattern, f), ex.getMessage());
}
@@ -3585,7 +3585,7 @@ public class CommandLineTest {
new CommandLine(new App());
fail("Expected exception");
} catch (InitializationException ex) {
String pattern = "A member cannot be both a @Mixin command and an @Option or @Parameters, but '%s' is both.";
String pattern = "A member cannot have both @Mixin and @Option annotations, but '%s' has both.";
Field f = App.class.getDeclaredField("unmatched");
assertEquals(format(pattern, f), ex.getMessage());
}
@@ -3600,7 +3600,7 @@ public class CommandLineTest {
new CommandLine(new App());
fail("Expected exception");
} catch (InitializationException ex) {
String pattern = "A member cannot be both a @Mixin command and an @Option or @Parameters, but '%s' is both.";
String pattern = "A member cannot have both @Mixin and @Parameters annotations, but '%s' has both.";
Field f = App.class.getDeclaredField("unmatched");
assertEquals(format(pattern, f), ex.getMessage());
}
@@ -3864,6 +3864,7 @@ public class CommandLineTest {
System.setOut(new PrintStream(baos));
System.setIn(new ByteArrayInputStream("123".getBytes()));
HelpTestUtil.setTraceLevel("DEBUG");
App app = new App();
CommandLine cmd = new CommandLine(app);
cmd.parse("987");

View File

@@ -49,17 +49,21 @@ public class InnerClassFactory implements IFactory {
public <K> K create(final Class<K> cls) throws Exception {
try {
Constructor<K> constructor = cls.getDeclaredConstructor(outer.getClass());
return constructor.newInstance(outer);
} catch (Exception ex) {
return CommandLine.defaultFactory().create(cls);
} catch (Exception ex0) {
try {
return cls.newInstance();
} catch (Exception ex2) {
Constructor<K> constructor = cls.getDeclaredConstructor(outer.getClass());
return constructor.newInstance(outer);
} catch (Exception ex) {
try {
Constructor<K> constructor = cls.getDeclaredConstructor();
return constructor.newInstance();
} catch (Exception ex3) {
throw new InitializationException("Could not instantiate " + cls.getName() + " either with or without construction parameter " + outer + ": " + ex, ex);
return cls.newInstance();
} catch (Exception ex2) {
try {
Constructor<K> constructor = cls.getDeclaredConstructor();
return constructor.newInstance();
} catch (Exception ex3) {
throw new InitializationException("Could not instantiate " + cls.getName() + " either with or without construction parameter " + outer + ": " + ex, ex);
}
}
}
}

View File

@@ -290,7 +290,7 @@ public class ModelArgSpecTest {
@Test
public void testArgSpecBuilderObjectBindingToString() {
Builder builder = OptionSpec.builder("-x");
assertEquals("picocli.CommandLine.Model.ObjectBinding(value=null)", builder.getter().toString());
assertEquals("ObjectBinding(value=null)", builder.getter().toString());
}
@Test

View File

@@ -68,34 +68,6 @@ public class ModelCommandReflectionTest {
}
}
static class TypedMemberObj {
int x;
}
@Test
public void testValidateMixin() throws Exception {
Class<?> reflection = Class.forName("picocli.CommandLine$Model$CommandReflection");
Method validateMixin = reflection.getDeclaredMethod("validateMixin", CommandLine.Model.TypedMember.class);
validateMixin.setAccessible(true);
CommandLine.Model.TypedMember typedMember = new CommandLine.Model.TypedMember(TypedMemberObj.class.getDeclaredField("x"));
try {
validateMixin.invoke(null, typedMember);
fail("expected Exception");
} catch (InvocationTargetException ite) {
IllegalStateException ex = (IllegalStateException) ite.getCause();
assertEquals("Bug: validateMixin() should only be called with mixins", ex.getMessage());
}
}
@Test
public void testValidateUnmatched() throws Exception {
Class<?> reflection = Class.forName("picocli.CommandLine$Model$CommandReflection");
Method validateUnmatched = reflection.getDeclaredMethod("validateUnmatched", CommandLine.Model.IAnnotatedElement.class);
validateUnmatched.setAccessible(true);
CommandLine.Model.TypedMember typedMember = new CommandLine.Model.TypedMember(TypedMemberObj.class.getDeclaredField("x"));
validateUnmatched.invoke(null, typedMember); // no error
}
static class ValidateArgSpecField {
@Mixin
@Option(names = "-x")
@@ -110,7 +82,7 @@ public class ModelCommandReflectionTest {
@Test
public void testValidateArgSpecField() throws Exception {
Class<?> reflection = Class.forName("picocli.CommandLine$Model$CommandReflection");
Method validateArgSpecField = reflection.getDeclaredMethod("validateArgSpecField", CommandLine.Model.TypedMember.class);
Method validateArgSpecField = reflection.getDeclaredMethod("validateArgSpecMember", CommandLine.Model.TypedMember.class);
validateArgSpecField.setAccessible(true);
CommandLine.Model.TypedMember typedMember = new CommandLine.Model.TypedMember(ValidateArgSpecField.class.getDeclaredField("x"));
@@ -119,14 +91,14 @@ public class ModelCommandReflectionTest {
fail("expected Exception");
} catch (InvocationTargetException ite) {
CommandLine.DuplicateOptionAnnotationsException ex = (CommandLine.DuplicateOptionAnnotationsException) ite.getCause();
assertEquals("A member cannot be both a @Mixin command and an @Option or @Parameters, but 'int picocli.ModelCommandReflectionTest$ValidateArgSpecField.x' is both.", ex.getMessage());
assertEquals("A member cannot have both @Option and @Mixin annotations, but 'int picocli.ModelCommandReflectionTest$ValidateArgSpecField.x' has both.", ex.getMessage());
}
}
@Test
public void testValidateArgSpecField_final() throws Exception {
Class<?> reflection = Class.forName("picocli.CommandLine$Model$CommandReflection");
Method validateArgSpecField = reflection.getDeclaredMethod("validateArgSpecField", CommandLine.Model.TypedMember.class);
Method validateArgSpecField = reflection.getDeclaredMethod("validateArgSpecMember", CommandLine.Model.TypedMember.class);
validateArgSpecField.setAccessible(true);
CommandLine.Model.TypedMember typedMember = new CommandLine.Model.TypedMember(ValidateArgSpecField.class.getDeclaredField("f"));
@@ -137,7 +109,7 @@ public class ModelCommandReflectionTest {
@Test
public void testValidateArgSpecField_neither() throws Exception {
Class<?> reflection = Class.forName("picocli.CommandLine$Model$CommandReflection");
Method validateArgSpecField = reflection.getDeclaredMethod("validateArgSpecField", CommandLine.Model.TypedMember.class);
Method validateArgSpecField = reflection.getDeclaredMethod("validateArgSpecMember", CommandLine.Model.TypedMember.class);
validateArgSpecField.setAccessible(true);
CommandLine.Model.TypedMember typedMember = new CommandLine.Model.TypedMember(ValidateArgSpecField.class.getDeclaredField("neither"));
@@ -147,23 +119,23 @@ public class ModelCommandReflectionTest {
static class ValidateInjectSpec {
int notAnnotated;
@CommandLine.Spec
@Spec
@Option(names = "-x")
int x;
@CommandLine.Spec
@Spec
@CommandLine.Parameters
int y;
@CommandLine.Spec
@Spec
@Unmatched
List<String> unmatched;
@CommandLine.Spec
@Spec
@Mixin
Object mixin = new Object();
@CommandLine.Spec
@Spec
Object invalidType;
}
@Test
@@ -194,7 +166,7 @@ public class ModelCommandReflectionTest {
fail("expected Exception");
} catch (InvocationTargetException ite) {
CommandLine.DuplicateOptionAnnotationsException ex = (CommandLine.DuplicateOptionAnnotationsException) ite.getCause();
assertEquals("A member cannot have both @Spec and @Option or @Parameters annotations, but 'int picocli.ModelCommandReflectionTest$ValidateInjectSpec.x' has both.", ex.getMessage());
assertEquals("A member cannot have both @Spec and @Option annotations, but 'int picocli.ModelCommandReflectionTest$ValidateInjectSpec.x' has both.", ex.getMessage());
}
}
@@ -210,7 +182,7 @@ public class ModelCommandReflectionTest {
fail("expected Exception");
} catch (InvocationTargetException ite) {
CommandLine.DuplicateOptionAnnotationsException ex = (CommandLine.DuplicateOptionAnnotationsException) ite.getCause();
assertEquals("A member cannot have both @Spec and @Option or @Parameters annotations, but 'int picocli.ModelCommandReflectionTest$ValidateInjectSpec.y' has both.", ex.getMessage());
assertEquals("A member cannot have both @Spec and @Parameters annotations, but 'int picocli.ModelCommandReflectionTest$ValidateInjectSpec.y' has both.", ex.getMessage());
}
}
@@ -264,7 +236,7 @@ public class ModelCommandReflectionTest {
@Test
public void testValidateArgSpec() throws Exception {
Class<?> reflection = Class.forName("picocli.CommandLine$Model$CommandReflection");
Method validateArgSpec = reflection.getDeclaredMethod("validateArgSpecField", CommandLine.Model.TypedMember.class);
Method validateArgSpec = reflection.getDeclaredMethod("validateArgSpecMember", CommandLine.Model.TypedMember.class);
validateArgSpec.setAccessible(true);
CommandLine.Model.TypedMember typedMember = new CommandLine.Model.TypedMember(ValidateInjectSpec.class.getDeclaredField("notAnnotated"));
@@ -273,7 +245,7 @@ public class ModelCommandReflectionTest {
fail("expected Exception");
} catch (InvocationTargetException ite) {
IllegalStateException ex = (IllegalStateException) ite.getCause();
assertEquals("Bug: validateArgSpecField() should only be called with an @Option or @Parameters member", ex.getMessage());
assertEquals("Bug: validateArgSpecMember() should only be called with an @Option or @Parameters member", ex.getMessage());
}
}

View File

@@ -53,6 +53,6 @@ public class ModelFieldBindingTest {
ModelMethodBindingBean value = new ModelMethodBindingBean();
FieldBinding binding = new FieldBinding(value, f);
assertEquals("picocli.CommandLine.Model.FieldBinding(int picocli.ModelMethodBindingBean.x)", binding.toString());
assertEquals("FieldBinding(int picocli.ModelMethodBindingBean.x)", binding.toString());
}
}

View File

@@ -3,6 +3,7 @@ package picocli;
import org.junit.Test;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Model.MethodBinding;
import picocli.CommandLine.Model.ObjectScope;
import picocli.CommandLine.ParameterException;
import picocli.CommandLine.PicocliException;
@@ -16,7 +17,7 @@ public class ModelMethodBindingTest {
@Test
public void testGetDoesNotInvokeMethod() throws Exception {
Method getX = ModelMethodBindingBean.class.getDeclaredMethod("getX");
MethodBinding binding = new MethodBinding(new ModelMethodBindingBean(), getX, CommandSpec.create());
MethodBinding binding = new MethodBinding(new ObjectScope(new ModelMethodBindingBean()), getX, CommandSpec.create());
binding.get(); // no IllegalAccessException
}
@@ -26,7 +27,7 @@ public class ModelMethodBindingTest {
getX.setAccessible(true);
ModelMethodBindingBean bean = new ModelMethodBindingBean();
MethodBinding binding = new MethodBinding(bean, getX, CommandSpec.create());
MethodBinding binding = new MethodBinding(new ObjectScope(bean), getX, CommandSpec.create());
assertNull(binding.get());
assertEquals("actual value returned by getX() method", 7, bean.publicGetX());
}
@@ -38,7 +39,7 @@ public class ModelMethodBindingTest {
ModelMethodBindingBean bean = new ModelMethodBindingBean();
CommandSpec spec = CommandSpec.create();
MethodBinding binding = new MethodBinding(bean, getX, spec);
MethodBinding binding = new MethodBinding(new ObjectScope(bean), getX, spec);
try {
binding.set(41);
@@ -57,7 +58,7 @@ public class ModelMethodBindingTest {
setX.setAccessible(true);
ModelMethodBindingBean bean = new ModelMethodBindingBean();
MethodBinding binding = new MethodBinding(bean, setX, CommandSpec.create());
MethodBinding binding = new MethodBinding(new ObjectScope(bean), setX, CommandSpec.create());
assertNull("initial", binding.get());
assertEquals(7, bean.publicGetX());
@@ -69,7 +70,7 @@ public class ModelMethodBindingTest {
@Test
public void testMethodMustBeAccessible() throws Exception {
Method setX = ModelMethodBindingBean.class.getDeclaredMethod("setX", int.class);
MethodBinding binding = new MethodBinding(new ModelMethodBindingBean(), setX, CommandSpec.create());
MethodBinding binding = new MethodBinding(new ObjectScope(new ModelMethodBindingBean()), setX, CommandSpec.create());
try {
binding.set(1);
fail("Expected exception");
@@ -84,7 +85,7 @@ public class ModelMethodBindingTest {
setX.setAccessible(true);
ModelMethodBindingBean value = new ModelMethodBindingBean();
MethodBinding binding = new MethodBinding(value, setX, CommandSpec.create());
MethodBinding binding = new MethodBinding(new ObjectScope(value), setX, CommandSpec.create());
binding.set(987);
assertEquals(987, value.publicGetX());
@@ -97,7 +98,7 @@ public class ModelMethodBindingTest {
setX.setAccessible(true);
CommandSpec spec = CommandSpec.create();
MethodBinding binding = new MethodBinding(null, setX, spec);
MethodBinding binding = new MethodBinding(new ObjectScope(null), setX, spec);
try {
binding.set(41);
@@ -117,7 +118,7 @@ public class ModelMethodBindingTest {
CommandSpec spec = CommandSpec.create();
CommandLine cmd = new CommandLine(spec);
spec.commandLine(cmd);
MethodBinding binding = new MethodBinding(null, setX, spec);
MethodBinding binding = new MethodBinding(new ObjectScope(null), setX, spec);
try {
binding.set(41);
@@ -135,7 +136,7 @@ public class ModelMethodBindingTest {
CommandSpec spec = CommandSpec.create();
assertNull(spec.commandLine());
MethodBinding binding = new MethodBinding(null, setX, spec);
MethodBinding binding = new MethodBinding(new ObjectScope(null), setX, spec);
try {
binding.set(41);
@@ -155,8 +156,8 @@ public class ModelMethodBindingTest {
setX.setAccessible(true);
ModelMethodBindingBean value = new ModelMethodBindingBean();
MethodBinding binding = new MethodBinding(value, setX, CommandSpec.create());
MethodBinding binding = new MethodBinding(new ObjectScope(value), setX, CommandSpec.create());
assertEquals("picocli.CommandLine.Model.MethodBinding(private void picocli.ModelMethodBindingBean.setX(int))", binding.toString());
assertEquals("MethodBinding(private void picocli.ModelMethodBindingBean.setX(int))", binding.toString());
}
}

View File

@@ -33,7 +33,7 @@ public class ModelTestUtil {
return option(obj, fieldName, CommandLine.defaultFactory());
}
public static OptionSpec option(Object obj, String fieldName, CommandLine.IFactory factory) throws Exception {
return OptionSpec.builder(TypedMember.createIfAnnotated(obj.getClass().getDeclaredField(fieldName), obj), factory).build();
return OptionSpec.builder(TypedMember.createIfAnnotated(obj.getClass().getDeclaredField(fieldName), new ObjectScope(obj)), factory).build();
}
public static OptionSpec[] options(Object obj, String... fieldNames) throws Exception {
OptionSpec[] result = new OptionSpec[fieldNames.length];

View File

@@ -40,7 +40,7 @@ public class ModelTypedMemberTest {
Method method = App.class.getDeclaredMethod("mymethod", char.class);
CommandLine.Model.MethodParam param = new CommandLine.Model.MethodParam(method, 0);
CommandLine.Model.TypedMember typedMember = new CommandLine.Model.TypedMember(param, new App());
CommandLine.Model.TypedMember typedMember = new CommandLine.Model.TypedMember(param, new CommandLine.Model.ObjectScope(new App()));
assertEquals(0, typedMember.getMethodParamPosition());
}
}

View File

@@ -58,7 +58,7 @@ public class PicocliTestUtil {
clear.setAccessible(true);
clear.invoke(interpreter); // initializes the interpreter instance
Field parseResultField = c.getDeclaredField("parseResult");
Field parseResultField = c.getDeclaredField("parseResultBuilder");
parseResultField.setAccessible(true);
Field nowProcessing = CommandLine.ParseResult.Builder.class.getDeclaredField("nowProcessing");
nowProcessing.setAccessible(true);