Skip to content

Instantly share code, notes, and snippets.

@mmpataki
Last active February 3, 2023 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mmpataki/8514550e3b8aa97f3e0cd98011e4e553 to your computer and use it in GitHub Desktop.
Save mmpataki/8514550e3b8aa97f3e0cd98011e4e553 to your computer and use it in GitHub Desktop.
Cli arg parser
@Retention(RetentionPolicy.RUNTIME)
public @interface Exposed {
String value();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Argument {
String[] keys();
String help();
boolean required() default false;
boolean multivalued() default false;
boolean sensitive() default false;
String parser() default "defaultParser";
}
public class Configuration {
@Argument(keys = {"-h", "--help"}, help = "Prints help")
private boolean help;
// input control
@Argument(keys = {"-i", "--input"}, help = "Input file (Can have SQL or shell commands)")
private String inputFile;
@Argument(keys = {"--props"}, help = "Config props file")
private String propsFile;
@Argument(keys = {"--printProps"}, help = "Prints sample props file")
private boolean printProps = false;
// internal for debugging
boolean __debug = false;
@Override
public String toString() {
return Arrays.stream(getClass().getDeclaredFields()).filter(f -> __debug || f.isAnnotationPresent(Exposed.class)).map(f -> {
try {
return String.format("%s = %s", f.getName(), f.get(this));
} catch (IllegalAccessException e) {
return "";
}
}).collect(Collectors.joining("\n"));
}
@SuppressWarnings("unchecked")
public void txParser(Field f, Configuration c, String s) throws Exception {
if (f.get(c) == null) f.set(c, new HashMap<>());
if (s == null || s.isEmpty()) return;
((Map) f.get(c)).put(s.substring(0, s.indexOf("=")), (Transformer) Class.forName(s.substring(s.indexOf('=') + 1)).newInstance());
}
public void defaultParser(Field f, Configuration c, String s) throws Exception {
Object val = null;
if (Integer.TYPE.isAssignableFrom(f.getType())) {
val = Integer.parseInt(s);
} else if (Long.TYPE.isAssignableFrom(f.getType())) {
val = Long.parseLong(s);
} else if (Double.TYPE.isAssignableFrom(f.getType())) {
val = Double.parseDouble(s);
} else if (Float.TYPE.isAssignableFrom(f.getType())) {
val = Float.parseFloat(s);
} else if (Boolean.TYPE.isAssignableFrom(f.getType())) {
val = s == null || Boolean.valueOf(s);
} else {
val = s.isEmpty() ? null : s;
}
f.set(c, val);
}
private void set(Field f, String val) throws Exception {
getClass().getMethod(f.getAnnotation(Argument.class).parser(), Field.class, Configuration.class, String.class)
.invoke(this, f, this, val);
}
public Configuration(String args[]) throws Exception {
Map<String, Field> fields = new LinkedHashMap<>();
Arrays.stream(Configuration.class.getDeclaredFields()).filter(f -> f.isAnnotationPresent(Argument.class)).forEach(f -> {
Argument arg = f.getAnnotation(Argument.class);
for (String key : arg.keys())
fields.put(key, f);
});
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if (!fields.containsKey(arg)) {
log("unknown argument: " + arg);
System.exit(0);
}
Field field = fields.get(arg);
set(field, field.getType().isAssignableFrom(boolean.class) ? "true" : args[++i]);
}
if (propsFile != null) {
Properties props = new Properties();
props.load(new FileReader(propsFile));
for (Map.Entry<Object, Object> entry : props.entrySet()) {
set(getClass().getDeclaredField(entry.getKey().toString()), entry.getValue().toString());
}
}
if (printProps) {
for (Field f : Configuration.class.getDeclaredFields()) {
if (f.isAnnotationPresent(Argument.class)) {
Argument arg = f.getAnnotation(Argument.class);
System.out.printf("# %s\n", arg.required() ? "REQUIRED" : "OPTIONAL");
System.out.printf("# %s\n", f.getAnnotation(Argument.class).help());
boolean noVal = f.get(this) == null || Map.class.isAssignableFrom(f.getType()) || Collection.class.isAssignableFrom(f.getType());
System.out.printf("%s=%s\n", f.getName(), noVal ? "" : f.get(this));
System.out.println();
}
}
System.exit(0);
}
if (!help) {
for (Field f : fields.values()) {
Argument argConf = f.getAnnotation(Argument.class);
if (!(f.getAnnotation(Argument.class).required() && f.get(this) == null)) continue;
System.out.printf("Enter %s (%s): ", f.getName(), argConf.help());
set(f, (argConf.sensitive()) ? new String(System.console().readPassword()) : sc.readLine());
}
}
}
public String getHelp() {
StringBuilder sb = new StringBuilder();
sb.append(String.format(" %-35s %4s %8s %s\n", "switch", "reqd", "multiple", "help"));
Arrays.stream(getClass().getDeclaredFields()).filter(f -> f.isAnnotationPresent(Argument.class)).forEach(f -> {
Argument arg = f.getAnnotation(Argument.class);
sb.append(String.format(
" %-35s %3s %4s %s\n",
String.join(", ", arg.keys()) + (f.getType().isAssignableFrom(boolean.class) ? "" : String.format(" <%s>", f.getName())),
arg.required() ? "*" : " ",
arg.multivalued() ? "*" : " ",
arg.help()
));
});
return sb.toString();
}
}
class Driver {
public static void main(String args[]) throws Exception {
Configuration conf = new Configuration(args);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment