merged cli implementation, closes #473

This commit is contained in:
hneemann 2020-06-10 18:14:39 +02:00
commit 3ed5dd8f8c
35 changed files with 1621 additions and 65 deletions

View File

@ -14,6 +14,9 @@ Head, planned as v0.25
- The layout shape uses a slightly different algorithm to determine
the pin positions. You may need to adjust usages of embedded circuits
that use the layout shape.
- The CommandLineTester has moved! Tests are now executed like
java -cp Digital.jar CLI test [file to test]
[-tests [optional file with test cases]]
v0.24, released on 1. Feb. 2020
- Improved SVG export

27
src/main/java/CLI.java Normal file
View File

@ -0,0 +1,27 @@
/*
* 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.
*/
import de.neemann.digital.cli.Main;
/**
* Entry point for the CLI interface.
* Used to allow a more compact command line.
* All work is delegated to {@link Main}.
*/
public final class CLI {
private CLI() {
}
/**
* Entry point for the CLI interface
*
* @param args the command line arguments
*/
public static void main(String[] args) {
new Main().main(args);
}
}

View File

@ -0,0 +1,64 @@
/*
* 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 javax.swing.table.TableModel;
import java.io.BufferedWriter;
import java.io.IOException;
/**
* Helper to write a {@link TableModel} to a csv file
*/
public class CSVWriter {
private final TableModel tableModel;
private boolean newLine = true;
/**
* Creates a new instance
*
* @param tableModel the table model to be written to a csv file
*/
public CSVWriter(TableModel tableModel) {
this.tableModel = tableModel;
}
/**
* Writes the {@link TableModel} to the given writer
*
* @param w the writer to use
* @throws IOException IOException
*/
public void writeTo(BufferedWriter w) throws IOException {
for (int c = 0; c < tableModel.getColumnCount(); c++)
writeCell(w, tableModel.getColumnName(c));
newLine(w);
for (int r = 0; r < tableModel.getRowCount(); r++) {
for (int c = 0; c < tableModel.getColumnCount(); c++)
writeCell(w, tableModel.getValueAt(r, c));
newLine(w);
}
w.close();
}
private void newLine(BufferedWriter w) throws IOException {
w.newLine();
newLine = true;
}
private void writeCell(BufferedWriter w, Object value) throws IOException {
if (!newLine)
w.write(',');
if (value != null) {
String v = value.toString();
boolean quotes = v.contains(",") || v.contains(";") || v.contains("\t") || v.contains("\n");
if (quotes) w.write('"');
w.write(v);
if (quotes) w.write('"');
}
newLine = false;
}
}

View File

@ -0,0 +1,105 @@
/*
* 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.core.Model;
import de.neemann.digital.core.NodeException;
import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.draw.elements.PinException;
import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.draw.library.ElementNotFoundException;
import de.neemann.digital.draw.model.ModelCreator;
import de.neemann.digital.draw.shapes.ShapeFactory;
import java.io.File;
import java.io.IOException;
/**
* Helper to make it easier to load a circuit.
*/
public final class CircuitLoader {
private final Circuit circuit;
private final ElementLibrary library;
private final ShapeFactory shapeFactory;
/**
* Loads a circuit.
*
* @param filename filename
* @throws IOException IOException
*/
public CircuitLoader(String filename) throws IOException {
this(new File(filename), true);
}
/**
* Loads a circuit.
*
* @param filename filename
* @param ieeeShapes if true ieee shapes are used
* @throws IOException IOException
*/
public CircuitLoader(String filename, boolean ieeeShapes) throws IOException {
this(new File(filename), ieeeShapes);
}
/**
* Loads a circuit.
*
* @param file filename
* @throws IOException IOException
*/
public CircuitLoader(File file) throws IOException {
this(file, true);
}
/**
* Loads a circuit.
*
* @param file filename
* @param ieeeShapes if true ieee shapes are used
* @throws IOException IOException
*/
public CircuitLoader(File file, boolean ieeeShapes) throws IOException {
library = new ElementLibrary();
library.setRootFilePath(file.getAbsoluteFile().getParentFile());
shapeFactory = new ShapeFactory(library, ieeeShapes);
circuit = Circuit.loadCircuit(file, shapeFactory);
}
/**
* @return the circuit
*/
public Circuit getCircuit() {
return circuit;
}
/**
* @return the created library
*/
public ElementLibrary getLibrary() {
return library;
}
/**
* @return the created shape factory
*/
public ShapeFactory getShapeFactory() {
return shapeFactory;
}
/**
* Creates a mode from the loaded circuit.
*
* @return the model
* @throws ElementNotFoundException ElementNotFoundException
* @throws PinException PinException
* @throws NodeException NodeException
*/
public Model createModel() throws ElementNotFoundException, PinException, NodeException {
return new ModelCreator(circuit, library).createModel(false);
}
}

View File

