diff --git a/.github/ISSUE_TEMPLATE/version_update.md b/.github/ISSUE_TEMPLATE/version_update.md index 5544634bc..efca3f72b 100644 --- a/.github/ISSUE_TEMPLATE/version_update.md +++ b/.github/ISSUE_TEMPLATE/version_update.md @@ -7,11 +7,11 @@ assignees: '' ## Description -After the release of USE version ${{env.OLD_USE_VERSION}} it is tiome to change +After the release of USE version ${{env.OLD_USE_VERSION}} it is time to change the version to a new one. For this, please change the following files: * ./pom.xml * ./use-assembly/pom.xml * ./use-core/pom.xml * ./use-core/src/main/java/org/tzi/use/config/Options.java -* ./use-gui/pom.xml +* ./use-gui/pom.xml \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml index b992b4245..882bb50c6 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -1,6 +1,9 @@ + + + diff --git a/pom.xml b/pom.xml index ce94a5f07..16a851563 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ org.tzi.use use pom - 7.1.1 + 7.5.0 use-assembly use-core diff --git a/use-assembly/pom.xml b/use-assembly/pom.xml index 79f587473..c1f23ac13 100644 --- a/use-assembly/pom.xml +++ b/use-assembly/pom.xml @@ -3,15 +3,23 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - use org.tzi.use - 7.1.1 + use + 7.5.0 4.0.0 pom use-assembly + + + org.tzi.use + use-gui + 7.5.0 + + + diff --git a/use-core/pom.xml b/use-core/pom.xml index dea503d43..d95ead947 100644 --- a/use-core/pom.xml +++ b/use-core/pom.xml @@ -3,13 +3,14 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - use org.tzi.use - 7.1.1 + use + 7.5.0 4.0.0 use-core + jar 21 @@ -20,7 +21,7 @@ com.google.guava guava - [30.0-jre,) + 33.5.0-jre org.eclipse.jdt @@ -55,7 +56,7 @@ com.google.guava guava-testlib - 20.0 + 33.5.0-jre test diff --git a/use-core/src/main/java/org/tzi/use/config/Options.java b/use-core/src/main/java/org/tzi/use/config/Options.java index e6171cab3..9064cdb5e 100644 --- a/use-core/src/main/java/org/tzi/use/config/Options.java +++ b/use-core/src/main/java/org/tzi/use/config/Options.java @@ -42,10 +42,10 @@ public class Options { // the release version - public static final String RELEASE_VERSION = "7.1.1"; + public static final String RELEASE_VERSION = "7.5.0"; // the copyright - public static final String COPYRIGHT = "Copyright (C) 1999-2024 University of Bremen & " + + public static final String COPYRIGHT = "Copyright (C) 1999-2025 University of Bremen & " + "University of Applied Sciences Hamburg"; // the trained support apes diff --git a/use-gui/pom.xml b/use-gui/pom.xml index 43025ae91..046f9540e 100644 --- a/use-gui/pom.xml +++ b/use-gui/pom.xml @@ -5,13 +5,22 @@ use org.tzi.use - 7.1.1 + 7.5.0 4.0.0 use-gui - + + ${project.groupId} + use-core + ${project.version} + + + com.google.guava + guava + 33.5.0-jre + org.openjfx javafx-graphics @@ -98,7 +107,7 @@ com.github.almasb fxgl - 17 + 21.1 org.openjfx @@ -106,13 +115,6 @@ - - - ${project.groupId} - use-core - ${project.version} - compile - com.itextpdf itextpdf @@ -129,10 +131,16 @@ + + com.ximpleware + vtd-xml + 2.13.4 + org.projectlombok lombok - 1.18.38 + ${lombok.version} + provided org.junit.jupiter @@ -169,12 +177,27 @@ UTF-8 5.8.2 + 1.18.42 21 21 + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + + + org.projectlombok + lombok + ${lombok.version} + + + + maven-assembly-plugin diff --git a/use-gui/src/it/java/org/tzi/use/main/shell/ShellIT.java b/use-gui/src/it/java/org/tzi/use/main/shell/ShellIT.java index 3496fa8d1..95f96f193 100644 --- a/use-gui/src/it/java/org/tzi/use/main/shell/ShellIT.java +++ b/use-gui/src/it/java/org/tzi/use/main/shell/ShellIT.java @@ -1,343 +1,306 @@ -//package org.tzi.use.main.shell; -// -//import com.github.difflib.DiffUtils; -//import com.github.difflib.patch.AbstractDelta; -//import com.github.difflib.patch.Patch; -//import org.junit.jupiter.api.DynamicTest; -//import org.junit.jupiter.api.TestFactory; -// -//import java.io.*; -//import java.net.URISyntaxException; -//import java.net.URL; -//import java.nio.charset.StandardCharsets; -//import java.nio.file.Files; -//import java.nio.file.Path; -//import java.util.Collections; -//import java.util.LinkedList; -//import java.util.List; -//import java.util.Optional; -//import java.util.function.Function; -//import java.util.stream.Stream; -// -//import static org.junit.jupiter.api.Assertions.fail; -// -///** -// * This class implements the shell integration tests. -// * -// * These tests represent nearly the exact behavior of the -// * USE shell. Only some whitespace differences and time outputs are ignored. -// * -// * Each test consists of the following files: -// *
    -// *
  1. a model file (suffix .use) - this file provides the (possible empty) model used -// * for the tests
  2. -// *
  3. an input file (suffix: .in) - this file can contain any commands USE supports. -// * The expected output must be specified by starting a line with a star *
  4. -// *
  5. any other used file from the command line, e.g., ASSL- or command-files.
  6. -// *
