Giter Site home page Giter Site logo

jewelcli's People

Contributors

laurence-myers avatar lexicalscope avatar tedyoung avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

jewelcli's Issues

Interface Not Visible from Class Loader

I've started implementing jewelcli and I like it so far, but I've ran into an issue.

[10:12:19] [Server thread/WARN]: java.lang.IllegalArgumentException: interface com.graywolf336.jail.command.commands.jewels.Jailing is not visible from class loader
[10:12:19] [Server thread/WARN]:    at java.lang.reflect.Proxy.getProxyClass(Unknown Source)
[10:12:19] [Server thread/WARN]:    at java.lang.reflect.Proxy.newProxyInstance(Unknown Source)
[10:12:19] [Server thread/WARN]:    at com.lexicalscope.jewelcli.internal.fluentreflection.dynamicproxy.$FluentProxy.dynamicProxy(FluentProxy.java:23)
[10:12:19] [Server thread/WARN]:    at com.lexicalscope.jewelcli.internal.fluentreflection.bean.$MapBean.bean(MapBean.java:36)
[10:12:19] [Server thread/WARN]:    at com.lexicalscope.jewel.cli.InterfaceArgumentPresentingStrategy.presentArguments(InterfaceArgumentPresentingStrategy.java:33)
[10:12:19] [Server thread/WARN]:    at com.lexicalscope.jewel.cli.ArgumentPresenterImpl.presentArguments(ArgumentPresenterImpl.java:63)
[10:12:19] [Server thread/WARN]:    at com.lexicalscope.jewel.cli.AbstractCliImpl.parseArguments(AbstractCliImpl.java:42)
[10:12:19] [Server thread/WARN]:    at com.lexicalscope.jewel.cli.CliFactory.parseArguments(CliFactory.java:67)
[10:12:19] [Server thread/WARN]:    at com.graywolf336.jail.command.subcommands.JailCommand.execute(JailCommand.java:62)
[10:12:19] [Server thread/WARN]:    at com.graywolf336.jail.command.JailHandler.parseCommand(JailHandler.java:124)
[10:12:19] [Server thread/WARN]:    at com.graywolf336.jail.JailMain.onCommand(JailMain.java:121)
[10:12:19] [Server thread/WARN]:    at org.bukkit.command.PluginCommand.execute(PluginCommand.java:44)
[10:12:19] [Server thread/WARN]:    at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:196)
[10:12:19] [Server thread/WARN]:    at org.bukkit.craftbukkit.v1_7_R1.CraftServer.dispatchCommand(CraftServer.java:546)
[10:12:19] [Server thread/WARN]:    at org.bukkit.craftbukkit.v1_7_R1.CraftServer.dispatchServerCommand(CraftServer.java:533)
[10:12:19] [Server thread/WARN]:    at net.minecraft.server.v1_7_R1.DedicatedServer.aw(DedicatedServer.java:309)
[10:12:19] [Server thread/WARN]:    at net.minecraft.server.v1_7_R1.DedicatedServer.u(DedicatedServer.java:274)
[10:12:19] [Server thread/WARN]:    at net.minecraft.server.v1_7_R1.MinecraftServer.t(MinecraftServer.java:540)
[10:12:19] [Server thread/WARN]:    at net.minecraft.server.v1_7_R1.MinecraftServer.run(MinecraftServer.java:446)
[10:12:19] [Server thread/WARN]:    at net.minecraft.server.v1_7_R1.ThreadServerApplication.run(SourceFile:617)

Any ideas on this as right now I'm clueless?

Allow setting a validator class for an option.

I would like to register a class to apply to the value of an option.

Implementations of the following interface would allow extremely flexible pre-validation without having to add this noise to your code and would allow better reuse of common validations.

public interface OptionValidator<T>
{
    public boolean isValid(@Nullable final T value);
    @Nonnull
    public String getErrorMessage();
}

If you could point me to where this should be added in I can write it and submit a pull request, but the indirection is obfuscating where this would go.

ClassCastException when defining list subclasses

If I try to define a ListSubclass with a single String parameter that does some parsing, I get a ClassCastException. Here's the sample code:

public static class MyList extends ArrayList<String> {
  public MyList(String string) {
    this.addAll(Arrays.asList(string.split(";;")));
  }
}
public interface Options {
  @Option
  MyList getList();
}
public static void main(String[] args) {
  Options options = CliFactory.parseArguments(Options.class, args);
  System.err.println(options.getList());
}

And here's the exception:

Exception in thread "main" java.lang.ClassCastException: java.util.LinkedList cannot be cast to Bug$MyList
at $Proxy4.getList(Unknown Source)
at Bug.main(Bug.java:22)

