Skip to content

Commit

Permalink
Fix remkop#1767 - NPE on OptionSpec.getValue()
Browse files Browse the repository at this point in the history
  • Loading branch information
rsenden committed Sep 7, 2022
1 parent 5366db8 commit 0541dad
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 11 deletions.
132 changes: 121 additions & 11 deletions src/main/java/picocli/CommandLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,47 @@
*/
package picocli;

import java.io.*;
import static java.util.Locale.ENGLISH;
import static picocli.CommandLine.Help.Column.Overflow.SPAN;
import static picocli.CommandLine.Help.Column.Overflow.TRUNCATE;
import static picocli.CommandLine.Help.Column.Overflow.WRAP;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.Console;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
Expand All @@ -35,23 +69,68 @@
import java.text.BreakIterator;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Currency;
import java.util.Date;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.Formatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.IllegalFormatException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Queue;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.Stack;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import picocli.CommandLine.Help;
import picocli.CommandLine.Help.Ansi;
import picocli.CommandLine.Help.Ansi.IStyle;
import picocli.CommandLine.Help.Ansi.Style;
import picocli.CommandLine.Help.Ansi.Text;
import picocli.CommandLine.Model.*;
import picocli.CommandLine.Model.ArgGroupSpec;
import picocli.CommandLine.Model.ArgSpec;
import picocli.CommandLine.Model.CaseAwareLinkedMap;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Model.IAnnotatedElement;
import picocli.CommandLine.Model.IOrdered;
import picocli.CommandLine.Model.ITypeInfo;
import picocli.CommandLine.Model.Messages;
import picocli.CommandLine.Model.OptionSpec;
import picocli.CommandLine.Model.ParserSpec;
import picocli.CommandLine.Model.PositionalParamSpec;
import picocli.CommandLine.Model.TypedMember;
import picocli.CommandLine.Model.UnmatchedArgsBinding;
import picocli.CommandLine.Model.UsageMessageSpec;
import picocli.CommandLine.ParseResult.GroupMatchContainer;

import static java.util.Locale.ENGLISH;
import static picocli.CommandLine.Help.Column.Overflow.SPAN;
import static picocli.CommandLine.Help.Column.Overflow.TRUNCATE;
import static picocli.CommandLine.Help.Column.Overflow.WRAP;

/**
* <p>
* CommandLine interpreter that uses reflection to initialize an annotated user object with values obtained from the
Expand Down Expand Up @@ -5995,6 +6074,15 @@ private Model() {}
* @since 4.0
*/
public interface IScope extends IGetter, ISetter {}

/** This interface provides access to an {@link IScope} instance.
*/
public interface IScoped {
/** Get the {@link IScope} instance.
*
* @return {@link IScope} instance */
IScope getScope();
}

/** Customizable getter for obtaining the current value of an option or positional parameter.
* When an option or positional parameter is matched on the command line, its getter or setter is invoked to capture the value.
Expand Down Expand Up @@ -9166,9 +9254,25 @@ private String defaultValueFromProvider() {
* @return whether this argument applies to all descendent subcommands of the command where it is defined
* @since 4.3 */
public ScopeType scopeType() { return scopeType; }

/** Check whether the {@link #getValue()} method is able to get an actual value from the current {@link #getter()}. */
public boolean isValueGettable() {
if (getter instanceof IScoped) {
IScoped scoped = (IScoped) getter;
IScope scope = scoped.getScope();
try {
Object obj = scope.get();
return obj != null;
} catch (Exception e) {
return false;
}
}
return true;
}

/** Returns the current value of this argument. Delegates to the current {@link #getter()}. */
public <T> T getValue() throws PicocliException {
if ( !isValueGettable() ) { return null; }
try {
return getter.<T>get();
} catch (PicocliException ex) { throw ex;
Expand Down Expand Up @@ -12018,11 +12122,14 @@ private static UnmatchedArgsBinding buildUnmatchedForMember(final IAnnotatedElem
}
}

static class FieldBinding implements IGetter, ISetter {
static class FieldBinding implements IGetter, ISetter, IScoped {
private final IScope scope;
private final Field field;
FieldBinding(Object scope, Field field) { this(ObjectScope.asScope(scope), field); }
FieldBinding(IScope scope, Field field) { this.scope = scope; this.field = field; }
public IScope getScope() {
return scope;
}
public <T> T get() throws PicocliException {
Object obj;
try { obj = scope.get(); }
Expand Down Expand Up @@ -12051,7 +12158,7 @@ public String toString() {
field.getDeclaringClass().getName(), field.getName());
}
}
static class MethodBinding implements IGetter, ISetter {
static class MethodBinding implements IGetter, ISetter, IScoped {
private final IScope scope;
private final Method method;
private final CommandSpec spec;
Expand All @@ -12061,6 +12168,9 @@ static class MethodBinding implements IGetter, ISetter {
this.method = method;
this.spec = spec;
}
public IScope getScope() {
return scope;
}
@SuppressWarnings("unchecked") public <T> T get() { return (T) currentValue; }
public <T> T set(T value) throws PicocliException {
Object obj;
Expand Down
41 changes: 41 additions & 0 deletions src/test/java/picocli/Issue1767.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package picocli;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static picocli.CommandLine.ScopeType.INHERIT;

import java.util.concurrent.Callable;

import org.junit.Test;

import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command;
import picocli.CommandLine.IFactory;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Option;
import picocli.CommandLine.Spec;

public class Issue1767 {
@Command(name = "test")
static class TestCommand implements Callable<Integer> {
@ArgGroup TestArgGroup testArgGroup;
@Spec CommandSpec spec;

public static class TestArgGroup {
@Option(names = "-r")
public Integer option1;
}

@Override
public Integer call() throws Exception {
Integer value = spec.options().get(0).getValue();
return value==null ? 0 : value;
}
}

@Test
public void testIssue1772() {
assertEquals(5, new CommandLine(new TestCommand()).execute("-r", "5"));
assertEquals(0, new CommandLine(new TestCommand()).execute());
}
}

0 comments on commit 0541dad

Please sign in to comment.