Initial commit
This commit is contained in:
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
.classpath
|
||||||
|
.idea
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
npm-debug.log
|
||||||
|
phantomjs
|
||||||
|
tags
|
||||||
|
*.DS_Store
|
||||||
|
*/bin/
|
||||||
|
**/.classpath
|
||||||
|
**/.settings
|
||||||
|
**/*.swp
|
||||||
|
**/target
|
||||||
|
**/.tern-project
|
||||||
|
**/*.iml
|
||||||
|
**/git.properties
|
||||||
|
target/**
|
14
bitbucket-pipelines.yml
Normal file
14
bitbucket-pipelines.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# This is a sample build configuration for Java (Maven).
|
||||||
|
# Check our guides at https://confluence.atlassian.com/x/zd-5Mw for more examples.
|
||||||
|
# Only use spaces to indent your .yml configuration.
|
||||||
|
# -----
|
||||||
|
# You can specify a custom docker image from Docker Hub as your build environment.
|
||||||
|
image: maven:3.3.9
|
||||||
|
|
||||||
|
pipelines:
|
||||||
|
default:
|
||||||
|
- step:
|
||||||
|
caches:
|
||||||
|
- maven
|
||||||
|
script: # Modify the commands below to build your repository.
|
||||||
|
- mvn -B verify # -B batch mode makes Maven less verbose
|
36
pom.xml
Normal file
36
pom.xml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>net.locuswors</groupId>
|
||||||
|
<artifactId>argparser</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>ArgParser</name>
|
||||||
|
<description>Library to parse command lined arguments into a class</description>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.2</version>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- https://mvnrepository.com/artifact/junit/junit -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
167
src/main/java/net/locusworks/argparser/ArgParser.java
Normal file
167
src/main/java/net/locusworks/argparser/ArgParser.java
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
package net.locusworks.argparser;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import net.locusworks.argparser.annotations.Parameter;
|
||||||
|
import net.locusworks.argparser.interfaces.IParameterConverter;
|
||||||
|
|
||||||
|
public class ArgParser {
|
||||||
|
|
||||||
|
private Class<?> argClass;
|
||||||
|
private List<Field> fields;
|
||||||
|
private String programName;
|
||||||
|
|
||||||
|
public ArgParser() { }
|
||||||
|
|
||||||
|
public static ArgParser newBuilder() {
|
||||||
|
return new ArgParser();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArgParser withArgClass(Class<?> argClass) {
|
||||||
|
this.argClass = argClass;
|
||||||
|
this.fields = Stream.of(argClass.getDeclaredFields())
|
||||||
|
.filter(f -> f.isAnnotationPresent(Parameter.class))
|
||||||
|
.map(f -> {
|
||||||
|
f.setAccessible(true);
|
||||||
|
return f;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (fields == null || fields.isEmpty()) throw new IllegalArgumentException("No fields with @Parameter defined");
|
||||||
|
|
||||||
|
fields.sort(new Comparator<Field>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(Field o1, Field o2) {
|
||||||
|
Integer o1Order = o1.getDeclaredAnnotation(Parameter.class).order();
|
||||||
|
Integer o2Order = o2.getDeclaredAnnotation(Parameter.class).order();
|
||||||
|
return o1Order.compareTo(o2Order);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
boolean positional = true;
|
||||||
|
for (Field f : fields) {
|
||||||
|
Parameter param = f.getDeclaredAnnotation(Parameter.class);
|
||||||
|
positional &= param.positional();
|
||||||
|
if (!positional && param.positional()) throw new IllegalArgumentException("All positional values have to be first in list");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArgParser setProgramName(String name) {
|
||||||
|
this.programName = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String help() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T parse(String[] args) {
|
||||||
|
try {
|
||||||
|
return parseItem(args);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
System.err.println("An exception occured: " + ex.getMessage());
|
||||||
|
System.err.println(help());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T> T parseItem(String[] args) throws InstantiationException, IllegalAccessException {
|
||||||
|
if (argClass == null) throw new IllegalArgumentException("Argument class was not specified");
|
||||||
|
Object obj = argClass.newInstance();
|
||||||
|
this.fields = Stream.of(obj.getClass().getDeclaredFields())
|
||||||
|
.filter(f -> f.isAnnotationPresent(Parameter.class))
|
||||||
|
.map(f -> {
|
||||||
|
f.setAccessible(true);
|
||||||
|
return f;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<String> argList = new ArrayList<>(Arrays.asList(args));
|
||||||
|
|
||||||
|
List<Field> positional = new ArrayList<>();
|
||||||
|
for(Iterator<Field> iter = fields.iterator(); iter.hasNext();) {
|
||||||
|
Field f = iter.next();
|
||||||
|
Parameter param = f.getDeclaredAnnotation(Parameter.class);
|
||||||
|
if (param.positional()) {
|
||||||
|
positional.add(f);
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Iterator<String> iter = argList.iterator(); iter.hasNext() && !positional.isEmpty();) {
|
||||||
|
String value = iter.next();
|
||||||
|
Field f = positional.remove(0);
|
||||||
|
Parameter param = f.getDeclaredAnnotation(Parameter.class);
|
||||||
|
IParameterConverter<?> converter = param.converter().newInstance();
|
||||||
|
f.set(obj, converter.convert(value));
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Field, List<String>> paramMap = new LinkedHashMap<>();
|
||||||
|
List<String> params = null;
|
||||||
|
for(Iterator<String> iter = argList.iterator(); iter.hasNext();) {
|
||||||
|
String value = iter.next();
|
||||||
|
Field f = findField(value);
|
||||||
|
if (f != null) {
|
||||||
|
if (!paramMap.containsKey(f)) {
|
||||||
|
paramMap.put(f, new ArrayList<>());
|
||||||
|
}
|
||||||
|
params = paramMap.get(f);
|
||||||
|
} else {
|
||||||
|
params.add(value);
|
||||||
|
}
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Entry<Field, List<String>> entry : paramMap.entrySet()) {
|
||||||
|
Field f = entry.getKey();
|
||||||
|
List<String> values = entry.getValue();
|
||||||
|
Parameter param = f.getDeclaredAnnotation(Parameter.class);
|
||||||
|
if (param.variableArity() && !f.getType().isAssignableFrom(List.class))
|
||||||
|
throw new IllegalArgumentException(String.format("Field %s is not of type list for the params", f.getName()));
|
||||||
|
IParameterConverter<?> converter = param.converter().newInstance();
|
||||||
|
List<Object> objs = values.stream().map(s -> converter.convert(s)).collect(Collectors.toList());
|
||||||
|
if (param.variableArity()) {
|
||||||
|
f.set(obj, getGenericList(f.getType(), objs));
|
||||||
|
} else {
|
||||||
|
f.set(obj, objs.get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T)obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Field findField(String value) {
|
||||||
|
for (Field f : fields) {
|
||||||
|
Parameter param = f.getDeclaredAnnotation(Parameter.class);
|
||||||
|
for (String s : Arrays.asList(param.names())) {
|
||||||
|
if (s.equals(value)) return f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <Type> List<Type> getGenericList(Class<Type> type, List<Object> items) {
|
||||||
|
List<Type> l = new ArrayList<>();
|
||||||
|
for (Object o : items) {
|
||||||
|
l.add((Type)o);
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package net.locusworks.argparser.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import net.locusworks.argparser.converters.NoConverter;
|
||||||
|
import net.locusworks.argparser.interfaces.IParameterConverter;
|
||||||
|
|
||||||
|
@Target(value= {ElementType.FIELD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Parameter {
|
||||||
|
|
||||||
|
String[] names();
|
||||||
|
String description() default "";
|
||||||
|
boolean positional() default false;
|
||||||
|
int order() default Integer.MAX_VALUE;
|
||||||
|
int arityCount() default 0;
|
||||||
|
boolean variableArity() default false;
|
||||||
|
boolean required() default false;
|
||||||
|
Class<? extends IParameterConverter<?>> converter() default NoConverter.class;
|
||||||
|
SubParameter[] subParameters() default {};
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package net.locusworks.argparser.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(value={ElementType.ANNOTATION_TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface SubParameter {
|
||||||
|
String parameter();
|
||||||
|
Class<?> subClass();
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package net.locusworks.argparser.converters;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import net.locusworks.argparser.interfaces.IParameterConverter;
|
||||||
|
|
||||||
|
public class FileConverter implements IParameterConverter<File> {
|
||||||
|
|
||||||
|
public File convert(String value) {
|
||||||
|
File file = new File(value);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package net.locusworks.argparser.converters;
|
||||||
|
|
||||||
|
import net.locusworks.argparser.interfaces.IParameterConverter;
|
||||||
|
|
||||||
|
public class IntegerConverter implements IParameterConverter<Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer convert(String value) {
|
||||||
|
return Integer.parseInt(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package net.locusworks.argparser.converters;
|
||||||
|
|
||||||
|
import net.locusworks.argparser.interfaces.IParameterConverter;
|
||||||
|
|
||||||
|
public class NoConverter implements IParameterConverter<String> {
|
||||||
|
|
||||||
|
public String convert(String value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package net.locusworks.argparser.interfaces;
|
||||||
|
|
||||||
|
public interface IParameterConverter<T> {
|
||||||
|
|
||||||
|
T convert(String value);
|
||||||
|
|
||||||
|
}
|
43
src/test/java/test/argparser/ArgParserTest.java
Normal file
43
src/test/java/test/argparser/ArgParserTest.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package test.argparser;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import net.locusworks.argparser.ArgParser;
|
||||||
|
import net.locusworks.argparser.annotations.Parameter;
|
||||||
|
import net.locusworks.argparser.converters.IntegerConverter;
|
||||||
|
|
||||||
|
public class ArgParserTest {
|
||||||
|
|
||||||
|
public static class Args {
|
||||||
|
@Parameter(names="position2", positional=true, order=2)
|
||||||
|
public String position2;
|
||||||
|
|
||||||
|
@Parameter(names="position1", positional=true, order=1)
|
||||||
|
public String position1;
|
||||||
|
|
||||||
|
@Parameter(names="-list", variableArity=true)
|
||||||
|
public List<String> values;
|
||||||
|
|
||||||
|
@Parameter(names="-count", converter=IntegerConverter.class)
|
||||||
|
public int count;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testArgParser() {
|
||||||
|
String[] args = new String[] {"Hello", "World", "-list", "hell", "world", "-count", "1"};
|
||||||
|
|
||||||
|
Args arg = ArgParser.newBuilder().withArgClass(Args.class).parse(args);
|
||||||
|
|
||||||
|
assertTrue(arg.position2.equals(args[0]));
|
||||||
|
assertTrue(arg.position1.equals(args[1]));
|
||||||
|
assertTrue(arg.values.size() == 2);
|
||||||
|
assertTrue(arg.count == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user