Seems like maybe it's testing whether MyList is a subclass of List, when it really should be checking if MyList is a superclass of LinkedList. Also, since MyList has a constructor with a single String argument, shouldn't that just be used instead?

The option "/?" cannot be registered

Trying to register the help command line option "/?" (which is common on the Windows platform) doesn't work. It silently gets translated to "-/". Is this a bug?

Create ArgumentValidationException with message

Is there an easy way to create one's own ArgumentValidationException with a message? I miss something like

    throw new ArgumentValidationException("Lorem ipsum");

It would help writing custom semantic validatiom.

boolean flag does not work with "shortName"

The below code will throw NPE when accessing cli.f().
If I rename f() to getF(), everything works well.

interface CliOptions {
    @Option(shortName = "f")
    boolean f();
}

public static void main(String[] args) {
    CliOptions cli = CliFactory.parseArguments(CliOptions.class, "-f");
    System.out.println("cli.f() = " + cli.f());
}

enum option

It will be very helpful to provide enum option support and should not be hard to implement.

Feature: Support options of types without (String) constructor

JewelCli 0.8.9 requires that the type of an option has a (String) constructor, but many value types do not meet this requirement.

Exception in thread "main" java.lang.ClassCastException: cannot convert class java.lang.String to interface java.nio.file.Path
	at com.lexicalscope.jewel.cli.ConvertTypeOfObject.convertValueTo(ConvertTypeOfObject.java:159)
	at com.lexicalscope.jewel.cli.ConvertTypeOfObject.convert(ConvertTypeOfObject.java:98)
	at com.lexicalscope.jewel.cli.ArgumentPresenterImpl.putValuesInMap(ArgumentPresenterImpl.java:79)
	at com.lexicalscope.jewel.cli.ArgumentPresenterImpl.presentArguments(ArgumentPresenterImpl.java:40)
	at com.lexicalscope.jewel.cli.AbstractCliImpl.parseArguments(AbstractCliImpl.java:42)

