Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve JTSOp command - error handling, testing #459

Merged
merged 6 commits into from
Aug 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ cache:
directories:
- $HOME/.m2
install: mvn install -B -V
dist: trusty

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.locationtech.jtstest.cmd;

public class CommandError extends RuntimeException {
public CommandError(String msg) {
super(msg);
}
public CommandError(String msg, String val) {
super(msg + ": " + val);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.locationtech.jtstest.cmd;

public class CommandOutput {

private StringBuilder output = new StringBuilder();
private boolean isCapture = false;

public CommandOutput() {

}

public CommandOutput(boolean isCapture) {
this.isCapture = true;
}

public void println() {
if (isCapture ) {
output.append("\n");
}
else {
System.out.println();
}
}

public void println(Object o) {
if (isCapture ) {
output.append(o);
output.append("\n");
}
else {
System.out.println(o);
}
}

public void print(String s) {
if (isCapture ) {
output.append(s);
}
else {
System.out.print(s);
}
}

public String getOutput() {
return output.toString();
}

}
154 changes: 106 additions & 48 deletions modules/app/src/main/java/org/locationtech/jtstest/cmd/JTSOpCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
*/
package org.locationtech.jtstest.cmd;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;

import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
Expand All @@ -36,7 +36,7 @@
import org.locationtech.jtstest.util.io.MultiFormatReader;

/**
* A simple CLI to run TestBuilder operations.
* A CLI to run JTS TestBuilder operations.
* Allows easier execution of JTS functions on test data for debugging purposes.
* <p>
* Examples:
Expand Down Expand Up @@ -69,6 +69,13 @@ public class JTSOpCmd {
// TODO: add option -ab to read both geoms from a file
// TODO: allow -a stdin to indicate reading from stdin.

public static final String ERR_FILE_NOT_FOUND = "File not found";
public static final String ERR_FUNCTION_NOT_FOUND = "Function not found";
public static final String ERR_REQUIRED_A = "Geometry A may be required";
public static final String ERR_REQUIRED_B = "Geometry B is required";
public static final String ERR_WRONG_ARG_COUNT = "Arguments and parameters do not match";
public static final String ERR_FUNCTION_ERR = "Error executing function";

static final String[] helpDoc = new String[] {
"",
"Usage: jtsop - CLI for JTS operations",
Expand Down Expand Up @@ -101,41 +108,38 @@ public static void main(String[] args)
{
JTSOpCmd cmd = new JTSOpCmd();
try {
CmdArgs cmdArgs = parseArgs(args);

if (args.length == 0 || isHelp) {
printHelp(isHelp);
System.exit(0);
}

CmdArgs cmdArgs = cmd.parseArgs(args);
cmd.execute(cmdArgs);
} catch (Exception e) {
}
catch (CommandError e) {
// for command errors, just print the message
System.err.println(e.getMessage() );
}
catch (Exception e) {
// unexpected errors get a stack track to help debugging
e.printStackTrace();
}
}

private static void printHelp(boolean showFunctions) {
private void printHelp(boolean showFunctions) {
for (String s : helpDoc) {
System.out.println(s);
out.println(s);
}
if (showFunctions) {
System.out.println();
System.out.println("Operations:");
out.println();
out.println("Operations:");
printFunctions();
}
}

private static void printFunctions() {
private void printFunctions() {
//TODO: include any loaded functions
DoubleKeyMap funcMap = funcRegistry.getCategorizedGeometryFunctions();
Collection categories = funcMap.keySet();
for (Iterator i = categories.iterator(); i.hasNext();) {
String category = (String) i.next();

Collection funcs = funcMap.values(category);
for (Iterator j = funcs.iterator(); j.hasNext();) {
GeometryFunction fun = (GeometryFunction) j.next();
System.out.println(category + "." + functionDesc(fun));
Collection<String> categories = funcMap.keySet();
for (String category : categories) {
Collection<GeometryFunction> funcs = funcMap.values(category);
for (GeometryFunction fun : funcs) {
out.println(category + "." + functionDesc(fun));
}
}
}
Expand All @@ -147,29 +151,41 @@ private static String functionDesc(GeometryFunction fun) {
//return geomFun.getName();
}

private static GeometryFactory geomFactory = new GeometryFactory();
private GeometryFactory geomFactory = new GeometryFactory();

private static GeometryFunctionRegistry funcRegistry = GeometryFunctionRegistry.createTestBuilderRegistry();
private static CommandLine commandLine = createCmdLine();
private GeometryFunctionRegistry funcRegistry = GeometryFunctionRegistry.createTestBuilderRegistry();
private CommandLine commandLine = createCmdLine();

private boolean isHelp = false;
private boolean isHelpWithFunctions = false;
private boolean isVerbose = false;

private static boolean isHelp = false;
private static boolean isVerbose = false;
private CommandOutput out = new CommandOutput();

private static class CmdArgs {
static class CmdArgs {
String operation;
String geomA;
public String geomB;
public String arg1;

String format = null;
}


public JTSOpCmd() {

}

private void execute(CmdArgs cmdArgs) throws IOException, Exception {
public void captureOutput() {
out = new CommandOutput(true);
}

public String getOutput() {
return out.getOutput();
}
void execute(CmdArgs cmdArgs) throws IOException, Exception {
if (isHelp || isHelpWithFunctions) {
printHelp(isHelpWithFunctions);
return;
}

Geometry geomA = null;
Geometry geomB = null;
Expand Down Expand Up @@ -203,31 +219,43 @@ private void execute(CmdArgs cmdArgs) throws IOException, Exception {
private Object executeFunction(CmdArgs cmdArgs, Geometry geomA, Geometry geomB) {
GeometryFunction func = getFunction(cmdArgs.operation);
if (func == null) {
System.err.println("Function not found: " + cmdArgs.operation);
return null;
throw new CommandError(ERR_FUNCTION_NOT_FOUND, cmdArgs.operation);
}
checkFunctionArgs(func, geomB, cmdArgs.arg1);
Object funArgs[] = createFunctionArgs(func, geomB, cmdArgs.arg1);

if (isVerbose) {
System.out.println(writeOpSummary(cmdArgs));
out.println(writeOpSummary(cmdArgs));
}

Stopwatch timer = new Stopwatch();
Object result = null;
try {
result = func.invoke(geomA, funArgs);
} finally {
}
catch (NullPointerException ex) {
if (geomA == null)
throw new CommandError(ERR_REQUIRED_A, cmdArgs.operation);
// if A is present then must be something else
throw new CommandError(ERR_FUNCTION_ERR, ex.getMessage());
}
finally {
timer.stop();
}
if (isVerbose) {
System.out.println("Time: " + timer.getTimeString());
out.println("Time: " + timer.getTimeString());
}
return result;
}

private Geometry readGeometry(String arg) throws Exception, IOException {
if (isFilename(arg)) {
return IOUtil.readFile(arg ,geomFactory );
try {
return IOUtil.readFile(arg ,geomFactory );
}
catch (FileNotFoundException ex) {
throw new CommandError(ERR_FILE_NOT_FOUND, arg);
}
}
MultiFormatReader rdr = new MultiFormatReader(geomFactory);
return rdr.read(arg);
Expand Down Expand Up @@ -269,7 +297,7 @@ private void printResult(Object result, String outputFormat) {
printGeometry((Geometry) result, outputFormat);
return;
}
System.out.println(result);
out.println(result);
}

private void printGeometry(Geometry geom, String outputFormat) {
Expand All @@ -292,7 +320,7 @@ else if (outputFormat.equalsIgnoreCase(FORMAT_SVG)) {
}

if (txt == null) return;
System.out.println(txt);
out.println(txt);
}

private String writeGeoJSON(Geometry geom) {
Expand All @@ -305,7 +333,7 @@ private void printGeometrySummary(String label, Geometry geom, String arg) {
if (! isVerbose) return;
String filename = "";
if (arg != null & isFilename(arg)) filename = " -- " + arg;
System.out.println( writeGeometrySummary(label, geom) + filename);
out.println( writeGeometrySummary(label, geom) + filename);
}

private String writeGeometrySummary(String label,
Expand All @@ -318,6 +346,30 @@ private String writeGeometrySummary(String label,
buf.append(" " + GeometryUtil.metricsSummary(g));
return buf.toString();
}

private void checkFunctionArgs(GeometryFunction func, Geometry geomB, String arg1) {
Class[] paramTypes = func.getParameterTypes();
int nParam = paramTypes.length;

if (func.isBinary() && geomB == null)
throw new CommandError(ERR_REQUIRED_B);
/**
// MD not sure whether to check this?
if (! func.isBinary() && geomB != null)
throw new CommandError(ERR_REQUIRED_B);
*/

/*
* check count of supplied args.
* Assumes B has been checked.
*/
int argCount = 0;
if (func.isBinary() && geomB != null) argCount++;
if (arg1 != null) argCount++;
if (nParam != argCount) {
throw new CommandError(ERR_WRONG_ARG_COUNT, func.getName());
}
}

private Object[] createFunctionArgs(GeometryFunction func, Geometry geomB, String arg1) {
Class[] paramTypes = func.getParameterTypes();
Expand All @@ -328,6 +380,7 @@ private Object[] createFunctionArgs(GeometryFunction func, Geometry geomB, Strin
paramVal[0] = geomB;
iparam++;
}
// just handling one scalar arg for now
if (iparam < paramVal.length) {
paramVal[iparam] = SwingUtil.coerce(arg1, paramTypes[iparam]);
}
Expand All @@ -345,20 +398,25 @@ private GeometryFunction getFunction(String operation) {
return funcRegistry.find(category, name);
}

private static CmdArgs parseArgs(String[] args) throws ParseException, ClassNotFoundException {
commandLine.parse(args);

CmdArgs parseArgs(String[] args) throws ParseException, ClassNotFoundException {
CmdArgs cmdArgs = new CmdArgs();

if (args.length == 0) {
isHelp = true;
return cmdArgs;
}

commandLine.parse(args);

if (commandLine.hasOption(CommandOptions.GEOMFUNC)) {
Option opt = commandLine.getOption(CommandOptions.GEOMFUNC);
for (int i = 0; i < opt.getNumArgs(); i++) {
String geomFuncClassname = opt.getArg(i);
try {
funcRegistry.add(geomFuncClassname);
System.out.println("Added Geometry Functions from: " + geomFuncClassname);
out.println("Added Geometry Functions from: " + geomFuncClassname);
} catch (ClassNotFoundException ex) {
System.out.println("Unable to load function class: " + geomFuncClassname);
out.println("Unable to load function class: " + geomFuncClassname);
}
}
}
Expand All @@ -370,7 +428,7 @@ private static CmdArgs parseArgs(String[] args) throws ParseException, ClassNotF
cmdArgs.format = commandLine.getOptionArg(CommandOptions.FORMAT, 0);
isVerbose = commandLine.hasOption(CommandOptions.VERBOSE)
|| commandLine.hasOption(CommandOptions.V);
isHelp = commandLine.hasOption(CommandOptions.HELP);
isHelpWithFunctions = commandLine.hasOption(CommandOptions.HELP);

String[] freeArgs = commandLine.getOptionArgs(OptionSpec.OPTION_FREE_ARGS);
if (freeArgs != null) {
Expand All @@ -385,7 +443,7 @@ private static CmdArgs parseArgs(String[] args) throws ParseException, ClassNotF
return cmdArgs;
}

private static CommandLine createCmdLine() {
private CommandLine createCmdLine() {
commandLine = new CommandLine('-');
commandLine.addOptionSpec(new OptionSpec(CommandOptions.GEOMFUNC, OptionSpec.NARGS_ONE_OR_MORE))
.addOptionSpec(new OptionSpec(CommandOptions.VERBOSE, 0))
Expand Down
Loading