mirror of
https://github.com/lucaspalomodevelop/txtmark.git
synced 2026-03-12 23:37:22 +00:00
Merge pull request #31 from rjeschke/new_cmd_tool
New command line interface
This commit is contained in:
commit
85c5fa63be
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,4 +8,5 @@ target/
|
||||
*~
|
||||
*.releaseBackup
|
||||
release.properties
|
||||
*.pyc
|
||||
|
||||
|
||||
106
bootstrap.py/txtmark.py
Normal file
106
bootstrap.py/txtmark.py
Normal file
@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8 :et:ts=4:sts=4
|
||||
#
|
||||
# Copyright 2015 René Jeschke <rene_jeschke@yahoo.de>
|
||||
#
|
||||
# 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.
|
||||
|
||||
import os
|
||||
import re
|
||||
import errno
|
||||
import urllib2
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
group_id = "com.github.rjeschke"
|
||||
artifact = "txtmark"
|
||||
jar_dest = os.path.join(os.path.expanduser("~"), ".txtmark_jar", "txtmark.jar")
|
||||
|
||||
oss_snapshots = "https://oss.sonatype.org/content/repositories/snapshots"
|
||||
maven_repo1 = "https://repo1.maven.org/maven2"
|
||||
|
||||
snap_url = oss_snapshots + "/" + group_id.replace(".", "/") + "/" + artifact
|
||||
rel_url = maven_repo1 + "/" + group_id.replace(".", "/") + "/" + artifact
|
||||
|
||||
def mkdirs(path):
|
||||
"""mkdir -p"""
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError as e:
|
||||
if e.errno != errno.EEXIST or not os.path.isdir(path):
|
||||
raise
|
||||
|
||||
|
||||
def read_mvn_infos(url):
|
||||
response = urllib2.urlopen(url + "/maven-metadata.xml")
|
||||
latest = None
|
||||
version = None
|
||||
last_modified = 0
|
||||
for line in response.read().split("\n"):
|
||||
line = line.strip()
|
||||
if re.match("^<latest>.+</latest>$", line):
|
||||
latest = line[8:-9]
|
||||
elif re.match("^<lastUpdated>.+</lastUpdated>$", line):
|
||||
last_modified = long(line[13:-14])
|
||||
elif not latest and re.match("^<version>.+</version>$", line):
|
||||
version = line[9:-10]
|
||||
|
||||
if latest:
|
||||
return [latest, last_modified]
|
||||
return [version, last_modified]
|
||||
|
||||
|
||||
def get_snapshot_version(url, version):
|
||||
response = urllib2.urlopen(url + "/maven-metadata.xml")
|
||||
timestamp = None
|
||||
build_number = 0
|
||||
for line in response.read().split("\n"):
|
||||
line = line.strip()
|
||||
if not timestamp and re.match("^<timestamp>.*</timestamp>$", line):
|
||||
timestamp = line[11:-12]
|
||||
elif build_number == 0 and re.match("^<buildNumber>.*</buildNumber>$", line):
|
||||
build_number = int(line[13:-14])
|
||||
return url + "/" + artifact + "-" + version[:version.find("-")] + "-" + timestamp + "-" + str(build_number) + ".jar"
|
||||
|
||||
|
||||
def download(is_snap, version):
|
||||
u = None
|
||||
if is_snap:
|
||||
u = get_snapshot_version(snap_url + "/" + version, version)
|
||||
else:
|
||||
u = rel_url + "/" + version + "/" + artifact + "-" + version + ".jar"
|
||||
|
||||
response = urllib2.urlopen(u)
|
||||
with open(jar_dest, "wb") as fd:
|
||||
fd.write(response.read())
|
||||
|
||||
|
||||
def fetch_artifact():
|
||||
if not os.path.exists(jar_dest):
|
||||
mkdirs(os.path.dirname(jar_dest))
|
||||
rel = read_mvn_infos(rel_url)
|
||||
snp = read_mvn_infos(snap_url)
|
||||
|
||||
if snp[1] > rel[1]:
|
||||
download(True, snp[0])
|
||||
else:
|
||||
download(False, rel[0])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
fetch_artifact()
|
||||
|
||||
cmd = ["java", "-cp", jar_dest, "com.github.rjeschke.txtmark.cmd.Run"]
|
||||
cmd.extend(sys.argv[1:])
|
||||
|
||||
exit(subprocess.call(cmd))
|
||||
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2015 René Jeschke <rene_jeschke@yahoo.de>
|
||||
*
|
||||
* 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.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation for command line parsing.
|
||||
*
|
||||
* This is a copy from {@link https://github.com/rjeschke/neetutils-base}.
|
||||
*
|
||||
* @author René Jeschke (rene_jeschke@yahoo.de)
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@interface CmdArgument
|
||||
{
|
||||
/**
|
||||
* Long name for argument. Default is 'none'. Either one or both of
|
||||
* {@code l}, {@code s} need to be provided.
|
||||
*/
|
||||
String l() default "";
|
||||
|
||||
/**
|
||||
* Short name (character) for argument. Default is 'none'. Either one or
|
||||
* both of {@code l}, {@code s} need to be provided.
|
||||
*/
|
||||
char s() default '\0';
|
||||
|
||||
/**
|
||||
* A description for this argument. Default is 'none'.
|
||||
*/
|
||||
String desc() default "";
|
||||
|
||||
/**
|
||||
* List item separator. Default is ','.
|
||||
*/
|
||||
char listSep() default ',';
|
||||
|
||||
/**
|
||||
* Class for List-type arguments. Default is {@code String.class}.
|
||||
*/
|
||||
Class<?> listType() default String.class;
|
||||
|
||||
/**
|
||||
* Set to {@code true} if this is a switch. Requires a {@code boolean} field
|
||||
* which gets set to {@code true} when this argument is provided.
|
||||
*/
|
||||
boolean isSwitch() default false;
|
||||
|
||||
/**
|
||||
* Set to {@code true} if this is a required argument.
|
||||
*/
|
||||
boolean required() default false;
|
||||
|
||||
/**
|
||||
* Set to {@code true} to set this as a catch-all argument. Requires a
|
||||
* {@code List} field and will parse all arguments following this switch
|
||||
* into the list.
|
||||
*/
|
||||
boolean catchAll() default false;
|
||||
|
||||
/**
|
||||
* Set to {@code false} to disable automatic default value printing for this
|
||||
* argument.
|
||||
*/
|
||||
boolean printDefault() default true;
|
||||
}
|
||||
691
src/main/java/com/github/rjeschke/txtmark/cmd/CmdLineParser.java
Normal file
691
src/main/java/com/github/rjeschke/txtmark/cmd/CmdLineParser.java
Normal file
@ -0,0 +1,691 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2015 René Jeschke <rene_jeschke@yahoo.de>
|
||||
*
|
||||
* 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 (rene_jeschke@yahoo.de)
|
||||
*/
|
||||
final class CmdLineParser
|
||||
{
|
||||
private CmdLineParser()
|
||||
{
|
||||
// meh!
|
||||
}
|
||||
|
||||
enum Type
|
||||
{
|
||||
UNSUPPORTED, STRING, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, LIST, BOOL;
|
||||
}
|
||||
|
||||
final static HashMap<Class<?>, Type> TYPE_MAP = new HashMap<Class<?>, Type>();
|
||||
final static Class<?>[] TYPE_CLASS_LIST = Colls.<Class<?>> 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<String> BOOL_TRUE = new HashSet<String>(Colls.list("on", "true", "yes"));
|
||||
final static HashSet<String> BOOL_FALSE = new HashSet<String>(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<String> 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<Arg> allArgs, final HashMap<String, Arg> shortArgs,
|
||||
final HashMap<String, Arg> 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<Arg> allArgs = Colls.list();
|
||||
final HashMap<String, Arg> shortArgs = new HashMap<String, Arg>();
|
||||
final HashMap<String, Arg> longArgs = new HashMap<String, Arg>();
|
||||
|
||||
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<String> 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<String> parse(final String[] args, final Object... objs) throws IOException
|
||||
{
|
||||
final List<String> ret = Colls.list();
|
||||
|
||||
final List<Arg> allArgs = Colls.list();
|
||||
final HashMap<String, Arg> shortArgs = new HashMap<String, Arg>();
|
||||
final HashMap<String, Arg> longArgs = new HashMap<String, Arg>();
|
||||
|
||||
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<String> 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<Arg>
|
||||
{
|
||||
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<String> list) throws IOException
|
||||
{
|
||||
this.setListField(list);
|
||||
}
|
||||
|
||||
public void setListField(final List<String> list) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.listType == Type.STRING)
|
||||
{
|
||||
this.field.set(this.object, list);
|
||||
}
|
||||
else
|
||||
{
|
||||
final List<Object> temp = Colls.list();
|
||||
for (final String i : list)
|
||||
{
|
||||
temp.add(this.toObject(i, this.listType));
|
||||
}
|
||||
this.field.set(this.object, temp);
|
||||
}
|
||||
}
|
||||
catch (final IllegalArgumentException e)
|
||||
{
|
||||
throw new IOException("Failed to write value", e);
|
||||
}
|
||||
catch (final IllegalAccessException e)
|
||||
{
|
||||
throw new IOException("Failed to write value", e);
|
||||
}
|
||||
}
|
||||
|
||||
Object safeFieldGet()
|
||||
{
|
||||
try
|
||||
{
|
||||
return this.field.get(this.object);
|
||||
}
|
||||
catch (final Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Object toObject(final String value, final Type type) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case STRING:
|
||||
return value;
|
||||
case BYTE:
|
||||
return Byte.parseByte(value);
|
||||
case SHORT:
|
||||
return Short.parseShort(value);
|
||||
case INT:
|
||||
return Integer.parseInt(value);
|
||||
case LONG:
|
||||
return Long.parseLong(value);
|
||||
case FLOAT:
|
||||
return Float.parseFloat(value);
|
||||
case DOUBLE:
|
||||
return Double.parseDouble(value);
|
||||
case BOOL:
|
||||
if (BOOL_TRUE.contains(value.toLowerCase()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (BOOL_FALSE.contains(value.toLowerCase()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
throw new IOException("Illegal bool value for:" + this.toString());
|
||||
default:
|
||||
throw new IOException("Illegal type: " + type.toString().toLowerCase());
|
||||
}
|
||||
}
|
||||
catch (final Throwable t)
|
||||
{
|
||||
throw new IOException("Parsing error for: " + this.toString() + "; '" + value + "'", t);
|
||||
}
|
||||
}
|
||||
|
||||
public void setField(final String value) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.isList())
|
||||
{
|
||||
this.setListField(Strings.split(value, this.itemSep));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.field.set(this.object, this.toObject(value, this.type));
|
||||
}
|
||||
}
|
||||
catch (final IllegalArgumentException e)
|
||||
{
|
||||
throw new IOException("Failed to write field: " + this.field.getName(), e);
|
||||
}
|
||||
catch (final IllegalAccessException e)
|
||||
{
|
||||
throw new IOException("Failed to write field: " + this.field.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPresent()
|
||||
{
|
||||
this.present = true;
|
||||
}
|
||||
|
||||
public boolean isOk()
|
||||
{
|
||||
return !this.required || this.present;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return this.id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj)
|
||||
{
|
||||
if (obj instanceof Arg)
|
||||
{
|
||||
return this.id.equals(((Arg)obj).id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
if (Strings.isEmpty(this.s))
|
||||
{
|
||||
return " --" + this.l;
|
||||
}
|
||||
if (Strings.isEmpty(this.l))
|
||||
{
|
||||
return "-" + this.s;
|
||||
}
|
||||
return "-" + this.s + ", --" + this.l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final Arg o)
|
||||
{
|
||||
final String a = Strings.isEmpty(this.s) ? this.l : this.s;
|
||||
final String b = Strings.isEmpty(o.s) ? o.l : o.s;
|
||||
return a.compareTo(b);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Once<T>
|
||||
{
|
||||
private final T first;
|
||||
private final T next;
|
||||
private boolean isFirst = true;
|
||||
|
||||
public Once(final T first, final T next)
|
||||
{
|
||||
this.first = first;
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
public static <T> Once<T> of(final T first, final T next)
|
||||
{
|
||||
return new Once<T>(first, next);
|
||||
}
|
||||
|
||||
public T get()
|
||||
{
|
||||
if (this.isFirst)
|
||||
{
|
||||
this.isFirst = false;
|
||||
return this.first;
|
||||
}
|
||||
|
||||
return this.next;
|
||||
}
|
||||
}
|
||||
|
||||
private final static class Colls
|
||||
{
|
||||
final static <T> T[] objArray(final T... ts)
|
||||
{
|
||||
return ts;
|
||||
}
|
||||
|
||||
final static <A> List<A> list(final A... coll)
|
||||
{
|
||||
final List<A> ret = new ArrayList<A>(coll.length);
|
||||
for (int i = 0; i < coll.length; i++)
|
||||
{
|
||||
ret.add(coll[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
private final static class Classes
|
||||
{
|
||||
final static boolean implementsInterface(final Class<?> clazz, final Class<?> interfce)
|
||||
{
|
||||
for (final Class<?> c : clazz.getInterfaces())
|
||||
{
|
||||
if (c.equals(interfce))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private final static class Strings
|
||||
{
|
||||
public final static boolean isEmpty(final String str)
|
||||
{
|
||||
return str == null || str.isEmpty();
|
||||
}
|
||||
|
||||
public final static List<String> split(final String str, final char ch)
|
||||
{
|
||||
final List<String> ret = Colls.list();
|
||||
|
||||
if (str != null)
|
||||
{
|
||||
int s = 0, e = 0;
|
||||
while (e < str.length())
|
||||
{
|
||||
if (str.charAt(e) == ch)
|
||||
{
|
||||
ret.add(str.substring(s, e));
|
||||
s = e + 1;
|
||||
}
|
||||
e++;
|
||||
}
|
||||
ret.add(str.substring(s, e));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2015 René Jeschke <rene_jeschke@yahoo.de>
|
||||
*
|
||||
* 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.util.List;
|
||||
|
||||
import com.github.rjeschke.txtmark.BlockEmitter;
|
||||
|
||||
final class CodeBlockEmitter implements BlockEmitter
|
||||
{
|
||||
private final String encoding;
|
||||
private final String program;
|
||||
|
||||
public CodeBlockEmitter(final String encoding, final String program)
|
||||
{
|
||||
this.encoding = encoding;
|
||||
this.program = program;
|
||||
}
|
||||
|
||||
private static void append(final StringBuilder out, final List<String> lines)
|
||||
{
|
||||
out.append("<pre class=\"pre_no_hl\">");
|
||||
for (final String l : lines)
|
||||
{
|
||||
HlUtils.escapedAdd(out, l);
|
||||
out.append('\n');
|
||||
}
|
||||
out.append("</pre>");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitBlock(final StringBuilder out, final List<String> lines, final String meta)
|
||||
{
|
||||
if (meta == null || meta.isEmpty())
|
||||
{
|
||||
append(out, lines);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
out.append(HlUtils.highlight(lines, meta, this.program, this.encoding));
|
||||
out.append('\n');
|
||||
}
|
||||
catch (final IOException e)
|
||||
{
|
||||
// Ignore or do something, still, pump out the lines
|
||||
append(out, lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
163
src/main/java/com/github/rjeschke/txtmark/cmd/HlUtils.java
Normal file
163
src/main/java/com/github/rjeschke/txtmark/cmd/HlUtils.java
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (C) 2015 René Jeschke <rene_jeschke@yahoo.de>
|
||||
*
|
||||
* 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.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
final class HlUtils
|
||||
{
|
||||
final static AtomicInteger IN_COUNT = new AtomicInteger(0);
|
||||
final static AtomicInteger OUT_COUNT = new AtomicInteger(0);
|
||||
final static long ID = System.nanoTime();
|
||||
|
||||
public static String highlight(final List<String> lines, final String meta, final String prog, final String encoding)
|
||||
throws IOException
|
||||
{
|
||||
final File tmpIn = new File(System.getProperty("java.io.tmpdir"),
|
||||
String.format("txtmark_code_%d_%d.in", ID, IN_COUNT.incrementAndGet()));
|
||||
final File tmpOut = new File(System.getProperty("java.io.tmpdir"),
|
||||
String.format("txtmark_code_%d_%d.out", ID, OUT_COUNT.incrementAndGet()));
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
final Writer w = new OutputStreamWriter(new FileOutputStream(tmpIn), encoding);
|
||||
|
||||
try
|
||||
{
|
||||
for (final String s : lines)
|
||||
{
|
||||
w.write(s);
|
||||
w.write('\n');
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
w.close();
|
||||
}
|
||||
|
||||
final List<String> command = new ArrayList<String>();
|
||||
|
||||
command.add(prog);
|
||||
command.add(meta);
|
||||
command.add(tmpIn.getAbsolutePath());
|
||||
command.add(tmpOut.getAbsolutePath());
|
||||
|
||||
final ProcessBuilder pb = new ProcessBuilder(command);
|
||||
final Process p = pb.start();
|
||||
final InputStream pIn = p.getInputStream();
|
||||
final byte[] buffer = new byte[2048];
|
||||
|
||||
int exitCode = 0;
|
||||
for (;;)
|
||||
{
|
||||
if (pIn.available() > 0)
|
||||
{
|
||||
pIn.read(buffer);
|
||||
}
|
||||
try
|
||||
{
|
||||
exitCode = p.exitValue();
|
||||
}
|
||||
catch (final IllegalThreadStateException itse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (exitCode == 0)
|
||||
{
|
||||
final Reader r = new InputStreamReader(new FileInputStream(tmpOut), encoding);
|
||||
try
|
||||
{
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (;;)
|
||||
{
|
||||
final int c = r.read();
|
||||
if (c >= 0)
|
||||
{
|
||||
sb.append((char)c);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
finally
|
||||
{
|
||||
r.close();
|
||||
}
|
||||
}
|
||||
|
||||
throw new IOException("Exited with exit code: " + exitCode);
|
||||
}
|
||||
finally
|
||||
{
|
||||
tmpIn.delete();
|
||||
tmpOut.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public static void escapedAdd(final StringBuilder sb, final String str)
|
||||
{
|
||||
for (int i = 0; i < str.length(); i++)
|
||||
{
|
||||
final char ch = str.charAt(i);
|
||||
if (ch < 33 || Character.isWhitespace(ch) || Character.isSpaceChar(ch))
|
||||
{
|
||||
sb.append(' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case '"':
|
||||
sb.append(""");
|
||||
break;
|
||||
case '\'':
|
||||
sb.append("'");
|
||||
break;
|
||||
case '<':
|
||||
sb.append("<");
|
||||
break;
|
||||
case '>':
|
||||
sb.append(">");
|
||||
break;
|
||||
case '&':
|
||||
sb.append("&");
|
||||
break;
|
||||
default:
|
||||
sb.append(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
156
src/main/java/com/github/rjeschke/txtmark/cmd/Run.java
Normal file
156
src/main/java/com/github/rjeschke/txtmark/cmd/Run.java
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2015 René Jeschke <rene_jeschke@yahoo.de>
|
||||
*
|
||||
* 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.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.github.rjeschke.txtmark.Configuration;
|
||||
import com.github.rjeschke.txtmark.Processor;
|
||||
|
||||
public final class Run
|
||||
{
|
||||
private final static void printUsage()
|
||||
{
|
||||
try
|
||||
{
|
||||
System.out.println("Usage: txtmark [options] [input-file]");
|
||||
System.out.println("Options:");
|
||||
System.out.println(CmdLineParser.generateHelp(80, false, new TxtmarkArguments()));
|
||||
}
|
||||
catch (final IOException e)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(final String[] args)
|
||||
{
|
||||
final TxtmarkArguments ta = new TxtmarkArguments();
|
||||
List<String> rest = new ArrayList<String>();
|
||||
|
||||
boolean parseError = false;
|
||||
try
|
||||
{
|
||||
rest = CmdLineParser.parse(args, ta);
|
||||
}
|
||||
catch (final IOException e)
|
||||
{
|
||||
parseError = true;
|
||||
}
|
||||
|
||||
if (ta.printHelp || parseError)
|
||||
{
|
||||
printUsage();
|
||||
System.exit(parseError ? 1 : 0);
|
||||
}
|
||||
|
||||
// Build configuration from command line arguments
|
||||
final Configuration.Builder cfgBuilder = Configuration.builder();
|
||||
cfgBuilder.setEncoding(ta.encoding)
|
||||
.setEnablePanicMode(ta.panicMode)
|
||||
.setSafeMode(ta.safeMode)
|
||||
.setAllowSpacesInFencedCodeBlockDelimiters(!ta.noFencedSpaced);
|
||||
// Check for extended profile
|
||||
if (ta.forceExtendedProfile)
|
||||
{
|
||||
cfgBuilder.forceExtentedProfile();
|
||||
}
|
||||
// Connect highlighter if any
|
||||
if (ta.highlighter != null && !ta.highlighter.isEmpty())
|
||||
{
|
||||
if (!new File(ta.highlighter).exists())
|
||||
{
|
||||
System.err.println("Program '" + ta.highlighter + "' not found");
|
||||
System.exit(1);
|
||||
}
|
||||
cfgBuilder.setCodeBlockEmitter(new CodeBlockEmitter(ta.encoding, ta.highlighter));
|
||||
}
|
||||
|
||||
// Ready for action
|
||||
final Configuration config = cfgBuilder.build();
|
||||
boolean processOk = true;
|
||||
InputStream input = null;
|
||||
Writer output = null;
|
||||
try
|
||||
{
|
||||
final String inFile = rest.isEmpty() ? "--" : rest.get(0);
|
||||
final String outFile = ta.outFile;
|
||||
|
||||
if (inFile.equals("--"))
|
||||
{
|
||||
input = System.in;
|
||||
}
|
||||
else
|
||||
{
|
||||
input = new FileInputStream(inFile);
|
||||
}
|
||||
|
||||
final String result = Processor.process(input, config);
|
||||
|
||||
if (outFile == null)
|
||||
{
|
||||
output = new OutputStreamWriter(System.out, ta.encoding);
|
||||
}
|
||||
else
|
||||
{
|
||||
output = new OutputStreamWriter(new FileOutputStream(outFile), ta.encoding);
|
||||
}
|
||||
|
||||
output.write(result);
|
||||
}
|
||||
catch (final IOException e)
|
||||
{
|
||||
processOk = false;
|
||||
System.err.println("Exception: " + e.toString());
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (input != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
input.close();
|
||||
}
|
||||
catch (final IOException e)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (output != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
output.close();
|
||||
}
|
||||
catch (final IOException e)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.exit(processOk ? 0 : 1);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2015 René Jeschke <rene_jeschke@yahoo.de>
|
||||
*
|
||||
* 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;
|
||||
|
||||
final class TxtmarkArguments
|
||||
{
|
||||
@CmdArgument(l = "help", s = 'h', isSwitch = true, desc = "prints a summary of command line arguments.")
|
||||
public boolean printHelp = false;
|
||||
|
||||
@CmdArgument(l = "extended", isSwitch = true, desc = "forces extended profile")
|
||||
public boolean forceExtendedProfile = false;
|
||||
|
||||
@CmdArgument(l = "panic-mode", isSwitch = true, desc = "enables panic mode")
|
||||
public boolean panicMode = false;
|
||||
|
||||
@CmdArgument(l = "safe-mode", isSwitch = true, desc = "enables safe mode")
|
||||
public boolean safeMode = false;
|
||||
|
||||
@CmdArgument(l = "no-fenced-spaces", isSwitch = true, desc = "disables spaces in fenced code block delimiters")
|
||||
public boolean noFencedSpaced = false;
|
||||
|
||||
@CmdArgument(l = "encoding", desc = "sets the IO encoding.")
|
||||
public String encoding = "UTF-8";
|
||||
|
||||
@CmdArgument(l = "out-file", s = 'o', desc = "specifies the output filename, writes to stdout otherwise")
|
||||
public String outFile = null;
|
||||
|
||||
@CmdArgument(l = "highlighter", desc = "specifies a program [meta in-file outfile] that should be used for highlighting fenced code blocks")
|
||||
public String highlighter = null;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user