The workaround is to build an intermediate type that parses a String into the desired type, but since JewelCli also does not support Java 8 default methods (#38), this cannot be done transparently.

Other option libraries such as jopt-simple (see withValuesConvertedBy) allow a factory method to be registered in case a constructor is not available or is otherwise undesirable.

Here's two different ways to do this:

  1. Add a parser attribute to @Option that accepts a Class<? extends Function<String, ?>>. The main downside of this approach is that, as far as I know, we cannot guarantee the function return type matches the annotated method's return type at compile time. :( It can also cause some duplication if there are multiple arguments of the same type, but on the other hand it has the flexibility of allowing different parsers (or validators) for each argument as well.

  2. Add a method to either Cli or CliFactory to register a parser, e.g. <T> Cli<O> withParser(Class<T>, Function<String, T>). The downside of this is that it is not aligned with the rest of JewelCli's annotation-centric API.

I think the first approach is more desirable since it maintains API consistency and option parsing tends to happen very early in the application, lessening the pain of errors at runtime.

Ignored Method annotated with Option throws unhelpful exception

I have a problem that appears to have come along in moving between the
old package 0.58 to the new one, 0.8.1. The issue is that in the old
version, an Option which did not follow the JavaBeans naming
convention for getters was processed, while in the new one it is
ignored and an exception will be thrown when one tries to use it.

Example: I have a simple interface
public interface Options {
@option String name();
}

The I parse the args
options = CliFactory.parseArguments(Options.class, args);
System.out.println("name: " + options.name())

I will get an exception
Exception in thread "main" java.lang.UnsupportedOperationException: no
implemention found for method public abstract java.lang.String
com.test.Main$Options.name()
at com.lexicalscope.jewelcli.internal.fluentreflection.dynamicproxy.
$Implementing.invoke(Implementing.java:165)
at $Proxy4.name(Unknown Source)

I understand why this happens, but perhaps there could be an exception
thrown during parsing? For example, if a method marked @option does
not follow the JavaBeans naming convention, throw an exception.

This has bitten me a couple of times in the last few months as I am
still using the 0.58 on some old projects. The exception message is
not helpful - I thought there was a problem with a proxying library or
dependency conflict.

Thanks!

Patrick

Support for interfaces with default methods

I think Java 8 default methods would be a great way to convert or enrich parsed arguments. For example, I tried the following to convert a String to a Path:

public interface Args {
    @Option(shortName = "d")
    String directory();

    default Path directoryPath() {
        return FileSystems.getDefault().getPath(directory());
    }
}

Unfortunately, jewelcli does not seem to support default methods. When calling directoryPath(), I get null.

Without having looked at the code, I feel like this should be easy to implement. All that needs to be done it to not override/implement default methods in proxies.

Extend enum support to allow it to return the specified Enum value

Hello,

I saw ticket #4 where a boolean is used to get a certain enum value.
It would be nice to have an option to specify one string value of an enum and get the typed Enum value from the interface.

I have worked around this by doing:
@option(shortName = "r", description = "value should be either: 'stopYYYStartXXX' or 'stopXXXstartYYY'")
ScenarioEnumWrapper getScenario();

public class ScenarioEnumWrapper extends EnumWrapper<Scenario> {
    public ScenarioEnumWrapper(final String value) {
        super(value);
    }
}

and then:
public abstract class EnumWrapper<T extends Enum<?>> {
private final T enumValue;

@SuppressWarnings({ "unchecked", "rawtypes" })
public EnumWrapper(final String value) {
    final List<Class<?>> classes = getTypeArguments(EnumWrapper.class, this.getClass());

    enumValue = (T) Enum.valueOf((Class) classes.get(0), value);
}

public T getValue() {
    return enumValue;
}

}

where the getTypeArguments is a whole lot of reflection magic going on to extract the value from T, it works, yet it would be nice if it could be hidden more inside jewelcli, so that I no longer have to get the ScenarioEnumWrapper I could just get the Scenario from a commandline value. It should even be easier to implement inside your proxy since you have the GenericReturnType of the Method in question all you need to do is return Enum.getValue(genericReturnType.class, incomingValue); and you don't need any special reflection tricks.

I might look into writing a patch.

Kris

OptionOrder.LEXICOGRAPHIC sorts by method name, not longName

It appears the default option ordering, OptionOrder.LEXICOGRAPHIC, sorts options based on the bean-style name of the method rather than the longName field of the Option annotation.

For example:

@CommandLineInterface(application="test")
public interface Test {

    @Option(longName="a")
    boolean getZ();

    @Option(longName="z")
    boolean getA();
}

Associated help message:

Usage: test [options]
    [--z]
    [--a]

optional values not working

boolean isName() does not seem to work as expected: trying to make an optional switch that takes an optional value. Leaving the value off throws an ArgumentValidationException. See testOptionalPresentNoValue in this Junit test.

import static org.junit.Assert.*;
import org.junit.Test;
import com.lexicalscope.jewel.cli.*;

public class JewelParseTest {
  public interface TestOption {
    @Option
    String getName();
    boolean isName();
  }

  @Test
  public void testOptionalMissing() throws ArgumentValidationException {
    final TestOption rv = CliFactory.parseArguments(TestOption.class);
    System.out.println(rv.toString());
    assertFalse(rv.isName());
    assertNull(rv.getName());
  }

  @Test
  public void testOptionalPresentNoValue()
      throws ArgumentValidationException {
    final TestOption rv = CliFactory.parseArguments(TestOption.class, "--name");
    System.out.println(rv.toString());
    assertTrue(rv.isName());
    assertNull(rv.getName());
  }

  @Test
  public void testOptionalPresentWithValue() throws ArgumentValidationException {
    final TestOption rv = CliFactory.parseArguments(TestOption.class, "--name",
        "value");
    System.out.println(rv.toString());
    assertTrue(rv.isName());
    assertEquals(rv.getName(), "value");
  }
}

Support for parsing negative numbers

In my project I need ability to pass negative numbers as option arguments, but unfortunately it doesn't work. It seems parser treats negative numbers as options themselves.

You can easily reproduce this by changing TestPrimitiveExample test (see "Option must have a value: --int value" failure)

--- a/jewelcli/src/test/java/com/lexicalscope/jewel/cli/examples/TestPrimitiveExample.java
+++ b/jewelcli/src/test/java/com/lexicalscope/jewel/cli/examples/TestPrimitiveExample.java
@@ -13,7 +13,7 @@ public class TestPrimitiveExample {
                 new String[] { "--boolean",
                          "--byte", "1",
                          "--short", "2",
-                         "--int", "3",
+                         "--int", "-3",
                          "--long", "4",
                          "--float", "4.1",
                          "--double", "4.2",

Repeatedly calling interface's method causes memory leak

My program repeatedly in call the jewelcli's generated object in a loop. I found this will lead to memory leak. It seems to me that jewelcli creates something every time when the method is called, rather than cache the parsed value.

Here is jmap's output.

num #instances #bytes class name

1: 7420760 653026880 java.lang.reflect.Method
2: 1679510 316381800 [B
3: 10261545 234341328 [Ljava.lang.Class;
4: 681145 186828472 [I
5: 5801706 185654592 java.util.AbstractList$Itr
6: 3092467 98958944 com.lexicalscope.jewelcli.internal.fluentreflection.$FluentClassImpl
7: 3092467 98958944 com.lexicalscope.jewelcli.internal.fluentreflection.$ReflectedMembersImpl
8: 3092467 98958944 com.lexicalscope.jewelcli.internal.fluentreflection.$ReflectedMethodsImpl
9: 3672323 88135752 com.lexicalscope.jewelcli.internal.guice.$TypeLiteral
10: 3672310 88135440 com.lexicalscope.jewelcli.internal.fluentreflection.$FluentAnnotatedImpl
11: 3092467 74219208 com.lexicalscope.jewelcli.internal.fluentreflection.$ReflectedFieldsImpl
12: 3092467 74219208 com.lexicalscope.jewelcli.internal.fluentreflection.$ReflectedSuperclassesAndInterfacesImpl
13: 3092467 74219208 com.lexicalscope.jewelcli.internal.fluentreflection.$ReflectedConstructorsImpl

Allow to specify a list of valid values to an option.

for example:

@Option(shortName = "f", longName = "format", defaultValue = "csv", validValues = "csv,json,xml", description = "Output File Format")
public Set<String> getFormats();

If it also auto generated an addition to the description like "Output File Format defaultValue=csv valid=[csv,json,xml]" it would go a long way to adding more information with no effort.

"maven site" fails because of missing class "org/sonatype/aether/graph/DependencyFilter"

Maven version: 3.3.9

"mvn site" fails with this error message:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-site-plugin:3.0:site (default-site) on project jewelcli-parent: Execution default-site of goal org.apache.maven.plugins:maven-site-plugin:3.0:site failed: A required class was missing while executing org.apache.maven.plugins:maven-site-plugin:3.0:site: org/sonatype/aether/graph/DependencyFilter

This issue was fixed with maven-site-plugin 3.3.

How to fix: Update maven plugin versions in pom.xml.

Links:

replace _ with - in option names

its common to have "-" in options names, which is not allowed in java; replacing _ with - in option names might be a neat way of solving this issue; although it cause trouble for anyone using _ at the moment and make it harder to get _ to work (would have to specify an explicit option name string). But its so much more likely for people to want - in the option that it might be worth it.

Java 7 required now?

This is really more of a question -- is it intended that Java 7 is required as of the 0.8.5 release? I noticed that it is compiled to target Java 7 since the major version is 51 in the JAR file. This makes it not load in Java 6.

support multi-mode CLI interfaces

Allow nested CLI definitions so options can be grouped depending on the mode of the application - the master option would control the availability of the slave options:

interface Top {
@option SlaveOne getSlaveOneOption();
boolean isSlaveOneOption();

@option SlaveTwo getSlaveTwoOption();
boolean isSlaveTwoOption();
}

interface SlaveOne {
@option getMandatorySlaveOneOption()
}

etc...

Option alternatives are poorly separated in help

I have a suggestion for a slight usability improvement. The help functionality doesn't separate the long and short names of an option very well. Example:

[--port -p value] : Webserver port

The specification of --port and -p as alternatives looks somewhat confusing in my opinion. It kind of implies that you need to specify both to use the option. Perhaps the format could be changed to something like:

[-p/--port value]: Webserver port

or:

[-p value | --port value]: Webserver port

or:

[--port value]: Webserver port (short form: -p)

There are probably other good formats as well.

Missing support for incremental integer parameters

There is currently no way to support incremental integer parameter like for verbose levels
specified by multiple repeated -v arguments on command line
This means that -v -v -v will lead to getVerbose() to return 3

boolean options are not working

Any boolean (or Boolean) options are not working, it will always return true. The following code will not work.

@option
boolean getOption1()

@option(defaultValue="false")
boolean getOption2()

If I did not pass any argument, both methods will return true. Your test case(http://jewelcli.sourceforge.net/xref-test/uk/co/flamingpenguin/jewel/cli/examples/TestPrimitiveExample.html) only tests when the boolean option arguments are present, however, there should also be test cases testing if no arguments are passed.

Throwing own exception in method causes "InvocationTargetRuntimeException"

When I throw an exception in a method that gets invoked by CliFactory.parseArgumentsUsingInstance() I get a InvocationTargetRuntimeException in the first place rather than my own exception.

I don't know if there are reasons against it, but I would expect to receive my own exception. That way I could do some argument validation in the methods and throw exceptions respectively.

Constraints on @Unparsed

Would be nice if it was possible to set some constraints on the result from @unparsed, such as "count=1" or "count > 0", maybe some more advanced things like patterns.

Thanks!

Create a usage message explicitly

It would be useful to be able to create the usage message without having a failed option parse.

My use case for this is that i have a number of unparsed arguments which must meet some moderately complex requirements (the first must be a URL, the second must be a method supported by the object referred to by that URL, etc), and if the user enters bad arguments, i would like to emit a usage message.

At the moment, i can say CliFactory.parseArguments(options.getClass(), "--help"), and handle the resulting exception as i would the exception from a failed option parse, but this is mildly icky.

Error parsing command line (copied from sf)

Command line: interface --mode STATIC --family ipv4 --mask 255.255.0.0 --gateway 10.77.1.4 --address 10.77.111.179 eth0
Use case: eth0 must be the interface name
Help:
Usage: interface options
--address -a /\d{1,3}+.\d{1,3}+.\d{1,3}+.\d{1,3}+/ : interface ip address
--family -f value : ip address family <ipv4, ipv6>
--gateway -g /\d{1,3}+.\d{1,3}+.\d{1,3}+.\d{1,3}+/ : gateway ip address
--mode -m value : modes <STATIC, DHCP, LOOPBACK>
--netmask -n /\d{1,3}+.\d{1,3}+.\d{1,3}+.\d{1,3}+/ : ip address net mask
Java interface:
@commandlineinterface(application = "interface")
public interface InterfaceCommand {
@option(shortName = "m", description = "modes <STATIC, DHCP, LOOPBACK>")
InterfaceMode getMode();

@option(shortName = "f", description = "ip address family <ipv4, ipv6>", defaultValue = "ipv4")
IPAddressFamily getFamily();

@option(shortName = "n", description = "ip address net mask", pattern = "\d{1,3}+.\d{1,3}+.\d{1,3}+.\d{1,3}+", defaultValue = "255.255.255.0")
String getNetmask();

@option(shortName = "g", description = "gateway ip address", pattern = "\d{1,3}+.\d{1,3}+.\d{1,3}+.\d{1,3}+", defaultValue = "127.0.0.1")
String getGateway();

@option(shortName = "a", description = "interface ip address", pattern = "\d{1,3}+.\d{1,3}+.\d{1,3}+.\d{1,3}+", defaultValue = "127.0.0.1")
String getAddress();

//@option(shortName = "i", description = "interface name")
@unparsed(name = "")
String getInterface();

enum IPAddressFamily {
ipv4, ipv6
}

enum InterfaceMode {
STATIC, DHCP, LOOPBACK
}
}
Error: Option does not take a value: uk.co.flamingpenguin.jewel.cli.UnparsedSpecificationImpl@4b222f

Multiple shortName Being Ignored

I have been looking at implementing this into my plugin for Bukkit (Minecraft server api) and was testing it out before I actually started using it. When doing so I made some unit tests and found something which looks like a bug but might be my ignorance on the usage. Here is the interface I am using:

public interface Jailing {
    @Option(shortName={"player", "pl", "p"})
    String getPlayer();
}

And here is the unit tests:

public class TestJewel {
    @Test
    public void testJewel() {
        String[] args = { "-pl", "graywolf336" };
        Jailing j = CliFactory.parseArguments(Jailing.class, args);

        Assert.assertEquals("graywolf336", j.getPlayer());
    }
}

And the result is an argument validation exception message which is wrong:

com.lexicalscope.jewel.cli.ArgumentValidationException: Option must have a value: --player -p -p -p value
Unexpected Option: l
    at com.lexicalscope.jewel.cli.ValidationErrorBuilderImpl.validate(ValidationErrorBuilderImpl.java:64)
    at com.lexicalscope.jewel.cli.validation.ArgumentValidatorImpl.finishedProcessing(ArgumentValidatorImpl.java:66)
    at com.lexicalscope.jewel.cli.ArgumentCollectionBuilder.processArguments(ArgumentCollectionBuilder.java:129)
    at com.lexicalscope.jewel.cli.AbstractCliImpl.parseArguments(AbstractCliImpl.java:42)
    at com.lexicalscope.jewel.cli.CliFactory.parseArguments(CliFactory.java:67)
    at test.java.com.graywolf336.jail.TestJewel.testJewel(TestJewel.java:14)

To my understanding of how this works, how I've got it setup and parsing it this should work. Am I missing something or this actually a bug?

Default value of false for boolean does not work

Newest version: Result of my parsed arguments is {cache=mysql, duration=0, fillCache=false, writeRequestsPerSecond=0, readRequestsPerSecond=0, readThreadCount=5, size=4096, verbose=false, writeThreadCount=0}
Calling .isVerose() or isFillCache() returns true.

Cannot debug it because there are no sources for the .internal classes.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.