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 5efbee2
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 11 deletions.
121 changes: 110 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 @@ -6023,6 +6102,15 @@ public interface ISetter {
* @throws Exception internally, picocli call sites will catch any exceptions thrown from here and rethrow them wrapped in a PicocliException */
<T> T set(T value) throws Exception;
}
/** Optional interface that can be implemented by {@link IGetter} implementations to
* indicate whether the {@link IGetter#get()} method is accessible.
*/
public interface IGetterAccessChecker {
/** Check whether the {@link IGetter#get()} method is accessible.
*
* @return true if the {@link IGetter#get()} method is accessible, false otherwise */
boolean isGetterAccessible();
}

/**
* This class provides a case-aware Linked HashMap. Supports both case-sensitive and case-insensitive modes.
Expand Down Expand Up @@ -9169,6 +9257,9 @@ private String defaultValueFromProvider() {

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

static class FieldBinding implements IGetter, ISetter {
static class FieldBinding implements IGetter, IGetterAccessChecker, ISetter {
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 boolean isGetterAccessible() {
try { return scope!=null && scope.get()!=null; }
catch (Exception ex) { return false; }
}
public <T> T get() throws PicocliException {
Object obj;
try { obj = scope.get(); }
Expand Down Expand Up @@ -12051,7 +12146,7 @@ public String toString() {
field.getDeclaringClass().getName(), field.getName());
}
}
static class MethodBinding implements IGetter, ISetter {
static class MethodBinding implements IGetter, IGetterAccessChecker, ISetter {
private final IScope scope;
private final Method method;
private final CommandSpec spec;
Expand All @@ -12061,6 +12156,10 @@ static class MethodBinding implements IGetter, ISetter {
this.method = method;
this.spec = spec;
}
public boolean isGetterAccessible() {
try { return scope!=null && scope.get()!=null; }
catch (Exception ex) { return false; }
}
@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 5efbee2

Please sign in to comment.