Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (C) 2013-2015 René Jeschke
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.rjeschke.txtmark.cmd;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
/**
* Generic command line parser.
*
* This is a copy from {@link https://github.com/rjeschke/neetutils-base}.
*
* @author René Jeschke ([email protected])
*/
final class CmdLineParser
{
private CmdLineParser()
{
// meh!
}
enum Type
{
UNSUPPORTED, STRING, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, LIST, BOOL;
}
final static HashMap, Type> TYPE_MAP = new HashMap, Type>();
final static Class[] TYPE_CLASS_LIST = Colls.> objArray(String.class, byte.class,
Byte.class, short.class, Short.class, int.class,
Integer.class, long.class, Long.class, float.class,
Float.class, double.class, Double.class, List.class,
Boolean.class, boolean.class);
final static Type[] TYPE_TYPE_LIST = Colls.objArray(Type.STRING, Type.BYTE, Type.BYTE,
Type.SHORT,
Type.SHORT, Type.INT, Type.INT, Type.LONG, Type.LONG,
Type.FLOAT, Type.FLOAT, Type.DOUBLE, Type.DOUBLE,
Type.LIST, Type.BOOL, Type.BOOL);
final static HashSet BOOL_TRUE = new HashSet(Colls.list("on", "true", "yes"));
final static HashSet BOOL_FALSE = new HashSet(Colls.list("off", "false", "no"));
static
{
for (int i = 0; i < TYPE_CLASS_LIST.length; i++)
{
TYPE_MAP.put(TYPE_CLASS_LIST[i], TYPE_TYPE_LIST[i]);
}
}
static Type getTypeFor(final Class clazz)
{
final Type type = TYPE_MAP.get(clazz);
if (type != null)
{
return type;
}
if (Classes.implementsInterface(clazz, List.class))
{
return Type.LIST;
}
return Type.UNSUPPORTED;
}
static String defaultToString(final Object value, final Type type, final Arg arg)
{
if (value == null || arg.isSwitch || arg.catchAll || !arg.printDefault)
{
return null;
}
if (type == Type.LIST)
{
final List list = (List)value;
if (list.isEmpty())
{
return null;
}
final StringBuilder sb = new StringBuilder();
final Once once = Once.of("", Character.toString(arg.itemSep));
for (final Object o : list)
{
sb.append(once.get());
sb.append(o.toString());
}
return sb.toString();
}
return value.toString();
}
private static void parseArgs(final Object[] objs, final List allArgs, final HashMap shortArgs,
final HashMap longArgs)
throws IOException
{
for (final Object obj : objs)
{
final Class cl = obj.getClass();
final Field[] fields = cl.getDeclaredFields();
for (final Field f : fields)
{
if (f.isAnnotationPresent(CmdArgument.class))
{
final Arg arg = new Arg(f.getAnnotation(CmdArgument.class), obj, f);
if (arg.type == Type.UNSUPPORTED)
{
throw new IOException("Unsupported parameter type: " + f.getType().getCanonicalName()
+ " for: " + arg);
}
if (arg.listType == Type.UNSUPPORTED || arg.listType == Type.LIST)
{
throw new IOException("Unsupported list type: " + f.getType().getCanonicalName() + " for: "
+ arg);
}
if (Strings.isEmpty(arg.s) && Strings.isEmpty(arg.l))
{
throw new IOException("Missing parameter name");
}
if (!Strings.isEmpty(arg.s))
{
if (shortArgs.containsKey(arg.s))
{
throw new IOException("Duplicate short argument: -" + arg.s);
}
shortArgs.put(arg.s, arg);
}
if (!Strings.isEmpty(arg.l))
{
if (longArgs.containsKey(arg.l))
{
throw new IOException("Duplicate long argument: --" + arg.l);
}
longArgs.put(arg.l, arg);
}
if (arg.isCatchAll() && arg.type != Type.LIST)
{
throw new IOException("Parameter '" + arg + "' requires a List field.");
}
if (arg.isSwitch && arg.type != Type.BOOL)
{
throw new IOException("Parameter '" + arg + "' requires a Boolean/boolean field.");
}
allArgs.add(arg);
}
}
}
}
/**
* Generates a formatted help (Unix-style) for the given argument objects.
*
* @param columnWidth
* Maximum column width. Words get wrapped at spaces.
* @param sort
* Set {@code true} to sort arguments before printing.
* @param objs
* One or more objects with annotated public fields.
* @return The formatted argument help text.
* @throws IOException
* if a parsing error occurred.
* @see CmdArgument
*/
public static String generateHelp(final int columnWidth, final boolean sort, final Object... objs)
throws IOException
{
final List allArgs = Colls.list();
final HashMap shortArgs = new HashMap();
final HashMap longArgs = new HashMap();
parseArgs(objs, allArgs, shortArgs, longArgs);
int minArgLen = 0;
for (final Arg a : allArgs)
{
int len = a.toString().length();
if (!a.isSwitch)
{
++len;
len += a.getResolvedType().toString().length();
if (a.isCatchAll())
{
++len;
}
else if (a.isList())
{
len += 6;
}
}
minArgLen = Math.max(minArgLen, len);
}
minArgLen += 2;
if (sort)
{
Collections.sort(allArgs);
}
final StringBuilder sb = new StringBuilder();
for (final Arg a : allArgs)
{
final StringBuilder line = new StringBuilder();
line.append(' ');
line.append(a);
if (!a.isSwitch)
{
line.append(' ');
line.append(a.getResolvedType().toString().toLowerCase());
if (a.isCatchAll())
{
line.append('s');
}
else if (a.isList())
{
line.append('[');
line.append(a.itemSep);
line.append("...]");
}
}
while (line.length() < minArgLen)
{
line.append(' ');
}
line.append(':');
final StringBuilder desc = new StringBuilder(a.desc.trim());
final String defVal = defaultToString(a.safeFieldGet(), a.type, a);
if (defVal != null)
{
desc.append(" Default is: '");
desc.append(defVal);
desc.append("'.");
}
final List toks = Strings.split(desc.toString(), ' ');
for (final String s : toks)
{
if (line.length() + s.length() + 1 > columnWidth)
{
sb.append(line);
sb.append('\n');
line.setLength(0);
while (line.length() <= minArgLen)
{
line.append(' ');
}
line.append(' ');
}
line.append(' ');
line.append(s);
}
if (line.length() > minArgLen)
{
sb.append(line);
sb.append('\n');
}
}
return sb.toString();
}
/**
* Parses command line arguments.
*
* @param args
* Array of arguments, like the ones provided by
* {@code void main(String[] args)}
* @param objs
* One or more objects with annotated public fields.
* @return A {@code List} containing all unparsed arguments (i.e. arguments
* that are no switches)
* @throws IOException
* if a parsing error occurred.
* @see CmdArgument
*/
public static List parse(final String[] args, final Object... objs) throws IOException
{
final List ret = Colls.list();
final List allArgs = Colls.list();
final HashMap shortArgs = new HashMap();
final HashMap longArgs = new HashMap();
parseArgs(objs, allArgs, shortArgs, longArgs);
for (int i = 0; i < args.length; i++)
{
final String s = args[i];
final Arg a;
if (s.startsWith("--"))
{
a = longArgs.get(s.substring(2));
if (a == null)
{
throw new IOException("Unknown switch: " + s);
}
}
else if (s.startsWith("-"))
{
a = shortArgs.get(s.substring(1));
if (a == null)
{
throw new IOException("Unknown switch: " + s);
}
}
else
{
a = null;
ret.add(s);
}
if (a != null)
{
if (a.isSwitch)
{
a.setField("true");
}
else
{
if (i + 1 >= args.length)
{
System.out.println("Missing parameter for: " + s);
}
if (a.isCatchAll())
{
final List ca = Colls.list();
for (++i; i < args.length; ++i)
{
ca.add(args[i]);
}
a.setCatchAll(ca);
}
else
{
++i;
a.setField(args[i]);
}
}
a.setPresent();
}
}
for (final Arg a : allArgs)
{
if (!a.isOk())
{
throw new IOException("Missing mandatory argument: " + a);
}
}
return ret;
}
private static class Arg implements Comparable
{
final String s;
final String l;
final String id;
final String desc;
final char itemSep;
final boolean isSwitch;
final boolean required;
final boolean catchAll;
final boolean printDefault;
final Type type;
final Type listType;
boolean present = false;
final Object object;
final Field field;
public Arg(final CmdArgument arg, final Object obj, final Field field)
{
this.s = arg.s() == 0 ? "" : Character.toString(arg.s());
this.l = arg.l();
this.desc = arg.desc();
this.isSwitch = arg.isSwitch();
this.required = arg.required();
this.catchAll = arg.catchAll();
this.itemSep = arg.listSep();
this.printDefault = arg.printDefault();
this.id = this.s + "/" + this.l;
this.object = obj;
this.field = field;
this.type = getTypeFor(this.field.getType());
this.listType = getTypeFor(arg.listType());
}
public Type getResolvedType()
{
return this.isList() ? this.listType : this.type;
}
public boolean isCatchAll()
{
return this.catchAll;
}
public boolean isList()
{
return this.type == Type.LIST;
}
public void setCatchAll(final List list) throws IOException
{
this.setListField(list);
}
public void setListField(final List list) throws IOException
{
try
{
if (this.listType == Type.STRING)
{
this.field.set(this.object, list);
}
else
{
final List