Merge pull request #31 from rjeschke/new_cmd_tool

New command line interface
This commit is contained in:
René Jeschke 2015-03-18 22:44:06 +01:00
commit 85c5fa63be
8 changed files with 1310 additions and 0 deletions

1
.gitignore vendored
View File

@ -8,4 +8,5 @@ target/
*~
*.releaseBackup
release.properties
*.pyc

106
bootstrap.py/txtmark.py Normal file
View 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))

View File

@ -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;
}

View 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;
}
}
}

View File

@ -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);
}
}
}
}

View 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("&quot;");
break;
case '\'':
sb.append("&apos;");
break;
case '<':
sb.append("&lt;");
break;
case '>':
sb.append("&gt;");
break;
case '&':
sb.append("&amp;");
break;
default:
sb.append(ch);
break;
}
}
}
}
}

View 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);
}
}

View File

@ -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;
}