From e33812e4e3d8c6eae2bc4228937c1b24a8b1c43e Mon Sep 17 00:00:00 2001 From: hneemann Date: Tue, 9 Jun 2020 13:18:39 +0200 Subject: [PATCH] some refactorings --- src/main/java/CLI.java | 4 +- .../de/neemann/digital/cli/CLITester.java | 7 +- .../de/neemann/digital/cli/SVGExport.java | 6 +- .../de/neemann/digital/cli/StatsExport.java | 12 ++- .../digital/cli/{ => cli}/Argument.java | 2 +- .../digital/cli/{ => cli}/ArgumentBase.java | 2 +- .../digital/cli/{ => cli}/ArgumentKey.java | 2 +- .../BasicCommand.java} | 24 ++--- .../digital/cli/{ => cli}/CLICommand.java | 2 +- .../digital/cli/{ => cli}/CLIException.java | 4 +- .../neemann/digital/cli/{ => cli}/Muxer.java | 16 ++-- .../neemann/digital/cli/cli/NamedCommand.java | 29 ++++++ .../neemann/digital/cli/cli/package-info.java | 10 ++ .../digital/testing/CommandLineTester.java | 2 +- .../de/neemann/digital/cli/ArgumentTest.java | 10 ++ ...CommandTest.java => BasicCommandTest.java} | 7 +- .../de/neemann/digital/cli/MuxerTest.java | 96 +++++++++++++++++++ 17 files changed, 193 insertions(+), 42 deletions(-) rename src/main/java/de/neemann/digital/cli/{ => cli}/Argument.java (98%) rename src/main/java/de/neemann/digital/cli/{ => cli}/ArgumentBase.java (98%) rename src/main/java/de/neemann/digital/cli/{ => cli}/ArgumentKey.java (98%) rename src/main/java/de/neemann/digital/cli/{SimpleCommand.java => cli/BasicCommand.java} (88%) rename src/main/java/de/neemann/digital/cli/{ => cli}/CLICommand.java (94%) rename src/main/java/de/neemann/digital/cli/{ => cli}/CLIException.java (94%) rename src/main/java/de/neemann/digital/cli/{ => cli}/Muxer.java (84%) create mode 100644 src/main/java/de/neemann/digital/cli/cli/NamedCommand.java create mode 100644 src/main/java/de/neemann/digital/cli/cli/package-info.java rename src/test/java/de/neemann/digital/cli/{SimpleCommandTest.java => BasicCommandTest.java} (92%) create mode 100644 src/test/java/de/neemann/digital/cli/MuxerTest.java diff --git a/src/main/java/CLI.java b/src/main/java/CLI.java index d389f656e..48a781953 100644 --- a/src/main/java/CLI.java +++ b/src/main/java/CLI.java @@ -4,8 +4,8 @@ * that can be found in the LICENSE file. */ -import de.neemann.digital.cli.CLIException; -import de.neemann.digital.cli.Muxer; +import de.neemann.digital.cli.cli.CLIException; +import de.neemann.digital.cli.cli.Muxer; /** * Entry point for the CLI interface diff --git a/src/main/java/de/neemann/digital/cli/CLITester.java b/src/main/java/de/neemann/digital/cli/CLITester.java index 66cdd31b1..8bc74b70a 100644 --- a/src/main/java/de/neemann/digital/cli/CLITester.java +++ b/src/main/java/de/neemann/digital/cli/CLITester.java @@ -5,6 +5,9 @@ */ package de.neemann.digital.cli; +import de.neemann.digital.cli.cli.Argument; +import de.neemann.digital.cli.cli.BasicCommand; +import de.neemann.digital.cli.cli.CLIException; import de.neemann.digital.lang.Lang; import de.neemann.digital.testing.CommandLineTester; @@ -12,9 +15,9 @@ import java.io.File; import java.io.IOException; /** - * Used to test circuits + * Used to test circuits. */ -public class CLITester extends SimpleCommand { +public class CLITester extends BasicCommand { private final Argument circ; private final Argument test; diff --git a/src/main/java/de/neemann/digital/cli/SVGExport.java b/src/main/java/de/neemann/digital/cli/SVGExport.java index 893484a3d..e01163f10 100644 --- a/src/main/java/de/neemann/digital/cli/SVGExport.java +++ b/src/main/java/de/neemann/digital/cli/SVGExport.java @@ -5,6 +5,10 @@ */ package de.neemann.digital.cli; +import de.neemann.digital.cli.cli.Argument; +import de.neemann.digital.cli.cli.ArgumentKey; +import de.neemann.digital.cli.cli.BasicCommand; +import de.neemann.digital.cli.cli.CLIException; import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.element.Key; import de.neemann.digital.draw.elements.Circuit; @@ -23,7 +27,7 @@ import java.io.OutputStream; /** * CLI svg exporter */ -public class SVGExport extends SimpleCommand { +public class SVGExport extends BasicCommand { private final ElementAttributes attr; private final Argument digFile; private final Argument svgFile; diff --git a/src/main/java/de/neemann/digital/cli/StatsExport.java b/src/main/java/de/neemann/digital/cli/StatsExport.java index 7455a76e8..80fc7eb16 100644 --- a/src/main/java/de/neemann/digital/cli/StatsExport.java +++ b/src/main/java/de/neemann/digital/cli/StatsExport.java @@ -5,6 +5,9 @@ */ package de.neemann.digital.cli; +import de.neemann.digital.cli.cli.Argument; +import de.neemann.digital.cli.cli.BasicCommand; +import de.neemann.digital.cli.cli.CLIException; import de.neemann.digital.core.Model; import de.neemann.digital.core.NodeException; import de.neemann.digital.core.stats.Statistics; @@ -21,7 +24,7 @@ import java.io.*; /** * CLI stats exporter */ -public class StatsExport extends SimpleCommand { +public class StatsExport extends BasicCommand { private final Argument digFile; private final Argument csvFile; @@ -46,13 +49,12 @@ public class StatsExport extends SimpleCommand { Model model = new ModelCreator(circuit, library).createModel(false); Statistics stats = new Statistics(model); - String outName; + BufferedWriter writer; if (csvFile.isSet()) - outName = csvFile.get(); + writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(csvFile.get()))); else - outName = digFile.get() + ".csv"; + writer = new BufferedWriter(new OutputStreamWriter(System.out)); - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outName))); new CSVWriter(stats.getTableModel()).writeTo(writer); } catch (IOException | ElementNotFoundException | PinException | NodeException e) { diff --git a/src/main/java/de/neemann/digital/cli/Argument.java b/src/main/java/de/neemann/digital/cli/cli/Argument.java similarity index 98% rename from src/main/java/de/neemann/digital/cli/Argument.java rename to src/main/java/de/neemann/digital/cli/cli/Argument.java index c0a61fb5b..512261bbd 100644 --- a/src/main/java/de/neemann/digital/cli/Argument.java +++ b/src/main/java/de/neemann/digital/cli/cli/Argument.java @@ -3,7 +3,7 @@ * Use of this source code is governed by the GPL v3 license * that can be found in the LICENSE file. */ -package de.neemann.digital.cli; +package de.neemann.digital.cli.cli; import de.neemann.digital.lang.Lang; diff --git a/src/main/java/de/neemann/digital/cli/ArgumentBase.java b/src/main/java/de/neemann/digital/cli/cli/ArgumentBase.java similarity index 98% rename from src/main/java/de/neemann/digital/cli/ArgumentBase.java rename to src/main/java/de/neemann/digital/cli/cli/ArgumentBase.java index e3c287c7c..79ac1fe86 100644 --- a/src/main/java/de/neemann/digital/cli/ArgumentBase.java +++ b/src/main/java/de/neemann/digital/cli/cli/ArgumentBase.java @@ -3,7 +3,7 @@ * Use of this source code is governed by the GPL v3 license * that can be found in the LICENSE file. */ -package de.neemann.digital.cli; +package de.neemann.digital.cli.cli; /** * The base class of all arguments diff --git a/src/main/java/de/neemann/digital/cli/ArgumentKey.java b/src/main/java/de/neemann/digital/cli/cli/ArgumentKey.java similarity index 98% rename from src/main/java/de/neemann/digital/cli/ArgumentKey.java rename to src/main/java/de/neemann/digital/cli/cli/ArgumentKey.java index c4a62ca44..6fa52c7b4 100644 --- a/src/main/java/de/neemann/digital/cli/ArgumentKey.java +++ b/src/main/java/de/neemann/digital/cli/cli/ArgumentKey.java @@ -3,7 +3,7 @@ * Use of this source code is governed by the GPL v3 license * that can be found in the LICENSE file. */ -package de.neemann.digital.cli; +package de.neemann.digital.cli.cli; import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.element.Key; diff --git a/src/main/java/de/neemann/digital/cli/SimpleCommand.java b/src/main/java/de/neemann/digital/cli/cli/BasicCommand.java similarity index 88% rename from src/main/java/de/neemann/digital/cli/SimpleCommand.java rename to src/main/java/de/neemann/digital/cli/cli/BasicCommand.java index d94c4491d..1ea20f5c7 100644 --- a/src/main/java/de/neemann/digital/cli/SimpleCommand.java +++ b/src/main/java/de/neemann/digital/cli/cli/BasicCommand.java @@ -3,7 +3,7 @@ * Use of this source code is governed by the GPL v3 license * that can be found in the LICENSE file. */ -package de.neemann.digital.cli; +package de.neemann.digital.cli.cli; import de.neemann.digital.lang.Lang; @@ -13,10 +13,9 @@ import java.util.Arrays; import java.util.Iterator; /** - * E simple executable command + * A simple executable command */ -public abstract class SimpleCommand implements CLICommand { - private final String name; +public abstract class BasicCommand extends NamedCommand { private final ArrayList> arguments; /** @@ -24,8 +23,8 @@ public abstract class SimpleCommand implements CLICommand { * * @param name the name of the command */ - public SimpleCommand(String name) { - this.name = name; + public BasicCommand(String name) { + super(name); arguments = new ArrayList<>(); } @@ -42,17 +41,10 @@ public abstract class SimpleCommand implements CLICommand { return argument; } - /** - * @return the name of the argument - */ - public String getName() { - return name; - } - @Override public void printDescription(PrintStream out, String prefix) { StringBuilder sb = new StringBuilder(); - sb.append(name); + sb.append(getName()); for (ArgumentBase a : arguments) { sb.append(" "); sb.append(a); @@ -63,7 +55,7 @@ public abstract class SimpleCommand implements CLICommand { prefix += " "; out.print(prefix + " "); - printString(out, prefix + " ", Lang.get("cli_help_" + name)); + printString(out, prefix + " ", Lang.get("cli_help_" + getName())); out.print(prefix); out.println(Lang.get("cli_options")); @@ -71,7 +63,7 @@ public abstract class SimpleCommand implements CLICommand { for (ArgumentBase a : arguments) { out.println(prefix + " " + a.toStringDef()); out.print(prefix + " "); - printString(out, prefix + " ", a.getDescription(name)); + printString(out, prefix + " ", a.getDescription(getName())); } } diff --git a/src/main/java/de/neemann/digital/cli/CLICommand.java b/src/main/java/de/neemann/digital/cli/cli/CLICommand.java similarity index 94% rename from src/main/java/de/neemann/digital/cli/CLICommand.java rename to src/main/java/de/neemann/digital/cli/cli/CLICommand.java index ababf9e51..862ccec34 100644 --- a/src/main/java/de/neemann/digital/cli/CLICommand.java +++ b/src/main/java/de/neemann/digital/cli/cli/CLICommand.java @@ -3,7 +3,7 @@ * Use of this source code is governed by the GPL v3 license * that can be found in the LICENSE file. */ -package de.neemann.digital.cli; +package de.neemann.digital.cli.cli; import java.io.PrintStream; diff --git a/src/main/java/de/neemann/digital/cli/CLIException.java b/src/main/java/de/neemann/digital/cli/cli/CLIException.java similarity index 94% rename from src/main/java/de/neemann/digital/cli/CLIException.java rename to src/main/java/de/neemann/digital/cli/cli/CLIException.java index 3b848510e..50caf4ddc 100644 --- a/src/main/java/de/neemann/digital/cli/CLIException.java +++ b/src/main/java/de/neemann/digital/cli/cli/CLIException.java @@ -3,7 +3,7 @@ * Use of this source code is governed by the GPL v3 license * that can be found in the LICENSE file. */ -package de.neemann.digital.cli; +package de.neemann.digital.cli.cli; import java.io.PrintStream; @@ -43,7 +43,7 @@ public class CLIException extends Exception { } /** - * Pronts a error message to the stream + * Prints a error message to the stream * * @param out the print stream */ diff --git a/src/main/java/de/neemann/digital/cli/Muxer.java b/src/main/java/de/neemann/digital/cli/cli/Muxer.java similarity index 84% rename from src/main/java/de/neemann/digital/cli/Muxer.java rename to src/main/java/de/neemann/digital/cli/cli/Muxer.java index 4c0dddd76..a9fdd7244 100644 --- a/src/main/java/de/neemann/digital/cli/Muxer.java +++ b/src/main/java/de/neemann/digital/cli/cli/Muxer.java @@ -3,8 +3,11 @@ * Use of this source code is governed by the GPL v3 license * that can be found in the LICENSE file. */ -package de.neemann.digital.cli; +package de.neemann.digital.cli.cli; +import de.neemann.digital.cli.CLITester; +import de.neemann.digital.cli.SVGExport; +import de.neemann.digital.cli.StatsExport; import de.neemann.digital.lang.Lang; import java.io.PrintStream; @@ -14,7 +17,7 @@ import java.util.HashMap; /** * The command muxer */ -public class Muxer implements CLICommand { +public class Muxer extends NamedCommand { /** * The main muxer */ @@ -24,7 +27,6 @@ public class Muxer implements CLICommand { .addCommand(new StatsExport()); private final HashMap commands; - private final String name; private Muxer() { this("java -cp Digital.jar CLI"); @@ -36,7 +38,7 @@ public class Muxer implements CLICommand { * @param name the name of the muxer */ public Muxer(String name) { - this.name = name; + super(name); this.commands = new HashMap<>(); } @@ -46,7 +48,7 @@ public class Muxer implements CLICommand { * @param command the command * @return this for chained calls */ - public Muxer addCommand(SimpleCommand command) { + public Muxer addCommand(NamedCommand command) { return addCommand(command.getName(), command); } @@ -65,7 +67,7 @@ public class Muxer implements CLICommand { @Override public void printDescription(PrintStream out, String prefix) { out.print(prefix); - out.print(name); + out.print(getName()); out.println(); for (CLICommand c : commands.values()) c.printDescription(out, prefix + " "); @@ -78,7 +80,7 @@ public class Muxer implements CLICommand { CLICommand command = commands.get(args[0]); if (command == null) - throw new CLIException(Lang.get("cli_command_N_hasNoSubCommand_N", name, args[0]), 101); + throw new CLIException(Lang.get("cli_command_N_hasNoSubCommand_N", getName(), args[0]), 101); command.execute(Arrays.copyOfRange(args, 1, args.length)); } diff --git a/src/main/java/de/neemann/digital/cli/cli/NamedCommand.java b/src/main/java/de/neemann/digital/cli/cli/NamedCommand.java new file mode 100644 index 000000000..8425ddddb --- /dev/null +++ b/src/main/java/de/neemann/digital/cli/cli/NamedCommand.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020 Helmut Neemann. + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ +package de.neemann.digital.cli.cli; + +/** + * The base class of all named commands + */ +public abstract class NamedCommand implements CLICommand { + private final String name; + + /** + * Create a new instance + * + * @param name the name of the command + */ + public NamedCommand(String name) { + this.name = name; + } + + /** + * @return the name of the command + */ + public String getName() { + return name; + } +} diff --git a/src/main/java/de/neemann/digital/cli/cli/package-info.java b/src/main/java/de/neemann/digital/cli/cli/package-info.java new file mode 100644 index 000000000..68914b44d --- /dev/null +++ b/src/main/java/de/neemann/digital/cli/cli/package-info.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2020 Helmut Neemann. + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ + +/** + * The classes needed to implement the command line interface + */ +package de.neemann.digital.cli.cli; diff --git a/src/main/java/de/neemann/digital/testing/CommandLineTester.java b/src/main/java/de/neemann/digital/testing/CommandLineTester.java index e84e2440e..93acd71ca 100644 --- a/src/main/java/de/neemann/digital/testing/CommandLineTester.java +++ b/src/main/java/de/neemann/digital/testing/CommandLineTester.java @@ -28,7 +28,7 @@ public class CommandLineTester { private final ElementLibrary library; private final ShapeFactory shapeFactory; - private Circuit circuit; + private final Circuit circuit; private PrintStream out = System.out; private ArrayList testCases; private int testsPassed; diff --git a/src/test/java/de/neemann/digital/cli/ArgumentTest.java b/src/test/java/de/neemann/digital/cli/ArgumentTest.java index 88aa79c06..a3c70ea9f 100644 --- a/src/test/java/de/neemann/digital/cli/ArgumentTest.java +++ b/src/test/java/de/neemann/digital/cli/ArgumentTest.java @@ -5,6 +5,8 @@ */ package de.neemann.digital.cli; +import de.neemann.digital.cli.cli.Argument; +import de.neemann.digital.cli.cli.CLIException; import junit.framework.TestCase; public class ArgumentTest extends TestCase { @@ -39,6 +41,14 @@ public class ArgumentTest extends TestCase { } } + public void testToggle() throws CLIException { + Argument a = new Argument<>("flag", false, false); + assertTrue(a.isBool()); + assertFalse(a.get()); + a.toggle(); + assertTrue(a.get()); + } + public void testInteger() throws CLIException { Argument a = new Argument<>("n", 2, false); assertEquals(2, (int) a.get()); diff --git a/src/test/java/de/neemann/digital/cli/SimpleCommandTest.java b/src/test/java/de/neemann/digital/cli/BasicCommandTest.java similarity index 92% rename from src/test/java/de/neemann/digital/cli/SimpleCommandTest.java rename to src/test/java/de/neemann/digital/cli/BasicCommandTest.java index b1443b83d..180a2a5c4 100644 --- a/src/test/java/de/neemann/digital/cli/SimpleCommandTest.java +++ b/src/test/java/de/neemann/digital/cli/BasicCommandTest.java @@ -5,11 +5,14 @@ */ package de.neemann.digital.cli; +import de.neemann.digital.cli.cli.Argument; +import de.neemann.digital.cli.cli.BasicCommand; +import de.neemann.digital.cli.cli.CLIException; import junit.framework.TestCase; -public class SimpleCommandTest extends TestCase { +public class BasicCommandTest extends TestCase { - private static class TestCommand extends SimpleCommand { + private static class TestCommand extends BasicCommand { private boolean wasExecuted; private TestCommand() { diff --git a/src/test/java/de/neemann/digital/cli/MuxerTest.java b/src/test/java/de/neemann/digital/cli/MuxerTest.java new file mode 100644 index 000000000..819c04d48 --- /dev/null +++ b/src/test/java/de/neemann/digital/cli/MuxerTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2020 Helmut Neemann. + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ +package de.neemann.digital.cli; + +import de.neemann.digital.cli.cli.Argument; +import de.neemann.digital.cli.cli.BasicCommand; +import de.neemann.digital.cli.cli.CLIException; +import de.neemann.digital.cli.cli.Muxer; +import junit.framework.TestCase; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +public class MuxerTest extends TestCase { + + /** + * Creates the documentation. + * Used to make the maven build fail, if a language key is missing. + */ + public void testDocu() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Muxer.MAIN_MUXER.printDescription(new PrintStream(baos), ""); + assertTrue(baos.toByteArray().length > 100); + } + + public void testErrors() throws CLIException { + TestCommand tc = new TestCommand(); + Muxer m = new Muxer("test").addCommand(tc); + + try { + m.execute(new String[]{"add"}); + fail(); + } catch (CLIException e) { + assertTrue(true); + } + + m.execute(new String[]{"add", "-arg", "zz"}); + assertEquals("zz", tc.arg.get()); + + try { + m.execute(new String[]{"add", "-foo", "zz"}); + fail(); + } catch (CLIException e) { + assertTrue(true); + } + + try { + m.execute(new String[]{"sub"}); + fail(); + } catch (CLIException e) { + assertTrue(true); + } + + try { + m.execute(new String[]{}); + fail(); + } catch (CLIException e) { + assertTrue(true); + } + } + + public void testNesting() throws CLIException { + TestCommand tc = new TestCommand(); + Muxer m = new Muxer("main") + .addCommand(new Muxer("arith") + .addCommand(tc)); + + m.execute(new String[]{"arith", "add", "-arg", "tt"}); + + assertTrue(tc.wasExecuted); + } + + private static class TestCommand extends BasicCommand { + private final Argument arg; + private boolean wasExecuted = false; + + public TestCommand() { + super("add"); + arg = addArgument(new Argument<>("arg", "def", false)); + } + + + @Override + public void printDescription(PrintStream out, String prefix) { + } + + @Override + protected void execute() throws CLIException { + wasExecuted = true; + } + + } +} \ No newline at end of file