Просмотр исходного кода

Add slf4j as logger and extracted CLI to own module

Dominik Fuchß 4 лет назад
Родитель
Сommit
ca28f10bdc
33 измененных файлов с 201 добавлено и 143 удалено
  1. 1 1
      .github/workflows/maven.yml
  2. 1 1
      README.md
  3. 64 0
      jplag.cli/pom.xml
  4. 9 32
      jplag.cli/src/main/java/de/jplag/CLI.java
  5. 0 0
      jplag.cli/src/main/java/de/jplag/CliGroupHelper.java
  6. 0 0
      jplag.cli/src/main/java/de/jplag/CommandLineArgument.java
  7. 0 0
      jplag.cli/src/test/java/de/jplag/cli/BaseCodeOptionTest.java
  8. 0 0
      jplag.cli/src/test/java/de/jplag/cli/ClusteringTest.java
  9. 0 0
      jplag.cli/src/test/java/de/jplag/cli/CommandLineInterfaceTest.java
  10. 0 0
      jplag.cli/src/test/java/de/jplag/cli/ComparisonModeTest.java
  11. 0 0
      jplag.cli/src/test/java/de/jplag/cli/LanguageOptionTest.java
  12. 0 0
      jplag.cli/src/test/java/de/jplag/cli/MinTokenMatchTest.java
  13. 0 0
      jplag.cli/src/test/java/de/jplag/cli/OldNewRootDirectoriesArgumentTest.java
  14. 0 0
      jplag.cli/src/test/java/de/jplag/cli/SimiliarityThresholdTest.java
  15. 0 0
      jplag.cli/src/test/java/de/jplag/cli/StoredMatchesTest.java
  16. 6 2
      jplag.frontend-testutils/src/test/java/de/jplag/testutils/TestErrorConsumer.java
  17. 5 3
      jplag.frontend-utils/src/main/java/de/jplag/TokenPrinter.java
  18. 5 1
      jplag.frontend.csharp-6/src/test/java/de/jplag/csharp/MinimalCSharpFrontendTest.java
  19. 6 1
      jplag.frontend.java/src/main/java/de/jplag/java/JavaToken.java
  20. 6 1
      jplag.frontend.python-3/src/main/java/de/jplag/python3/Python3Token.java
  21. 6 1
      jplag.frontend.text/src/main/java/de/jplag/text/Parser.java
  22. 0 50
      jplag/pom.xml
  23. 10 6
      jplag/src/main/java/de/jplag/ErrorCollector.java
  24. 7 2
      jplag/src/main/java/de/jplag/JPlag.java
  25. 12 16
      jplag/src/main/java/de/jplag/SubmissionSetBuilder.java
  26. 5 1
      jplag/src/main/java/de/jplag/clustering/algorithm/BayesianOptimization.java
  27. 7 2
      jplag/src/main/java/de/jplag/options/JPlagOptions.java
  28. 1 1
      jplag/src/main/java/de/jplag/reporting/JsonReport.java
  29. 5 0
      jplag/src/main/java/de/jplag/reporting/Report.java
  30. 7 2
      jplag/src/main/java/de/jplag/reporting/jsonfactory/JsonFactory.java
  31. 9 18
      jplag/src/main/java/de/jplag/reporting/reportobject/ReportObjectFactory.java
  32. 6 1
      jplag/src/main/java/de/jplag/strategy/AbstractComparisonStrategy.java
  33. 23 1
      pom.xml

+ 1 - 1
.github/workflows/maven.yml

@@ -31,6 +31,6 @@ jobs:
       uses: actions/upload-artifact@v3
       with:
         name: "JPlag"
-        path: "jplag/target/jplag-*-jar-with-dependencies.jar"
+        path: "jplag.cli/target/jplag-*-jar-with-dependencies.jar"
       
 

+ 1 - 1
README.md