@ -3,17 +3,22 @@
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.testing;
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.draw.elements.Circuit;
import de.neemann.digital.draw.elements.PinException;
import de.neemann.digital.draw.elements.VisualElement;
import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.draw.library.ElementNotFoundException;
import de.neemann.digital.draw.model.ModelCreator;
import de.neemann.digital.draw.shapes.ShapeFactory;
import de.neemann.digital.lang.Lang;
import de.neemann.digital.testing.TestCaseDescription;
import de.neemann.digital.testing.TestCaseElement;
import de.neemann.digital.testing.TestExecutor;
import de.neemann.digital.testing.TestingDataException;
import de.neemann.digital.testing.parser.ParserException;
import java.io.File;
@ -26,10 +31,7 @@ import java.util.ArrayList;
*/
public class CommandLineTester {
private final ElementLibrary library;
private final ShapeFactory shapeFactory;
private Circuit circuit;
private PrintStream out = System.out;
private final CircuitLoader circuitLoader;
private ArrayList<TestCase> testCases;
private int testsPassed;
@ -40,20 +42,7 @@ public class CommandLineTester {
* @throws IOException IOException
*/
public CommandLineTester(File file) throws IOException {
library = new ElementLibrary();
shapeFactory = new ShapeFactory(library);
circuit = Circuit.loadCircuit(file, shapeFactory);
}
/**
* Sets the printer to use
*
* @param out the {@link PrintStream}
* @return this for chained calls
*/
public CommandLineTester setOutputs(PrintStream out) {
this.out = out;
return this;
circuitLoader = new CircuitLoader(file);
}
/**
@ -64,7 +53,7 @@ public class CommandLineTester {
* @throws IOException IOException
*/
public CommandLineTester useTestCasesFrom(File file) throws IOException {
Circuit c = Circuit.loadCircuit(file, shapeFactory);
Circuit c = Circuit.loadCircuit(file, circuitLoader.getShapeFactory());
testCases = getTestCasesFrom(c);
return this;
}
@ -82,11 +71,12 @@ public class CommandLineTester {
/**
* Executes test test
*
* @param out Stream to output messages
* @return the number of failed test cases
*/
public int execute() {
public int execute(PrintStream out) {
if (testCases == null)
testCases = getTestCasesFrom(circuit);
testCases = getTestCasesFrom(circuitLoader.getCircuit());
int errorCount = 0;
@ -100,7 +90,7 @@ public class CommandLineTester {
label = "unnamed";
try {
Model model = new ModelCreator(circuit, library).createModel(false);
Model model = circuitLoader.createModel();
TestExecutor te = new TestExecutor(t.getTestCaseDescription()).create(model);
if (te.allPassed()) {
@ -145,23 +135,42 @@ public class CommandLineTester {
}
/**
* Entry point of the command line tester.
*
* @param args the program arguments
* @throws IOException IOException
* The test command
*/
public static void main(String[] args) throws IOException {
if (args.length == 0) {
System.err.println("no command line arguments given!\n");
System.err.println("usage:\n");
System.err.println("java -cp Digital.jar " + CommandLineTester.class.getName() + " [dig file to test] [[optional dig file with test cases]]");
System.exit(1);
public static class TestCommand extends BasicCommand {
private final Argument<String> circ;
private final Argument<String> tests;
private int testsPassed;
/**
* Creates a new CLI command
*/
public TestCommand() {
super("test");
circ = addArgument(new Argument<>("circ", "", false));
tests = addArgument(new Argument<>("tests", "", true));
}
CommandLineTester clt = new CommandLineTester(new File(args[0]));
if (args.length > 1)
clt.useTestCasesFrom(new File(args[1]));
int errors = clt.execute();
System.exit(errors);
@Override
protected void execute() throws CLIException {
try {
CommandLineTester clt = new CommandLineTester(new File(circ.get()));
if (tests.isSet())
clt.useTestCasesFrom(new File(tests.get()));
int errors = clt.execute(System.out);
testsPassed = clt.getTestsPassed();
if (errors > 0)
throw new CLIException(Lang.get("cli_thereAreTestFailures"), errors).hideHelp();
} catch (IOException e) {
throw new CLIException(Lang.get("cli_errorExecutingTests"), e);
}
}
/**
* @return the number of tests passed
*/
public int getTestsPassed() {
return testsPassed;
}
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.CLIException;
import de.neemann.digital.cli.cli.Muxer;
/**
* Entry point for the command line interface
*/
public class Main extends Muxer {
/**
* Creates a new CLI main
*/
public Main() {
super("java -cp Digital.jar CLI");
addCommand(new CommandLineTester.TestCommand());
addCommand(new SVGExport());
addCommand(new StatsExport());
}
/**
* Evaluates the command line arguments
*
* @param args the cli arguments
*/
public void main(String[] args) {
try {
execute(args);
} catch (CLIException e) {
e.printMessage(System.out);
if (e.showHelp()) {
System.out.println();
printDescription(System.out, "");
}
System.exit(e.getExitCode());
}
}
}

View File

@ -0,0 +1,65 @@
/*
* 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.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;
import de.neemann.digital.draw.graphics.Export;
import de.neemann.digital.draw.graphics.GraphicSVG;
import de.neemann.digital.draw.graphics.SVGSettings;
import de.neemann.digital.lang.Lang;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* CLI svg exporter
*/
public class SVGExport extends BasicCommand {
private final ElementAttributes attr;
private final Argument<String> digFile;
private final Argument<String> svgFile;
private final Argument<Boolean> ieeeShapes;
/**
* Creates the SVG export command
*/
public SVGExport() {
super("svg");
digFile = addArgument(new Argument<>("dig", "", false));
svgFile = addArgument(new Argument<>("svg", "", true));
ieeeShapes = addArgument(new Argument<>("ieee", false, true));
attr = new ElementAttributes();
for (Key<?> k : SVGSettings.createKeyList())
addArgument(new ArgumentKey<>(k, attr, 4));
}
@Override
protected void execute() throws CLIException {
try {
Circuit circuit = new CircuitLoader(digFile.get(), ieeeShapes.get()).getCircuit();
String outName;
if (svgFile.isSet())
outName = svgFile.get();
else
outName = digFile.get() + ".svg";
OutputStream out = new FileOutputStream(outName);
new Export(circuit, o -> new GraphicSVG(o, attr)).export(out);
} catch (IOException e) {
throw new CLIException(Lang.get("cli_errorCreatingSVG"), e);
}
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.core.Model;
import de.neemann.digital.core.NodeException;
import de.neemann.digital.core.stats.Statistics;
import de.neemann.digital.draw.elements.PinException;
import de.neemann.digital.draw.library.ElementNotFoundException;
import de.neemann.digital.lang.Lang;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
/**
* CLI stats exporter
*/
public class StatsExport extends BasicCommand {
private final Argument<String> digFile;
private final Argument<String> csvFile;
/**
* Creates the stats export command
*/
public StatsExport() {
super("stats");
digFile = addArgument(new Argument<>("dig", "", false));
csvFile = addArgument(new Argument<>("csv", "", true));
}
@Override
protected void execute() throws CLIException {
try {
Model model = new CircuitLoader(digFile.get()).createModel();
Statistics stats = new Statistics(model);
BufferedWriter writer;
if (csvFile.isSet())
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(csvFile.get())));
else
writer = new BufferedWriter(new OutputStreamWriter(System.out));
new CSVWriter(stats.getTableModel()).writeTo(writer);
} catch (IOException | ElementNotFoundException | PinException | NodeException e) {
throw new CLIException(Lang.get("cli_errorCreatingStats"), e);
}
}
}

View File

@ -0,0 +1,100 @@
/*
* 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;
import de.neemann.digital.lang.Lang;
/**
* A command cline argument
*
* @param <T> the type of the argument
*/
public class Argument<T> extends ArgumentBase<T> {
private final String name;
private final boolean optional;
private T value;
private boolean isSet;
/**
* Creates a new argument
*
* @param name the name of the argument
* @param def the default value
* @param optional true if argument is optional
*/
public Argument(String name, T def, boolean optional) {
this.name = name;
this.optional = optional;
if (def == null)
throw new NullPointerException();
value = def;
}
@Override
public T get() {
return value;
}
@Override
public void setString(String val) throws CLIException {
value = (T) fromString(val, value);
isSet = true;
}
/**
* Creates a value from a string
*
* @param val the value as a string
* @param defValue the default value
* @return the value converted to the type of the default value
* @throws CLIException CLIException
*/
public static Object fromString(String val, Object defValue) throws CLIException {
if (defValue instanceof String)
return val;
else if (defValue instanceof Boolean)
switch (val.toLowerCase()) {
case "yes":
case "1":
case "true":
return true;
case "no":
case "0":
case "false":
return false;
default:
throw new CLIException(Lang.get("cli_notABool_N", val), 106);
}
else if (defValue instanceof Integer) {
try {
return Integer.parseInt(val);
} catch (NumberFormatException e) {
throw new CLIException(Lang.get("cli_notANumber_N", val), e);
}
} else
throw new CLIException(Lang.get("cli_invalidType_N", defValue.getClass().getSimpleName()), 203);
}
@Override
public boolean isSet() {
return isSet;
}
@Override
public String getDescription(String command) {
return Lang.get("cli_help_" + command + "_" + name);
}
@Override
public String getName() {
return name;
}
@Override
public boolean isOptional() {
return optional;
}
}

View File

@ -0,0 +1,109 @@
/*
* 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 arguments
*
* @param <T> the type of the argument
*/
public abstract class ArgumentBase<T> {
/**
* @return the name of this argument
*/
public abstract String getName();
/**
* @return true if this argument is optional
*/
public abstract boolean isOptional();
/**
* Sets a string value
*
* @param val the value to set
* @throws CLIException CLIException
*/
public abstract void setString(String val) throws CLIException;
/**
* @return if this argument was set
*/
public abstract boolean isSet();
/**
* Returns the description of the argument
*
* @param command the name of the command this argument belongs to.
* @return the description
*/
public abstract String getDescription(String command);
/**
* @return the value of the option
*/
public abstract T get();
@Override
public String toString() {
String s;
if (isBool())
s = "-" + getName();
else
s = "-" + getName()
+ " ["
+ get().getClass().getSimpleName()
+ "]";
return optionalBrace(s);
}
/**
* @return a string representation containing the default value
*/
public String toStringDef() {
String s;
if (isBool())
s = "-" + getName()
+ "(def: "
+ get()
+ ")";
else
s = "-" + getName()
+ " ["
+ get().getClass().getSimpleName()
+ "(def: "
+ get()
+ ")]";
return optionalBrace(s);
}
private String optionalBrace(String s) {
if (isOptional())
return "[" + s + "]";
return s;
}
/**
* @return true if this is a bool flag
*/
public boolean isBool() {
return get() instanceof Boolean;
}
/**
* Toggles a bool value
*
* @throws CLIException CLIException
*/
public void toggle() throws CLIException {
if (isBool()) {
boolean b = (Boolean) get();
setString(Boolean.toString(!b));
}
}
}

View File

@ -0,0 +1,77 @@
/*
* 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;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.Key;
/**
* A cli argument based on a Key instance.
*
* @param <T> the type of the argument.
*/
public class ArgumentKey<T> extends ArgumentBase<T> {
private final Key<T> key;
private final ElementAttributes attr;
private final String name;
/**
* Creates a new instance
*
* @param key the key
* @param attr the attribute to store the values
*/
public ArgumentKey(Key<T> key, ElementAttributes attr) {
this(key, attr, 0);
}
/**
* Creates a new instance
*
* @param key the key
* @param attr the attribute to store the values
* @param stripFromKey number of characters to strip from the key name
*/
public ArgumentKey(Key<T> key, ElementAttributes attr, int stripFromKey) {
this.key = key;
this.attr = attr;
if (stripFromKey > 0)
name = this.key.getKey().substring(stripFromKey);
else
name = this.key.getKey();
}
@Override
public String getName() {
return name;
}
@Override
public boolean isOptional() {
return true;
}
@Override
public void setString(String val) throws CLIException {
attr.set(key, (T) Argument.fromString(val, key.getDefault()));
}
@Override
public boolean isSet() {
return attr.contains(key);
}
@Override
public String getDescription(String command) {
return key.getDescription();
}
@Override
public T get() {
return attr.get(key);
}
}

View File

@ -0,0 +1,174 @@
/*
* 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;
import de.neemann.digital.lang.Lang;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
/**
* A simple executable command
*/
public abstract class BasicCommand extends NamedCommand {
private final ArrayList<ArgumentBase<?>> arguments;
/**
* Creates a new command
*
* @param name the name of the command
*/
public BasicCommand(String name) {
super(name);
arguments = new ArrayList<>();
}
/**
* Adds an argument to the command
*
* @param argument the argument
* @param <T> the type of the arguments value
* @param <A> the type of the argument
* @return the argument itself
*/
public <T, A extends ArgumentBase<T>> A addArgument(A argument) {
arguments.add(argument);
return argument;
}
@Override
public void printDescription(PrintStream out, String prefix) {
StringBuilder sb = new StringBuilder();
sb.append(getName());
for (ArgumentBase<?> a : arguments) {
sb.append(" ");
sb.append(a);
}
sb.append(":");
out.print(prefix);
printString(out, prefix + " ", sb.toString());
prefix += " ";
out.print(prefix + " ");
printString(out, prefix + " ", Lang.get("cli_help_" + getName()));
out.print(prefix);
out.println(Lang.get("cli_options"));
for (ArgumentBase<?> a : arguments) {
out.println(prefix + " " + a.toStringDef());
out.print(prefix + " ");
printString(out, prefix + " ", a.getDescription(getName()));
}
}
@Override
public void printXMLDescription(Writer w) throws IOException {
w.write("<indent>\n");
w.append(getName());
for (ArgumentBase<?> a : arguments) {
w.append(" ");
w.append(a.toString());
}
w.append(":");
w.write("<indent>\n");
w.write(Lang.get("cli_help_" + getName()));
w.write("</indent>\n");
w.write("<indent>\n");
w.write(Lang.get("cli_options"));
for (ArgumentBase<?> a : arguments) {
w.write("<indent>\n");
w.write(a.toStringDef());
w.write("<indent>\n");
w.write(a.getDescription(getName()));
w.write("</indent>\n");
w.write("</indent>\n");
}
w.write("</indent>\n");
w.write("</indent>\n");
}
void printString(PrintStream out, String prefix, String message) {
boolean lastWasSpace = false;
int col = prefix.length();
for (int i = 0; i < message.length(); i++) {
char c = message.charAt(i);
if (c == '\n')
c = ' ';
if (c != ' ' || !lastWasSpace) {
if (c == ' ') {
if (col > 70) {
out.print('\n');
out.print(prefix);
col = prefix.length();
} else {
out.print(c);
col++;
}
} else {
out.print(c);
col++;
}
}
lastWasSpace = c == ' ';
}
out.println();
}
private void set(String arg, Iterator<String> args) throws CLIException {
for (ArgumentBase<?> a : arguments)
if (arg.equals(a.getName())) {
if (a.isBool())
a.toggle();
else {
if (!args.hasNext())
throw new CLIException(Lang.get("cli_notEnoughArgumentsGiven"), 100);
a.setString(args.next());
}
return;
}
throw new CLIException(Lang.get("cli_noArgument_N_available", arg), 104);
}
@Override
public void execute(String[] args) throws CLIException {
int nonOptional = 0;
Iterator<String> it = Arrays.asList(args).iterator();
while (it.hasNext()) {
String n = it.next();
if (n.startsWith("-")) {
set(n.substring(1), it);
} else {
while (nonOptional < arguments.size() && arguments.get(nonOptional).isOptional()) {
nonOptional++;
}
if (nonOptional == arguments.size())
throw new CLIException(Lang.get("cli_toMuchArguments"), 105);
arguments.get(nonOptional).setString(n);
nonOptional++;
}
}
for (ArgumentBase<?> a : arguments)
if (!a.isOptional() && !a.isSet())
throw new CLIException(Lang.get("cli_nonOptionalArgumentMissing_N", a), 105);
execute();
}
/**
* Executes the command
*
* @throws CLIException CLIException
*/
protected abstract void execute() throws CLIException;
}

View File

@ -0,0 +1,42 @@
/*
* 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;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Writer;
/**
* A cli command
*/
public interface CLICommand {
/**
* Executes the command
*
* @param args the arguments
* @throws CLIException CLIException
*/
void execute(String[] args) throws CLIException;
/**
* Prints the description
*
* @param out the print stream
* @param prefix the prefex string which should
* printed at the beginning of each line
*/
void printDescription(PrintStream out, String prefix);
/**
* Prints the description in xml format
*
* @param w the writer to write to
* @throws IOException IOException
*/
void printXMLDescription(Writer w) throws IOException;
}

View File

@ -0,0 +1,78 @@
/*
* 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;
import java.io.PrintStream;
/**
* he command line exception
*/
public class CLIException extends Exception {
private final int exitCode;
private boolean showHelp = true;
/**
* Creates a new instance
*
* @param message the message
* @param exitCode the exit code
*/
public CLIException(String message, int exitCode) {
super(message);
this.exitCode = exitCode;
}
/**
* Creates a new instance
*
* @param message the message
* @param cause the cause
*/
public CLIException(String message, Throwable cause) {
super(message, cause);
exitCode = 200;
}
/**
* @return the exit code
*/
public int getExitCode() {
return exitCode;
}
/**
* Prints a error message to the stream
*
* @param out the print stream
*/
public void printMessage(PrintStream out) {
out.println(getMessage());
Throwable c = getCause();
if (c != null) {
if (c instanceof CLIException)
((CLIException) c).printMessage(out);
else
out.println(c.getMessage());
}
}
/**
* Hides the help text
*
* @return this for chained calls
*/
public CLIException hideHelp() {
showHelp = false;
return this;
}
/**
* @return true if help has to be shown
*/
public boolean showHelp() {
return showHelp;
}
}

View File

@ -0,0 +1,84 @@
/*
* 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;
import de.neemann.digital.lang.Lang;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Writer;
import java.util.Arrays;
import java.util.HashMap;
/**
* The command muxer
*/
public class Muxer extends NamedCommand {
private final HashMap<String, CLICommand> commands;
/**
* Creates a new muxer
*
* @param name the name of the muxer
*/
public Muxer(String name) {
super(name);
this.commands = new HashMap<>();
}
/**
* Adds a command to the muxer
*
* @param command the command
* @return this for chained calls
*/
public Muxer addCommand(NamedCommand command) {
return addCommand(command.getName(), command);
}
/**
* Adds a command to the muxer
*
* @param name the name of the command
* @param command the command
* @return this for chained calls
*/
public Muxer addCommand(String name, CLICommand command) {
commands.put(name, command);
return this;
}
@Override
public void printDescription(PrintStream out, String prefix) {
out.print(prefix);
out.print(getName());
out.println();
for (CLICommand c : commands.values())
c.printDescription(out, prefix + " ");
}
@Override
public void printXMLDescription(Writer w) throws IOException {
w.write("<indent>\n");
w.write(getName());
for (CLICommand c : commands.values())
c.printXMLDescription(w);
w.write("</indent>\n");
}
@Override
public void execute(String[] args) throws CLIException {
if (args.length == 0)
throw new CLIException(Lang.get("cli_notEnoughArgumentsGiven"), 100);
CLICommand command = commands.get(args[0]);
if (command == null)
throw new CLIException(Lang.get("cli_command_N_hasNoSubCommand_N", getName(), args[0]), 101);
command.execute(Arrays.copyOfRange(args, 1, args.length));
}
}

View File

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

View File

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

View File

@ -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 command line interface
*/
package de.neemann.digital.cli;

View File

@ -52,7 +52,10 @@ public final class SVGSettings extends SettingsBase {
super(createKeyList(), ".svgStyle.cfg");
}
private static List<Key> createKeyList() {
/**
* @return a list of available SVG export keys
*/
public static List<Key> createKeyList() {
ArrayList<Key> list = new ArrayList<>();
list.add(LATEX);
list.add(PINS_IN_MATH_MODE);

View File

@ -69,7 +69,7 @@ public final class InfoDialog implements Iterable<InfoDialog.Manifest> {
*/
private String createMessage(String message) {
StringBuilder sb = new StringBuilder("<html>");
sb.append(message.replace("\n", "<br/>"));
sb.append(message.replace("\n\n", "<br/><br/>"));
sb.append("\n\n");
for (Manifest m : infos) {
m.createInfoString(sb);
@ -93,6 +93,9 @@ public final class InfoDialog implements Iterable<InfoDialog.Manifest> {
ta.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, true);
ta.setEditable(false);
ta.setBackground(new JLabel().getBackground());
ta.setSize(new Dimension(400, 800));
ta.setPreferredSize(new Dimension(400, ta.getPreferredSize().height + 30));
Font font = ta.getFont().deriveFont(Font.BOLD);
ta.setFont(font);
int border = font.getSize();

View File

@ -0,0 +1,9 @@
/*
* 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 command line interface
*/

View File

@ -1541,6 +1541,43 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
<string name="lib_ram">RAM</string>
<string name="lib_eeprom">EEPROM</string>
<string name="cli_cli">Steuerung per Kommandozeile</string>
<string name="cli_nonOptionalArgumentMissing_N">Es fehlt das nicht optionale Argument {0}.</string>
<string name="cli_notABool_N">Der Wert {0} ist kein bool.</string>
<string name="cli_notANumber_N">Der Wert {0} ist keine Zahl.</string>
<string name="cli_noArgument_N_available">Das Argument {0} fehlt.</string>
<string name="cli_notEnoughArgumentsGiven">Es sind nicht genug Argumente vorhanden.</string>
<string name="cli_toMuchArguments">Es gibt zu viele Argumente.</string>
<string name="cli_invalidType_N">Ungültiger Typ.</string>
<string name="cli_command_N_hasNoSubCommand_N">Der Befehl {0} hat keinen Subbefehl {1}.</string>
<string name="cli_options">Optionen:</string>
<string name="cli_help_test">Der erste Dateiname gibt die zu testende Schaltung an.
Wenn ein zweiter Dateiname angegeben wird, werden die Testfälle aus dieser Datei ausgeführt.
Wird kein zweiter Dateiname angegeben, werden die Tests aus der ersten Datei ausgeführt.
</string>
<string name="cli_help_test_circ">Name der zu testenden Datei.</string>
<string name="cli_help_test_tests">Name einer Datei mit Testfällen.</string>
<string name="cli_thereAreTestFailures">Es sind Tests fehlgeschlagen.</string>
<string name="cli_errorExecutingTests">Es ist ein Fehler bei der Ausführung der Tests aufgetreten.</string>
<string name="cli_help_svg">Kann verwendet werden, um aus einer Schaltung
eine SVG-Datei zu erzeugen.
</string>
<string name="cli_help_svg_dig">Der Dateiname der Schaltung.</string>
<string name="cli_help_svg_svg">Der Name der zu schreibenden SVG-Datei.</string>
<string name="cli_help_svg_ieee">Verwendung der IEEE Symbole.</string>
<string name="cli_errorCreatingSVG">Fehler bei der Erzeugung der SVG Datei!</string>
<string name="cli_help_stats">Erzeugt eine CSV Datei welche die Schaltungsstatistik enthält.
Aufgeführt sind alle verwendeten Komponenten.
</string>
<string name="cli_help_stats_dig">Name der Schaltung.</string>
<string name="cli_help_stats_csv">Name der Ausgabedatei.
Wenn diese Option fehlt, erfolgt eine Ausgabe auf die Konsole.
</string>
<string name="cli_errorCreatingStats">Fehler bei der Erzeugung der CSV Datei!</string>
<string name="menu_window">Fenster</string>
<string name="menu_about">Über Digital</string>
<string name="menu_analyse">Analyse</string>

View File

@ -1505,6 +1505,41 @@
<string name="lib_ram">RAM</string>
<string name="lib_eeprom">EEPROM</string>
<string name="cli_cli">Command Line Interface</string>
<string name="cli_nonOptionalArgumentMissing_N">The non-optional argument {0} is missing.</string>
<string name="cli_notABool_N">The value {0} is no bool.</string>
<string name="cli_notANumber_N">The value {0} is not a number.</string>
<string name="cli_noArgument_N_available">The argument {0} is missing.</string>
<string name="cli_notEnoughArgumentsGiven">There are not enough arguments.</string>
<string name="cli_toMuchArguments">There are too many arguments.</string>
<string name="cli_invalidType_N">Invalid type.</string>
<string name="cli_command_N_hasNoSubCommand_N">The command {0} has no sub-command {1}.</string>
<string name="cli_options">Options:</string>
<string name="cli_help_test">The first file name specifies the circuit to be tested.
If a second file name is specified, the test cases are executed from this file.
If no second file name is specified, the tests are executed from the first file.
</string>
<string name="cli_help_test_circ">Name of the file to be tested.</string>
<string name="cli_help_test_tests">Name of a file with test cases.</string>
<string name="cli_thereAreTestFailures">Tests have failed.</string>
<string name="cli_errorExecutingTests">An error has occurred during the execution of the tests.</string>
<string name="cli_help_svg">Can be used to create an SVG file from a circuit.</string>
<string name="cli_help_svg_dig">The file name of the circuit.</string>
<string name="cli_help_svg_svg">The name of the SVG file to be written.</string>
<string name="cli_help_svg_ieee">Use of the IEEE symbols.</string>
<string name="cli_errorCreatingSVG">Error while creating the SVG file!</string>
<string name="cli_help_stats">Creates a CSV file which contains the circuit statistics.
All components used are listed in the CSV file.
</string>
<string name="cli_help_stats_dig">File name of the circuit.</string>
<string name="cli_help_stats_csv">Name of the csv file to be created.
If this option is missing, the table is written to stdout.
</string>
<string name="cli_errorCreatingStats">Error while creating the stats file!</string>
<string name="menu_window">Windows</string>
<string name="menu_about">About</string>
<string name="menu_analyse">Analysis</string>

View File

@ -0,0 +1,65 @@
/*
* 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.CLIException;
import junit.framework.TestCase;
public class ArgumentTest extends TestCase {
public void testString() throws CLIException {
Argument<String> a = new Argument<>("n", "a", false);
assertEquals("a", a.get());
a.setString("hello");
assertEquals("hello", a.get());
}
public void testBool() throws CLIException {
Argument<Boolean> a = new Argument<>("n", true, false);
assertTrue(a.get());
a.setString("false");
assertFalse(a.get());
a.setString("true");
assertTrue(a.get());
a.setString("0");
assertFalse(a.get());
a.setString("1");
assertTrue(a.get());
a.setString("no");
assertFalse(a.get());
a.setString("yes");
assertTrue(a.get());
try {
a.setString("foo");
fail();
} catch (CLIException e) {
}
}
public void testToggle() throws CLIException {
Argument<Boolean> a = new Argument<>("flag", false, false);
assertTrue(a.isBool());
assertFalse(a.get());
a.toggle();
assertTrue(a.get());
}
public void testInteger() throws CLIException {
Argument<Integer> a = new Argument<>("n", 2, false);
assertEquals(2, (int) a.get());
a.setString("5");
assertEquals(5, (int) a.get());
try {
a.setString("foo");
fail();
} catch (CLIException e) {
}
}
}

View File

@ -0,0 +1,106 @@
/*
* 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 junit.framework.TestCase;
public class BasicCommandTest extends TestCase {
private static class TestCommand extends BasicCommand {
private boolean wasExecuted;
private TestCommand() {
super("test");
}
@Override
protected void execute() {
wasExecuted = true;
}
public void testExecutes(boolean shouldBeExecuted) {
assertEquals(shouldBeExecuted, wasExecuted);
}
}
public void testOptional() throws CLIException {
TestCommand tc = new TestCommand();
Argument<String> n1 = tc.addArgument(new Argument<>("n1", "", false));
Argument<String> n2 = tc.addArgument(new Argument<>("n2", "", false));
tc.execute(new String[]{"name1", "name2"});
assertEquals("name1", n1.get());
assertEquals("name2", n2.get());
}
public void testOptional2() throws CLIException {
TestCommand tc = new TestCommand();
Argument<String> n1 = tc.addArgument(new Argument<>("n1", "", false));
Argument<String> n2 = tc.addArgument(new Argument<>("n2", "", false));
tc.execute(new String[]{"-n1", "name1", "-n2", "name2"});
assertEquals("name1", n1.get());
assertEquals("name2", n2.get());
}
public void testOptional3() {
TestCommand tc = new TestCommand();
Argument<String> n1 = tc.addArgument(new Argument<>("n1", "", false));
Argument<String> n2 = tc.addArgument(new Argument<>("n2", "", false));
try {
tc.execute(new String[]{"name1"});
fail();
} catch (CLIException e) {
}
}
public void testOptional4() throws CLIException {
TestCommand tc = new TestCommand();
Argument<String> n1 = tc.addArgument(new Argument<>("n1", "n1", true));
Argument<String> n2 = tc.addArgument(new Argument<>("n2", "n2", true));
tc.execute(new String[]{});
assertEquals("n1", n1.get());
assertEquals("n2", n2.get());
}
public void testOptional5() throws CLIException {
TestCommand tc = new TestCommand();
Argument<String> n1 = tc.addArgument(new Argument<>("n1", "n1", true));
Argument<String> n2 = tc.addArgument(new Argument<>("n2", "n2", true));
try {
tc.execute(new String[]{"test"});
fail();
} catch (CLIException e) {
}
}
public void testWrongArgument() {
TestCommand tc = new TestCommand();
Argument<String> n1 = tc.addArgument(new Argument<>("n1", "", true));
Argument<String> n2 = tc.addArgument(new Argument<>("n2", "", true));
try {
tc.execute(new String[]{"-n3", "test"});
fail();
} catch (CLIException e) {
}
try {
tc.execute(new String[]{"-n1"});
fail();
} catch (CLIException e) {
}
}
}

View File

@ -3,8 +3,9 @@
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.testing;
package de.neemann.digital.cli;
import de.neemann.digital.cli.cli.CLIException;
import de.neemann.digital.integration.Resources;
import junit.framework.TestCase;
@ -16,7 +17,7 @@ public class CommandLineTesterTest extends TestCase {
public void test74181() throws IOException {
File source = new File(Resources.getRoot(), "../../main/dig/lib/DIL Chips/74xx/arithmetic/74181.dig");
CommandLineTester tester = new CommandLineTester(source);
int errors = tester.execute();
int errors = tester.execute(System.out);
assertEquals(0, errors);
assertEquals(32, tester.getTestsPassed());
}
@ -24,7 +25,7 @@ public class CommandLineTesterTest extends TestCase {
public void testFailing() throws IOException {
File source = new File(Resources.getRoot(), "dig/failingTest.dig");
CommandLineTester tester = new CommandLineTester(source);
int errors = tester.execute();
int errors = tester.execute(System.out);
assertEquals(1, errors);
assertEquals(0, tester.getTestsPassed());
}
@ -34,9 +35,28 @@ public class CommandLineTesterTest extends TestCase {
CommandLineTester tester = new CommandLineTester(source)
.useTestCasesFrom(new File(Resources.getRoot(), "../../main/dig/sequential/Counter-D.dig"));
int errors = tester.execute();
int errors = tester.execute(System.out);
assertEquals(0, errors);
assertEquals(1, tester.getTestsPassed());
}
public void testCommand() throws CLIException {
File source = new File(Resources.getRoot(), "dig/failingTest.dig");
File tests = new File(Resources.getRoot(), "../../main/dig/sequential/Counter-D.dig");
CommandLineTester.TestCommand tc = new CommandLineTester.TestCommand();
tc.execute(new String[]{source.getPath(), "-tests", tests.getPath()});
assertEquals(1, tc.getTestsPassed());
}
public void testCommandTestFail() {
try {
File source = new File(Resources.getRoot(), "dig/failingTest.dig");
CommandLineTester.TestCommand tc = new CommandLineTester.TestCommand();
tc.execute(new String[]{source.getPath()});
fail();
} catch (CLIException e) {
assertEquals(1, e.getExitCode());
}
}
}

View File

@ -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();
new Main().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<String> 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() {
wasExecuted = true;
}
}
}

View File

@ -0,0 +1,34 @@
/*
* 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.CLIException;
import de.neemann.digital.integration.Resources;
import junit.framework.TestCase;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
public class StatsExportTest extends TestCase {
public void testStats() throws CLIException {
File source = new File(Resources.getRoot(), "../../main/dig/sequential/Counter-T.dig");
PrintStream old = System.out;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (PrintStream out = new PrintStream(baos)) {
System.setOut(out);
new StatsExport().execute(new String[]{source.getPath()});
}
String outStr = baos.toString();
assertTrue(outStr.contains("FlipflopT,,,,4"));
} finally {
System.setOut(old);
}
}
}

View File

@ -5,6 +5,7 @@
*/
package de.neemann.digital.docu;
import de.neemann.digital.cli.Main;
import de.neemann.digital.core.NodeException;
import de.neemann.digital.core.element.*;
import de.neemann.digital.draw.elements.PinException;
@ -89,6 +90,8 @@ public class DocuTest extends TestCase {
w.append(" </circuit>\n");
w.append(" </settings>\n");
writeCLIDescription(w);
ElementLibrary library = new ElementLibrary();
ShapeFactory shapeFactory = new ShapeFactory(library, !language.equals("de"));
String actPath = null;
@ -139,6 +142,12 @@ public class DocuTest extends TestCase {
w.append("</root>");
}
private void writeCLIDescription(Writer w) throws IOException {
w.append(" <cli heading=\"").append(Lang.get("cli_cli")).append("\">\n");
new Main().printXMLDescription(w);
w.append(" </cli>\n");
}
private void writeAttributes(Writer w, List<Key> keyList) throws IOException {
w.append(" <attributes name=\"").append(Lang.get("elem_Help_attributes")).append("\">\n");
for (Key k : keyList) {

View File

@ -68,7 +68,7 @@ public class TestLang extends TestCase {
StringBuilder sb = new StringBuilder();
for (String key : map.keySet()) {
if (!keys.contains(key)) {
if (!(key.startsWith("key_") || key.startsWith("elem_") || key.startsWith("attr_panel_") || key.startsWith("tutorial"))) {
if (!(key.startsWith("key_") || key.startsWith("elem_") || key.startsWith("attr_panel_") || key.startsWith("tutorial") || key.startsWith("cli_help_"))) {
if (sb.length() > 0)
sb.append(", ");
sb.append('"').append(key).append('"');

View File

@ -78,43 +78,86 @@
<fo:block page-break-before="always" margin-bottom="5mm" font-size="18pt" font-weight="bold">
<xsl:value-of select="@toc"/>
</fo:block>
<!-- table of contents -->
<!-- table of contents -->
<fo:block margin-bottom="2mm" font-weight="bold">
A <fo:inline padding-left="1mm"><xsl:value-of select="@general"/></fo:inline>
A
<fo:inline padding-left="1mm">
<xsl:value-of select="@general"/>
</fo:inline>
</fo:block>
<xsl:apply-templates select="document(@static)/*" mode="toc"/>
<fo:block margin-top="2mm" margin-bottom="2mm" font-weight="bold">
<fo:basic-link show-destination="replace" internal-destination="chap_settings">
B <fo:inline padding-left="2mm"><xsl:value-of select="settings/@name"/></fo:inline>
B
<fo:inline padding-left="2mm">
<xsl:value-of select="settings/@name"/>
</fo:inline>
</fo:basic-link>
</fo:block>
<fo:block margin-top="2mm" margin-bottom="2mm" font-weight="bold">
C <fo:inline padding-left="2mm"><xsl:value-of select="@components"/></fo:inline>
<fo:basic-link show-destination="replace" internal-destination="chap_cli">
C
<fo:inline padding-left="2mm">
<xsl:value-of select="cli/@heading"/>
</fo:inline>
</fo:basic-link>
</fo:block>
<fo:block margin-top="2mm" margin-bottom="2mm" font-weight="bold">
D
<fo:inline padding-left="2mm">
<xsl:value-of select="@components"/>
</fo:inline>
</fo:block>
<xsl:apply-templates select="lib" mode="toc"/>
<fo:block margin-top="2mm" margin-bottom="2mm" font-weight="bold">
<fo:basic-link show-destination="replace" internal-destination="chap_library">
D <fo:inline padding-left="2mm"><xsl:value-of select="@lib"/></fo:inline>
E
<fo:inline padding-left="2mm">
<xsl:value-of select="@lib"/>
</fo:inline>
</fo:basic-link>
</fo:block>
<fo:block page-break-before="always"/>
<!-- the content -->
<fo:block margin-top="4mm" margin-bottom="4mm" font-size="16pt" font-weight="bold">
A <fo:inline padding-left="2mm"><xsl:value-of select="@general"/></fo:inline>
A
<fo:inline padding-left="2mm">
<xsl:value-of select="@general"/>
</fo:inline>
</fo:block>
<xsl:apply-templates select="document(@static)/*" mode="full"/>
<fo:block page-break-before="always" margin-top="4mm" margin-bottom="4mm" font-size="16pt" font-weight="bold" id="chap_settings">
B <fo:inline padding-left="2mm"><xsl:value-of select="settings/@name"/></fo:inline>
<fo:block page-break-before="always" margin-top="4mm" margin-bottom="4mm" font-size="16pt"
font-weight="bold" id="chap_settings">
B
<fo:inline padding-left="2mm">
<xsl:value-of select="settings/@name"/>
</fo:inline>
</fo:block>
<xsl:apply-templates select="settings" mode="full"/>
<fo:block page-break-before="always" margin-top="4mm" margin-bottom="4mm" font-size="16pt"
font-weight="bold" id="chap_cli">
C
<fo:inline padding-left="2mm">
<xsl:value-of select="cli/@heading"/>
</fo:inline>
</fo:block>
<xsl:apply-templates select="cli" mode="full"/>
<fo:block page-break-before="always" margin-bottom="4mm" font-size="16pt" font-weight="bold">
C <fo:inline padding-left="2mm"><xsl:value-of select="@components"/></fo:inline>
D
<fo:inline padding-left="2mm">
<xsl:value-of select="@components"/>
</fo:inline>
</fo:block>
<xsl:apply-templates select="lib" mode="full"/>
<fo:block page-break-before="always" margin-bottom="4mm" font-size="16pt" font-weight="bold" id="chap_library">
D <fo:inline padding-left="2mm"><xsl:value-of select="@lib"/></fo:inline>
<fo:block page-break-before="always" margin-bottom="4mm" font-size="16pt" font-weight="bold"
id="chap_library">
E
<fo:inline padding-left="2mm">
<xsl:value-of select="@lib"/>
</fo:inline>
</fo:block>
<xsl:apply-templates select="document(@library)/*"/>
<fo:block id="LastPage"/>
@ -294,14 +337,25 @@
<xsl:apply-templates select="circuit/attributes"/>
</xsl:template>
<xsl:template match="cli" mode="full">
<xsl:apply-templates select="indent"/>
</xsl:template>
<xsl:template match="indent">
<fo:block margin-left="4mm" start-indent="2mm" margin-top="2mm" margin-bottom="2mm">
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="lib" mode="full">
<fo:block page-break-after="avoid" margin-top="4mm" margin-bottom="4mm" font-size="16pt" font-weight="bold">
<xsl:value-of select="position()"/>. <xsl:value-of select="@name"/>
<xsl:value-of select="position()"/>.
<xsl:value-of select="@name"/>
</fo:block>
<xsl:apply-templates select="element" mode="full">
<xsl:with-param name="number" select="position()"/>
</xsl:apply-templates>
</xsl:template>
</xsl:template>
<xsl:template match="element" mode="full">
<xsl:param name="number" />

View File

@ -515,7 +515,7 @@
</par>
<par>
<code>
java -cp Digital.jar de.neemann.digital.testing.CommandLineTester [zu testende Datei] [[optionale Datei
java -cp Digital.jar CLI test [zu testende Datei] [-tests [optionale Datei
mit Testfällen]]
</code>
</par>

View File

@ -484,7 +484,7 @@
</par>
<par>
<code>
java -cp Digital.jar de.neemann.digital.testing.CommandLineTester [file to test] [[optional file with
java -cp Digital.jar CLI test [file to test] [-tests [optional file with
test cases]]
</code>
</par>

View File

@ -310,7 +310,7 @@
</par>
<par>
<code>
java -cp Digital.jar de.neemann.digital.testing.CommandLineTester [file to test] [[optional file with
java -cp Digital.jar CLI test [file to test] [-tests [optional file with
test cases]]
</code>
</par>

View File

@ -556,7 +556,7 @@
</par>
<par>
<code>
java -cp Digital.jar de.neemann.digital.testing.CommandLineTester [file to test] [[optional file with
java -cp Digital.jar CLI test [file to test] [-tests [optional file with
test cases]]
</code>
</par>