-// * -// * All .use and .in files must share the same name, e.g., t555.use and t555.in for test -// * case 555. These files must be placed in the folder it/resources/testfiles/shell. -// * -// * If an integration test fails, two additional files are created: -// *
    -// *
  1. {testcasename}.expected - The expected output calculated from the {testcase}.in-file
  2. -// *
  3. {testcasename}.actual - The ouptut captured while running the test.
  4. -// *
-// * These files can be used to easily diff expected and current output. -// */ -//public class ShellIT { -// -// /** -// * This TestFactory enumerates the .in-files in th folder testfiles/shell. -// * For each file a DynamicTest is created with the name of the file. -// * -// * @return A Stream with one DynamicTest for each *.in-file. -// */ -// @TestFactory -// public Stream evaluateExpressionFiles() { -// URL testDirURL = getClass().getClassLoader().getResource("testfiles/shell"); -// Path testDirPath = null; -// -// if (testDirURL == null) { -// fail("Directory for shell integration tests not found!"); -// } -// -// try { -// testDirPath = Path.of(testDirURL.toURI()); -// } catch (URISyntaxException e) { -// fail("Directory for shell integration tests not found!"); -// } -// -// try { -// return Files.walk(testDirPath).filter( -// path -> path.getFileName().toString().endsWith(".in") -// ).map(mapInFileToTest()); -// } catch (IOException e) { -// fail("Error iterating shell integration test input files!"); -// } -// -// return Stream.empty(); -// } -// -// /** -// * This Function is used to map -// * a given testinput-file given as a Path -// * to a DynamicTest. -// * -// * @return A DynamicTest that uses the function assertShellExpression -// * to test the given testinput file. -// */ -// private Function mapInFileToTest() { -// return path -> { -// final String modelFilename = path.getFileName().toString().replace(".in", ".use"); -// final Path modelPath = path.resolveSibling(modelFilename); -// -// return DynamicTest.dynamicTest(path.getFileName().toString(), path.toUri(), () -> assertShellExpressions(path, modelPath)); -// }; -// } -// -// /** -// * This function controls the overall process for test for a single testfile. -// * -// * The process is as follow: -// *
    -// *
  1. a command file and the expected output are created by examining the input file (via createCommandFile.
  2. -// *
  3. USE is executed using the useFile and the created command file (runUSE).
  4. -// *
  5. The output of USE is compared to the expected output created in 1. (validateOutput).
  6. -// *
-// * -// * @param testFile Path to the test input file to execute. -// * @param useFile Path to the USE file containing the model to load for the test. -// */ -// private void assertShellExpressions(Path testFile, Path useFile) { -// -// Path cmdFile = testFile.resolveSibling(testFile.getFileName() + ".cmd"); -// -// List expectedOutput = createCommandFile(testFile, cmdFile); -// -// List actualOutput = runUSE(useFile, cmdFile); -// -// validateOutput(testFile, expectedOutput, actualOutput); -// } -// -// /** -// * Compares the two lists of strings expectedOutput -// * and actualOutput. -// * If they differ, two files are written at the location of the -// * testFile. One with the expected output (.expected) -// * and one with the actual output (.actual). -// * -// * @param testFile The Path to the testFile -// * @param expectedOutput List of strings with the expected output (one String per line) -// * @param actualOutput List of strings with the actual output (one String per line) -// */ -// private void validateOutput(Path testFile, List expectedOutput, List actualOutput) { -// Patch patch = DiffUtils.diff(expectedOutput, actualOutput); -// boolean nonWhitespaceChange = false; -// -// // Check if non whitespace diffs are present -// nonWhitespaceChange = patch.getDeltas().stream().anyMatch( -// (delta) -> delta.getSource().getLines().stream().anyMatch(line -> !line.isBlank()) || -// delta.getTarget().getLines().stream().anyMatch(line -> !line.isBlank()) -// ); -// -// if (nonWhitespaceChange) { -// StringBuilder diffMsg = new StringBuilder("USE output does not match expected output!").append(System.lineSeparator()); -// -// diffMsg.append("Testfile: ").append(testFile).append(System.lineSeparator()); -// -// diffMsg.append(System.lineSeparator()).append("Note: the position is not the position in the input file!"); -// diffMsg.append(System.lineSeparator()).append(System.lineSeparator()); -// -// //simple output the computed patch to console -// for (AbstractDelta delta : patch.getDeltas()) { -// diffMsg.append("Diff [") -// .append(delta.getType()) -// .append("] Source: '") -// .append(delta.getSource().toString()) -// .append("' Target: '" ) -// .append(delta.getTarget().toString()) -// .append("'") -// .append(System.lineSeparator()); -// } -// -// writeToFile(expectedOutput, testFile.getParent().resolve(testFile.getFileName().toString() + ".expected" )); -// writeToFile(actualOutput, testFile.getParent().resolve(testFile.getFileName().toString() + ".actual" )); -// -// fail(diffMsg.toString()); -// } -// } -// -// /** -// * Helper method that writes the list of strings data -// * to the file loctaed by the Path file. -// * -// * If the file is not accessible, i.e., an IOException is thrown, -// * the exceptions is cathed and the test case fails. -// * @param data The List of string (lines) to write. -// * @param file The path to the file to write (file is overwritten). -// */ -// private void writeToFile(List data, Path file) { -// try (FileWriter writer = new FileWriter(file.toFile())) { -// -// for (String line : data) { -// writer.write(line); -// writer.write(System.lineSeparator()); -// } -// } catch (IOException e) { -// fail("Testoutput could not be written!", e); -// } -// } -// -// /** -// * Creates a USE-commandfile at the position located by the path cmdFile. -// * The file contains all commands that are specified in the inFile. -// * The expected output, i.e., lines starting with a * are added to the list expectedOutput. -// * -// * @param inFile The Path to the test input file. -// * @param cmdFile The Path where to create the command file. -// * @return A List which is filled with the expected output of USE. -// */ -// private List createCommandFile(Path inFile, Path cmdFile) { -// List expectedOutput = new LinkedList<>(); -// -// // Build USE command file and build expected output -// try ( -// Stream linesStream = Files.lines(inFile, StandardCharsets.UTF_8); -// FileWriter cmdWriter = new FileWriter(cmdFile.toFile(), StandardCharsets.UTF_8, false) -// ) { -// // USE writes a prompt including the filename -// String prompt = cmdFile.getFileName().toString() + "> "; -// -// linesStream.forEach(inputLine -> { -// if (inputLine.startsWith("*")) { -// // Input line minus prefix(*) is expected output -// expectedOutput.add(inputLine.substring(1).trim()); -// } else if (!inputLine.startsWith("#")) { // Not a comment -// inputLine = inputLine.trim(); -// -// if (inputLine.isEmpty()) { -// return; -// } -// -// try { -// cmdWriter.write(inputLine); -// cmdWriter.write(System.lineSeparator()); -// -// expectedOutput.add((prompt + inputLine).trim()); -// } catch (IOException e1) { -// fail("Could not write USE command file for test!", e1); -// } -// } -// }); -// } catch (IOException e) { -// fail("Could not write USE command file for test!", e); -// } -// -// return expectedOutput; -// } -// -// /** -// * Executes USE with the given useFile as the model -// * and the cmdFile to execute commands. -// * The output is captured from the output and error streams. -// * -// * @param useFile Path to the USE model to load on startup -// * @param cmdFile Path to the commands file to execute -// * -// * @return A List of strings. Each string is one line of output. -// */ -// private List runUSE(Path useFile, Path cmdFile) { -// // Find USE jar -// Optional useJar; -// -// try { -// Path targetDir = useFile.getParent().getParent().getParent().getParent(); -// useJar = Files.walk(targetDir).filter(p -> p.getFileName().toString().matches("use-gui.jar")).findFirst(); -// } catch (IOException e) { -// fail("Could not find USE jar!", e); -// return Collections.emptyList(); -// } -// -// if (useJar.isEmpty()) { -// fail("Could not find USE jar!"); -// } -// -// Path javaHome = Path.of(System.getProperty("java.home")); -// Path javaBinary = javaHome.resolve("bin/java.exe"); -// -// if (!javaBinary.toFile().exists()) { -// javaBinary = javaHome.resolve("bin/java"); -// -// if (!javaBinary.toFile().exists()) { -// fail("Java binary could not be found"); -// } -// } -// -// ProcessBuilder pb = new ProcessBuilder( -// javaBinary.toString(), -// "-Duser.country=US", -// "-Duser.language=en", -// "-jar", -// useJar.get().toString(), -// "-nogui", -// "-nr", -// "-t", -// "-oclAnyCollectionsChecks:E", -// "-extendedTypeSystemChecks:E", -// /* This is currently an unstable workaround -// USE determines the plugin and the extensions to OCL by fixed paths. -// For now, the use-core module contains the directories including the extensions -// and an empty plugins folder. -// The folder is located: use/use-core/target/classes -// Therefore, this is used as the USE home -// */ -// "-H=" + useJar.get().getParent().resolve("../../use-core/target/classes").toString(), -// useFile.getFileName().toString(), -// cmdFile.getFileName().toString()); -// -// pb.redirectErrorStream(true); -// pb.directory(useFile.getParent().toFile()); -// -// // Run a java app in a separate system process -// Process proc = null; -// try { -// proc = pb.start(); -// } catch (IOException e) { -// fail("USE could not be started!", e); -// } -// -// // Then retrieve the process output -// InputStream in = proc.getInputStream(); -// BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); -// String line; -// List actualOutput = new LinkedList<>(); -// boolean firstLine = true; -// -// while(proc.isAlive()) { -// try { -// line = reader.readLine(); -// -// if (firstLine) { -// // USE writes some information at the beginning -// // We ignore this. -// firstLine = false; -// continue; -// } -// } catch (IOException e) { -// e.printStackTrace(); -// break; -// } -// -// if (proc.isAlive()) { -// line = line == null ? "" : line.trim(); -// if (!line.equals("")) { -// actualOutput.add(line); -// } -// } -// } -// -// return actualOutput; -// } -//} \ No newline at end of file +package org.tzi.use.main.shell; + +import com.github.difflib.text.DiffRow; +import com.github.difflib.text.DiffRowGenerator; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; +import org.tzi.use.config.Options; +import org.tzi.use.main.gui.Main; +import org.tzi.use.util.USEWriter; + +import java.io.ByteArrayOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.fail; + +/** + * This class implements the shell integration tests. + * + *

These tests represent nearly the exact behavior of the + * USE shell. Only some whitespace differences and time outputs are ignored.

+ * + *

Each test consists of the following files: + *

    + *
  1. A model file (suffix {@code .use}) - this file provides the (possible empty) model used + * for the tests
  2. + *
  3. An input file (suffix: {@code .in}) - this file can contain any commands USE supports. + * The expected output must be specified by starting a line with a star {@code *}
  4. + *
  5. Any other used file from the command line, e.g., ASSL- or command-files.
  6. + *
+ *

+ * + *

All {@code .use and .in} files must share the same name, e.g., t555.use and t555.in for test + * case 555. These files must be placed in the folder {@code it/resources/testfiles/shell}.

+ * + *

If an integration test fails, two additional files are created: + *

    + *
  1. {@code {testcasename}.expected - The expected output calculated from the {testcase}.in}-file
  2. + *
  3. {@code {testcasename}.actual} - The output captured while running the test.
  4. + *
+ * These files can be used to easily diff expected and current output.

+ */ +public class ShellIT { + + /** + * This TestFactory enumerates the {@code .in
-files in th folder testfiles/shell}. + * For each file a {@code DynamicTest} is created with the name of the file. + * + * @return A {@code Stream with one DynamicTest for each *.in}-file. + */ + @TestFactory + public Stream evaluateExpressionFiles() { + URL testDirURL = getClass().getClassLoader().getResource("testfiles/shell"); + Path testDirPath = null; + + if (testDirURL == null) { + fail("Directory for shell integration tests not found!"); + } + + try { + testDirPath = Path.of(testDirURL.toURI()); + } catch (URISyntaxException e) { + fail("Directory for shell integration tests not found!"); + } + + try { + return Files.walk(testDirPath).filter( + path -> path.getFileName().toString().endsWith(".in") + ).map(mapInFileToTest()); + } catch (IOException e) { + fail("Error iterating shell integration test input files!"); + } + + return Stream.empty(); + } + + /** + * This {@code Function} is used to map + * a given testinput-file given as a {@code Path} + * to a {@code DynamicTest}. + * + * @return A {@code DynamicTest that uses the function assertShellExpression} + * to test the given testinput file. + */ + private Function mapInFileToTest() { + return path -> { + final String modelFilename = path.getFileName().toString().replace(".in", ".use"); + final Path modelPath = path.resolveSibling(modelFilename); + + return DynamicTest.dynamicTest(path.getFileName().toString(), path.toUri(), () -> assertShellExpressions(path, modelPath)); + }; + } + + /** + *

This function controls the overall process for test for a single testfile.

+ * + *

The process is as follows: + *

    + *
  1. A command file and the expected output are created by examining the input file (via {@code createCommandFile}).
  2. + *
  3. USE is executed using the {@code useFile and the created command file (runUSE}).
  4. + *
  5. The output of USE is compared to the expected output created in 1. ({@code validateOutput}).
  6. + *
+ *

+ * + * @param testFile {@code Path} to the test input file to execute. + * @param useFile {@code Path} to the USE file containing the model to load for the test. + */ + private void assertShellExpressions(Path testFile, Path useFile) { + + Path cmdFile = testFile.resolveSibling(testFile.getFileName() + ".cmd"); + + List expectedOutput = createCommandFile(testFile, cmdFile); + + List actualOutput = runUSE(useFile, cmdFile).collect(Collectors.toList()); + + validateOutput(testFile, expectedOutput, actualOutput); + } + + /** + * Compares the two lists of strings {@code expectedOutput} + * and {@code actualOutput}. + * If they differ, two files are written at the location of the + * {@code testFile
. One with the expected output (.expected}) + * and one with the actual output ({@code .actual}). + * + * @param testFile The {@code Path to the testFile} + * @param expectedOutput List of strings with the expected output (one String per line) + * @param actualOutput List of strings with the actual output (one String per line) + */ + private void validateOutput(Path testFile, List expectedOutput, List actualOutput) { + //create a configured DiffRowGenerator + DiffRowGenerator generator = DiffRowGenerator.create() + .showInlineDiffs(true) + .mergeOriginalRevised(true) + .inlineDiffByWord(true) + .ignoreWhiteSpaces(true) + .lineNormalizer( (s) -> s ) // No normalization required + .oldTag((f, start) -> start ? "-\033[9m" : "\033[m-") //introduce style for strikethrough + .newTag((f, start) -> start ? "+\033[97;42m" : "\033[m+") //introduce style for bold + .build(); + + //compute the differences for two test texts. + List rows = generator.generateDiffRows(expectedOutput, actualOutput); + Predicate filter = d -> d.getTag() != DiffRow.Tag.EQUAL; + + if (rows.stream().anyMatch(filter)) { + StringBuilder diffMsg = new StringBuilder("USE output does not match expected output!").append(System.lineSeparator()); + + diffMsg.append("Test file: ").append(testFile).append(System.lineSeparator()); + + diffMsg.append(System.lineSeparator()).append("Note: the position is not the position in the input file!"); + diffMsg.append(System.lineSeparator()).append(System.lineSeparator()); + + rows.stream().filter(filter).forEach( + row ->diffMsg.append(System.lineSeparator()).append(row.getOldLine()) + ); + + writeToFile(expectedOutput, testFile.getParent().resolve(testFile.getFileName().toString() + ".expected")); + writeToFile(actualOutput, testFile.getParent().resolve(testFile.getFileName().toString() + ".actual")); + + fail(diffMsg.toString()); + } + } + + /** + *

Helper method that writes the list of strings {@code data} + * to the file located by the {@code Path} {@code file}.

+ * + *

If the file is not accessible, i.e., an IOException is thrown, + * the exception is caught and the test case fails.

+ * @param data The {@code List} of string (lines) to write. + * @param file The path to the file to write (the file is overwritten). + */ + private void writeToFile(List data, Path file) { + try (FileWriter writer = new FileWriter(file.toFile())) { + + for (String line : data) { + writer.write(line); + writer.write(System.lineSeparator()); + } + } catch (IOException e) { + fail("Test output could not be written!", e); + } + } + + /** + * Creates a USE-command file at the position located by the path {@code cmdFile}. + * The file contains all commands that are specified in the {@code inFile}. + * The expected output, i.e., lines starting with a {@code *} are added to the list {@code expectedOutput}. + * + * @param inFile The {@code Path} to the test input file. + * @param cmdFile The {@code Path} where to create the command file. + * @return A {@code List} which is filled with the expected output of USE. + */ + private List createCommandFile(Path inFile, Path cmdFile) { + List expectedOutput = new LinkedList<>(); + + // Build USE command file and build expected output + try ( + Stream linesStream = Files.lines(inFile, StandardCharsets.UTF_8); + FileWriter cmdWriter = new FileWriter(cmdFile.toFile(), StandardCharsets.UTF_8, false) + ) { + + linesStream.forEach(inputLine -> { + + // Ignore empty lines in expected, since they are also suppressed in the actual output + if (inputLine.isBlank()) + return; + + if ((inputLine.startsWith("*") || inputLine.startsWith("#")) + && inputLine.substring(1).isBlank()) { + return; + } + + if (inputLine.startsWith("*")) { + // Input line minus prefix(*) is expected output + expectedOutput.add(inputLine.substring(1).trim()); + } else if (!inputLine.startsWith("#")) { // Not a comment + try { + cmdWriter.write(inputLine); + cmdWriter.write(System.lineSeparator()); + + // Multi-line commands (backslash and dot) are ignored + if (!inputLine.matches("^[\\\\.]$")) { + expectedOutput.add(inputLine); + } + } catch (IOException e1) { + fail("Could not write USE command file for test!", e1); + } + } + }); + } catch (IOException e) { + fail("Could not write USE command file for test!", e); + } + + return expectedOutput; + } + + /** + * Executes USE with the given {@code useFile} as the model + * and the {@code cmdFile} to execute commands. + * The output is captured from the output and error streams. + * + * @param useFile Path to the USE model to load on startup + * @param cmdFile Path to the commands file to execute + * @return A {@code List} of strings. Each string is one line of output. + */ + private Stream runUSE(Path useFile, Path cmdFile) { + + // We need to specify a concrete locale to always get the same formatted result + Locale.setDefault(Locale.forLanguageTag("en-US")); + + Options.resetOptions(); + USEWriter.getInstance().clearLog(); + + String homeDir; + try { + homeDir = useFile.getParent().resolve("../../../../../use-core/target/classes").toFile().getCanonicalPath(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + String[] args = new String[] { + "-nogui", + "-nr", + "-t", + "-it", + "-q", + "-oclAnyCollectionsChecks:E", + "-extendedTypeSystemChecks:E", + /* This is currently an unstable workaround + USE determines the plugin and the extensions to OCL by fixed paths. + For now, the use-core module contains the directories including the extensions + and an empty plugins folder. + The folder is located: use/use-core/target/classes + Therefore, this is used as the USE home + */ + "-H=" + homeDir, + useFile.toString(), + cmdFile.toString()}; + + Main.main(args); + + try (ByteArrayOutputStream protocol = new ByteArrayOutputStream()) { + USEWriter.getInstance().writeProtocolFile(protocol); + String output = protocol.toString(); + return output.lines().filter(l -> !l.isBlank()); + } catch (IOException e) { + fail(e); + } + + return Stream.empty(); + } +} diff --git a/use-gui/src/main/java/module-info.java b/use-gui/src/main/java/module-info.java index 0846bb894..28a36d0fd 100644 --- a/use-gui/src/main/java/module-info.java +++ b/use-gui/src/main/java/module-info.java @@ -1,15 +1,14 @@ module use.gui { - requires org.eclipse.jdt.annotation; requires use.core; requires vtd.xml; - requires com.google.common; requires itextpdf; requires javafx.fxml; requires javafx.web; requires org.kordamp.desktoppanefx.core; requires javafx.swing; requires annotations; - requires jline; + requires lombok; + requires com.google.common; opens org.tzi.use.gui.main to javafx.fxml; exports org.tzi.use.main.gui; exports org.tzi.use.gui.views; @@ -32,6 +31,4 @@ exports org.tzi.use.gui.views.diagrams.behavior.shared to com.google.common; exports org.tzi.use.gui.views.selection to com.google.common; exports org.tzi.use.gui.views.diagrams.statemachine to com.google.common; - - } \ No newline at end of file diff --git a/use-gui/src/main/java/org/tzi/use/gui/mainFX/MainWindow.java b/use-gui/src/main/java/org/tzi/use/gui/mainFX/MainWindow.java index ed54cfc10..157bd4aee 100644 --- a/use-gui/src/main/java/org/tzi/use/gui/mainFX/MainWindow.java +++ b/use-gui/src/main/java/org/tzi/use/gui/mainFX/MainWindow.java @@ -1418,7 +1418,7 @@ private void createClassDiagram() { // Calling the Swing MainWindow to get the ClassDiagram out of it org.tzi.use.gui.main.MainWindow mainwindow = org.tzi.use.gui.main.MainWindow.create(fSession, fPluginRuntime); - ClassDiagramView cdv = new ClassDiagramView(mainwindow, fSession.system(), loadLayout); + ClassDiagramView cdv = new ClassDiagramView(mainwindow, fSession.system(), loadLayout, fPluginRuntime); ViewFrame f = new ViewFrame("Class diagram", cdv, "ClassDiagram.gif"); JComponent c = (JComponent) f.getContentPane(); diff --git a/use-gui/src/main/java/org/tzi/use/gui/views/diagrams/elements/edges/EdgeBase.java b/use-gui/src/main/java/org/tzi/use/gui/views/diagrams/elements/edges/EdgeBase.java index 983eaaab0..2cd4efd9b 100644 --- a/use-gui/src/main/java/org/tzi/use/gui/views/diagrams/elements/edges/EdgeBase.java +++ b/use-gui/src/main/java/org/tzi/use/gui/views/diagrams/elements/edges/EdgeBase.java @@ -30,7 +30,6 @@ import java.util.Map; import java.util.Set; -import lombok.Getter; import org.tzi.use.graph.DirectedEdgeBase; import org.tzi.use.gui.util.PersistHelper; import org.tzi.use.gui.views.diagrams.DiagramOptions; @@ -155,7 +154,7 @@ public enum PropertyOwner { * @param source The source node of the edge. * @param target The target node of the edge. * @param edgeName The name of the edge. - * @param diagram The diagram this edge belongs to. + * @param opt The diagram options this edge uses to. * @param completeEdgeMoveMovesUserWayPoints If true, user defined way points are moved by the edge if * source and target are selected. */ diff --git a/use-gui/src/main/java/org/tzi/use/main/gui/Main.java b/use-gui/src/main/java/org/tzi/use/main/gui/Main.java index 6ac6ec488..258cbaea0 100644 --- a/use-gui/src/main/java/org/tzi/use/main/gui/Main.java +++ b/use-gui/src/main/java/org/tzi/use/main/gui/Main.java @@ -2,15 +2,21 @@ import org.tzi.use.main.gui.fx.MainJavaFX; import org.tzi.use.main.gui.swing.MainSwing; +import org.tzi.use.util.USEWriter; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { + // set System.out to the USEWriter to protocol the output. + System.setOut(USEWriter.getInstance().getOut()); + // set System.err to the USEWriter to protocol the output. + System.setErr(USEWriter.getInstance().getErr()); + boolean useJavaFX = false; // cleanedArgs is needed to avoid giving -jfx into the MainJavaFX, - // because Application.launch() tries to parse all arguments as JavaFX-compatible + // because Application.launch() tries to parse all arguments as JavaFX compatible // and throws an error if it encounters an unknown one (like -jfx). List cleanedArgs = new ArrayList<>(); for (String arg : args) {