@@ -28,7 +28,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.
-5. You will find the generated JARs in the subdirectory `jplag/target`.
+5. You will find the generated JARs in the subdirectory `jplag.cli/target`.
 
 ## Usage
 JPlag can either be used via the CLI or directly via its Java API. For more information, see the [usage information in the wiki](https://github.com/jplag/JPlag/wiki/1.-How-to-Use-JPlag).

+ 64 - 0
jplag.cli/pom.xml

@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>de.jplag</groupId>
+        <artifactId>aggregator</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <artifactId>cli</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>de.jplag</groupId>
+            <artifactId>jplag</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>net.sourceforge.argparse4j</groupId>
+            <artifactId>argparse4j</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <mainClass>de.jplag.CLI</mainClass>
+                            <packageName>jplag</packageName>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <descriptorRefs>
+                        <descriptorRef>jar-with-dependencies</descriptorRef>
+                    </descriptorRefs>
+                    <archive>
+                        <manifest>
+                            <mainClass>de.jplag.CLI</mainClass>
+                            <packageName>jplag</packageName>
+                        </manifest>
+                    </archive>
+                    <finalName>jplag-${project.version}</finalName>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <!-- Skip deployment of CLI to Maven Central -->
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 9 - 32
jplag/src/main/java/de/jplag/CLI.java → jplag.cli/src/main/java/de/jplag/CLI.java

@@ -1,34 +1,6 @@
 package de.jplag;
 
-import static de.jplag.CommandLineArgument.BASE_CODE;
-import static de.jplag.CommandLineArgument.CLUSTER_AGGLOMERATIVE_INTER_CLUSTER_SIMILARITY;
-import static de.jplag.CommandLineArgument.CLUSTER_AGGLOMERATIVE_THRESHOLD;
-import static de.jplag.CommandLineArgument.CLUSTER_ALGORITHM;
-import static de.jplag.CommandLineArgument.CLUSTER_ENABLE;
-import static de.jplag.CommandLineArgument.CLUSTER_METRIC;
-import static de.jplag.CommandLineArgument.CLUSTER_PREPROCESSING_CDF;
-import static de.jplag.CommandLineArgument.CLUSTER_PREPROCESSING_NONE;
-import static de.jplag.CommandLineArgument.CLUSTER_PREPROCESSING_PERCENTILE;
-import static de.jplag.CommandLineArgument.CLUSTER_PREPROCESSING_THRESHOLD;
-import static de.jplag.CommandLineArgument.CLUSTER_SPECTRAL_BANDWIDTH;
-import static de.jplag.CommandLineArgument.CLUSTER_SPECTRAL_KMEANS_ITERATIONS;
-import static de.jplag.CommandLineArgument.CLUSTER_SPECTRAL_MAX_RUNS;
-import static de.jplag.CommandLineArgument.CLUSTER_SPECTRAL_MIN_RUNS;
-import static de.jplag.CommandLineArgument.CLUSTER_SPECTRAL_NOISE;
-import static de.jplag.CommandLineArgument.COMPARISON_MODE;
-import static de.jplag.CommandLineArgument.DEBUG;
-import static de.jplag.CommandLineArgument.EXCLUDE_FILE;
-import static de.jplag.CommandLineArgument.LANGUAGE;
-import static de.jplag.CommandLineArgument.MIN_TOKEN_MATCH;
-import static de.jplag.CommandLineArgument.NEW_DIRECTORY;
-import static de.jplag.CommandLineArgument.OLD_DIRECTORY;
-import static de.jplag.CommandLineArgument.RESULT_FOLDER;
-import static de.jplag.CommandLineArgument.ROOT_DIRECTORY;
-import static de.jplag.CommandLineArgument.SHOWN_COMPARISONS;
-import static de.jplag.CommandLineArgument.SIMILARITY_THRESHOLD;
-import static de.jplag.CommandLineArgument.SUBDIRECTORY;
-import static de.jplag.CommandLineArgument.SUFFIXES;
-import static de.jplag.CommandLineArgument.VERBOSITY;
+import static de.jplag.CommandLineArgument.*;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -40,6 +12,9 @@ import net.sourceforge.argparse4j.inf.ArgumentParser;
 import net.sourceforge.argparse4j.inf.ArgumentParserException;
 import net.sourceforge.argparse4j.inf.Namespace;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import de.jplag.clustering.ClusteringAlgorithm;
 import de.jplag.clustering.ClusteringOptions;
 import de.jplag.clustering.Preprocessing;
@@ -59,6 +34,8 @@ import de.jplag.strategy.ComparisonMode;
  */
 public class CLI {
 
+    private static final Logger logger = LoggerFactory.getLogger(CLI.class);
+
     private static final String CREDITS = "Created by IPD Tichy, Guido Malpohl, and others. JPlag logo designed by Sandro Koch. Currently maintained by Sebastian Hahner and Timur Saglam.";
 
     private static final String[] DESCRIPTIONS = {"Detecting Software Plagiarism", "Software-Archaeological Playground", "Since 1996",
@@ -81,12 +58,12 @@ public class CLI {
             Namespace arguments = cli.parseArguments(args);
             JPlagOptions options = cli.buildOptionsFromArguments(arguments);
             JPlag program = new JPlag(options);
-            System.out.println("JPlag initialized");
+            logger.info("JPlag initialized");
             JPlagResult result = program.run();
             Report report = new JsonReport();
             report.saveReport(result, arguments.getString(RESULT_FOLDER.flagWithoutDash()));
         } catch (ExitException exception) {
-            System.out.println("Error: " + exception.getMessage());
+            logger.error(exception.getMessage(), exception);
             System.exit(1);
         }
     }
@@ -148,7 +125,7 @@ public class CLI {
         options.setSimilarityThreshold(SIMILARITY_THRESHOLD.getFrom(namespace));
         options.setMaximumNumberOfComparisons(SHOWN_COMPARISONS.getFrom(namespace));
         ComparisonMode.fromName(COMPARISON_MODE.getFrom(namespace)).ifPresentOrElse(options::setComparisonMode,
-                () -> System.out.println("Unknown comparison mode, using default mode!"));
+                () -> logger.warn("Unknown comparison mode, using default mode!"));
 
         ClusteringOptions.Builder clusteringBuilder = new ClusteringOptions.Builder();
         Optional.ofNullable((Boolean) CLUSTER_ENABLE.getFrom(namespace)).ifPresent(clusteringBuilder::enabled);

+ 0 - 0
jplag/src/main/java/de/jplag/CliGroupHelper.java → jplag.cli/src/main/java/de/jplag/CliGroupHelper.java


+ 0 - 0
jplag/src/main/java/de/jplag/CommandLineArgument.java → jplag.cli/src/main/java/de/jplag/CommandLineArgument.java


+ 0 - 0
jplag/src/test/java/de/jplag/cli/BaseCodeOptionTest.java → jplag.cli/src/test/java/de/jplag/cli/BaseCodeOptionTest.java


+ 0 - 0
jplag/src/test/java/de/jplag/cli/ClusteringTest.java → jplag.cli/src/test/java/de/jplag/cli/ClusteringTest.java


+ 0 - 0
jplag/src/test/java/de/jplag/cli/CommandLineInterfaceTest.java → jplag.cli/src/test/java/de/jplag/cli/CommandLineInterfaceTest.java


+ 0 - 0
jplag/src/test/java/de/jplag/cli/ComparisonModeTest.java → jplag.cli/src/test/java/de/jplag/cli/ComparisonModeTest.java


+ 0 - 0
jplag/src/test/java/de/jplag/cli/LanguageOptionTest.java → jplag.cli/src/test/java/de/jplag/cli/LanguageOptionTest.java


+ 0 - 0
jplag/src/test/java/de/jplag/cli/MinTokenMatchTest.java → jplag.cli/src/test/java/de/jplag/cli/MinTokenMatchTest.java


+ 0 - 0
jplag/src/test/java/de/jplag/cli/OldNewRootDirectoriesArgumentTest.java → jplag.cli/src/test/java/de/jplag/cli/OldNewRootDirectoriesArgumentTest.java


+ 0 - 0
jplag/src/test/java/de/jplag/cli/SimiliarityThresholdTest.java → jplag.cli/src/test/java/de/jplag/cli/SimiliarityThresholdTest.java


+ 0 - 0
jplag/src/test/java/de/jplag/cli/StoredMatchesTest.java → jplag.cli/src/test/java/de/jplag/cli/StoredMatchesTest.java


+ 6 - 2
jplag.frontend-testutils/src/test/java/de/jplag/testutils/TestErrorConsumer.java

@@ -2,6 +2,9 @@ package de.jplag.testutils;
 
 import static org.junit.jupiter.api.Assertions.fail;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import de.jplag.ErrorConsumer;
 
 /**
@@ -9,16 +12,17 @@ import de.jplag.ErrorConsumer;
  * @author Timur Saglam
  */
 public class TestErrorConsumer implements ErrorConsumer {
+    private final Logger logger = LoggerFactory.getLogger("JPlag-Test");
 
     @Override
     public void addError(String errorMessage) {
-        System.err.println(errorMessage);
+        logger.error(errorMessage);
         fail(errorMessage);
     }
 
     @Override
     public void print(String message, String longMessage) {
-        System.out.println(message);
+        logger.info(message);
     }
 
 }

+ 5 - 3
jplag.frontend-utils/src/main/java/de/jplag/TokenPrinter.java

@@ -11,6 +11,9 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 /**
  * Utility class for printing JPlag tokens from a submission. Each line of code is printed starting with the line
  * number. Under these lines the tokens are annotated in the format <code>|TOKEN|</code>. The first vertical line marks
@@ -20,7 +23,7 @@ import java.util.Map;
  * @author Timur Saglam
  */
 public final class TokenPrinter {
-
+    private static final Logger logger = LoggerFactory.getLogger(TokenPrinter.class);
     // Representation:
     private static final String BAR = "|";
     private static final String TAB = "\t";
@@ -145,8 +148,7 @@ public final class TokenPrinter {
         try {
             return Files.readAllLines(file.toPath());
         } catch (IOException exception) {
-            System.err.println("Cannot read " + file.getAbsolutePath() + ":");
-            exception.printStackTrace();
+            logger.error("Cannot read " + file.getAbsolutePath() + ":", exception);
         }
         return Collections.emptyList();
     }

+ 5 - 1
jplag.frontend.csharp-6/src/test/java/de/jplag/csharp/MinimalCSharpFrontendTest.java

@@ -13,6 +13,8 @@ import java.util.stream.StreamSupport;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import de.jplag.Token;
 import de.jplag.TokenConstants;
@@ -21,6 +23,8 @@ import de.jplag.TokenPrinter;
 import de.jplag.testutils.TestErrorConsumer;
 
 class MinimalCSharpFrontendTest {
+    private final Logger logger = LoggerFactory.getLogger("JPlag-Test");
+
     private static final int EXPEXTED_NUMBER_OF_TOKENS = 15;
     private static final Path BASE_PATH = Path.of("src", "test", "resources", "de", "jplag", "csharp");
     private static final String TEST_SUBJECT = "TestClass.cs";
@@ -44,7 +48,7 @@ class MinimalCSharpFrontendTest {
         // Parse test input
         String[] input = new String[] {TEST_SUBJECT};
         TokenList result = frontend.parse(baseDirectory, input);
-        System.out.println(TokenPrinter.printTokens(result, baseDirectory, Arrays.asList(input)));
+        logger.info(TokenPrinter.printTokens(result, baseDirectory, Arrays.asList(input)));
 
         // Compare parsed tokens:
         assertEquals(EXPEXTED_NUMBER_OF_TOKENS, result.size());

+ 6 - 1
jplag.frontend.java/src/main/java/de/jplag/java/JavaToken.java

@@ -1,10 +1,15 @@
 package de.jplag.java;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import de.jplag.Token;
 import de.jplag.TokenConstants;
 
 public class JavaToken extends Token implements JavaTokenConstants {
 
+    private final Logger logger = LoggerFactory.getLogger(JavaToken.class);
+
     public JavaToken(int type, String file, int line, int column, int length) {
         super(type, file, line, column, length);
     }
@@ -154,7 +159,7 @@ public class JavaToken extends Token implements JavaTokenConstants {
                 return "}RECORD";
 
             default:
-                System.err.println("UNKNOWN: " + type);
+                logger.error("UNKNOWN: " + type);
                 return "<UNKNOWN" + type + ">";
         }
     }

+ 6 - 1
jplag.frontend.python-3/src/main/java/de/jplag/python3/Python3Token.java

@@ -1,9 +1,14 @@
 package de.jplag.python3;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import de.jplag.Token;
 
 public class Python3Token extends Token implements Python3TokenConstants {
 
+    private static final Logger logger = LoggerFactory.getLogger(Python3Token.class);
+
     public Python3Token(int type, String file, int line, int column, int length) {
         super(type, file, line, column, length);
     }
@@ -76,7 +81,7 @@ public class Python3Token extends Token implements Python3TokenConstants {
             case WITH_END:
                 return "}WITH   ";
             default:
-                System.err.println("*UNKNOWN: " + type);
+                logger.error("*UNKNOWN: " + type);
                 return "*UNKNOWN" + type;
         }
     }

+ 6 - 1
jplag.frontend.text/src/main/java/de/jplag/text/Parser.java

@@ -4,6 +4,9 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.util.Hashtable;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import antlr.Token;
 
 import de.jplag.AbstractParser;
@@ -13,6 +16,8 @@ import de.jplag.TokenList;
 
 public class Parser extends AbstractParser {
 
+    private static final Logger logger = LoggerFactory.getLogger(Parser.class);
+
     protected Hashtable<String, Integer> table = new Hashtable<>();
     protected int serial = 1; // 0 is FILE_END token
 
@@ -56,7 +61,7 @@ public class Parser extends AbstractParser {
         runOut = true;
         errors++;
         errorConsumer.print("ERROR: Out of serials!", null);
-        System.out.println("de.jplag.text.Parser: ERROR: Out of serials!");
+        logger.error("de.jplag.text.Parser: ERROR: Out of serials!");
     }
 
     private boolean parseFile(File directory, String file) {

+ 0 - 50
jplag/pom.xml

@@ -11,10 +11,6 @@
     <artifactId>jplag</artifactId>
 
     <dependencies>
-        <dependency>
-            <groupId>net.sourceforge.argparse4j</groupId>
-            <artifactId>argparse4j</artifactId>
-        </dependency>
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
@@ -58,50 +54,4 @@
             <artifactId>chars</artifactId>
         </dependency>
     </dependencies>
-
-    <build>
-        <resources>
-            <resource>
-                <filtering>true</filtering>
-                <directory>src/main/resources</directory>
-            </resource>
-
-            <resource>
-                <filtering>false</filtering>
-                <directory>src/main/resources</directory>
-                <excludes>
-                    <exclude>de/jplag/version.properties</exclude>
-                </excludes>
-            </resource>
-        </resources>
-
-        <plugins>
-            <plugin>
-                <artifactId>maven-jar-plugin</artifactId>
-                <configuration>
-                    <archive>
-                        <manifest>
-                            <mainClass>de.jplag.CLI</mainClass>
-                            <packageName>jplag</packageName>
-                        </manifest>
-                    </archive>
-                </configuration>
-            </plugin>
-
-            <plugin>
-                <artifactId>maven-assembly-plugin</artifactId>
-                <configuration>
-                    <descriptorRefs>
-                        <descriptorRef>jar-with-dependencies</descriptorRef>
-                    </descriptorRefs>
-                    <archive>
-                        <manifest>
-                            <mainClass>de.jplag.CLI</mainClass>
-                            <packageName>jplag</packageName>
-                        </manifest>
-                    </archive>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
 </project>

+ 10 - 6
jplag/src/main/java/de/jplag/ErrorCollector.java

@@ -5,6 +5,9 @@ import static de.jplag.options.Verbosity.LONG;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import de.jplag.options.JPlagOptions;
 import de.jplag.options.Verbosity;
 
@@ -12,8 +15,9 @@ import de.jplag.options.Verbosity;
  * Error collector class that collects errors but also allows printing the collected errors.
  * @author Timur Saglam
  */
-public class ErrorCollector implements ErrorConsumer { // TODO TS should be eventually replaced with a true logger/logging manager
-
+public class ErrorCollector implements ErrorConsumer {
+    // TODO DF We should replace *all* usages of the ErrorConsumer by a suitable logger
+    private final Logger logger = LoggerFactory.getLogger("JPlag");
     private final List<String> collectedErrors; // List of errors that occurred during the execution of the errorConsumer.
     private final JPlagOptions options;
     private String currentSubmissionName;
@@ -37,10 +41,10 @@ public class ErrorCollector implements ErrorConsumer { // TODO TS should be even
         }
         Verbosity verbosity = options.getVerbosity();
         if (message != null) {
-            System.out.println(message);
+            logger.info(message);
         }
         if (longMessage != null && verbosity == LONG) {
-            System.out.println(longMessage);
+            logger.info(message);
         }
     }
 
@@ -49,13 +53,13 @@ public class ErrorCollector implements ErrorConsumer { // TODO TS should be even
      */
     public void printCollectedErrors() {
         StringBuilder errorReport = new StringBuilder();
-        System.out.println("The following errors occured: ");
+        logger.error("The following errors occured: ");
         for (String message : collectedErrors) {
             errorReport.append(message);
             errorReport.append('\n');
         }
 
-        System.out.println(errorReport);
+        logger.error(errorReport.toString());
     }
 
     /**

+ 7 - 2
jplag/src/main/java/de/jplag/JPlag.java

@@ -12,6 +12,9 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import de.jplag.clustering.ClusteringFactory;
 import de.jplag.exceptions.ExitException;
 import de.jplag.exceptions.SubmissionException;
@@ -26,6 +29,8 @@ import de.jplag.strategy.ParallelComparisonStrategy;
  * This class coordinates the whole errorConsumer flow.
  */
 public class JPlag {
+    private static final Logger logger = LoggerFactory.getLogger("JPlag");
+
     private final JPlagOptions options;
 
     private final Language language;
@@ -63,7 +68,7 @@ public class JPlag {
             }
             return excludedFileNames;
         } catch (IOException e) {
-            System.out.println("Could not read exclusion file: " + e.getMessage());
+            logger.error("Could not read exclusion file: " + e.getMessage(), e);
             return Collections.emptySet();
         }
     }
@@ -114,7 +119,7 @@ public class JPlag {
 
             this.options.setLanguage(language);
             this.options.setLanguageDefaults(language);
-            System.out.println("Initialized language " + language.getName());
+            logger.info("Initialized language " + language.getName());
             return language;
         } catch (NoSuchMethodException | SecurityException | ClassNotFoundException | InstantiationException | IllegalAccessException
                 | IllegalArgumentException | InvocationTargetException e) {

+ 12 - 16
jplag/src/main/java/de/jplag/SubmissionSetBuilder.java

@@ -2,18 +2,12 @@ package de.jplag;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
+import java.util.*;
 import java.util.function.Function;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import de.jplag.exceptions.BasecodeException;
 import de.jplag.exceptions.ExitException;
 import de.jplag.exceptions.RootDirectoryException;
@@ -26,6 +20,8 @@ import de.jplag.options.JPlagOptions;
  */
 public class SubmissionSetBuilder {
 
+    private static final Logger logger = LoggerFactory.getLogger(SubmissionSetBuilder.class);
+
     private final Language language;
     private final JPlagOptions options;
     private final ErrorCollector errorCollector;
@@ -98,7 +94,7 @@ public class SubmissionSetBuilder {
             rootDirectory = makeCanonical(rootDirectory, it -> new RootDirectoryException("Cannot read root directory: " + rootDirectoryName, it));
             if (!canonicalRootDirectories.add(rootDirectory)) {
                 // Root directory was already added, report a warning.
-                System.out.printf("Warning: Root directory \"%s\" was specified more than once, duplicates will be ignored.", rootDirectoryName);
+                logger.warn("Root directory \"{}\" was specified more than once, duplicates will be ignored.", rootDirectoryName);
             }
         }
         return canonicalRootDirectories;
@@ -119,7 +115,7 @@ public class SubmissionSetBuilder {
         // former use can be removed without affecting the result of the checks.
         oldSubmissionDirectories.removeAll(commonRootdirectories);
         for (File rootDirectory : commonRootdirectories) {
-            System.out.println("Warning: Root directory \"" + rootDirectory.toString()
+            logger.warn("Root directory \"" + rootDirectory.toString()
                     + "\" is specified both for plagiarism checking and for prior submissions, will perform plagiarism checking only.");
         }
     }
@@ -146,12 +142,12 @@ public class SubmissionSetBuilder {
             }
             // TODO Optional.of() will cause a NPTR-Exception of baseCode is null. Is this a bug?
             baseCodeSubmission = Optional.of(baseCode);
-            System.out.printf("Basecode directory \"%s\" will be used.%n", baseCode.getName());
+            logger.info("Basecode directory \"{}\" will be used.", baseCode.getName());
 
             // Basecode may also be registered as a user submission. If so, remove the latter.
             Submission removed = foundSubmissions.remove(baseCode.getRoot());
             if (removed != null) {
-                System.out.printf("Submission \"%s\" is the specified basecode, it will be skipped during comparison.%n", removed.getName());
+                logger.info("Submission \"{}\" is the specified basecode, it will be skipped during comparison.", removed.getName());
             }
         }
         return baseCodeSubmission;
@@ -216,7 +212,7 @@ public class SubmissionSetBuilder {
             throw new BasecodeException(String.format("Basecode path \"%s\" relative to the working directory could not be found.", baseCodeName));
         } else {
             // Found a base code as a submission, report about legacy usage.
-            System.out.println("Legacy use of the -bc option found, please specify the basecode by path instead of by name!");
+            logger.info("Legacy use of the -bc option found, please specify the basecode by path instead of by name!");
         }
         return baseCode;
     }
@@ -306,7 +302,7 @@ public class SubmissionSetBuilder {
                 Submission submission = processSubmission(submissionName, submissionFile, isNew);
                 foundSubmissions.put(submission.getRoot(), submission);
             } else {
-                System.out.println(errorMessage);
+                logger.error(errorMessage);
             }
         }
     }

+ 5 - 1
jplag/src/main/java/de/jplag/clustering/algorithm/BayesianOptimization.java

@@ -14,12 +14,16 @@ import org.apache.commons.math3.linear.ArrayRealVector;
 import org.apache.commons.math3.linear.RealVector;
 import org.apache.commons.math3.random.HaltonSequenceGenerator;
 import org.apache.commons.math3.random.RandomVectorGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Maximizes a function using bayesian optimization.
  */
 public class BayesianOptimization {
 
+    private static final Logger logger = LoggerFactory.getLogger(BayesianOptimization.class);
+
     private static final int STOP_AFTER_CONSECUTIVE_RANDOM_PICKS = 3;
     private static final int MAX_NON_ZERO_ACQ_FN_EVALS_PER_ITERATION = 50;
     private static final int MAXIMUM_ACQ_FN_EVALS_PER_ITERATION = 1000;
@@ -156,7 +160,7 @@ public class BayesianOptimization {
                 // GPR
                 GaussianProcess gpr = fit(testedCoordinates, observations);
                 if (debug) {
-                    System.out.println(gpr.toString(minima, maxima, 100, 25, 0));
+                    logger.debug(gpr.toString(minima, maxima, 100, 25, 0));
                 }
                 // TODO Check that best is not null here
                 coordinates = maxAcq(gpr, best.score, poiSampler, zeroAcquisitionsCounter);

+ 7 - 2
jplag/src/main/java/de/jplag/options/JPlagOptions.java

@@ -10,12 +10,17 @@ import java.util.List;
 import java.util.Optional;
 import java.util.Set;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import de.jplag.Language;
 import de.jplag.clustering.ClusteringOptions;
 import de.jplag.strategy.ComparisonMode;
 
 public class JPlagOptions {
 
+    private static final Logger logger = LoggerFactory.getLogger("JPlag");
+
     public static final ComparisonMode DEFAULT_COMPARISON_MODE = NORMAL;
     public static final float DEFAULT_SIMILARITY_THRESHOLD = 0;
     public static final int DEFAULT_SHOWN_COMPARISONS = 30;
@@ -284,10 +289,10 @@ public class JPlagOptions {
 
     public void setSimilarityThreshold(float similarityThreshold) {
         if (similarityThreshold > 100) {
-            System.out.println("Maximum threshold of 100 used instead of " + similarityThreshold);
+            logger.warn("Maximum threshold of 100 used instead of " + similarityThreshold);
             this.similarityThreshold = 100;
         } else if (similarityThreshold < 0) {
-            System.out.println("Minimum threshold of 0 used instead of " + similarityThreshold);
+            logger.warn("Minimum threshold of 0 used instead of " + similarityThreshold);
             this.similarityThreshold = 0;
         } else {
             this.similarityThreshold = similarityThreshold;

+ 1 - 1
jplag/src/main/java/de/jplag/reporting/JsonReport.java

@@ -27,7 +27,7 @@ public class JsonReport implements Report {
         File dir = new File(path);
         if (!dir.exists()) {
             if (!dir.mkdir()) {
-                System.out.println("Failed to create dir.");
+                logger.error("Failed to create dir.");
             }
         }
         return JsonFactory.saveJsonFiles(report, path);

+ 5 - 0
jplag/src/main/java/de/jplag/reporting/Report.java

@@ -2,6 +2,9 @@ package de.jplag.reporting;
 
 import java.util.List;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import de.jplag.JPlagResult;
 
 // ReportStrategy -> Report
@@ -11,6 +14,8 @@ import de.jplag.JPlagResult;
  */
 public interface Report {
 
+    Logger logger = LoggerFactory.getLogger(Report.class);
+
     /**
      * This function returns a list containing the report objects as simple strings. The first element is the string of the
      * overview object. Each following element is a string of a comparison report object.

+ 7 - 2
jplag/src/main/java/de/jplag/reporting/jsonfactory/JsonFactory.java

@@ -5,6 +5,9 @@ import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
@@ -16,6 +19,8 @@ import de.jplag.reporting.reportobject.model.JPlagReport;
  */
 public class JsonFactory {
 
+    private static final Logger logger = LoggerFactory.getLogger(JsonFactory.class);
+
     private static final ObjectMapper mapper = new ObjectMapper();
 
     /**
@@ -31,7 +36,7 @@ public class JsonFactory {
                 jsonReports.add(mapper.writeValueAsString(comparisonReport));
             }
         } catch (JsonProcessingException e) {
-            System.out.println("Error converting object to json " + e.getMessage());
+            logger.error("Error converting object to json " + e.getMessage());
         }
         return jsonReports;
     }
@@ -49,7 +54,7 @@ public class JsonFactory {
                 mapper.writeValue(Path.of(folderPath, name).toFile(), report);
             }
         } catch (IOException e) {
-            System.out.println("Failed to save json files: " + e.getMessage());
+            logger.error("Failed to save json files: " + e.getMessage(), e);
             return false;
         }
         return true;

+ 9 - 18
jplag/src/main/java/de/jplag/reporting/reportobject/ReportObjectFactory.java

@@ -5,32 +5,23 @@ import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
+import java.util.*;
 import java.util.stream.Collectors;
 
-import de.jplag.JPlagComparison;
-import de.jplag.JPlagResult;
-import de.jplag.Submission;
-import de.jplag.Token;
-import de.jplag.TokenList;
-import de.jplag.reporting.reportobject.model.Cluster;
-import de.jplag.reporting.reportobject.model.ComparisonReport;
-import de.jplag.reporting.reportobject.model.FilesOfSubmission;
-import de.jplag.reporting.reportobject.model.JPlagReport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import de.jplag.*;
+import de.jplag.reporting.reportobject.model.*;
 import de.jplag.reporting.reportobject.model.Match;
-import de.jplag.reporting.reportobject.model.Metric;
-import de.jplag.reporting.reportobject.model.OverviewReport;
-import de.jplag.reporting.reportobject.model.TopComparison;
 
 /**
  * Factory class, responsible for converting a JPlagResult object to Overview and Comparison DTO classes.
  */
 public class ReportObjectFactory {
 
+    private static final Logger logger = LoggerFactory.getLogger(ReportObjectFactory.class);
+
     /**
      * Converts a JPlagResult to a JPlagReport.
      * @return JPlagReport for the given JPlagResult.
@@ -190,7 +181,7 @@ public class ReportObjectFactory {
                 lines.add(line);
             }
         } catch (IOException exception) {
-            System.out.println("Could not read file: " + exception.getMessage());
+            logger.error("Could not read file: " + exception.getMessage());
         }
         return lines;
     }

+ 6 - 1
jplag/src/main/java/de/jplag/strategy/AbstractComparisonStrategy.java

@@ -4,6 +4,9 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import de.jplag.GreedyStringTiling;
 import de.jplag.JPlagComparison;
 import de.jplag.Submission;
@@ -12,6 +15,8 @@ import de.jplag.options.JPlagOptions;
 
 public abstract class AbstractComparisonStrategy implements ComparisonStrategy {
 
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
     private final GreedyStringTiling greedyStringTiling;
 
     protected JPlagOptions options;
@@ -42,7 +47,7 @@ public abstract class AbstractComparisonStrategy implements ComparisonStrategy {
      */
     protected Optional<JPlagComparison> compareSubmissions(Submission first, Submission second, boolean withBaseCode) {
         JPlagComparison comparison = greedyStringTiling.compare(first, second);
-        System.out.println("Comparing " + first.getName() + "-" + second.getName() + ": " + comparison.similarity());
+        logger.info("Comparing " + first.getName() + "-" + second.getName() + ": " + comparison.similarity());
 
         if (options.getSimilarityMetric().isAboveThreshold(comparison, options.getSimilarityThreshold())) {
             return Optional.of(comparison);

+ 23 - 1
pom.xml

@@ -52,6 +52,7 @@
         <module>jplag.frontend.scheme</module>
         <module>jplag.frontend.text</module>
         <module>jplag</module>
+        <module>jplag.cli</module>
     </modules>
     <scm>
         <connection>scm:git:git://github.com/jplag/JPlag.git</connection>
@@ -83,6 +84,7 @@
         <maven.compiler.source>17</maven.compiler.source>
         <maven.compiler.target>17</maven.compiler.target>
         <spotless.version>2.22.4</spotless.version>
+        <slf4j.version>1.7.36</slf4j.version>
         <!-- The Revision of JPlag -->
         <revision>4.0.0-SNAPSHOT</revision>
     </properties>
@@ -115,13 +117,24 @@
                 <artifactId>antlr3-runtime</artifactId>
                 <version>3.5</version>
             </dependency>
-
             <dependency>
                 <groupId>net.sourceforge.argparse4j</groupId>
                 <artifactId>argparse4j</artifactId>
                 <version>0.9.0</version>
             </dependency>
 
+            <!-- LOGGER -->
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-api</artifactId>
+                <version>${slf4j.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-simple</artifactId>
+                <version>${slf4j.version}</version>
+            </dependency>
+
             <!-- JPLAG -->
             <dependency>
                 <groupId>${project.groupId}</groupId>
@@ -195,6 +208,15 @@
             <version>4.5.1</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>