From fd89367ec092b62c8eb4a71a1ddca4fe6821446e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Fuch=C3=9F?= Date: Wed, 5 Jul 2023 23:04:45 +0200 Subject: [PATCH 01/37] Testing local report viewer --- cli/src/main/java/de/jplag/cli/CLI.java | 17 +++- .../main/java/de/jplag/cli/ReportViewer.java | 95 +++++++++++++++++++ cli/src/main/resources/README.md | 1 + cli/src/main/resources/index.html | 9 ++ .../java/de/jplag/cli/ReportViewerTest.java | 29 ++++++ pom.xml | 4 +- 6 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 cli/src/main/java/de/jplag/cli/ReportViewer.java create mode 100644 cli/src/main/resources/README.md create mode 100644 cli/src/main/resources/index.html create mode 100644 cli/src/test/java/de/jplag/cli/ReportViewerTest.java diff --git a/cli/src/main/java/de/jplag/cli/CLI.java b/cli/src/main/java/de/jplag/cli/CLI.java index 7182d3a98..ec5bf478a 100644 --- a/cli/src/main/java/de/jplag/cli/CLI.java +++ b/cli/src/main/java/de/jplag/cli/CLI.java @@ -3,7 +3,10 @@ import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_FOOTER; import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_OPTION_LIST; +import java.awt.*; import java.io.File; +import java.io.IOException; +import java.net.URI; import java.security.SecureRandom; import java.util.HashSet; import java.util.List; @@ -72,8 +75,20 @@ public static void main(String[] args) { JPlagResult result = JPlag.run(options); ReportObjectFactory reportObjectFactory = new ReportObjectFactory(); reportObjectFactory.createAndSaveReport(result, cli.getResultFolder()); + + // ReportViewer + ReportViewer reportViewer = new ReportViewer(); + int port = reportViewer.start(); + logger.info("ReportViewer started on port http://localhost:{}", port); + Desktop.getDesktop().browse(URI.create("http://localhost:" + port + "/")); + + // Wait for input of user + System.out.println("Press any key to exit..."); + System.in.read(); + reportViewer.stop(); + } - } catch (ExitException exception) { + } catch (ExitException | IOException exception) { logger.error(exception.getMessage()); // do not pass exception here to keep log clean finalizeLogger(); System.exit(1); diff --git a/cli/src/main/java/de/jplag/cli/ReportViewer.java b/cli/src/main/java/de/jplag/cli/ReportViewer.java new file mode 100644 index 000000000..77fea9cf4 --- /dev/null +++ b/cli/src/main/java/de/jplag/cli/ReportViewer.java @@ -0,0 +1,95 @@ +package de.jplag.cli; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; + +public class ReportViewer implements HttpHandler { + private static final Logger logger = LoggerFactory.getLogger(ReportViewer.class); + private static final int SUCCESS_RESPONSE = 200; + private static final int METHOD_NOT_ALLOWED_RESPONSE = 405; + private static final int NOT_FOUND_RESPONSE = 404; + private final Map cache = new HashMap<>(); + + private HttpServer server; + + public int start() throws IOException { + if (server != null) { + throw new IllegalStateException("Server already started"); + } + server = HttpServer.create(new InetSocketAddress(0), 0); + server.createContext("/", this); + server.setExecutor(null); + server.start(); + + return server.getAddress().getPort(); + } + + public void stop() { + server.stop(0); + } + + public void handle(HttpExchange exchange) throws IOException { + if (!exchange.getRequestMethod().equals("GET")) { + exchange.sendResponseHeaders(METHOD_NOT_ALLOWED_RESPONSE, 0); + exchange.getResponseBody().close(); + return; + } + + String path = exchange.getRequestURI().getPath(); + path = path.endsWith("/") ? path + "index.html" : path; + + logger.debug("Serving {}", path); + + byte[] data = cache.get(path); + if (data == null) { + InputStream stream = getClass().getResourceAsStream(path); + if (stream != null) { + try (stream) { + data = stream.readAllBytes(); + } + cache.put(path, data); + } + } + + if (data != null) { + String fileEnding = path.substring(path.lastIndexOf('.')); + String contentType = switch (fileEnding) { + case ".html" -> "text/html; charset=utf-8"; + case ".js" -> "application/javascript; charset=utf-8"; + case ".css" -> "text/css; charset=utf-8"; + case ".png" -> "image/png"; + default -> "text/plain; charset=utf-8"; + }; + + Headers responseHeaders = exchange.getResponseHeaders(); + responseHeaders.add("Accept-Ranges", "bytes"); + responseHeaders.add("Content-Type", contentType); + exchange.sendResponseHeaders(SUCCESS_RESPONSE, 0); + + OutputStream outputStream = exchange.getResponseBody(); + ByteArrayInputStream dataStream = new ByteArrayInputStream(data); + dataStream.transferTo(outputStream); + + outputStream.flush(); + outputStream.close(); + dataStream.close(); + return; + } + + exchange.sendResponseHeaders(NOT_FOUND_RESPONSE, 0); + exchange.getResponseBody().close(); + } +} diff --git a/cli/src/main/resources/README.md b/cli/src/main/resources/README.md new file mode 100644 index 000000000..5355d847c --- /dev/null +++ b/cli/src/main/resources/README.md @@ -0,0 +1 @@ +Copy ReportViewer to a directory called `JPlag` \ No newline at end of file diff --git a/cli/src/main/resources/index.html b/cli/src/main/resources/index.html new file mode 100644 index 000000000..fe5144e1c --- /dev/null +++ b/cli/src/main/resources/index.html @@ -0,0 +1,9 @@ + + + JPlag Report Viewer + + + +This page has moved. Moving to /JPlag/ + + \ No newline at end of file diff --git a/cli/src/test/java/de/jplag/cli/ReportViewerTest.java b/cli/src/test/java/de/jplag/cli/ReportViewerTest.java new file mode 100644 index 000000000..b3795b90c --- /dev/null +++ b/cli/src/test/java/de/jplag/cli/ReportViewerTest.java @@ -0,0 +1,29 @@ +package de.jplag.cli; + +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import java.awt.*; +import java.net.URI; +import java.util.concurrent.TimeUnit; + +import javax.swing.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +@Timeout(value = 5, unit = TimeUnit.MINUTES) +class ReportViewerTest { + @Test + void testStartViewer() throws Exception { + assumeTrue(Desktop.isDesktopSupported()); + ReportViewer viewer = new ReportViewer(); + + int port = viewer.start(); + Desktop.getDesktop().browse(URI.create("http://localhost:" + port)); + + // Open Dialog to keep the test running + JOptionPane.showMessageDialog(null, "Press OK to stop the server"); + viewer.stop(); + } + +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 242d8b994..ea37ec29e 100644 --- a/pom.xml +++ b/pom.xml @@ -72,8 +72,8 @@ ${maven.multiModuleProjectDirectory}/coverage-report/target/site/jacoco-aggregate/jacoco.xml - 17 - 17 + 20 + 20 2.37.0 2.0.7 5.9.3 From 739fa34c37672f957fdafcb9041765ee4c42f6d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Fuch=C3=9F?= Date: Sun, 16 Jul 2023 21:55:42 +0000 Subject: [PATCH 02/37] Update to Java JDK 20 --- .github/workflows/maven.yml | 2 +- .github/workflows/publish.yml | 2 +- .github/workflows/sonarcloud-branch.yml | 2 +- .github/workflows/sonarcloud-pr.yml | 2 +- .github/workflows/spotless.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index a9ec43e97..ab5dee4dc 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -31,7 +31,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: - java-version: 17 + java-version: 20 distribution: 'temurin' - name: Run Tests diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ca224e041..8720031c7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: - java-version: '17' + java-version: '20' distribution: 'temurin' - name: Set maven settings.xml uses: whelk-io/maven-settings-xml-action@v21 diff --git a/.github/workflows/sonarcloud-branch.yml b/.github/workflows/sonarcloud-branch.yml index 93c743268..b4e0c08f2 100644 --- a/.github/workflows/sonarcloud-branch.yml +++ b/.github/workflows/sonarcloud-branch.yml @@ -23,7 +23,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'temurin' - java-version: 17 + java-version: 20 - name: Cache SonarCloud packages uses: actions/cache@v3 diff --git a/.github/workflows/sonarcloud-pr.yml b/.github/workflows/sonarcloud-pr.yml index 360b37dd7..8cff8fdde 100644 --- a/.github/workflows/sonarcloud-pr.yml +++ b/.github/workflows/sonarcloud-pr.yml @@ -51,7 +51,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'temurin' - java-version: 17 + java-version: 20 - name: Cache SonarCloud packages uses: actions/cache@v3 diff --git a/.github/workflows/spotless.yml b/.github/workflows/spotless.yml index a920d8faf..ee6cfaea3 100644 --- a/.github/workflows/spotless.yml +++ b/.github/workflows/spotless.yml @@ -31,7 +31,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: - java-version: 17 + java-version: 20 distribution: 'temurin' - name: Check with Spotless From 5d2c8df3d0cae181083c3d41460ce0dfb7f8307f Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Thu, 12 Oct 2023 09:17:28 +0200 Subject: [PATCH 03/37] Implemented routing for internal report viewer. --- cli/pom.xml | 42 ++ cli/src/main/java/de/jplag/cli/CLI.java | 1 + .../main/java/de/jplag/cli/ReportViewer.java | 95 ----- .../java/de/jplag/cli/server/ContentType.java | 14 + .../java/de/jplag/cli/server/HttpMethod.java | 26 ++ .../de/jplag/cli/server/ReportViewer.java | 79 ++++ .../java/de/jplag/cli/server/Routing.java | 15 + .../java/de/jplag/cli/server/RoutingPath.java | 49 +++ .../de/jplag/cli/server/RoutingResources.java | 35 ++ .../java/de/jplag/cli/server/RoutingTree.java | 65 +++ .../java/de/jplag/cli/ReportViewerTest.java | 2 + report-viewer/package-lock.json | 388 +++++++++++++++++- report-viewer/package.json | 2 + 13 files changed, 701 insertions(+), 112 deletions(-) delete mode 100644 cli/src/main/java/de/jplag/cli/ReportViewer.java create mode 100644 cli/src/main/java/de/jplag/cli/server/ContentType.java create mode 100644 cli/src/main/java/de/jplag/cli/server/HttpMethod.java create mode 100644 cli/src/main/java/de/jplag/cli/server/ReportViewer.java create mode 100644 cli/src/main/java/de/jplag/cli/server/Routing.java create mode 100644 cli/src/main/java/de/jplag/cli/server/RoutingPath.java create mode 100644 cli/src/main/java/de/jplag/cli/server/RoutingResources.java create mode 100644 cli/src/main/java/de/jplag/cli/server/RoutingTree.java diff --git a/cli/pom.xml b/cli/pom.xml index b6812a3eb..72ccc2f24 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -125,6 +125,12 @@ + + + report-viewer + ../report-viewer/dist + + maven-jar-plugin @@ -154,6 +160,42 @@ jplag-${project.version} + + org.codehaus.mojo + exec-maven-plugin + 1.3.2 + + + npm install + + exec + + generate-resources + + npm + ../report-viewer + + install + + + + + npm build + + exec + + generate-resources + + npm + ../report-viewer + + run + build + + + + + diff --git a/cli/src/main/java/de/jplag/cli/CLI.java b/cli/src/main/java/de/jplag/cli/CLI.java index 91c90709a..31fd5d45b 100644 --- a/cli/src/main/java/de/jplag/cli/CLI.java +++ b/cli/src/main/java/de/jplag/cli/CLI.java @@ -23,6 +23,7 @@ import de.jplag.JPlagResult; import de.jplag.Language; import de.jplag.cli.logger.CollectedLoggerFactory; +import de.jplag.cli.server.ReportViewer; import de.jplag.clustering.ClusteringOptions; import de.jplag.clustering.Preprocessing; import de.jplag.exceptions.ExitException; diff --git a/cli/src/main/java/de/jplag/cli/ReportViewer.java b/cli/src/main/java/de/jplag/cli/ReportViewer.java deleted file mode 100644 index 77fea9cf4..000000000 --- a/cli/src/main/java/de/jplag/cli/ReportViewer.java +++ /dev/null @@ -1,95 +0,0 @@ -package de.jplag.cli; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.util.HashMap; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; - -public class ReportViewer implements HttpHandler { - private static final Logger logger = LoggerFactory.getLogger(ReportViewer.class); - private static final int SUCCESS_RESPONSE = 200; - private static final int METHOD_NOT_ALLOWED_RESPONSE = 405; - private static final int NOT_FOUND_RESPONSE = 404; - private final Map cache = new HashMap<>(); - - private HttpServer server; - - public int start() throws IOException { - if (server != null) { - throw new IllegalStateException("Server already started"); - } - server = HttpServer.create(new InetSocketAddress(0), 0); - server.createContext("/", this); - server.setExecutor(null); - server.start(); - - return server.getAddress().getPort(); - } - - public void stop() { - server.stop(0); - } - - public void handle(HttpExchange exchange) throws IOException { - if (!exchange.getRequestMethod().equals("GET")) { - exchange.sendResponseHeaders(METHOD_NOT_ALLOWED_RESPONSE, 0); - exchange.getResponseBody().close(); - return; - } - - String path = exchange.getRequestURI().getPath(); - path = path.endsWith("/") ? path + "index.html" : path; - - logger.debug("Serving {}", path); - - byte[] data = cache.get(path); - if (data == null) { - InputStream stream = getClass().getResourceAsStream(path); - if (stream != null) { - try (stream) { - data = stream.readAllBytes(); - } - cache.put(path, data); - } - } - - if (data != null) { - String fileEnding = path.substring(path.lastIndexOf('.')); - String contentType = switch (fileEnding) { - case ".html" -> "text/html; charset=utf-8"; - case ".js" -> "application/javascript; charset=utf-8"; - case ".css" -> "text/css; charset=utf-8"; - case ".png" -> "image/png"; - default -> "text/plain; charset=utf-8"; - }; - - Headers responseHeaders = exchange.getResponseHeaders(); - responseHeaders.add("Accept-Ranges", "bytes"); - responseHeaders.add("Content-Type", contentType); - exchange.sendResponseHeaders(SUCCESS_RESPONSE, 0); - - OutputStream outputStream = exchange.getResponseBody(); - ByteArrayInputStream dataStream = new ByteArrayInputStream(data); - dataStream.transferTo(outputStream); - - outputStream.flush(); - outputStream.close(); - dataStream.close(); - return; - } - - exchange.sendResponseHeaders(NOT_FOUND_RESPONSE, 0); - exchange.getResponseBody().close(); - } -} diff --git a/cli/src/main/java/de/jplag/cli/server/ContentType.java b/cli/src/main/java/de/jplag/cli/server/ContentType.java new file mode 100644 index 000000000..6c36d1170 --- /dev/null +++ b/cli/src/main/java/de/jplag/cli/server/ContentType.java @@ -0,0 +1,14 @@ +package de.jplag.cli.server; + +public enum ContentType { + ; + private final String value; + + ContentType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/cli/src/main/java/de/jplag/cli/server/HttpMethod.java b/cli/src/main/java/de/jplag/cli/server/HttpMethod.java new file mode 100644 index 000000000..2a2565164 --- /dev/null +++ b/cli/src/main/java/de/jplag/cli/server/HttpMethod.java @@ -0,0 +1,26 @@ +package de.jplag.cli.server; + +public enum HttpMethod { + GET("GET"), + POST("POST"); + + private final String name; + + HttpMethod(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static HttpMethod fromName(String name) { + for (HttpMethod value : HttpMethod.values()) { + if (value.name.equals(name)) { + return value; + } + } + + return null; + } +} diff --git a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java new file mode 100644 index 000000000..083380071 --- /dev/null +++ b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java @@ -0,0 +1,79 @@ +package de.jplag.cli.server; + +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; + +public class ReportViewer implements HttpHandler { + private static final Logger logger = LoggerFactory.getLogger(ReportViewer.class); + private static final int SUCCESS_RESPONSE = 200; + private static final int METHOD_NOT_ALLOWED_RESPONSE = 405; + private static final int NOT_FOUND_RESPONSE = 404; + + private final RoutingTree routingTree; + + private HttpServer server; + + public ReportViewer() { + this.routingTree = new RoutingTree(); + + this.routingTree.insertRouting(new RoutingPath(""), new RoutingResources("report-viewer")); + } + + public int start() throws IOException { + if (server != null) { + throw new IllegalStateException("Server already started"); + } + server = HttpServer.create(new InetSocketAddress(0), 0); + server.createContext("/", this); + server.setExecutor(null); + server.start(); + + return server.getAddress().getPort(); + } + + public void stop() { + server.stop(0); + } + + public void handle(HttpExchange exchange) throws IOException { + if (!exchange.getRequestMethod().equals("GET")) { + exchange.sendResponseHeaders(METHOD_NOT_ALLOWED_RESPONSE, 0); + exchange.getResponseBody().close(); + return; + } + + RoutingPath path = new RoutingPath(exchange.getRequestURI().getPath()); + Pair resolved = this.routingTree.resolveRouting(path); + HttpMethod method = HttpMethod.fromName(exchange.getRequestMethod()); + + if (resolved == null || !ArrayUtils.contains(resolved.getRight().allowedMethods(), method)) { + exchange.sendResponseHeaders(NOT_FOUND_RESPONSE, 0); + exchange.close(); + return; + } + + logger.debug("Serving {}", path); + + Pair data = resolved.getRight().fetchData(resolved.getLeft(), exchange, this); + + if (data.getRight() != null) { + exchange.getResponseHeaders().set("Content-Type", data.getRight().getValue()); + } + exchange.sendResponseHeaders(SUCCESS_RESPONSE, 0); + + data.getLeft().transferTo(exchange.getResponseBody()); + exchange.getResponseBody().flush(); + exchange.getResponseBody().close(); + data.getLeft().close(); + } +} diff --git a/cli/src/main/java/de/jplag/cli/server/Routing.java b/cli/src/main/java/de/jplag/cli/server/Routing.java new file mode 100644 index 000000000..1f9f0cb35 --- /dev/null +++ b/cli/src/main/java/de/jplag/cli/server/Routing.java @@ -0,0 +1,15 @@ +package de.jplag.cli.server; + +import java.io.InputStream; + +import org.apache.commons.lang3.tuple.Pair; + +import com.sun.net.httpserver.HttpExchange; + +public interface Routing { + default HttpMethod[] allowedMethods() { + return new HttpMethod[] {HttpMethod.GET}; + } + + Pair fetchData(RoutingPath subPath, HttpExchange request, ReportViewer viewer); +} diff --git a/cli/src/main/java/de/jplag/cli/server/RoutingPath.java b/cli/src/main/java/de/jplag/cli/server/RoutingPath.java new file mode 100644 index 000000000..5ce6933fb --- /dev/null +++ b/cli/src/main/java/de/jplag/cli/server/RoutingPath.java @@ -0,0 +1,49 @@ +package de.jplag.cli.server; + +public class RoutingPath { + private final String[] components; + private final int offset; + + public RoutingPath(String path) { + this.components = path.split("/"); + this.offset = 0; + } + + private RoutingPath(String[] components, int offset) { + this.components = components; + this.offset = offset; + } + + public String head() { + return components[offset]; + } + + public RoutingPath tail() { + if (!hasTail()) { + throw new IllegalStateException("Routing path is done."); + } + + return new RoutingPath(this.components, this.offset + 1); + } + + public boolean hasTail() { + return this.components.length > this.offset; + } + + public boolean isEmpty() { + return this.offset == this.components.length; + } + + public String asPath() { + StringBuilder builder = new StringBuilder(); + + for (int i = this.offset; i < this.components.length; i++) { + if (i > this.offset) { + builder.append("/"); + } + builder.append(this.components[i]); + } + + return builder.toString(); + } +} diff --git a/cli/src/main/java/de/jplag/cli/server/RoutingResources.java b/cli/src/main/java/de/jplag/cli/server/RoutingResources.java new file mode 100644 index 000000000..f4a0ffe9f --- /dev/null +++ b/cli/src/main/java/de/jplag/cli/server/RoutingResources.java @@ -0,0 +1,35 @@ +package de.jplag.cli.server; + +import java.io.InputStream; + +import org.apache.commons.lang3.tuple.Pair; + +import com.sun.net.httpserver.HttpExchange; + +public class RoutingResources implements Routing { + private String prefix; + + public RoutingResources(String prefix) { + this.prefix = prefix; + + if (!this.prefix.startsWith("/")) { + this.prefix = "/" + this.prefix; + } + + if (!this.prefix.endsWith("/")) { + this.prefix = this.prefix + "/"; + } + } + + @Override + public Pair fetchData(RoutingPath subPath, HttpExchange request, ReportViewer viewer) { + String fullPath = this.prefix + subPath.asPath(); + InputStream stream = this.getClass().getResourceAsStream(fullPath); + + if (stream == null) { + return null; + } + + return Pair.of(stream, null); + } +} diff --git a/cli/src/main/java/de/jplag/cli/server/RoutingTree.java b/cli/src/main/java/de/jplag/cli/server/RoutingTree.java new file mode 100644 index 000000000..21da7c5dd --- /dev/null +++ b/cli/src/main/java/de/jplag/cli/server/RoutingTree.java @@ -0,0 +1,65 @@ +package de.jplag.cli.server; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.tuple.Pair; + +public class RoutingTree { + private RoutingTreeNode root; + + public RoutingTree() { + this.root = new RoutingTreeNode(); + } + + public void insertRouting(RoutingPath path, Routing routing) { + this.root.buildRouting(path, routing); + } + + public Pair resolveRouting(RoutingPath path) { + return this.root.resolve(path); + } + + private static class RoutingTreeNode { + private Map children; + private Routing routing; + + public RoutingTreeNode(RoutingPath building, Routing routing) { + this(); + this.buildRouting(building, routing); + } + + public RoutingTreeNode() { + this.children = new HashMap<>(); + } + + public void buildRouting(RoutingPath building, Routing routing) { + if (building.isEmpty()) { + this.routing = routing; + } else { + if (this.children.containsKey(building.head())) { + this.children.get(building.head()).buildRouting(building.tail(), routing); + } else { + this.children.put(building.head(), new RoutingTreeNode(building.tail(), routing)); + } + } + } + + public Pair resolve(RoutingPath path) { + if ((path.isEmpty() || !this.children.containsKey(path.head())) && this.routing != null) { + return Pair.of(path, this.routing); + } + + if (this.children.containsKey(path.head()) && !path.isEmpty()) { + Pair childResolved = this.children.get(path.head()).resolve(path.tail()); + if (childResolved == null && this.routing != null) { + return Pair.of(path, this.routing); + } else { + return childResolved; + } + } + + return null; + } + } +} diff --git a/cli/src/test/java/de/jplag/cli/ReportViewerTest.java b/cli/src/test/java/de/jplag/cli/ReportViewerTest.java index b3795b90c..969696544 100644 --- a/cli/src/test/java/de/jplag/cli/ReportViewerTest.java +++ b/cli/src/test/java/de/jplag/cli/ReportViewerTest.java @@ -11,6 +11,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; +import de.jplag.cli.server.ReportViewer; + @Timeout(value = 5, unit = TimeUnit.MINUTES) class ReportViewerTest { @Test diff --git a/report-viewer/package-lock.json b/report-viewer/package-lock.json index 3cd65c76e..61bfddd30 100644 --- a/report-viewer/package-lock.json +++ b/report-viewer/package-lock.json @@ -12,11 +12,13 @@ "@fortawesome/free-regular-svg-icons": "^6.4.2", "@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/vue-fontawesome": "^3.0.3", + "build": "^0.1.4", "chart.js": "^4.4.0", "chartjs-plugin-datalabels": "^2.2.0", "highlight.js": "^11.8.0", "jszip": "^3.10.0", "pinia": "^2.1.6", + "run": "^1.4.0", "slash": "^5.1.0", "vue": "^3.3.4", "vue-chartjs": "^5.2.0", @@ -83,6 +85,24 @@ "node": ">=6.0.0" } }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@esbuild/android-arm": { "version": "0.18.14", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.14.tgz", @@ -801,6 +821,11 @@ "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", "dev": true }, + "node_modules/@types/triple-beam": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.3.tgz", + "integrity": "sha512-6tOUG+nVHn0cJbVp25JFayS5UE6+xlbcNF9Lo9mU7U0zk3zeUShZied4YEQZjy1JBF043FSkdXw8YkUJuVtB5g==" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.59.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz", @@ -1535,6 +1560,11 @@ "node": "*" } }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1593,8 +1623,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/big-integer": { "version": "1.6.51", @@ -1636,7 +1665,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1686,6 +1714,26 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/build": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/build/-/build-0.1.4.tgz", + "integrity": "sha512-KwbDJ/zrsU8KZRRMfoURG14cKIAStUlS8D5jBDvtrZbwO5FEkYqc3oB8HIhRiyD64A48w1lc+sOmQ+mmBw5U/Q==", + "dependencies": { + "cssmin": "0.3.x", + "jsmin": "1.x", + "jxLoader": "*", + "moo-server": "*", + "promised-io": "*", + "timespan": "2.x", + "uglify-js": "1.x", + "walker": "1.x", + "winston": "*", + "wrench": "1.3.x" + }, + "engines": { + "node": ">v0.4.12" + } + }, "node_modules/bundle-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", @@ -1893,6 +1941,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1908,8 +1965,29 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/colorette": { "version": "2.0.20", @@ -1917,6 +1995,15 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1941,8 +2028,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/config-chain": { "version": "1.1.13", @@ -1985,6 +2071,14 @@ "node": ">=4" } }, + "node_modules/cssmin": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/cssmin/-/cssmin-0.3.2.tgz", + "integrity": "sha512-bynxGIAJ8ybrnFobjsQotIjA8HFDDgPwbeUWNXXXfR+B4f9kkxdcUyagJoQCSUOfMV+ZZ6bMn8bvbozlCzUGwQ==", + "bin": { + "cssmin": "bin/cssmin" + } + }, "node_modules/cssstyle": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", @@ -2251,6 +2345,11 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, "node_modules/entities": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -2747,6 +2846,11 @@ "reusify": "^1.0.4" } }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2806,6 +2910,11 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -3778,6 +3887,17 @@ } } }, + "node_modules/jsmin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/jsmin/-/jsmin-1.0.1.tgz", + "integrity": "sha512-OPuL5X/bFKgVdMvEIX3hnpx3jbVpFCrEM8pKPXjFkZUqg521r41ijdyTz7vACOhW6o1neVlcLyd+wkbK5fNHRg==", + "bin": { + "jsmin": "bin/jsmin" + }, + "engines": { + "node": ">=0.1.93" + } + }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -3813,6 +3933,33 @@ "setimmediate": "^1.0.5" } }, + "node_modules/jxLoader": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jxLoader/-/jxLoader-0.1.1.tgz", + "integrity": "sha512-ClEvAj3K68y8uKhub3RgTmcRPo5DfIWvtxqrKQdDPyZ1UVHIIKvVvjrAsJFSVL5wjv0rt5iH9SMCZ0XRKNzeUA==", + "dependencies": { + "js-yaml": "0.3.x", + "moo-server": "1.3.x", + "promised-io": "*", + "walker": "1.x" + }, + "engines": { + "node": ">v0.4.10" + } + }, + "node_modules/jxLoader/node_modules/js-yaml": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-0.3.7.tgz", + "integrity": "sha512-/7PsVDNP2tVe2Z1cF9kTEkjamIwz4aooDpRKmN1+g/9eePCgcxsv4QDvEbxO0EH+gdDD7MLyDoR6BASo3hH51g==", + "engines": { + "node": "> 0.4.11" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4034,6 +4181,27 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/logform": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", + "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", + "dependencies": { + "@colors/colors": "1.5.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + } + }, + "node_modules/logform/node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/loupe": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", @@ -4054,6 +4222,14 @@ "node": ">=12" } }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dependencies": { + "tmpl": "1.0.5" + } + }, "node_modules/memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", @@ -4128,7 +4304,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4153,11 +4328,18 @@ "ufo": "^1.3.0" } }, + "node_modules/moo-server": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/moo-server/-/moo-server-1.3.0.tgz", + "integrity": "sha512-9A8/eor2DXwpv1+a4pZAAydqLFVrWoKoO1fzdzqLUhYVXAO1Kgd1FR2gFZi7YdHzF0s4W8cDNwCfKJQrvLqxDw==", + "engines": { + "node": ">v0.4.10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/muggle-string": { "version": "0.3.1", @@ -4542,6 +4724,14 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/onetime": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", @@ -5128,6 +5318,11 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "node_modules/promised-io": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/promised-io/-/promised-io-0.3.6.tgz", + "integrity": "sha512-bNwZusuNIW4m0SPR8jooSyndD35ggirHlxVl/UhIaZD/F0OBv9ebfc6tNmbpZts3QXHggkjIBH8lvtnzhtcz0A==" + }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -5413,6 +5608,20 @@ "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", "dev": true }, + "node_modules/run": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/run/-/run-1.4.0.tgz", + "integrity": "sha512-962oBW07IjQ9SizyMHdoteVbDKt/e2nEsnTRZ0WjK/zs+jfQQICqH0qj0D5lqZNuy0JkbzfA6IOqw0Sk7C3DlQ==", + "dependencies": { + "minimatch": "*" + }, + "bin": { + "runjs": "cli.js" + }, + "engines": { + "node": ">=v0.9.0" + } + }, "node_modules/run-applescript": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", @@ -5559,6 +5768,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -5671,6 +5888,19 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", @@ -5710,12 +5940,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/sortablejs": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz", - "integrity": "sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==", - "peer": true - }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -5756,6 +5980,14 @@ "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", "dev": true }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -6088,6 +6320,11 @@ "node": ">=14.0.0" } }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -6115,6 +6352,14 @@ "node": ">=0.8" } }, + "node_modules/timespan": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/timespan/-/timespan-2.3.0.tgz", + "integrity": "sha512-0Jq9+58T2wbOyLth0EU+AUb6JMGCLaTWIykJFa7hyAybjVH9gpVMTfUAwo5fWAvtFt2Tjh/Elg8JtgNpnMnM8g==", + "engines": { + "node": ">= 0.2.0" + } + }, "node_modules/tinybench": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.0.tgz", @@ -6151,6 +6396,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6190,6 +6440,14 @@ "node": ">=14" } }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -6268,7 +6526,7 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "devOptional": true, + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6283,6 +6541,14 @@ "integrity": "sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==", "dev": true }, + "node_modules/uglify-js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-1.3.5.tgz", + "integrity": "sha512-YPX1DjKtom8l9XslmPFQnqWzTBkvI4N0pbkzLuPZZ4QTyig0uQqvZz9NgUdfEV+qccJzi7fVcGWdESvRIjWptQ==", + "bin": { + "uglifyjs": "bin/uglifyjs" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -6699,6 +6965,14 @@ "node": ">=14" } }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dependencies": { + "makeerror": "1.0.12" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -6809,6 +7083,77 @@ "node": ">=8" } }, + "node_modules/winston": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz", + "integrity": "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", + "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 6.4.0" + } + }, + "node_modules/winston-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/winston/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/winston/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -6871,6 +7216,15 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/wrench": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/wrench/-/wrench-1.3.9.tgz", + "integrity": "sha512-srTJQmLTP5YtW+F5zDuqjMEZqLLr/eJOZfDI5ibfPfRMeDh3oBUefAscuH0q5wBKE339ptH/S/0D18ZkfOfmKQ==", + "deprecated": "wrench.js is deprecated! You should check out fs-extra (https://github.com/jprichardson/node-fs-extra) for any operations you were using wrench for. Thanks for all the usage over the years.", + "engines": { + "node": ">=0.1.97" + } + }, "node_modules/ws": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", diff --git a/report-viewer/package.json b/report-viewer/package.json index b55301372..d386ae07a 100644 --- a/report-viewer/package.json +++ b/report-viewer/package.json @@ -21,11 +21,13 @@ "@fortawesome/free-regular-svg-icons": "^6.4.2", "@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/vue-fontawesome": "^3.0.3", + "build": "^0.1.4", "chart.js": "^4.4.0", "chartjs-plugin-datalabels": "^2.2.0", "highlight.js": "^11.8.0", "jszip": "^3.10.0", "pinia": "^2.1.6", + "run": "^1.4.0", "slash": "^5.1.0", "vue": "^3.3.4", "vue-chartjs": "^5.2.0", From 7e2d1bda38c61f9e44857086f33caa1e209e957b Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Thu, 12 Oct 2023 13:46:28 +0200 Subject: [PATCH 04/37] Implemented server side for local mode. --- cli/pom.xml | 6 +++ cli/src/main/java/de/jplag/cli/CLI.java | 44 ++++++++++++------- .../main/java/de/jplag/cli/CliOptions.java | 3 ++ cli/src/main/java/de/jplag/cli/JPlagMode.java | 7 +++ .../java/de/jplag/cli/server/ContentType.java | 19 +++++++- .../de/jplag/cli/server/ReportViewer.java | 30 ++++++------- .../de/jplag/cli/server/ResponseData.java | 34 ++++++++++++++ .../java/de/jplag/cli/server/Routing.java | 10 ++--- .../de/jplag/cli/server/RoutingFallback.java | 23 ++++++++++ .../java/de/jplag/cli/server/RoutingPath.java | 4 +- .../de/jplag/cli/server/RoutingRedirect.java | 31 +++++++++++++ .../de/jplag/cli/server/RoutingResources.java | 14 +----- .../jplag/cli/server/RoutingStaticFile.java | 35 +++++++++++++++ .../java/de/jplag/cli/server/RoutingTree.java | 8 +++- .../java/de/jplag/cli/ReportViewerTest.java | 4 +- .../reportobject/ReportObjectFactory.java | 5 ++- .../endtoend/model/ResultDescription.java | 4 +- 17 files changed, 224 insertions(+), 57 deletions(-) create mode 100644 cli/src/main/java/de/jplag/cli/JPlagMode.java create mode 100644 cli/src/main/java/de/jplag/cli/server/ResponseData.java create mode 100644 cli/src/main/java/de/jplag/cli/server/RoutingFallback.java create mode 100644 cli/src/main/java/de/jplag/cli/server/RoutingRedirect.java create mode 100644 cli/src/main/java/de/jplag/cli/server/RoutingStaticFile.java diff --git a/cli/pom.xml b/cli/pom.xml index 72ccc2f24..e2d2a0444 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -123,6 +123,12 @@ picocli 4.7.5 + + + org.jline + jline + 3.23.0 + diff --git a/cli/src/main/java/de/jplag/cli/CLI.java b/cli/src/main/java/de/jplag/cli/CLI.java index 31fd5d45b..07cab8114 100644 --- a/cli/src/main/java/de/jplag/cli/CLI.java +++ b/cli/src/main/java/de/jplag/cli/CLI.java @@ -15,6 +15,8 @@ import java.util.Set; import java.util.stream.Collectors; +import org.jline.terminal.Terminal; +import org.jline.terminal.TerminalBuilder; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -78,22 +80,11 @@ public static void main(String[] args) { ParseResult parseResult = cli.parseOptions(args); if (!parseResult.isUsageHelpRequested() && !(parseResult.subcommand() != null && parseResult.subcommand().isUsageHelpRequested())) { - JPlagOptions options = cli.buildOptionsFromArguments(parseResult); - JPlagResult result = JPlag.run(options); - ReportObjectFactory reportObjectFactory = new ReportObjectFactory(); - reportObjectFactory.createAndSaveReport(result, cli.getResultFolder()); - - // ReportViewer - ReportViewer reportViewer = new ReportViewer(); - int port = reportViewer.start(); - logger.info("ReportViewer started on port http://localhost:{}", port); - Desktop.getDesktop().browse(URI.create("http://localhost:" + port + "/")); - - // Wait for input of user - System.out.println("Press any key to exit..."); - System.in.read(); - reportViewer.stop(); - + switch (cli.options.mode) { + case RUN -> cli.runJPlag(parseResult); + case VIEWER -> cli.runViewer(null); + case RUN_AND_VIEW -> cli.runViewer(cli.runJPlag(parseResult)); + } } } catch (ExitException | IOException exception) { logger.error(exception.getMessage()); // do not pass exception here to keep log clean @@ -126,6 +117,27 @@ public CLI() { this.commandLine.setAllowSubcommandsAsOptionParameters(true); } + public File runJPlag(ParseResult parseResult) throws ExitException { + JPlagOptions options = buildOptionsFromArguments(parseResult); + JPlagResult result = JPlag.run(options); + ReportObjectFactory reportObjectFactory = new ReportObjectFactory(); + return reportObjectFactory.createAndSaveReport(result, getResultFolder()); + } + + public void runViewer(File zipFile) throws IOException { + ReportViewer reportViewer = new ReportViewer(zipFile); + int port = reportViewer.start(); + logger.info("ReportViewer started on port http://localhost:{}", port); + Desktop.getDesktop().browse(URI.create("http://localhost:" + port + "/")); + + Terminal terminal = TerminalBuilder.terminal(); + terminal.writer().print("Press any key to exit..."); + terminal.writer().flush(); + terminal.enterRawMode(); + terminal.reader().read(); + reportViewer.stop(); + } + private List buildSubcommands() { return LanguageLoader.getAllAvailableLanguages().values().stream().map(language -> { CommandSpec command = CommandSpec.create().name(language.getIdentifier()); diff --git a/cli/src/main/java/de/jplag/cli/CliOptions.java b/cli/src/main/java/de/jplag/cli/CliOptions.java index c55e71f3b..931714a9c 100644 --- a/cli/src/main/java/de/jplag/cli/CliOptions.java +++ b/cli/src/main/java/de/jplag/cli/CliOptions.java @@ -53,6 +53,9 @@ public class CliOptions implements Runnable { "--result-directory"}, description = "Name of the directory in which the comparison results will be stored (default: result)%n") public String resultFolder = "results"; + @Option(names = {"--mode"}, description = "The mode to run jplag in") + public JPlagMode mode = JPlagMode.RUN_AND_VIEW; + @ArgGroup(heading = "Advanced%n", exclusive = false) public Advanced advanced = new Advanced(); diff --git a/cli/src/main/java/de/jplag/cli/JPlagMode.java b/cli/src/main/java/de/jplag/cli/JPlagMode.java new file mode 100644 index 000000000..e660fa7d9 --- /dev/null +++ b/cli/src/main/java/de/jplag/cli/JPlagMode.java @@ -0,0 +1,7 @@ +package de.jplag.cli; + +public enum JPlagMode { + RUN, + VIEWER, + RUN_AND_VIEW +} diff --git a/cli/src/main/java/de/jplag/cli/server/ContentType.java b/cli/src/main/java/de/jplag/cli/server/ContentType.java index 6c36d1170..c47d07944 100644 --- a/cli/src/main/java/de/jplag/cli/server/ContentType.java +++ b/cli/src/main/java/de/jplag/cli/server/ContentType.java @@ -1,7 +1,13 @@ package de.jplag.cli.server; public enum ContentType { - ; + HTML("text/html; charset=utf-8"), + JS("application/javascript; charset=utf-8"), + CSS("text/css; charset=utf-8"), + PNG("image/png"), + PLAIN("text/plain; charset=utf-8"), + ZIP("application/zip"); + private final String value; ContentType(String value) { @@ -11,4 +17,15 @@ public enum ContentType { public String getValue() { return value; } + + public static ContentType fromPath(String path) { + return switch (path.substring(path.lastIndexOf('.'))) { + case ".html" -> ContentType.HTML; + case ".js" -> ContentType.JS; + case ".css" -> ContentType.CSS; + case ".png" -> ContentType.PNG; + case ".zip" -> ContentType.ZIP; + default -> ContentType.PLAIN; + }; + } } diff --git a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java index 083380071..d16892ab0 100644 --- a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java +++ b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java @@ -1,5 +1,6 @@ package de.jplag.cli.server; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.InetSocketAddress; @@ -16,17 +17,17 @@ public class ReportViewer implements HttpHandler { private static final Logger logger = LoggerFactory.getLogger(ReportViewer.class); private static final int SUCCESS_RESPONSE = 200; - private static final int METHOD_NOT_ALLOWED_RESPONSE = 405; private static final int NOT_FOUND_RESPONSE = 404; private final RoutingTree routingTree; private HttpServer server; - public ReportViewer() { + public ReportViewer(File zipFile) throws IOException { this.routingTree = new RoutingTree(); - this.routingTree.insertRouting(new RoutingPath(""), new RoutingResources("report-viewer")); + this.routingTree.insertRouting("", new RoutingResources("report-viewer").or(new RoutingRedirect("index.html"))); + this.routingTree.insertRouting("result.zip", new RoutingStaticFile(zipFile, ContentType.ZIP)); } public int start() throws IOException { @@ -46,12 +47,6 @@ public void stop() { } public void handle(HttpExchange exchange) throws IOException { - if (!exchange.getRequestMethod().equals("GET")) { - exchange.sendResponseHeaders(METHOD_NOT_ALLOWED_RESPONSE, 0); - exchange.getResponseBody().close(); - return; - } - RoutingPath path = new RoutingPath(exchange.getRequestURI().getPath()); Pair resolved = this.routingTree.resolveRouting(path); HttpMethod method = HttpMethod.fromName(exchange.getRequestMethod()); @@ -64,16 +59,21 @@ public void handle(HttpExchange exchange) throws IOException { logger.debug("Serving {}", path); - Pair data = resolved.getRight().fetchData(resolved.getLeft(), exchange, this); + ResponseData responseData = resolved.getRight().fetchData(resolved.getLeft(), exchange, this); + InputStream inputStream = responseData.stream(); - if (data.getRight() != null) { - exchange.getResponseHeaders().set("Content-Type", data.getRight().getValue()); + if (responseData.contentType() != null) { + exchange.getResponseHeaders().set("Content-Type", responseData.contentType().getValue()); } - exchange.sendResponseHeaders(SUCCESS_RESPONSE, 0); + exchange.sendResponseHeaders(SUCCESS_RESPONSE, responseData.size()); - data.getLeft().transferTo(exchange.getResponseBody()); + inputStream.transferTo(exchange.getResponseBody()); exchange.getResponseBody().flush(); exchange.getResponseBody().close(); - data.getLeft().close(); + inputStream.close(); + } + + public RoutingTree getRoutingTree() { + return routingTree; } } diff --git a/cli/src/main/java/de/jplag/cli/server/ResponseData.java b/cli/src/main/java/de/jplag/cli/server/ResponseData.java new file mode 100644 index 000000000..d480c7b5a --- /dev/null +++ b/cli/src/main/java/de/jplag/cli/server/ResponseData.java @@ -0,0 +1,34 @@ +package de.jplag.cli.server; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; + +public record ResponseData(InputStream stream, ContentType contentType, int size) { + public ResponseData(InputStream data) { + this(data, ContentType.PLAIN, 0); + } + + public ResponseData(InputStream data, ContentType contentType) { + this(data, contentType, 0); + } + + public ResponseData(File file) throws FileNotFoundException { + this(new FileInputStream(file), ContentType.fromPath(file.getName()), 0); + } + + public static ResponseData fromResourceUrl(String url) { + if (url.endsWith("/")) { + return null; + } + + InputStream inputStream = ResponseData.class.getResourceAsStream(url); + + if (inputStream != null) { + return new ResponseData(inputStream, ContentType.fromPath(url)); + } else { + return null; + } + } +} diff --git a/cli/src/main/java/de/jplag/cli/server/Routing.java b/cli/src/main/java/de/jplag/cli/server/Routing.java index 1f9f0cb35..4894ffdda 100644 --- a/cli/src/main/java/de/jplag/cli/server/Routing.java +++ b/cli/src/main/java/de/jplag/cli/server/Routing.java @@ -1,9 +1,5 @@ package de.jplag.cli.server; -import java.io.InputStream; - -import org.apache.commons.lang3.tuple.Pair; - import com.sun.net.httpserver.HttpExchange; public interface Routing { @@ -11,5 +7,9 @@ default HttpMethod[] allowedMethods() { return new HttpMethod[] {HttpMethod.GET}; } - Pair fetchData(RoutingPath subPath, HttpExchange request, ReportViewer viewer); + ResponseData fetchData(RoutingPath subPath, HttpExchange request, ReportViewer viewer); + + default Routing or(Routing other) { + return new RoutingFallback(this, other); + } } diff --git a/cli/src/main/java/de/jplag/cli/server/RoutingFallback.java b/cli/src/main/java/de/jplag/cli/server/RoutingFallback.java new file mode 100644 index 000000000..93ecc09fe --- /dev/null +++ b/cli/src/main/java/de/jplag/cli/server/RoutingFallback.java @@ -0,0 +1,23 @@ +package de.jplag.cli.server; + +import com.sun.net.httpserver.HttpExchange; + +public class RoutingFallback implements Routing { + private final Routing first; + private final Routing second; + + public RoutingFallback(Routing first, Routing second) { + this.first = first; + this.second = second; + } + + @Override + public ResponseData fetchData(RoutingPath subPath, HttpExchange request, ReportViewer viewer) { + ResponseData attempt = this.first.fetchData(subPath, request, viewer); + if (attempt != null) { + return attempt; + } + + return this.second.fetchData(subPath, request, viewer); + } +} diff --git a/cli/src/main/java/de/jplag/cli/server/RoutingPath.java b/cli/src/main/java/de/jplag/cli/server/RoutingPath.java index 5ce6933fb..9f79f6ffb 100644 --- a/cli/src/main/java/de/jplag/cli/server/RoutingPath.java +++ b/cli/src/main/java/de/jplag/cli/server/RoutingPath.java @@ -1,11 +1,13 @@ package de.jplag.cli.server; +import java.util.Arrays; + public class RoutingPath { private final String[] components; private final int offset; public RoutingPath(String path) { - this.components = path.split("/"); + this.components = Arrays.stream(path.split("/", 0)).filter(it -> !it.isBlank()).toArray(String[]::new); this.offset = 0; } diff --git a/cli/src/main/java/de/jplag/cli/server/RoutingRedirect.java b/cli/src/main/java/de/jplag/cli/server/RoutingRedirect.java new file mode 100644 index 000000000..0866c165a --- /dev/null +++ b/cli/src/main/java/de/jplag/cli/server/RoutingRedirect.java @@ -0,0 +1,31 @@ +package de.jplag.cli.server; + +import org.apache.commons.lang3.tuple.Pair; + +import com.sun.net.httpserver.HttpExchange; + +public class RoutingRedirect implements Routing { + private final RoutingPath path; + + public RoutingRedirect(RoutingPath path) { + this.path = path; + } + + public RoutingRedirect(String path) { + this(new RoutingPath(path)); + } + + @Override + public ResponseData fetchData(RoutingPath subPath, HttpExchange request, ReportViewer viewer) { + if (subPath.hasTail()) { + return null; + } + + Pair redirect = viewer.getRoutingTree().resolveRouting(path); + if (redirect == null) { + return null; + } + + return redirect.getValue().fetchData(redirect.getLeft(), request, viewer); + } +} diff --git a/cli/src/main/java/de/jplag/cli/server/RoutingResources.java b/cli/src/main/java/de/jplag/cli/server/RoutingResources.java index f4a0ffe9f..421b14249 100644 --- a/cli/src/main/java/de/jplag/cli/server/RoutingResources.java +++ b/cli/src/main/java/de/jplag/cli/server/RoutingResources.java @@ -1,9 +1,5 @@ package de.jplag.cli.server; -import java.io.InputStream; - -import org.apache.commons.lang3.tuple.Pair; - import com.sun.net.httpserver.HttpExchange; public class RoutingResources implements Routing { @@ -22,14 +18,8 @@ public RoutingResources(String prefix) { } @Override - public Pair fetchData(RoutingPath subPath, HttpExchange request, ReportViewer viewer) { + public ResponseData fetchData(RoutingPath subPath, HttpExchange request, ReportViewer viewer) { String fullPath = this.prefix + subPath.asPath(); - InputStream stream = this.getClass().getResourceAsStream(fullPath); - - if (stream == null) { - return null; - } - - return Pair.of(stream, null); + return ResponseData.fromResourceUrl(fullPath); } } diff --git a/cli/src/main/java/de/jplag/cli/server/RoutingStaticFile.java b/cli/src/main/java/de/jplag/cli/server/RoutingStaticFile.java new file mode 100644 index 000000000..a8131e70e --- /dev/null +++ b/cli/src/main/java/de/jplag/cli/server/RoutingStaticFile.java @@ -0,0 +1,35 @@ +package de.jplag.cli.server; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import com.sun.net.httpserver.HttpExchange; + +public class RoutingStaticFile implements Routing { + private final byte[] data; + private final ContentType contentType; + + public RoutingStaticFile(File file, ContentType contentType) throws IOException { + if (file != null) { + try (FileInputStream inputStream = new FileInputStream(file)) { + this.data = inputStream.readAllBytes(); + + this.contentType = contentType; + } + } else { + this.data = null; + this.contentType = contentType; + } + } + + @Override + public ResponseData fetchData(RoutingPath subPath, HttpExchange request, ReportViewer viewer) { + if (this.data != null) { + return new ResponseData(new ByteArrayInputStream(this.data), contentType, this.data.length); + } else { + return null; + } + } +} diff --git a/cli/src/main/java/de/jplag/cli/server/RoutingTree.java b/cli/src/main/java/de/jplag/cli/server/RoutingTree.java index 21da7c5dd..87cc4ac9e 100644 --- a/cli/src/main/java/de/jplag/cli/server/RoutingTree.java +++ b/cli/src/main/java/de/jplag/cli/server/RoutingTree.java @@ -6,7 +6,7 @@ import org.apache.commons.lang3.tuple.Pair; public class RoutingTree { - private RoutingTreeNode root; + private final RoutingTreeNode root; public RoutingTree() { this.root = new RoutingTreeNode(); @@ -16,12 +16,16 @@ public void insertRouting(RoutingPath path, Routing routing) { this.root.buildRouting(path, routing); } + public void insertRouting(String path, Routing routing) { + this.insertRouting(new RoutingPath(path), routing); + } + public Pair resolveRouting(RoutingPath path) { return this.root.resolve(path); } private static class RoutingTreeNode { - private Map children; + private final Map children; private Routing routing; public RoutingTreeNode(RoutingPath building, Routing routing) { diff --git a/cli/src/test/java/de/jplag/cli/ReportViewerTest.java b/cli/src/test/java/de/jplag/cli/ReportViewerTest.java index 969696544..051b87336 100644 --- a/cli/src/test/java/de/jplag/cli/ReportViewerTest.java +++ b/cli/src/test/java/de/jplag/cli/ReportViewerTest.java @@ -8,6 +8,7 @@ import javax.swing.*; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -16,9 +17,10 @@ @Timeout(value = 5, unit = TimeUnit.MINUTES) class ReportViewerTest { @Test + @Disabled void testStartViewer() throws Exception { assumeTrue(Desktop.isDesktopSupported()); - ReportViewer viewer = new ReportViewer(); + ReportViewer viewer = new ReportViewer(null); int port = viewer.start(); Desktop.getDesktop().browse(URI.create("http://localhost:" + port)); diff --git a/core/src/main/java/de/jplag/reporting/reportobject/ReportObjectFactory.java b/core/src/main/java/de/jplag/reporting/reportobject/ReportObjectFactory.java index 0f8f142da..89817fc1a 100644 --- a/core/src/main/java/de/jplag/reporting/reportobject/ReportObjectFactory.java +++ b/core/src/main/java/de/jplag/reporting/reportobject/ReportObjectFactory.java @@ -61,8 +61,7 @@ public class ReportObjectFactory { * @param result The JPlagResult to be converted into a report. * @param path The Path to save the report to */ - public void createAndSaveReport(JPlagResult result, String path) { - + public File createAndSaveReport(JPlagResult result, String path) { try { logger.info("Start writing report files..."); createDirectory(path); @@ -76,10 +75,12 @@ public void createAndSaveReport(JPlagResult result, String path) { logger.info("Zipping report files..."); zipAndDelete(path); + return new File(path + ".zip"); } catch (IOException e) { logger.error(DIRECTORY_ERROR, e, path); } + return null; } private void zipAndDelete(String path) { diff --git a/endtoend-testing/src/main/java/de/jplag/endtoend/model/ResultDescription.java b/endtoend-testing/src/main/java/de/jplag/endtoend/model/ResultDescription.java index e5935f9f4..1a4231a1e 100644 --- a/endtoend-testing/src/main/java/de/jplag/endtoend/model/ResultDescription.java +++ b/endtoend-testing/src/main/java/de/jplag/endtoend/model/ResultDescription.java @@ -7,8 +7,8 @@ /** * Object that maps the results of the end top end tests using the identifierToResultMap. this creates a map of test - * data and its results for each possible option specified. this is important both for serializing the data into json - * format and for deserialization. + * stream and its results for each possible option specified. this is important both for serializing the stream into + * json format and for deserialization. */ public record ResultDescription(@JsonIgnore String languageIdentifier, @JsonProperty("options") Options options, @JsonProperty("tests") Map identifierToResultMap) { From e27f6323dca7315c86ac10ae8ce99694e0df986d Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Thu, 12 Oct 2023 13:53:37 +0200 Subject: [PATCH 05/37] Fixed 404 response codes for local mode. --- cli/src/main/java/de/jplag/cli/server/ReportViewer.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java index d16892ab0..2940ab230 100644 --- a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java +++ b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java @@ -60,6 +60,12 @@ public void handle(HttpExchange exchange) throws IOException { logger.debug("Serving {}", path); ResponseData responseData = resolved.getRight().fetchData(resolved.getLeft(), exchange, this); + if (responseData == null) { + exchange.sendResponseHeaders(NOT_FOUND_RESPONSE, 0); + exchange.close(); + return; + } + InputStream inputStream = responseData.stream(); if (responseData.contentType() != null) { From 3085de071fed28b27fc41240cdd9e138c58fe7f0 Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Thu, 12 Oct 2023 15:51:29 +0200 Subject: [PATCH 06/37] frontend logic for local mode --- .../de/jplag/cli/server/ReportViewer.java | 2 +- report-viewer/package-lock.json | 8 +++- .../src/model/factories/BaseFactory.ts | 18 ++++++--- report-viewer/src/stores/state.ts | 9 ----- report-viewer/src/stores/store.ts | 10 ++--- report-viewer/src/views/FileUploadView.vue | 40 +++++++------------ 6 files changed, 39 insertions(+), 48 deletions(-) diff --git a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java index 2940ab230..ab40cf503 100644 --- a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java +++ b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java @@ -27,7 +27,7 @@ public ReportViewer(File zipFile) throws IOException { this.routingTree = new RoutingTree(); this.routingTree.insertRouting("", new RoutingResources("report-viewer").or(new RoutingRedirect("index.html"))); - this.routingTree.insertRouting("result.zip", new RoutingStaticFile(zipFile, ContentType.ZIP)); + this.routingTree.insertRouting("results.zip", new RoutingStaticFile(zipFile, ContentType.ZIP)); } public int start() throws IOException { diff --git a/report-viewer/package-lock.json b/report-viewer/package-lock.json index 61bfddd30..5bd281bd5 100644 --- a/report-viewer/package-lock.json +++ b/report-viewer/package-lock.json @@ -5940,6 +5940,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/sortablejs": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz", + "integrity": "sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==", + "peer": true + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -6526,7 +6532,7 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/report-viewer/src/model/factories/BaseFactory.ts b/report-viewer/src/model/factories/BaseFactory.ts index ec7fe0dcd..f0a289312 100644 --- a/report-viewer/src/model/factories/BaseFactory.ts +++ b/report-viewer/src/model/factories/BaseFactory.ts @@ -5,6 +5,8 @@ import { ZipFileHandler } from '@/utils/fileHandling/ZipFileHandler' * This class provides some basic functionality for the factories. */ export class BaseFactory { + private static zipFileName = 'results.zip' + /** * Returns the content of a file through the stored loading type. * @param path - Path to the file @@ -13,16 +15,15 @@ export class BaseFactory { */ protected static async getFile(path: string): Promise { if (store().state.localModeUsed) { - if (store().state.zipModeUsed) { - await new ZipFileHandler().handleFile(await this.getLocalFile('results.zip')) - return this.getFileFromStore(path) - } else { - return await (await this.getLocalFile(`/files/${path}`)).text() - } + return await (await this.getLocalFile(`/files/${path}`)).text() } else if (store().state.zipModeUsed) { return this.getFileFromStore(path) } else if (store().state.singleModeUsed) { return store().state.singleFillRawContent + } else if (await this.useLocalZipMode()) { + await new ZipFileHandler().handleFile(await this.getLocalFile(this.zipFileName)) + store().setLoadingType('zip') + return this.getFileFromStore(path) } throw new Error('No loading type specified') } @@ -53,4 +54,9 @@ export class BaseFactory { throw new Error(`Could not find ${path} in local files.`) } } + + public static async useLocalZipMode() { + const response = await fetch(window.location.origin + '/' + this.zipFileName) + return response.status != 404 + } } diff --git a/report-viewer/src/stores/state.ts b/report-viewer/src/stores/state.ts index 9eb9d80db..dad2578b7 100644 --- a/report-viewer/src/stores/state.ts +++ b/report-viewer/src/stores/state.ts @@ -58,15 +58,6 @@ export interface SubmissionFile extends File { submissionId: string } -/** - * Load configuration is used to indicate which mode is used. - */ -export interface LoadConfiguration { - local: boolean - zip: boolean - single: boolean -} - export interface UIState { useDarkMode: boolean comparisonTableSortingMetric: MetricType diff --git a/report-viewer/src/stores/store.ts b/report-viewer/src/stores/store.ts index 08f2ad702..0578725aa 100644 --- a/report-viewer/src/stores/store.ts +++ b/report-viewer/src/stores/store.ts @@ -1,5 +1,5 @@ import { defineStore } from 'pinia' -import type { State, SubmissionFile, File, LoadConfiguration, UIState } from './state' +import type { State, SubmissionFile, File, UIState } from './state' import { MetricType } from '@/model/MetricType' /** @@ -158,10 +158,10 @@ const store = defineStore('store', { * Sets the loading type * @param payload Type used to input JPlag results */ - setLoadingType(payload: LoadConfiguration) { - this.state.localModeUsed = payload.local - this.state.zipModeUsed = payload.zip - this.state.singleModeUsed = payload.single + setLoadingType(loadingType: 'zip' | 'local' | 'single') { + this.state.localModeUsed = loadingType == 'local' + this.state.zipModeUsed = loadingType == 'zip' + this.state.singleModeUsed = loadingType == 'single' }, /** * Sets the raw content of the single file mode diff --git a/report-viewer/src/views/FileUploadView.vue b/report-viewer/src/views/FileUploadView.vue index 2594cc107..40e229160 100644 --- a/report-viewer/src/views/FileUploadView.vue +++ b/report-viewer/src/views/FileUploadView.vue @@ -32,7 +32,7 @@
Or click here to select a file
(No files will be uploaded)
- @@ -48,7 +48,7 @@ From 0defe379c9fe8f37468410a34cc7f3996cf9ce6b Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Thu, 12 Oct 2023 20:48:23 +0200 Subject: [PATCH 07/37] Removed accidental changes from package.json --- report-viewer/package.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/report-viewer/package.json b/report-viewer/package.json index d386ae07a..b55301372 100644 --- a/report-viewer/package.json +++ b/report-viewer/package.json @@ -21,13 +21,11 @@ "@fortawesome/free-regular-svg-icons": "^6.4.2", "@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/vue-fontawesome": "^3.0.3", - "build": "^0.1.4", "chart.js": "^4.4.0", "chartjs-plugin-datalabels": "^2.2.0", "highlight.js": "^11.8.0", "jszip": "^3.10.0", "pinia": "^2.1.6", - "run": "^1.4.0", "slash": "^5.1.0", "vue": "^3.3.4", "vue-chartjs": "^5.2.0", From 5a278485dd3631f707ccb77ac2815313701bd285 Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Mon, 16 Oct 2023 12:15:50 +0200 Subject: [PATCH 08/37] remove unneeded dependencies --- report-viewer/package-lock.json | 380 +------------------------------- report-viewer/package.json | 2 - 2 files changed, 10 insertions(+), 372 deletions(-) diff --git a/report-viewer/package-lock.json b/report-viewer/package-lock.json index 5bd281bd5..3cd65c76e 100644 --- a/report-viewer/package-lock.json +++ b/report-viewer/package-lock.json @@ -12,13 +12,11 @@ "@fortawesome/free-regular-svg-icons": "^6.4.2", "@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/vue-fontawesome": "^3.0.3", - "build": "^0.1.4", "chart.js": "^4.4.0", "chartjs-plugin-datalabels": "^2.2.0", "highlight.js": "^11.8.0", "jszip": "^3.10.0", "pinia": "^2.1.6", - "run": "^1.4.0", "slash": "^5.1.0", "vue": "^3.3.4", "vue-chartjs": "^5.2.0", @@ -85,24 +83,6 @@ "node": ">=6.0.0" } }, - "node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, "node_modules/@esbuild/android-arm": { "version": "0.18.14", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.14.tgz", @@ -821,11 +801,6 @@ "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", "dev": true }, - "node_modules/@types/triple-beam": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.3.tgz", - "integrity": "sha512-6tOUG+nVHn0cJbVp25JFayS5UE6+xlbcNF9Lo9mU7U0zk3zeUShZied4YEQZjy1JBF043FSkdXw8YkUJuVtB5g==" - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.59.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz", @@ -1560,11 +1535,6 @@ "node": "*" } }, - "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1623,7 +1593,8 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/big-integer": { "version": "1.6.51", @@ -1665,6 +1636,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1714,26 +1686,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/build": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/build/-/build-0.1.4.tgz", - "integrity": "sha512-KwbDJ/zrsU8KZRRMfoURG14cKIAStUlS8D5jBDvtrZbwO5FEkYqc3oB8HIhRiyD64A48w1lc+sOmQ+mmBw5U/Q==", - "dependencies": { - "cssmin": "0.3.x", - "jsmin": "1.x", - "jxLoader": "*", - "moo-server": "*", - "promised-io": "*", - "timespan": "2.x", - "uglify-js": "1.x", - "walker": "1.x", - "winston": "*", - "wrench": "1.3.x" - }, - "engines": { - "node": ">v0.4.12" - } - }, "node_modules/bundle-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", @@ -1941,15 +1893,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1965,29 +1908,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/colorette": { "version": "2.0.20", @@ -1995,15 +1917,6 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -2028,7 +1941,8 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/config-chain": { "version": "1.1.13", @@ -2071,14 +1985,6 @@ "node": ">=4" } }, - "node_modules/cssmin": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/cssmin/-/cssmin-0.3.2.tgz", - "integrity": "sha512-bynxGIAJ8ybrnFobjsQotIjA8HFDDgPwbeUWNXXXfR+B4f9kkxdcUyagJoQCSUOfMV+ZZ6bMn8bvbozlCzUGwQ==", - "bin": { - "cssmin": "bin/cssmin" - } - }, "node_modules/cssstyle": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", @@ -2345,11 +2251,6 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, "node_modules/entities": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -2846,11 +2747,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2910,11 +2806,6 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -3887,17 +3778,6 @@ } } }, - "node_modules/jsmin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/jsmin/-/jsmin-1.0.1.tgz", - "integrity": "sha512-OPuL5X/bFKgVdMvEIX3hnpx3jbVpFCrEM8pKPXjFkZUqg521r41ijdyTz7vACOhW6o1neVlcLyd+wkbK5fNHRg==", - "bin": { - "jsmin": "bin/jsmin" - }, - "engines": { - "node": ">=0.1.93" - } - }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -3933,33 +3813,6 @@ "setimmediate": "^1.0.5" } }, - "node_modules/jxLoader": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jxLoader/-/jxLoader-0.1.1.tgz", - "integrity": "sha512-ClEvAj3K68y8uKhub3RgTmcRPo5DfIWvtxqrKQdDPyZ1UVHIIKvVvjrAsJFSVL5wjv0rt5iH9SMCZ0XRKNzeUA==", - "dependencies": { - "js-yaml": "0.3.x", - "moo-server": "1.3.x", - "promised-io": "*", - "walker": "1.x" - }, - "engines": { - "node": ">v0.4.10" - } - }, - "node_modules/jxLoader/node_modules/js-yaml": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-0.3.7.tgz", - "integrity": "sha512-/7PsVDNP2tVe2Z1cF9kTEkjamIwz4aooDpRKmN1+g/9eePCgcxsv4QDvEbxO0EH+gdDD7MLyDoR6BASo3hH51g==", - "engines": { - "node": "> 0.4.11" - } - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4181,27 +4034,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/logform": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", - "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", - "dependencies": { - "@colors/colors": "1.5.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - } - }, - "node_modules/logform/node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/loupe": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", @@ -4222,14 +4054,6 @@ "node": ">=12" } }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dependencies": { - "tmpl": "1.0.5" - } - }, "node_modules/memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", @@ -4304,6 +4128,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4328,18 +4153,11 @@ "ufo": "^1.3.0" } }, - "node_modules/moo-server": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/moo-server/-/moo-server-1.3.0.tgz", - "integrity": "sha512-9A8/eor2DXwpv1+a4pZAAydqLFVrWoKoO1fzdzqLUhYVXAO1Kgd1FR2gFZi7YdHzF0s4W8cDNwCfKJQrvLqxDw==", - "engines": { - "node": ">v0.4.10" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/muggle-string": { "version": "0.3.1", @@ -4724,14 +4542,6 @@ "wrappy": "1" } }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "dependencies": { - "fn.name": "1.x.x" - } - }, "node_modules/onetime": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", @@ -5318,11 +5128,6 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "node_modules/promised-io": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/promised-io/-/promised-io-0.3.6.tgz", - "integrity": "sha512-bNwZusuNIW4m0SPR8jooSyndD35ggirHlxVl/UhIaZD/F0OBv9ebfc6tNmbpZts3QXHggkjIBH8lvtnzhtcz0A==" - }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -5608,20 +5413,6 @@ "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", "dev": true }, - "node_modules/run": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/run/-/run-1.4.0.tgz", - "integrity": "sha512-962oBW07IjQ9SizyMHdoteVbDKt/e2nEsnTRZ0WjK/zs+jfQQICqH0qj0D5lqZNuy0JkbzfA6IOqw0Sk7C3DlQ==", - "dependencies": { - "minimatch": "*" - }, - "bin": { - "runjs": "cli.js" - }, - "engines": { - "node": ">=v0.9.0" - } - }, "node_modules/run-applescript": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", @@ -5768,14 +5559,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", - "engines": { - "node": ">=10" - } - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -5888,19 +5671,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, "node_modules/slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", @@ -5986,14 +5756,6 @@ "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", "dev": true }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "engines": { - "node": "*" - } - }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -6326,11 +6088,6 @@ "node": ">=14.0.0" } }, - "node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -6358,14 +6115,6 @@ "node": ">=0.8" } }, - "node_modules/timespan": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/timespan/-/timespan-2.3.0.tgz", - "integrity": "sha512-0Jq9+58T2wbOyLth0EU+AUb6JMGCLaTWIykJFa7hyAybjVH9gpVMTfUAwo5fWAvtFt2Tjh/Elg8JtgNpnMnM8g==", - "engines": { - "node": ">= 0.2.0" - } - }, "node_modules/tinybench": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.0.tgz", @@ -6402,11 +6151,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6446,14 +6190,6 @@ "node": ">=14" } }, - "node_modules/triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "engines": { - "node": ">= 14.0.0" - } - }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -6547,14 +6283,6 @@ "integrity": "sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==", "dev": true }, - "node_modules/uglify-js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-1.3.5.tgz", - "integrity": "sha512-YPX1DjKtom8l9XslmPFQnqWzTBkvI4N0pbkzLuPZZ4QTyig0uQqvZz9NgUdfEV+qccJzi7fVcGWdESvRIjWptQ==", - "bin": { - "uglifyjs": "bin/uglifyjs" - } - }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -6971,14 +6699,6 @@ "node": ">=14" } }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dependencies": { - "makeerror": "1.0.12" - } - }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -7089,77 +6809,6 @@ "node": ">=8" } }, - "node_modules/winston": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz", - "integrity": "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==", - "dependencies": { - "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.4.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.5.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/winston-transport": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", - "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", - "dependencies": { - "logform": "^2.3.2", - "readable-stream": "^3.6.0", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 6.4.0" - } - }, - "node_modules/winston-transport/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/winston/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/winston/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -7222,15 +6871,6 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "node_modules/wrench": { - "version": "1.3.9", - "resolved": "https://registry.npmjs.org/wrench/-/wrench-1.3.9.tgz", - "integrity": "sha512-srTJQmLTP5YtW+F5zDuqjMEZqLLr/eJOZfDI5ibfPfRMeDh3oBUefAscuH0q5wBKE339ptH/S/0D18ZkfOfmKQ==", - "deprecated": "wrench.js is deprecated! You should check out fs-extra (https://github.com/jprichardson/node-fs-extra) for any operations you were using wrench for. Thanks for all the usage over the years.", - "engines": { - "node": ">=0.1.97" - } - }, "node_modules/ws": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", diff --git a/report-viewer/package.json b/report-viewer/package.json index d386ae07a..b55301372 100644 --- a/report-viewer/package.json +++ b/report-viewer/package.json @@ -21,13 +21,11 @@ "@fortawesome/free-regular-svg-icons": "^6.4.2", "@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/vue-fontawesome": "^3.0.3", - "build": "^0.1.4", "chart.js": "^4.4.0", "chartjs-plugin-datalabels": "^2.2.0", "highlight.js": "^11.8.0", "jszip": "^3.10.0", "pinia": "^2.1.6", - "run": "^1.4.0", "slash": "^5.1.0", "vue": "^3.3.4", "vue-chartjs": "^5.2.0", From 853ff7f7867cea4f05d10680349392f04dc62935 Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Wed, 18 Oct 2023 09:28:09 +0200 Subject: [PATCH 09/37] Changed local report viewer to return index html for every path, that is not used for something else. --- .../de/jplag/cli/server/ReportViewer.java | 5 +- ...RoutingRedirect.java => RoutingAlias.java} | 10 +- report-viewer/package-lock.json | 380 +----------------- 3 files changed, 16 insertions(+), 379 deletions(-) rename cli/src/main/java/de/jplag/cli/server/{RoutingRedirect.java => RoutingAlias.java} (74%) diff --git a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java index 2940ab230..a679d3c96 100644 --- a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java +++ b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java @@ -3,6 +3,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.net.InetAddress; import java.net.InetSocketAddress; import org.apache.commons.lang3.ArrayUtils; @@ -26,7 +27,7 @@ public class ReportViewer implements HttpHandler { public ReportViewer(File zipFile) throws IOException { this.routingTree = new RoutingTree(); - this.routingTree.insertRouting("", new RoutingResources("report-viewer").or(new RoutingRedirect("index.html"))); + this.routingTree.insertRouting("", new RoutingResources("report-viewer").or(new RoutingAlias("index.html"))); this.routingTree.insertRouting("result.zip", new RoutingStaticFile(zipFile, ContentType.ZIP)); } @@ -34,7 +35,7 @@ public int start() throws IOException { if (server != null) { throw new IllegalStateException("Server already started"); } - server = HttpServer.create(new InetSocketAddress(0), 0); + server = HttpServer.create(new InetSocketAddress(InetAddress.getLocalHost(), 0), 0); server.createContext("/", this); server.setExecutor(null); server.start(); diff --git a/cli/src/main/java/de/jplag/cli/server/RoutingRedirect.java b/cli/src/main/java/de/jplag/cli/server/RoutingAlias.java similarity index 74% rename from cli/src/main/java/de/jplag/cli/server/RoutingRedirect.java rename to cli/src/main/java/de/jplag/cli/server/RoutingAlias.java index 0866c165a..575bd0ec8 100644 --- a/cli/src/main/java/de/jplag/cli/server/RoutingRedirect.java +++ b/cli/src/main/java/de/jplag/cli/server/RoutingAlias.java @@ -4,23 +4,19 @@ import com.sun.net.httpserver.HttpExchange; -public class RoutingRedirect implements Routing { +public class RoutingAlias implements Routing { private final RoutingPath path; - public RoutingRedirect(RoutingPath path) { + public RoutingAlias(RoutingPath path) { this.path = path; } - public RoutingRedirect(String path) { + public RoutingAlias(String path) { this(new RoutingPath(path)); } @Override public ResponseData fetchData(RoutingPath subPath, HttpExchange request, ReportViewer viewer) { - if (subPath.hasTail()) { - return null; - } - Pair redirect = viewer.getRoutingTree().resolveRouting(path); if (redirect == null) { return null; diff --git a/report-viewer/package-lock.json b/report-viewer/package-lock.json index 61bfddd30..29e123c89 100644 --- a/report-viewer/package-lock.json +++ b/report-viewer/package-lock.json @@ -12,13 +12,11 @@ "@fortawesome/free-regular-svg-icons": "^6.4.2", "@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/vue-fontawesome": "^3.0.3", - "build": "^0.1.4", "chart.js": "^4.4.0", "chartjs-plugin-datalabels": "^2.2.0", "highlight.js": "^11.8.0", "jszip": "^3.10.0", "pinia": "^2.1.6", - "run": "^1.4.0", "slash": "^5.1.0", "vue": "^3.3.4", "vue-chartjs": "^5.2.0", @@ -85,24 +83,6 @@ "node": ">=6.0.0" } }, - "node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, "node_modules/@esbuild/android-arm": { "version": "0.18.14", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.14.tgz", @@ -821,11 +801,6 @@ "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", "dev": true }, - "node_modules/@types/triple-beam": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.3.tgz", - "integrity": "sha512-6tOUG+nVHn0cJbVp25JFayS5UE6+xlbcNF9Lo9mU7U0zk3zeUShZied4YEQZjy1JBF043FSkdXw8YkUJuVtB5g==" - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.59.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz", @@ -1560,11 +1535,6 @@ "node": "*" } }, - "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1623,7 +1593,8 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/big-integer": { "version": "1.6.51", @@ -1665,6 +1636,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1714,26 +1686,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/build": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/build/-/build-0.1.4.tgz", - "integrity": "sha512-KwbDJ/zrsU8KZRRMfoURG14cKIAStUlS8D5jBDvtrZbwO5FEkYqc3oB8HIhRiyD64A48w1lc+sOmQ+mmBw5U/Q==", - "dependencies": { - "cssmin": "0.3.x", - "jsmin": "1.x", - "jxLoader": "*", - "moo-server": "*", - "promised-io": "*", - "timespan": "2.x", - "uglify-js": "1.x", - "walker": "1.x", - "winston": "*", - "wrench": "1.3.x" - }, - "engines": { - "node": ">v0.4.12" - } - }, "node_modules/bundle-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", @@ -1941,15 +1893,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1965,29 +1908,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/colorette": { "version": "2.0.20", @@ -1995,15 +1917,6 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -2028,7 +1941,8 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/config-chain": { "version": "1.1.13", @@ -2071,14 +1985,6 @@ "node": ">=4" } }, - "node_modules/cssmin": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/cssmin/-/cssmin-0.3.2.tgz", - "integrity": "sha512-bynxGIAJ8ybrnFobjsQotIjA8HFDDgPwbeUWNXXXfR+B4f9kkxdcUyagJoQCSUOfMV+ZZ6bMn8bvbozlCzUGwQ==", - "bin": { - "cssmin": "bin/cssmin" - } - }, "node_modules/cssstyle": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", @@ -2345,11 +2251,6 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, "node_modules/entities": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -2846,11 +2747,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2910,11 +2806,6 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -3887,17 +3778,6 @@ } } }, - "node_modules/jsmin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/jsmin/-/jsmin-1.0.1.tgz", - "integrity": "sha512-OPuL5X/bFKgVdMvEIX3hnpx3jbVpFCrEM8pKPXjFkZUqg521r41ijdyTz7vACOhW6o1neVlcLyd+wkbK5fNHRg==", - "bin": { - "jsmin": "bin/jsmin" - }, - "engines": { - "node": ">=0.1.93" - } - }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -3933,33 +3813,6 @@ "setimmediate": "^1.0.5" } }, - "node_modules/jxLoader": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jxLoader/-/jxLoader-0.1.1.tgz", - "integrity": "sha512-ClEvAj3K68y8uKhub3RgTmcRPo5DfIWvtxqrKQdDPyZ1UVHIIKvVvjrAsJFSVL5wjv0rt5iH9SMCZ0XRKNzeUA==", - "dependencies": { - "js-yaml": "0.3.x", - "moo-server": "1.3.x", - "promised-io": "*", - "walker": "1.x" - }, - "engines": { - "node": ">v0.4.10" - } - }, - "node_modules/jxLoader/node_modules/js-yaml": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-0.3.7.tgz", - "integrity": "sha512-/7PsVDNP2tVe2Z1cF9kTEkjamIwz4aooDpRKmN1+g/9eePCgcxsv4QDvEbxO0EH+gdDD7MLyDoR6BASo3hH51g==", - "engines": { - "node": "> 0.4.11" - } - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4181,27 +4034,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/logform": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", - "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", - "dependencies": { - "@colors/colors": "1.5.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - } - }, - "node_modules/logform/node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/loupe": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", @@ -4222,14 +4054,6 @@ "node": ">=12" } }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dependencies": { - "tmpl": "1.0.5" - } - }, "node_modules/memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", @@ -4304,6 +4128,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4328,18 +4153,11 @@ "ufo": "^1.3.0" } }, - "node_modules/moo-server": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/moo-server/-/moo-server-1.3.0.tgz", - "integrity": "sha512-9A8/eor2DXwpv1+a4pZAAydqLFVrWoKoO1fzdzqLUhYVXAO1Kgd1FR2gFZi7YdHzF0s4W8cDNwCfKJQrvLqxDw==", - "engines": { - "node": ">v0.4.10" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/muggle-string": { "version": "0.3.1", @@ -4724,14 +4542,6 @@ "wrappy": "1" } }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "dependencies": { - "fn.name": "1.x.x" - } - }, "node_modules/onetime": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", @@ -5318,11 +5128,6 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "node_modules/promised-io": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/promised-io/-/promised-io-0.3.6.tgz", - "integrity": "sha512-bNwZusuNIW4m0SPR8jooSyndD35ggirHlxVl/UhIaZD/F0OBv9ebfc6tNmbpZts3QXHggkjIBH8lvtnzhtcz0A==" - }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -5608,20 +5413,6 @@ "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", "dev": true }, - "node_modules/run": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/run/-/run-1.4.0.tgz", - "integrity": "sha512-962oBW07IjQ9SizyMHdoteVbDKt/e2nEsnTRZ0WjK/zs+jfQQICqH0qj0D5lqZNuy0JkbzfA6IOqw0Sk7C3DlQ==", - "dependencies": { - "minimatch": "*" - }, - "bin": { - "runjs": "cli.js" - }, - "engines": { - "node": ">=v0.9.0" - } - }, "node_modules/run-applescript": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", @@ -5768,14 +5559,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", - "engines": { - "node": ">=10" - } - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -5888,19 +5671,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, "node_modules/slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", @@ -5980,14 +5750,6 @@ "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", "dev": true }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "engines": { - "node": "*" - } - }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -6320,11 +6082,6 @@ "node": ">=14.0.0" } }, - "node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -6352,14 +6109,6 @@ "node": ">=0.8" } }, - "node_modules/timespan": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/timespan/-/timespan-2.3.0.tgz", - "integrity": "sha512-0Jq9+58T2wbOyLth0EU+AUb6JMGCLaTWIykJFa7hyAybjVH9gpVMTfUAwo5fWAvtFt2Tjh/Elg8JtgNpnMnM8g==", - "engines": { - "node": ">= 0.2.0" - } - }, "node_modules/tinybench": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.0.tgz", @@ -6396,11 +6145,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6440,14 +6184,6 @@ "node": ">=14" } }, - "node_modules/triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "engines": { - "node": ">= 14.0.0" - } - }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -6541,14 +6277,6 @@ "integrity": "sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==", "dev": true }, - "node_modules/uglify-js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-1.3.5.tgz", - "integrity": "sha512-YPX1DjKtom8l9XslmPFQnqWzTBkvI4N0pbkzLuPZZ4QTyig0uQqvZz9NgUdfEV+qccJzi7fVcGWdESvRIjWptQ==", - "bin": { - "uglifyjs": "bin/uglifyjs" - } - }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -6965,14 +6693,6 @@ "node": ">=14" } }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dependencies": { - "makeerror": "1.0.12" - } - }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -7083,77 +6803,6 @@ "node": ">=8" } }, - "node_modules/winston": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz", - "integrity": "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==", - "dependencies": { - "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.4.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.5.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/winston-transport": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", - "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", - "dependencies": { - "logform": "^2.3.2", - "readable-stream": "^3.6.0", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 6.4.0" - } - }, - "node_modules/winston-transport/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/winston/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/winston/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -7216,15 +6865,6 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "node_modules/wrench": { - "version": "1.3.9", - "resolved": "https://registry.npmjs.org/wrench/-/wrench-1.3.9.tgz", - "integrity": "sha512-srTJQmLTP5YtW+F5zDuqjMEZqLLr/eJOZfDI5ibfPfRMeDh3oBUefAscuH0q5wBKE339ptH/S/0D18ZkfOfmKQ==", - "deprecated": "wrench.js is deprecated! You should check out fs-extra (https://github.com/jprichardson/node-fs-extra) for any operations you were using wrench for. Thanks for all the usage over the years.", - "engines": { - "node": ">=0.1.97" - } - }, "node_modules/ws": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", From 4c8ea193e4d0ebd1dced29609ba27e71129bed56 Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Wed, 18 Oct 2023 09:36:57 +0200 Subject: [PATCH 10/37] Fixed small error for local mode. --- cli/src/main/java/de/jplag/cli/server/ReportViewer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java index af9df1853..77c847d34 100644 --- a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java +++ b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java @@ -35,7 +35,7 @@ public int start() throws IOException { if (server != null) { throw new IllegalStateException("Server already started"); } - server = HttpServer.create(new InetSocketAddress(InetAddress.getLocalHost(), 0), 0); + server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0); server.createContext("/", this); server.setExecutor(null); server.start(); From 30ac721477ea27c97818126483341aa4dff5919f Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Sat, 21 Oct 2023 12:24:54 +0200 Subject: [PATCH 11/37] fix new tabs on comparison --- .../src/components/ComparisonsTable.vue | 7 ++++++- .../src/model/factories/ComparisonFactory.ts | 9 ++------- report-viewer/src/router/index.ts | 2 +- .../src/viewWrapper/ComparisonViewWrapper.vue | 16 +++------------- report-viewer/src/views/ComparisonView.vue | 10 ++-------- 5 files changed, 14 insertions(+), 30 deletions(-) diff --git a/report-viewer/src/components/ComparisonsTable.vue b/report-viewer/src/components/ComparisonsTable.vue index c22f40f70..36fdb83cd 100644 --- a/report-viewer/src/components/ComparisonsTable.vue +++ b/report-viewer/src/components/ComparisonsTable.vue @@ -63,7 +63,12 @@ diff --git a/report-viewer/src/model/factories/ComparisonFactory.ts b/report-viewer/src/model/factories/ComparisonFactory.ts index 355e2c795..03419f6c4 100644 --- a/report-viewer/src/model/factories/ComparisonFactory.ts +++ b/report-viewer/src/model/factories/ComparisonFactory.ts @@ -10,13 +10,8 @@ import { MetricType } from '../MetricType' * Factory class for creating Comparison objects */ export class ComparisonFactory extends BaseFactory { - public static async getComparison(id1: string, id2: string): Promise { - const filePath = store().getComparisonFileName(id1, id2) - if (!filePath) { - throw new Error('Comparison file not specified') - } - - return await this.extractComparison(JSON.parse(await this.getFile(filePath))) + public static async getComparison(fileName: string): Promise { + return await this.extractComparison(JSON.parse(await this.getFile(fileName))) } /** diff --git a/report-viewer/src/router/index.ts b/report-viewer/src/router/index.ts index 31283c13d..2db68f8f4 100644 --- a/report-viewer/src/router/index.ts +++ b/report-viewer/src/router/index.ts @@ -23,7 +23,7 @@ const router = createRouter({ component: OverviewViewWrapper }, { - path: '/comparison/:firstId/:secondId', + path: '/comparison/:comparisonFileName', name: 'ComparisonView', component: ComparisonViewWrapper, props: true diff --git a/report-viewer/src/viewWrapper/ComparisonViewWrapper.vue b/report-viewer/src/viewWrapper/ComparisonViewWrapper.vue index 96395207b..d64490bcb 100644 --- a/report-viewer/src/viewWrapper/ComparisonViewWrapper.vue +++ b/report-viewer/src/viewWrapper/ComparisonViewWrapper.vue @@ -1,11 +1,5 @@ \ No newline at end of file + From 1dcc4b5400ec169e8862524b5b581d8f7f854c82 Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Tue, 23 Jan 2024 13:01:46 +0100 Subject: [PATCH 22/37] fix loading index html file --- .../src/model/factories/BaseFactory.ts | 17 +++++++++++++---- report-viewer/src/views/FileUploadView.vue | 9 ++++----- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/report-viewer/src/model/factories/BaseFactory.ts b/report-viewer/src/model/factories/BaseFactory.ts index b82112d04..933ef9bef 100644 --- a/report-viewer/src/model/factories/BaseFactory.ts +++ b/report-viewer/src/model/factories/BaseFactory.ts @@ -50,17 +50,26 @@ export class BaseFactory { * @return Content of the file * @throws Error if the file could not be found */ - protected static async getLocalFile(path: string): Promise { + public static async getLocalFile(path: string): Promise { const request = await fetch(`${window.location.origin}${import.meta.env.BASE_URL}${path}`) if (request.status == 200) { - return request.blob() + const blob = await request.blob() + // Check that file is not the index.html + if (blob.type == 'text/html') { + throw new Error(`Could not find ${path} in local files.`) + } + return blob } else { throw new Error(`Could not find ${path} in local files.`) } } public static async useLocalZipMode() { - const response = await fetch(window.location.origin + '/' + this.zipFileName) - return response.status != 404 + try { + await this.getLocalFile(this.zipFileName) + return true + } catch (e) { + return false + } } } diff --git a/report-viewer/src/views/FileUploadView.vue b/report-viewer/src/views/FileUploadView.vue index cb92cd251..d306fb050 100644 --- a/report-viewer/src/views/FileUploadView.vue +++ b/report-viewer/src/views/FileUploadView.vue @@ -66,15 +66,14 @@ store().clearStore() const exampleFiles = ref(import.meta.env.MODE == 'demo') const localFiles = ref(false) // Checks whether local files exist -fetch('/files/overview.json') - .then((response) => { - if (response.status == 200) { - localFiles.value = true - } +BaseFactory.getLocalFile('files/overview.json') + .then(() => { + localFiles.value = true }) .catch(() => {}) BaseFactory.useLocalZipMode().then((value) => { + console.log('Using local zip mode:', value) if (value) { navigateToOverview() } From 4b9c925dc1706dd99e0a99ee7aeed44bf101194e Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Tue, 23 Jan 2024 15:16:17 +0100 Subject: [PATCH 23/37] file name in tab --- report-viewer/src/model/factories/BaseFactory.ts | 2 +- report-viewer/src/views/FileUploadView.vue | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/report-viewer/src/model/factories/BaseFactory.ts b/report-viewer/src/model/factories/BaseFactory.ts index 933ef9bef..ada649267 100644 --- a/report-viewer/src/model/factories/BaseFactory.ts +++ b/report-viewer/src/model/factories/BaseFactory.ts @@ -5,7 +5,7 @@ import { ZipFileHandler } from '@/utils/fileHandling/ZipFileHandler' * This class provides some basic functionality for the factories. */ export class BaseFactory { - private static zipFileName = 'results.zip' + public static zipFileName = 'results.zip' /** * Returns the content of a file through the stored loading type. diff --git a/report-viewer/src/views/FileUploadView.vue b/report-viewer/src/views/FileUploadView.vue index d306fb050..b3611d3f9 100644 --- a/report-viewer/src/views/FileUploadView.vue +++ b/report-viewer/src/views/FileUploadView.vue @@ -75,6 +75,7 @@ BaseFactory.getLocalFile('files/overview.json') BaseFactory.useLocalZipMode().then((value) => { console.log('Using local zip mode:', value) if (value) { + store().state.uploadedFileName = BaseFactory.zipFileName navigateToOverview() } }) @@ -199,7 +200,7 @@ async function loadQueryFile(url: URL) { * Handles click on Continue with local files. */ function continueWithLocal() { - store().state.uploadedFileName = 'results.zip' + store().state.uploadedFileName = BaseFactory.zipFileName store().setLoadingType('local') navigateToOverview() } From d4920db54add3caf4f69cf9e9162fddf088f9064 Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Tue, 23 Jan 2024 17:45:07 +0100 Subject: [PATCH 24/37] fix anonymous propagation --- report-viewer/src/components/NameElement.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/report-viewer/src/components/NameElement.vue b/report-viewer/src/components/NameElement.vue index a3445bf49..52a89dcfc 100644 --- a/report-viewer/src/components/NameElement.vue +++ b/report-viewer/src/components/NameElement.vue @@ -33,6 +33,7 @@ const props = defineProps({ function changeAnonymous(event: Event) { event.stopPropagation() + event.preventDefault() if (store().isAnonymous(props.id)) { store().removeAnonymous([props.id]) } else { From 7956543c71dff1a35d09d80606b0b159e4de3b2f Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Tue, 6 Feb 2024 09:44:59 +0100 Subject: [PATCH 25/37] Fixed some minor code style issues in the internal report viewer. --- cli/src/main/java/de/jplag/cli/CLI.java | 2 +- .../main/java/de/jplag/cli/CliOptions.java | 2 +- cli/src/main/java/de/jplag/cli/JPlagMode.java | 4 +-- .../java/de/jplag/cli/server/ContentType.java | 34 ++++++++++--------- .../java/de/jplag/cli/server/HttpMethod.java | 2 +- .../de/jplag/cli/server/ReportViewer.java | 10 ++++-- .../java/de/jplag/cli/ReportViewerTest.java | 2 +- 7 files changed, 31 insertions(+), 25 deletions(-) diff --git a/cli/src/main/java/de/jplag/cli/CLI.java b/cli/src/main/java/de/jplag/cli/CLI.java index b8fd95895..9b96ff2ba 100644 --- a/cli/src/main/java/de/jplag/cli/CLI.java +++ b/cli/src/main/java/de/jplag/cli/CLI.java @@ -131,7 +131,7 @@ public void runViewer(File zipFile) throws IOException { logger.info("ReportViewer started on port http://localhost:{}", port); Desktop.getDesktop().browse(URI.create("http://localhost:" + port + "/")); - System.out.println("Press Enter kex to exit..."); + System.out.println("Press Enter key to exit..."); System.in.read(); reportViewer.stop(); } diff --git a/cli/src/main/java/de/jplag/cli/CliOptions.java b/cli/src/main/java/de/jplag/cli/CliOptions.java index a381e637c..962be5bda 100644 --- a/cli/src/main/java/de/jplag/cli/CliOptions.java +++ b/cli/src/main/java/de/jplag/cli/CliOptions.java @@ -54,7 +54,7 @@ public class CliOptions implements Runnable { public String resultFolder = "results"; @Option(names = {"--mode"}, description = "The mode to run jplag in") - public JPlagMode mode = JPlagMode.RUN_AND_VIEW; + public JPlagMode mode = JPlagMode.RUN; @ArgGroup(heading = "Advanced%n", exclusive = false) public Advanced advanced = new Advanced(); diff --git a/cli/src/main/java/de/jplag/cli/JPlagMode.java b/cli/src/main/java/de/jplag/cli/JPlagMode.java index eb67d168c..212e9ab3e 100644 --- a/cli/src/main/java/de/jplag/cli/JPlagMode.java +++ b/cli/src/main/java/de/jplag/cli/JPlagMode.java @@ -1,11 +1,11 @@ package de.jplag.cli; /** - * The mode Jplag runs in. This influences which steps JPlag will execute + * The mode JPlag runs in. This influences which steps JPlag will execute */ public enum JPlagMode { /** - * Only run jplag and create a results.zip + * Only run JPlag and create a results.zip */ RUN, /** diff --git a/cli/src/main/java/de/jplag/cli/server/ContentType.java b/cli/src/main/java/de/jplag/cli/server/ContentType.java index 4014f6ab0..cf90673b3 100644 --- a/cli/src/main/java/de/jplag/cli/server/ContentType.java +++ b/cli/src/main/java/de/jplag/cli/server/ContentType.java @@ -1,20 +1,23 @@ package de.jplag.cli.server; /** - * The type of data + * Data types used by JPlag in the context of http. Contains the according mime type. */ public enum ContentType { - HTML("text/html; charset=utf-8"), - JS("application/javascript; charset=utf-8"), - CSS("text/css; charset=utf-8"), - PNG("image/png"), - PLAIN("text/plain; charset=utf-8"), - ZIP("application/zip"); + HTML("text/html; charset=utf-8", ".html"), + JS("application/javascript; charset=utf-8", ".js"), + CSS("text/css; charset=utf-8", ".css"), + PNG("image/png", ".png"), + PLAIN("text/plain; charset=utf-8", null), + ZIP("application/zip", ".zip"); private final String value; - ContentType(String value) { + private final String nameSuffix; + + ContentType(String value, String nameSuffix) { this.value = value; + this.nameSuffix = nameSuffix; } public String getValue() { @@ -27,13 +30,12 @@ public String getValue() { * @return The guessed type */ public static ContentType fromPath(String path) { - return switch (path.substring(path.lastIndexOf('.'))) { - case ".html" -> ContentType.HTML; - case ".js" -> ContentType.JS; - case ".css" -> ContentType.CSS; - case ".png" -> ContentType.PNG; - case ".zip" -> ContentType.ZIP; - default -> ContentType.PLAIN; - }; + String suffix = path.substring(path.lastIndexOf('.')); + for (ContentType value : ContentType.values()) { + if (suffix.equals(value.nameSuffix)) { + return value; + } + } + return ContentType.PLAIN; } } diff --git a/cli/src/main/java/de/jplag/cli/server/HttpMethod.java b/cli/src/main/java/de/jplag/cli/server/HttpMethod.java index 73e26e190..cabebcaba 100644 --- a/cli/src/main/java/de/jplag/cli/server/HttpMethod.java +++ b/cli/src/main/java/de/jplag/cli/server/HttpMethod.java @@ -1,7 +1,7 @@ package de.jplag.cli.server; /** - * Available http methods + * Wraps the http methods used by JPlag. */ public enum HttpMethod { GET("GET"), diff --git a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java index 986876ac3..b2808aaa5 100644 --- a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java +++ b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java @@ -19,6 +19,10 @@ * Manages the internal report viewer. Serves the static files for the report viewer and the results.zip. */ public class ReportViewer implements HttpHandler { + private static final String REPORT_VIEWER_RESOURCE_PREFIX = "report-viewer"; + private static final String INDEX_PATH = "index.html"; + private static final String RESULT_PATH = "results.zip"; + private static final Logger logger = LoggerFactory.getLogger(ReportViewer.class); private static final int SUCCESS_RESPONSE = 200; private static final int NOT_FOUND_RESPONSE = 404; @@ -36,13 +40,13 @@ public class ReportViewer implements HttpHandler { public ReportViewer(File zipFile, int port) throws IOException { this.routingTree = new RoutingTree(); - this.routingTree.insertRouting("", new RoutingResources("report-viewer").or(new RoutingAlias("index.html"))); - this.routingTree.insertRouting("results.zip", new RoutingStaticFile(zipFile, ContentType.ZIP)); + this.routingTree.insertRouting("", new RoutingResources(REPORT_VIEWER_RESOURCE_PREFIX).or(new RoutingAlias(INDEX_PATH))); + this.routingTree.insertRouting(RESULT_PATH, new RoutingStaticFile(zipFile, ContentType.ZIP)); this.port = port; } /** - * Starts the server + * Starts the server and serves the internal report viewer. If available, the result.zip is also exposed. * @return The port the server runs at * @throws IOException If the server cannot be started */ diff --git a/cli/src/test/java/de/jplag/cli/ReportViewerTest.java b/cli/src/test/java/de/jplag/cli/ReportViewerTest.java index 7ffec8bbd..8abc87cd0 100644 --- a/cli/src/test/java/de/jplag/cli/ReportViewerTest.java +++ b/cli/src/test/java/de/jplag/cli/ReportViewerTest.java @@ -17,7 +17,7 @@ @Timeout(value = 5, unit = TimeUnit.MINUTES) class ReportViewerTest { @Test - @Disabled + @Disabled("Starts the internal server for manual testing. Does not terminal automatically.") void testStartViewer() throws Exception { assumeTrue(Desktop.isDesktopSupported()); ReportViewer viewer = new ReportViewer(null, 0); From ccaddcea87ad6da63c20f66c61e6afd0f2e30f60 Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Tue, 6 Feb 2024 15:03:55 +0100 Subject: [PATCH 26/37] add example zip name again --- report-viewer/src/views/FileUploadView.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/report-viewer/src/views/FileUploadView.vue b/report-viewer/src/views/FileUploadView.vue index fd461e74b..1a67fb81c 100644 --- a/report-viewer/src/views/FileUploadView.vue +++ b/report-viewer/src/views/FileUploadView.vue @@ -201,7 +201,7 @@ async function loadQueryFile(url: URL) { * Handles click on Continue with local files. */ function continueWithLocal() { - store().state.uploadedFileName = BaseFactory.zipFileName + store().state.uploadedFileName = exampleFiles.value ? 'progpedia.zip' : BaseFactory.zipFileName store().setLoadingType('local') navigateToOverview() } From 7b724bb2ed8c1ba74c550dcc35d0e80431b213af Mon Sep 17 00:00:00 2001 From: Alex | Kronox Date: Tue, 6 Feb 2024 15:08:06 +0100 Subject: [PATCH 27/37] add node js link to docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ebda394f0..f35e2c2b0 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ JPlag is released on [Maven Central](https://search.maven.org/search?q=de.jplag) 1. Download or clone the code from this repository. 2. Run `mvn clean package` from the root of the repository to compile and build all submodules. Run `mvn clean package assembly:single` instead if you need the full jar which includes all dependencies. - Run `mvn -Pwith-report-viewer clean package assembly:single` to build the full jar with the report viewer. In this case, you'll need `npm` installed. + Run `mvn -Pwith-report-viewer clean package assembly:single` to build the full jar with the report viewer. In this case, you'll need [Node.js](https://nodejs.org/en/download) installed. 3. You will find the generated JARs in the subdirectory `cli/target`. ## Usage From 70a449fb2ff09a6aa594287dca5a1b56d4b2b560 Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Fri, 9 Feb 2024 11:48:56 +0100 Subject: [PATCH 28/37] Written tests for internal server. --- .../jplag/cli/server/RoutingFallbackTest.java | 43 +++++++++++++++ .../de/jplag/cli/server/RoutingPathTest.java | 43 +++++++++++++++ .../cli/server/RoutingResourcesTest.java | 19 +++++++ .../cli/server/RoutingStaticFileTest.java | 42 +++++++++++++++ .../de/jplag/cli/server/RoutingTreeTest.java | 54 +++++++++++++++++++ cli/src/test/resources/testResource.txt | 1 + 6 files changed, 202 insertions(+) create mode 100644 cli/src/test/java/de/jplag/cli/server/RoutingFallbackTest.java create mode 100644 cli/src/test/java/de/jplag/cli/server/RoutingPathTest.java create mode 100644 cli/src/test/java/de/jplag/cli/server/RoutingResourcesTest.java create mode 100644 cli/src/test/java/de/jplag/cli/server/RoutingStaticFileTest.java create mode 100644 cli/src/test/java/de/jplag/cli/server/RoutingTreeTest.java create mode 100644 cli/src/test/resources/testResource.txt diff --git a/cli/src/test/java/de/jplag/cli/server/RoutingFallbackTest.java b/cli/src/test/java/de/jplag/cli/server/RoutingFallbackTest.java new file mode 100644 index 000000000..35a0cfaaf --- /dev/null +++ b/cli/src/test/java/de/jplag/cli/server/RoutingFallbackTest.java @@ -0,0 +1,43 @@ +package de.jplag.cli.server; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.File; +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +class RoutingFallbackTest { + private final Routing nullRouting; + private final Routing contentRouting; + + RoutingFallbackTest() throws IOException { + File testFile = File.createTempFile("content", ".any"); + this.nullRouting = new RoutingStaticFile(null, ContentType.PLAIN); + this.contentRouting = new RoutingStaticFile(testFile, ContentType.PLAIN); + } + + @Test + void testSecondNull() { + Routing routing = this.nullRouting.or(this.contentRouting); + assertNotNull(routing.fetchData(null, null, null)); + } + + @Test + void testFirstNull() { + Routing routing = this.contentRouting.or(this.nullRouting); + assertNotNull(routing.fetchData(null, null, null)); + } + + @Test + void testNeitherNull() { + Routing routing = this.contentRouting.or(this.contentRouting); + assertNotNull(routing.fetchData(null, null, null)); + } + + @Test + void testBothNull() { + Routing routing = this.nullRouting.or(this.nullRouting); + assertNull(routing.fetchData(null, null, null)); + } +} \ No newline at end of file diff --git a/cli/src/test/java/de/jplag/cli/server/RoutingPathTest.java b/cli/src/test/java/de/jplag/cli/server/RoutingPathTest.java new file mode 100644 index 000000000..b9730d933 --- /dev/null +++ b/cli/src/test/java/de/jplag/cli/server/RoutingPathTest.java @@ -0,0 +1,43 @@ +package de.jplag.cli.server; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class RoutingPathTest { + private static final String TEST_PATH = "some/path/to/index.html"; + private static final String TEST_PATH_WITH_BEGINNING_SLASH = "/some/path/to/index.html"; + private static final String TEST_PATH_WITH_ADDITIONAL_SLASHES = "///some/path////to/index.html"; + + private static final String[] TEST_PATH_PARTS = new String[] {"some", "path", "to", "index.html"}; + + @ParameterizedTest + @ValueSource(strings = {TEST_PATH_WITH_BEGINNING_SLASH, TEST_PATH, TEST_PATH_WITH_ADDITIONAL_SLASHES}) + void testAsPath(String path) { + RoutingPath routingPath = new RoutingPath(path); + assertEquals(TEST_PATH, routingPath.asPath()); + } + + @Test + void testIterating() { + RoutingPath routingPath = new RoutingPath(TEST_PATH); + for (String expectedPart : TEST_PATH_PARTS) { + String currentPart = routingPath.head(); + routingPath = routingPath.tail(); + assertEquals(expectedPart, currentPart); + } + + assertFalse(routingPath.hasTail()); + assertTrue(routingPath.isEmpty()); + } + + @Test + void testErrorWithEmptyTail() { + assertThrowsExactly(IllegalStateException.class, () -> { + RoutingPath routingPath = new RoutingPath(""); + routingPath.tail(); + }); + } +} \ No newline at end of file diff --git a/cli/src/test/java/de/jplag/cli/server/RoutingResourcesTest.java b/cli/src/test/java/de/jplag/cli/server/RoutingResourcesTest.java new file mode 100644 index 000000000..3e4b4f501 --- /dev/null +++ b/cli/src/test/java/de/jplag/cli/server/RoutingResourcesTest.java @@ -0,0 +1,19 @@ +package de.jplag.cli.server; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class RoutingResourcesTest { + private static final RoutingResources routing = new RoutingResources("/"); + + @Test + void testExistingFile() { + assertNotNull(routing.fetchData(new RoutingPath("testResource.txt"), null, null)); + } + + @Test + void testNotExistingFile() { + assertNull(routing.fetchData(new RoutingPath("otherFile.txt"), null, null)); + } +} \ No newline at end of file diff --git a/cli/src/test/java/de/jplag/cli/server/RoutingStaticFileTest.java b/cli/src/test/java/de/jplag/cli/server/RoutingStaticFileTest.java new file mode 100644 index 000000000..4626e7a48 --- /dev/null +++ b/cli/src/test/java/de/jplag/cli/server/RoutingStaticFileTest.java @@ -0,0 +1,42 @@ +package de.jplag.cli.server; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class RoutingStaticFileTest { + private static final String TEST_FILE_CONTENT = "some test content."; + private static final ContentType TEST_CONTENT_TYPE = ContentType.PLAIN; + private static RoutingStaticFile routing; + + @BeforeAll + static void setUp() throws IOException { + File testFile = File.createTempFile("testFile", ".txt"); + try (FileWriter writer = new FileWriter(testFile)) { + writer.write(TEST_FILE_CONTENT); + } + routing = new RoutingStaticFile(testFile, TEST_CONTENT_TYPE); + } + + @Test + void testRespondsWithFileContent() throws IOException { + ResponseData responseData = routing.fetchData(null, null, null); + assertEquals(TEST_CONTENT_TYPE, responseData.contentType()); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(responseData.stream()))) { + assertEquals(TEST_FILE_CONTENT, reader.readLine()); + } + } + + @Test + void testWithNullFile() throws IOException { + RoutingStaticFile nullRouting = new RoutingStaticFile(null, TEST_CONTENT_TYPE); + assertNull(nullRouting.fetchData(null, null, null)); + } +} \ No newline at end of file diff --git a/cli/src/test/java/de/jplag/cli/server/RoutingTreeTest.java b/cli/src/test/java/de/jplag/cli/server/RoutingTreeTest.java new file mode 100644 index 000000000..bd7680592 --- /dev/null +++ b/cli/src/test/java/de/jplag/cli/server/RoutingTreeTest.java @@ -0,0 +1,54 @@ +package de.jplag.cli.server; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.apache.commons.lang3.tuple.Pair; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.sun.net.httpserver.HttpExchange; + +class RoutingTreeTest { + private static final String firstRoutingPath = "/content/image.png"; + private static final String secondRoutingPath = "/index.html"; + private RoutingTree routingTree; + + @BeforeEach + void setUp() { + this.routingTree = new RoutingTree(); + this.routingTree.insertRouting(firstRoutingPath, new TestRouting(firstRoutingPath)); + this.routingTree.insertRouting(secondRoutingPath, new TestRouting(secondRoutingPath)); + } + + @Test + public void testAccessRoutingTree() { + Pair firstRouting = this.routingTree.resolveRouting(new RoutingPath(firstRoutingPath)); + Pair secondRouting = this.routingTree.resolveRouting(new RoutingPath(secondRoutingPath + "/suffix")); + + assertTrue(firstRouting.getLeft().isEmpty()); + assertFalse(secondRouting.getLeft().isEmpty()); + assertEquals("suffix", secondRouting.getLeft().asPath()); + + assertInstanceOf(TestRouting.class, firstRouting.getRight()); + assertInstanceOf(TestRouting.class, secondRouting.getRight()); + + assertEquals(firstRoutingPath, ((TestRouting) firstRouting.getRight()).path); + assertEquals(secondRoutingPath, ((TestRouting) secondRouting.getRight()).path); + } + + private static class TestRouting implements Routing { + private final String path; + + public TestRouting(String path) { + this.path = path; + } + + @Override + public ResponseData fetchData(RoutingPath subPath, HttpExchange request, ReportViewer viewer) { + return null; + } + } +} \ No newline at end of file diff --git a/cli/src/test/resources/testResource.txt b/cli/src/test/resources/testResource.txt new file mode 100644 index 000000000..c29d01b9b --- /dev/null +++ b/cli/src/test/resources/testResource.txt @@ -0,0 +1 @@ +Test resource file for RoutingResourcesTest \ No newline at end of file From 889cc053821acce0c37b8e45067f86ef3f9c0b7c Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Fri, 9 Feb 2024 11:51:05 +0100 Subject: [PATCH 29/37] Improved clarity for HttpRequestMethod --- .../java/de/jplag/cli/server/HttpMethod.java | 29 ----------------- .../jplag/cli/server/HttpRequestMethod.java | 32 +++++++++++++++++++ .../de/jplag/cli/server/ReportViewer.java | 2 +- .../java/de/jplag/cli/server/Routing.java | 4 +-- 4 files changed, 35 insertions(+), 32 deletions(-) delete mode 100644 cli/src/main/java/de/jplag/cli/server/HttpMethod.java create mode 100644 cli/src/main/java/de/jplag/cli/server/HttpRequestMethod.java diff --git a/cli/src/main/java/de/jplag/cli/server/HttpMethod.java b/cli/src/main/java/de/jplag/cli/server/HttpMethod.java deleted file mode 100644 index cabebcaba..000000000 --- a/cli/src/main/java/de/jplag/cli/server/HttpMethod.java +++ /dev/null @@ -1,29 +0,0 @@ -package de.jplag.cli.server; - -/** - * Wraps the http methods used by JPlag. - */ -public enum HttpMethod { - GET("GET"), - POST("POST"); - - private final String name; - - HttpMethod(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public static HttpMethod fromName(String name) { - for (HttpMethod value : HttpMethod.values()) { - if (value.name.equals(name)) { - return value; - } - } - - return null; - } -} diff --git a/cli/src/main/java/de/jplag/cli/server/HttpRequestMethod.java b/cli/src/main/java/de/jplag/cli/server/HttpRequestMethod.java new file mode 100644 index 000000000..acbf4a5b9 --- /dev/null +++ b/cli/src/main/java/de/jplag/cli/server/HttpRequestMethod.java @@ -0,0 +1,32 @@ +package de.jplag.cli.server; + +/** + * Wraps the http request methods used by JPlag. Request methods determine the capabilities of a http request. + */ +public enum HttpRequestMethod { + GET("GET"), + POST("POST"); + + private final String name; + + /** + * @param name The name of the request method + */ + HttpRequestMethod(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static HttpRequestMethod fromName(String name) { + for (HttpRequestMethod value : HttpRequestMethod.values()) { + if (value.name.equals(name)) { + return value; + } + } + + return null; + } +} diff --git a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java index b2808aaa5..3932ea7f4 100644 --- a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java +++ b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java @@ -77,7 +77,7 @@ public void stop() { public void handle(HttpExchange exchange) throws IOException { RoutingPath path = new RoutingPath(exchange.getRequestURI().getPath()); Pair resolved = this.routingTree.resolveRouting(path); - HttpMethod method = HttpMethod.fromName(exchange.getRequestMethod()); + HttpRequestMethod method = HttpRequestMethod.fromName(exchange.getRequestMethod()); if (resolved == null || !ArrayUtils.contains(resolved.getRight().allowedMethods(), method)) { exchange.sendResponseHeaders(NOT_FOUND_RESPONSE, 0); diff --git a/cli/src/main/java/de/jplag/cli/server/Routing.java b/cli/src/main/java/de/jplag/cli/server/Routing.java index 84e94e105..a6152a031 100644 --- a/cli/src/main/java/de/jplag/cli/server/Routing.java +++ b/cli/src/main/java/de/jplag/cli/server/Routing.java @@ -9,8 +9,8 @@ public interface Routing { /** * @return The methods, that this routing can be used for. */ - default HttpMethod[] allowedMethods() { - return new HttpMethod[] {HttpMethod.GET}; + default HttpRequestMethod[] allowedMethods() { + return new HttpRequestMethod[] {HttpRequestMethod.GET}; } /** From 93b6655f0784afb72ac3ea6167981eebcce22866 Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Tue, 13 Feb 2024 15:38:14 +0100 Subject: [PATCH 30/37] Added port lookup. --- .../de/jplag/cli/server/ReportViewer.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java index 3932ea7f4..3f7cf73df 100644 --- a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java +++ b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java @@ -3,6 +3,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.net.BindException; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -26,6 +27,7 @@ public class ReportViewer implements HttpHandler { private static final Logger logger = LoggerFactory.getLogger(ReportViewer.class); private static final int SUCCESS_RESPONSE = 200; private static final int NOT_FOUND_RESPONSE = 404; + private static final int MAX_PORT_LOOKUPS = 4; private final RoutingTree routingTree; private final int port; @@ -34,7 +36,7 @@ public class ReportViewer implements HttpHandler { /** * @param zipFile The zip file to use for the report viewer - * @param port The port to use for the server. You can use 0 to use any free port. + * @param port The port to use for the server. You can use 0 to use any free port. * @throws IOException If the zip file cannot be read */ public ReportViewer(File zipFile, int port) throws IOException { @@ -47,6 +49,8 @@ public ReportViewer(File zipFile, int port) throws IOException { /** * Starts the server and serves the internal report viewer. If available, the result.zip is also exposed. + * If the given port is already in use, the next free port will be used. + * * @return The port the server runs at * @throws IOException If the server cannot be started */ @@ -54,7 +58,21 @@ public int start() throws IOException { if (server != null) { throw new IllegalStateException("Server already started"); } - server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), this.port), 0); + + int port = this.port; + int remainingLookups = MAX_PORT_LOOKUPS; + BindException lastException = new BindException("Could not bind server."); + while (server == null && remainingLookups-- > 0) { + try { + server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), port), 0); + } catch (BindException e) { + lastException = e; + port++; + } + } + if (server == null) { + throw lastException; + } server.createContext("/", this); server.setExecutor(null); server.start(); @@ -71,6 +89,7 @@ public void stop() { /** * Do not call manually. Called by the running web server. + * * @param exchange The http reqest * @throws IOException If the IO handling goes wrong */ From 1f27130f5a70b19a7e40ab61c6509824f579317d Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Tue, 13 Feb 2024 15:40:37 +0100 Subject: [PATCH 31/37] spotless --- cli/src/main/java/de/jplag/cli/server/ReportViewer.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java index 3f7cf73df..509203015 100644 --- a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java +++ b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java @@ -36,7 +36,7 @@ public class ReportViewer implements HttpHandler { /** * @param zipFile The zip file to use for the report viewer - * @param port The port to use for the server. You can use 0 to use any free port. + * @param port The port to use for the server. You can use 0 to use any free port. * @throws IOException If the zip file cannot be read */ public ReportViewer(File zipFile, int port) throws IOException { @@ -48,9 +48,8 @@ public ReportViewer(File zipFile, int port) throws IOException { } /** - * Starts the server and serves the internal report viewer. If available, the result.zip is also exposed. - * If the given port is already in use, the next free port will be used. - * + * Starts the server and serves the internal report viewer. If available, the result.zip is also exposed. If the given + * port is already in use, the next free port will be used. * @return The port the server runs at * @throws IOException If the server cannot be started */ @@ -89,7 +88,6 @@ public void stop() { /** * Do not call manually. Called by the running web server. - * * @param exchange The http reqest * @throws IOException If the IO handling goes wrong */ From 13d40b6c0f2b62c37fa5b893a0f0af2fcd79463a Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Wed, 14 Feb 2024 18:53:49 +0100 Subject: [PATCH 32/37] Clean up logging for internal server and remove unnecessary index.html --- README.md | 2 +- cli/src/main/java/de/jplag/cli/JPlagMode.java | 2 +- cli/src/main/java/de/jplag/cli/server/ReportViewer.java | 3 ++- cli/src/main/java/de/jplag/cli/server/ResponseData.java | 9 --------- cli/src/main/resources/index.html | 9 --------- 5 files changed, 4 insertions(+), 21 deletions(-) delete mode 100644 cli/src/main/resources/index.html diff --git a/README.md b/README.md index f35e2c2b0..0bbf4ce1d 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ JPlag is released on [Maven Central](https://search.maven.org/search?q=de.jplag) 1. Download or clone the code from this repository. 2. Run `mvn clean package` from the root of the repository to compile and build all submodules. Run `mvn clean package assembly:single` instead if you need the full jar which includes all dependencies. - Run `mvn -Pwith-report-viewer clean package assembly:single` to build the full jar with the report viewer. In this case, you'll need [Node.js](https://nodejs.org/en/download) installed. + Run `mvn -P with-report-viewer clean package assembly:single` to build the full jar with the report viewer. In this case, you'll need [Node.js](https://nodejs.org/en/download) installed. 3. You will find the generated JARs in the subdirectory `cli/target`. ## Usage diff --git a/cli/src/main/java/de/jplag/cli/JPlagMode.java b/cli/src/main/java/de/jplag/cli/JPlagMode.java index 212e9ab3e..519565a39 100644 --- a/cli/src/main/java/de/jplag/cli/JPlagMode.java +++ b/cli/src/main/java/de/jplag/cli/JPlagMode.java @@ -11,7 +11,7 @@ public enum JPlagMode { /** * Only start the report viewer */ - VIEWER, + VIEW, /** * Run JPlag and open the result in report viewer */ diff --git a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java index 509203015..1170cb1d9 100644 --- a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java +++ b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java @@ -60,11 +60,12 @@ public int start() throws IOException { int port = this.port; int remainingLookups = MAX_PORT_LOOKUPS; - BindException lastException = new BindException("Could not bind server."); + BindException lastException = new BindException("Could not create server. Probably due to no free port found."); while (server == null && remainingLookups-- > 0) { try { server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), port), 0); } catch (BindException e) { + logger.info("Port {} is not available. Trying to find a different one.", this.port); lastException = e; port++; } diff --git a/cli/src/main/java/de/jplag/cli/server/ResponseData.java b/cli/src/main/java/de/jplag/cli/server/ResponseData.java index 94c527abb..297faf3a7 100644 --- a/cli/src/main/java/de/jplag/cli/server/ResponseData.java +++ b/cli/src/main/java/de/jplag/cli/server/ResponseData.java @@ -2,9 +2,6 @@ import java.io.InputStream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * Data for a http response * @param stream The stream containing the binary data @@ -12,9 +9,6 @@ * @param size The total size of the data */ public record ResponseData(InputStream stream, ContentType contentType, int size) { - - private static final Logger logger = LoggerFactory.getLogger(ResponseData.class); - /** * Constructor with unknown type and size. Type will be set to PLAIN. * @param data The binary data to respond with @@ -47,9 +41,6 @@ public static ResponseData fromResourceUrl(String url) { if (inputStream != null) { return new ResponseData(inputStream, ContentType.fromPath(url)); } else { - logger.info( - "No response data available for resource url {}. This is probably normal behaviour and the data will be pulled from a different source.", - url); return null; } } diff --git a/cli/src/main/resources/index.html b/cli/src/main/resources/index.html deleted file mode 100644 index fe5144e1c..000000000 --- a/cli/src/main/resources/index.html +++ /dev/null @@ -1,9 +0,0 @@ - - - JPlag Report Viewer - - - -This page has moved. Moving to /JPlag/ - - \ No newline at end of file From c1982745e28057472f44539adeffa592fdf3871a Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Wed, 14 Feb 2024 18:58:45 +0100 Subject: [PATCH 33/37] Fixed error due to renaming of enum constant. --- cli/src/main/java/de/jplag/cli/CLI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/de/jplag/cli/CLI.java b/cli/src/main/java/de/jplag/cli/CLI.java index f4fefacc6..bfe2b3d47 100644 --- a/cli/src/main/java/de/jplag/cli/CLI.java +++ b/cli/src/main/java/de/jplag/cli/CLI.java @@ -83,7 +83,7 @@ public static void main(String[] args) { if (!parseResult.isUsageHelpRequested() && !(parseResult.subcommand() != null && parseResult.subcommand().isUsageHelpRequested())) { switch (cli.options.mode) { case RUN -> cli.runJPlag(parseResult); - case VIEWER -> cli.runViewer(null); + case VIEW -> cli.runViewer(null); case RUN_AND_VIEW -> cli.runViewer(cli.runJPlag(parseResult)); } } From c3fcf62d0913f208990e10c7f7496bf91e0e023e Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Fri, 16 Feb 2024 08:43:33 +0100 Subject: [PATCH 34/37] Added more tests for RoutingTree --- .../de/jplag/cli/server/RoutingTreeTest.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/cli/src/test/java/de/jplag/cli/server/RoutingTreeTest.java b/cli/src/test/java/de/jplag/cli/server/RoutingTreeTest.java index bd7680592..75b02d9c1 100644 --- a/cli/src/test/java/de/jplag/cli/server/RoutingTreeTest.java +++ b/cli/src/test/java/de/jplag/cli/server/RoutingTreeTest.java @@ -3,6 +3,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.commons.lang3.tuple.Pair; @@ -39,6 +41,30 @@ public void testAccessRoutingTree() { assertEquals(secondRoutingPath, ((TestRouting) secondRouting.getRight()).path); } + @Test + public void testUnknownPath() { + assertNull(this.routingTree.resolveRouting(new RoutingPath("/unknown.html"))); + } + + @Test + public void testPartialPathRoute() { + RoutingTree routingTree = new RoutingTree(); + routingTree.insertRouting("/path/", new TestRouting("")); + assertNotNull(routingTree.resolveRouting(new RoutingPath("/path/index.html"))); + } + + @Test + public void testPartialPathRouteWithSubpath() { + RoutingTree routingTree = new RoutingTree(); + routingTree.insertRouting("/path/", new TestRouting("/path/")); + routingTree.insertRouting("/path/subPath/a.html", new TestRouting("")); + + Pair result = routingTree.resolveRouting(new RoutingPath("/path/subPath/b.html")); + assertNotNull(result); + assertInstanceOf(TestRouting.class, result.getRight()); + assertEquals("/path/", ((TestRouting) result.getRight()).path); + } + private static class TestRouting implements Routing { private final String path; From 00d7d8c6932a0d221e22e0b5c7b5765c3c55b667 Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Fri, 16 Feb 2024 09:34:14 +0100 Subject: [PATCH 35/37] Fixed log message if port is not available. --- cli/src/main/java/de/jplag/cli/server/ReportViewer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java index 1170cb1d9..1bcec1240 100644 --- a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java +++ b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java @@ -65,7 +65,7 @@ public int start() throws IOException { try { server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), port), 0); } catch (BindException e) { - logger.info("Port {} is not available. Trying to find a different one.", this.port); + logger.info("Port {} is not available. Trying to find a different one.", port); lastException = e; port++; } From cec3c6dd7da974533b93c4d9f54e00ec4e69db2c Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Tue, 20 Feb 2024 12:06:35 +0100 Subject: [PATCH 36/37] Sonarcloud issues --- cli/src/main/java/de/jplag/cli/CLI.java | 4 ++-- .../main/java/de/jplag/cli/server/ReportViewer.java | 10 +++++----- .../test/java/de/jplag/cli/server/RoutingTreeTest.java | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cli/src/main/java/de/jplag/cli/CLI.java b/cli/src/main/java/de/jplag/cli/CLI.java index bfe2b3d47..959675e70 100644 --- a/cli/src/main/java/de/jplag/cli/CLI.java +++ b/cli/src/main/java/de/jplag/cli/CLI.java @@ -119,8 +119,8 @@ public CLI() { } public File runJPlag(ParseResult parseResult) throws ExitException, FileNotFoundException { - JPlagOptions options = buildOptionsFromArguments(parseResult); - JPlagResult result = JPlag.run(options); + JPlagOptions jplagOptions = buildOptionsFromArguments(parseResult); + JPlagResult result = JPlag.run(jplagOptions); File target = new File(getResultFolder() + ".zip"); ReportObjectFactory reportObjectFactory = new ReportObjectFactory(target); reportObjectFactory.createAndSaveReport(result); diff --git a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java index 1bcec1240..c43cf4c1b 100644 --- a/cli/src/main/java/de/jplag/cli/server/ReportViewer.java +++ b/cli/src/main/java/de/jplag/cli/server/ReportViewer.java @@ -58,16 +58,16 @@ public int start() throws IOException { throw new IllegalStateException("Server already started"); } - int port = this.port; + int currentPort = this.port; int remainingLookups = MAX_PORT_LOOKUPS; BindException lastException = new BindException("Could not create server. Probably due to no free port found."); while (server == null && remainingLookups-- > 0) { try { - server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), port), 0); + server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), currentPort), 0); } catch (BindException e) { - logger.info("Port {} is not available. Trying to find a different one.", port); + logger.info("Port {} is not available. Trying to find a different one.", currentPort); lastException = e; - port++; + currentPort++; } } if (server == null) { @@ -107,7 +107,7 @@ public void handle(HttpExchange exchange) throws IOException { ResponseData responseData = resolved.getRight().fetchData(resolved.getLeft(), exchange, this); if (responseData == null) { - logger.warn("No response data found for path: " + path.asPath()); + logger.warn("No response data found for path: {}", path.asPath()); exchange.sendResponseHeaders(NOT_FOUND_RESPONSE, 0); exchange.close(); return; diff --git a/cli/src/test/java/de/jplag/cli/server/RoutingTreeTest.java b/cli/src/test/java/de/jplag/cli/server/RoutingTreeTest.java index 75b02d9c1..318c00db2 100644 --- a/cli/src/test/java/de/jplag/cli/server/RoutingTreeTest.java +++ b/cli/src/test/java/de/jplag/cli/server/RoutingTreeTest.java @@ -26,7 +26,7 @@ void setUp() { } @Test - public void testAccessRoutingTree() { + void testAccessRoutingTree() { Pair firstRouting = this.routingTree.resolveRouting(new RoutingPath(firstRoutingPath)); Pair secondRouting = this.routingTree.resolveRouting(new RoutingPath(secondRoutingPath + "/suffix")); @@ -42,19 +42,19 @@ public void testAccessRoutingTree() { } @Test - public void testUnknownPath() { + void testUnknownPath() { assertNull(this.routingTree.resolveRouting(new RoutingPath("/unknown.html"))); } @Test - public void testPartialPathRoute() { + void testPartialPathRoute() { RoutingTree routingTree = new RoutingTree(); routingTree.insertRouting("/path/", new TestRouting("")); assertNotNull(routingTree.resolveRouting(new RoutingPath("/path/index.html"))); } @Test - public void testPartialPathRouteWithSubpath() { + void testPartialPathRouteWithSubpath() { RoutingTree routingTree = new RoutingTree(); routingTree.insertRouting("/path/", new TestRouting("/path/")); routingTree.insertRouting("/path/subPath/a.html", new TestRouting("")); From d4303bc769a4c69df68480f091fe90af3edee7b1 Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Tue, 20 Feb 2024 12:18:56 +0100 Subject: [PATCH 37/37] Code style and documentation --- cli/src/main/java/de/jplag/cli/CLI.java | 2 +- .../main/java/de/jplag/cli/CliOptions.java | 47 ++++++++++--------- cli/src/main/java/de/jplag/cli/JPlagMode.java | 2 +- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/cli/src/main/java/de/jplag/cli/CLI.java b/cli/src/main/java/de/jplag/cli/CLI.java index 06388e8a6..ec72e3e1d 100644 --- a/cli/src/main/java/de/jplag/cli/CLI.java +++ b/cli/src/main/java/de/jplag/cli/CLI.java @@ -25,8 +25,8 @@ import de.jplag.JPlagResult; import de.jplag.Language; import de.jplag.cli.logger.CollectedLoggerFactory; -import de.jplag.cli.server.ReportViewer; import de.jplag.cli.logger.TongfeiProgressBarProvider; +import de.jplag.cli.server.ReportViewer; import de.jplag.clustering.ClusteringOptions; import de.jplag.clustering.Preprocessing; import de.jplag.exceptions.ExitException; diff --git a/cli/src/main/java/de/jplag/cli/CliOptions.java b/cli/src/main/java/de/jplag/cli/CliOptions.java index 1a915f660..f66c18967 100644 --- a/cli/src/main/java/de/jplag/cli/CliOptions.java +++ b/cli/src/main/java/de/jplag/cli/CliOptions.java @@ -20,26 +20,26 @@ public class CliOptions implements Runnable { public static final Language defaultLanguage = new JavaLanguage(); - @Parameters(paramLabel = "root-dirs", description = "Root-directory with submissions to check for plagiarism%n", split = ",") + @Parameters(paramLabel = "root-dirs", description = "Root-directory with submissions to check for plagiarism", split = ",") public File[] rootDirectory = new File[0]; @Option(names = {"--new", - "-new"}, split = ",", description = "Root-directory with submissions to check for plagiarism (same as the root directory)%n") + "-new"}, split = ",", description = "Root-directory with submissions to check for plagiarism (same as the root directory)") public File[] newDirectories = new File[0]; - @Option(names = {"--old", "-old"}, split = ",", description = "Root-directory with prior submissions to compare against%n") + @Option(names = {"--old", "-old"}, split = ",", description = "Root-directory with prior submissions to compare against") public File[] oldDirectories = new File[0]; @Option(names = {"--language", - "-l"}, arity = "1", converter = LanguageConverter.class, completionCandidates = LanguageCandidates.class, description = "Select the language to parse the submissions (default: ${DEFAULT-VALUE}). The language names are the same as the subcommands.%n") + "-l"}, arity = "1", converter = LanguageConverter.class, completionCandidates = LanguageCandidates.class, description = "Select the language to parse the submissions (default: ${DEFAULT-VALUE}). The language names are the same as the subcommands.") public Language language = defaultLanguage; @Option(names = {"-bc", "--bc", - "--base-code"}, description = "Path of the directory containing the base code (common framework used in all submissions)%n") + "--base-code"}, description = "Path of the directory containing the base code (common framework used in all submissions)") public String baseCode; @Option(names = {"-t", "--min-tokens"}, description = "Tunes the comparison sensitivity by adjusting the minimum token required to be counted " - + "as a matching section. A smaller increases the sensitivity but might lead to more " + "false-positives%n") + + "as a matching section. A smaller increases the sensitivity but might lead to more " + "false-positives") public Integer minTokenMatch = null; @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help and exit") @@ -47,23 +47,24 @@ public class CliOptions implements Runnable { @Option(names = {"-n", "--shown-comparisons"}, description = "The maximum number of comparisons that will be shown in the generated report, if set " - + "to -1 all comparisons will be shown (default: ${DEFAULT-VALUE})%n") + + "to -1 all comparisons will be shown (default: ${DEFAULT-VALUE})") public int shownComparisons = JPlagOptions.DEFAULT_SHOWN_COMPARISONS; @Option(names = {"-r", - "--result-file"}, description = "Name of the file in which the comparison results will be stored (default: ${DEFAULT-VALUE}). Missing .zip endings will be automatically added.%n") + "--result-file"}, description = "Name of the file in which the comparison results will be stored (default: ${DEFAULT-VALUE}). Missing .zip endings will be automatically added.") public String resultFile = "results"; - @Option(names = {"--mode"}, description = "The mode to run jplag in") + @Option(names = { + "--mode"}, description = "The mode of JPlag: either only run analysis, only open the viewer, or do both (default: ${DEFAULT_VALUE})") public JPlagMode mode = JPlagMode.RUN; - @ArgGroup(heading = "Advanced%n", exclusive = false) + @ArgGroup(heading = "%nAdvanced%n", exclusive = false) public Advanced advanced = new Advanced(); - @ArgGroup(validate = false, heading = "Clustering%n") + @ArgGroup(validate = false, heading = "%nClustering%n") public Clustering clustering = new Clustering(); - @ArgGroup(validate = false, heading = "Merging of neighboring matches to increase the similarity of concealed plagiarism:%n") + @ArgGroup(validate = false, heading = "%nMerging of neighboring matches to increase the similarity of concealed plagiarism:%n") public Merging merging = new Merging(); @Option(names = {"--normalize"}, description = "Activate the normalization of tokens. Supported for languages: Java, C++.") @@ -78,22 +79,22 @@ public void run() { } public static class Advanced { - @Option(names = {"-d", "--debug"}, description = "Debug parser. Non-parsable files will be stored (default: ${DEFAULT-VALUE})%n") + @Option(names = {"-d", "--debug"}, description = "Debug parser. Non-parsable files will be stored (default: ${DEFAULT-VALUE})") public boolean debug; - @Option(names = {"-s", "--subdirectory"}, description = "Look in directories /*/ for programs%n") + @Option(names = {"-s", "--subdirectory"}, description = "Look in directories /*/ for programs") public String subdirectory; - @Option(names = {"-p", "--suffixes"}, split = ",", description = "comma-separated list of all filename suffixes that are included%n") + @Option(names = {"-p", "--suffixes"}, split = ",", description = "comma-separated list of all filename suffixes that are included") public String[] suffixes = new String[0]; @Option(names = {"-x", - "--exclusion-file"}, description = "All files named in this file will be ignored in the comparison (line-separated list)%n") + "--exclusion-file"}, description = "All files named in this file will be ignored in the comparison (line-separated list)") public String exclusionFileName; @Option(names = {"-m", "--similarity-threshold"}, description = "Comparison similarity threshold [0.0-1.0]: All comparisons above this threshold will " - + "be saved (default: ${DEFAULT-VALUE})%n") + + "be saved (default: ${DEFAULT-VALUE})") public double similarityThreshold = JPlagOptions.DEFAULT_SIMILARITY_THRESHOLD; @Option(names = {"-P", "--port"}, description = "The port used for the internal report viewer.") @@ -104,7 +105,7 @@ public static class Advanced { } public static class Clustering { - @Option(names = {"--cluster-skip"}, description = "Skips the clustering (default: ${DEFAULT-VALUE})%n") + @Option(names = {"--cluster-skip"}, description = "Skips the clustering (default: ${DEFAULT-VALUE})") public boolean disable; @ArgGroup @@ -115,25 +116,25 @@ public static class ClusteringEnabled { "--cluster-algorithm"}, description = "Which clustering algorithm to use. Agglomerative merges similar submissions bottom up. " + "Spectral clustering is combined with Bayesian Optimization to execute the k-Means " + "clustering algorithm multiple times, hopefully finding a \"good\" clustering " - + "automatically. (default: ${DEFAULT-VALUE})%n") + + "automatically. (default: ${DEFAULT-VALUE})") public ClusteringAlgorithm algorithm = new ClusteringOptions().algorithm(); @Option(names = { "--cluster-metric"}, description = "The metric used for clustering. AVG is intersection over union, MAX can expose some " - + "attempts of obfuscation. (default: ${DEFAULT-VALUE})%n") + + "attempts of obfuscation. (default: ${DEFAULT-VALUE})") public SimilarityMetric metric = new ClusteringOptions().similarityMetric(); } } public static class Merging { - @Option(names = {"--match-merging"}, description = "Enables match merging (default: ${DEFAULT-VALUE})%n") + @Option(names = {"--match-merging"}, description = "Enables match merging (default: ${DEFAULT-VALUE})") public boolean enabled = MergingOptions.DEFAULT_ENABLED; - @Option(names = {"--neighbor-length"}, description = "Defines how short a match can be, to be considered (default: ${DEFAULT-VALUE})%n") + @Option(names = {"--neighbor-length"}, description = "Defines how short a match can be, to be considered (default: ${DEFAULT-VALUE})") public int minimumNeighborLength = MergingOptions.DEFAULT_NEIGHBOR_LENGTH; @Option(names = { - "--gap-size"}, description = "Defines how many token there can be between two neighboring matches (default: ${DEFAULT-VALUE})%n") + "--gap-size"}, description = "Defines how many token there can be between two neighboring matches (default: ${DEFAULT-VALUE})") public int maximumGapSize = MergingOptions.DEFAULT_GAP_SIZE; } diff --git a/cli/src/main/java/de/jplag/cli/JPlagMode.java b/cli/src/main/java/de/jplag/cli/JPlagMode.java index 519565a39..402a18b58 100644 --- a/cli/src/main/java/de/jplag/cli/JPlagMode.java +++ b/cli/src/main/java/de/jplag/cli/JPlagMode.java @@ -1,7 +1,7 @@ package de.jplag.cli; /** - * The mode JPlag runs in. This influences which steps JPlag will execute + * The mode JPlag runs in. This influences which steps JPlag will execute. */ public enum JPlagMode { /**