浏览代码

Merge remote-tracking branch 'origin/master' into feature/own-logger

# Conflicts:
#	jplag.frontend-utils/src/main/java/de/jplag/AbstractParser.java
#	jplag/src/main/java/de/jplag/Submission.java
#	jplag/src/main/java/de/jplag/SubmissionSet.java
#	jplag/src/main/java/de/jplag/SubmissionSetBuilder.java
Dominik Fuchß 4 年之前
父节点
当前提交
e9fd5af797
共有 45 个文件被更改,包括 164 次插入 和 351 次删除
  1. 7 5
      jplag.cli/src/main/java/de/jplag/CLI.java
  2. 1 1
      jplag.cli/src/test/java/de/jplag/cli/CommandLineInterfaceTest.java
  3. 1 1
      jplag.frontend.csharp-6/src/main/java/de/jplag/csharp/CSharpListener.java
  4. 1 1
      jplag.frontend.csharp-6/src/main/java/de/jplag/csharp/CSharpParserAdapter.java
  5. 11 7
      jplag.frontend.csharp-6/src/main/java/de/jplag/csharp/grammar/CSharpPreprocessorParserBase.java
  6. 1 1
      jplag.frontend.java/src/main/java/de/jplag/java/JavaTokenConstants.java
  7. 1 0
      jplag.frontend.text/src/main/java/de/jplag/text/InputState.java
  8. 7 6
      jplag/src/main/java/de/jplag/GreedyStringTiling.java
  9. 2 1
      jplag/src/main/java/de/jplag/JPlagComparison.java
  10. 1 1
      jplag/src/main/java/de/jplag/JPlagResult.java
  11. 7 42
      jplag/src/main/java/de/jplag/Match.java
  12. 1 1
      jplag/src/main/java/de/jplag/Submission.java
  13. 11 12
      jplag/src/main/java/de/jplag/SubmissionSet.java
  14. 18 6
      jplag/src/main/java/de/jplag/SubmissionSetBuilder.java
  15. 1 3
      jplag/src/main/java/de/jplag/clustering/ClusteringFactory.java
  16. 1 1
      jplag/src/main/java/de/jplag/clustering/ClusteringResult.java
  17. 3 2
      jplag/src/main/java/de/jplag/clustering/IntegerMapping.java
  18. 1 1
      jplag/src/main/java/de/jplag/clustering/PreprocessorHelper.java
  19. 23 51
      jplag/src/main/java/de/jplag/clustering/algorithm/AgglomerativeClustering.java
  20. 2 4
      jplag/src/main/java/de/jplag/clustering/algorithm/BayesianOptimization.java
  21. 1 1
      jplag/src/main/java/de/jplag/clustering/algorithm/GaussianProcess.java
  22. 3 3
      jplag/src/main/java/de/jplag/clustering/algorithm/InterClusterSimilarity.java
  23. 2 2
      jplag/src/main/java/de/jplag/clustering/algorithm/SpectralClustering.java
  24. 2 2
      jplag/src/main/java/de/jplag/exceptions/ExitException.java
  25. 0 21
      jplag/src/main/java/de/jplag/exceptions/ReportGenerationException.java
  26. 2 2
      jplag/src/main/java/de/jplag/options/JPlagOptions.java
  27. 2 2
      jplag/src/main/java/de/jplag/reporting/jsonfactory/JsonFactory.java
  28. 13 13
      jplag/src/main/java/de/jplag/reporting/reportobject/ReportObjectFactory.java
  29. 4 44
      jplag/src/main/java/de/jplag/reporting/reportobject/model/ComparisonReport.java
  30. 2 2
      jplag/src/main/java/de/jplag/reporting/reportobject/model/FilesOfSubmission.java
  31. 3 60
      jplag/src/main/java/de/jplag/reporting/reportobject/model/Match.java
  32. 0 1
      jplag/src/main/java/de/jplag/reporting/reportobject/model/TopComparison.java
  33. 3 3
      jplag/src/main/java/de/jplag/strategy/AbstractComparisonStrategy.java
  34. 2 4
      jplag/src/main/java/de/jplag/strategy/ComparisonMode.java
  35. 1 1
      jplag/src/main/java/de/jplag/strategy/NormalComparisonStrategy.java
  36. 16 17
      jplag/src/main/java/de/jplag/strategy/ParallelComparisonStrategy.java
  37. 2 15
      jplag/src/main/java/de/jplag/strategy/SubmissionTuple.java
  38. 0 1
      jplag/src/main/resources/de/jplag/messages_de.properties
  39. 0 1
      jplag/src/main/resources/de/jplag/messages_en.properties
  40. 0 2
      jplag/src/main/resources/de/jplag/messages_es.properties
  41. 0 1
      jplag/src/main/resources/de/jplag/messages_fr.properties
  42. 0 1
      jplag/src/main/resources/de/jplag/messages_pt.properties
  43. 3 3
      jplag/src/test/java/de/jplag/clustering/algorithm/BayesianOptimizationTest.java
  44. 1 1
      jplag/src/test/java/de/jplag/clustering/algorithm/ClusteringData.java
  45. 1 1
      jplag/src/test/java/de/jplag/clustering/preprocessors/PreprocessingTestBase.java

+ 7 - 5
jplag.cli/src/main/java/de/jplag/CLI.java

@@ -2,6 +2,7 @@ package de.jplag;
 
 import static de.jplag.CommandLineArgument.*;
 
+import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
@@ -34,10 +35,12 @@ import de.jplag.strategy.ComparisonMode;
  * Command line interface class, allows using via command line.
  * @see CLI#main(String[])
  */
-public class CLI {
+public final class CLI {
 
     private static final Logger logger = LoggerFactory.getLogger(CLI.class);
 
+    private static final Random RANDOM = new SecureRandom();
+
     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",
@@ -175,15 +178,14 @@ public class CLI {
     }
 
     private String generateDescription() {
-        var randomDescription = DESCRIPTIONS[new Random().nextInt(DESCRIPTIONS.length)];
-        return String.format("JPlag - %s" + System.lineSeparator() + CREDITS, randomDescription);
+        var randomDescription = DESCRIPTIONS[RANDOM.nextInt(DESCRIPTIONS.length)];
+        return String.format("JPlag - %s%n%s", CREDITS, randomDescription);
     }
 
     private void addAllMultiValueArgument(List<List<String>> argumentValues, List<String> destinationRootDirectories) {
         if (argumentValues == null) {
             return;
         }
-
-        argumentValues.stream().forEach(value -> destinationRootDirectories.addAll(value));
+        argumentValues.forEach(destinationRootDirectories::addAll);
     }
 }

+ 1 - 1
jplag.cli/src/test/java/de/jplag/cli/CommandLineInterfaceTest.java

@@ -17,7 +17,7 @@ import de.jplag.options.JPlagOptions;
  * line arguments, thus these tests do not test the functionality of the options during the comparison.
  * @author Timur Saglam
  */
-public class CommandLineInterfaceTest {
+public abstract class CommandLineInterfaceTest {
     protected static final String CURRENT_DIRECTORY = ".";
     protected static final float DELTA = 0.0001f;
 

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

@@ -215,7 +215,7 @@ public class CSharpListener extends CSharpParserBaseListener implements CSharpTo
 
     @Override
     public void enterUsingStatement(UsingStatementContext context) {
-        transformToken(USING, null);
+        transformToken(USING, context.getStart());
         super.enterUsingStatement(context);
     }
 

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

@@ -52,7 +52,7 @@ public class CSharpParserAdapter extends AbstractParser {
 
     private boolean parseFile(File directory, String fileName) {
         File file = new File(directory, fileName);
-        try (FileInputStream inputStream = new FileInputStream(file);) {
+        try (FileInputStream inputStream = new FileInputStream(file)) {
             currentFile = fileName;
 
             // create a lexer, a parser and a buffer between them.

+ 11 - 7
jplag.frontend.csharp-6/src/main/java/de/jplag/csharp/grammar/CSharpPreprocessorParserBase.java

@@ -1,14 +1,18 @@
 package de.jplag.csharp.grammar;
 
 import java.util.HashSet;
+import java.util.Objects;
 import java.util.Stack;
 
-import org.antlr.v4.runtime.*;
+import org.antlr.v4.runtime.Parser;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.TokenStream;
 
 /**
  * This class was taken from <a href="https://github.com/antlr/grammars-v4/tree/master/csharp">antlr/grammars-v4</a>. It
  * was originally written by Ken Domino. Note that this class is licensed under Eclipse Public License - v 1.0.
  */
+@SuppressWarnings("java:S100")
 abstract class CSharpPreprocessorParserBase extends Parser {
     protected CSharpPreprocessorParserBase(TokenStream input) {
         super(input);
@@ -16,7 +20,7 @@ abstract class CSharpPreprocessorParserBase extends Parser {
         ConditionalSymbols.add("DEBUG");
     }
 
-    Stack<Boolean> conditions = new Stack<Boolean>();
+    Stack<Boolean> conditions = new Stack<>();
     public HashSet<String> ConditionalSymbols = new HashSet<String>();
 
     protected Boolean AllConditions() {
@@ -51,7 +55,7 @@ abstract class CSharpPreprocessorParserBase extends Parser {
     protected void OnPreprocessorDirectiveElif() {
         ParserRuleContext c = this._ctx;
         CSharpPreprocessorParser.PreprocessorConditionalContext d = (CSharpPreprocessorParser.PreprocessorConditionalContext) c;
-        if (!conditions.peek()) {
+        if (Boolean.FALSE.equals(conditions.peek())) {
             conditions.pop();
             d.value = d.expr.value.equals("true") && AllConditions();
             conditions.push(d.expr.value.equals("true"));
@@ -63,9 +67,9 @@ abstract class CSharpPreprocessorParserBase extends Parser {
     protected void OnPreprocessorDirectiveElse() {
         ParserRuleContext c = this._ctx;
         CSharpPreprocessorParser.PreprocessorConditionalContext d = (CSharpPreprocessorParser.PreprocessorConditionalContext) c;
-        if (!conditions.peek()) {
+        if (Boolean.FALSE.equals(conditions.peek())) {
             conditions.pop();
-            d.value = true && AllConditions();
+            d.value = AllConditions();
             conditions.push(true);
         } else {
             d.value = false;
@@ -154,13 +158,13 @@ abstract class CSharpPreprocessorParserBase extends Parser {
     protected void OnPreprocessorExpressionConditionalEq() {
         ParserRuleContext c = this._ctx;
         CSharpPreprocessorParser.Preprocessor_expressionContext d = (CSharpPreprocessorParser.Preprocessor_expressionContext) c;
-        d.value = (d.expr1.value == d.expr2.value ? "true" : "false");
+        d.value = (Objects.equals(d.expr1.value, d.expr2.value) ? "true" : "false");
     }
 
     protected void OnPreprocessorExpressionConditionalNe() {
         ParserRuleContext c = this._ctx;
         CSharpPreprocessorParser.Preprocessor_expressionContext d = (CSharpPreprocessorParser.Preprocessor_expressionContext) c;
-        d.value = (d.expr1.value != d.expr2.value ? "true" : "false");
+        d.value = (!Objects.equals(d.expr1.value, d.expr2.value) ? "true" : "false");
     }
 
     protected void OnPreprocessorExpressionConditionalAnd() {

+ 1 - 1
jplag.frontend.java/src/main/java/de/jplag/java/JavaTokenConstants.java

@@ -84,5 +84,5 @@ public interface JavaTokenConstants extends TokenConstants {
     int J_RECORD_BEGIN = 68;
     int J_RECORD_END = 69;
 
-    final static int NUM_DIFF_TOKENS = 70;	    // @formatter:on
+    int NUM_DIFF_TOKENS = 70;	    // @formatter:on
 }

+ 1 - 0
jplag.frontend.text/src/main/java/de/jplag/text/InputState.java

@@ -10,6 +10,7 @@ import antlr.LexerSharedInputState;
  * This object contains the data associated with an input stream of characters. Multiple lexers share a single
  * LexerSharedInputState to lex the same input stream.
  */
+@SuppressWarnings({"java:S1104", "java:S2387"})
 public class InputState extends LexerSharedInputState {
     public int column = 0;
     public int tokenColumn = 0;

+ 7 - 6
jplag/src/main/java/de/jplag/GreedyStringTiling.java

@@ -98,7 +98,8 @@ public class GreedyStringTiling {
     }
 
     private JPlagComparison swapAndCompare(Submission firstSubmission, Submission secondSubmission, boolean isBaseCodeComparison) {
-        Submission smallerSubmission, largerSubmission;
+        Submission smallerSubmission;
+        Submission largerSubmission;
         if (firstSubmission.getTokenList().size() > secondSubmission.getTokenList().size()) {
             smallerSubmission = secondSubmission;
             largerSubmission = firstSubmission;
@@ -187,11 +188,11 @@ public class GreedyStringTiling {
                 }
             }
             for (int i = matches.size() - 1; i >= 0; i--) {
-                int x = matches.get(i).getStartOfFirst();  // Beginning of/in sequence A
-                int y = matches.get(i).getStartOfSecond();  // Beginning of/in sequence B
-                comparison.addMatch(x, y, matches.get(i).getLength());
+                int x = matches.get(i).startOfFirst();  // Beginning of/in sequence A
+                int y = matches.get(i).startOfSecond();  // Beginning of/in sequence B
+                comparison.addMatch(x, y, matches.get(i).length());
                 // in order that "Match" will be newly build (because reusing)
-                for (int j = matches.get(i).getLength(); j > 0; j--) {
+                for (int j = matches.get(i).length(); j > 0; j--) {
                     first.getToken(x).setMarked(true); // mark all Tokens!
                     second.getToken(y).setMarked(true);
                     if (isBaseCodeComparison) {
@@ -220,7 +221,7 @@ public class GreedyStringTiling {
     /**
      * Disable finding a match at separator tokens and basecode matches for non-basecode comparisons.
      * @param tokenList Tokens to mark.
-     * @param isBaseCodeComparison Whether the {@link Token#basecode} matches should be enabled for matching.
+     * @param isBaseCodeComparison Whether the {@link Token#isBasecode()} matches should be enabled for matching.
      */
     private void markTokens(TokenList tokenList, boolean isBaseCodeComparison) {
         for (Token token : tokenList.allTokens()) {

+ 2 - 1
jplag/src/main/java/de/jplag/JPlagComparison.java

@@ -40,6 +40,7 @@ public class JPlagComparison implements Comparator<JPlagComparison> { // FIXME T
         return Float.compare(comparison2.similarity(), comparison1.similarity()); // comparison2 first!
     }
 
+    // TODO DF: hashCode is not implemented!!
     @Override
     public boolean equals(Object other) {
         if (!(other instanceof JPlagComparison)) {
@@ -76,7 +77,7 @@ public class JPlagComparison implements Comparator<JPlagComparison> { // FIXME T
         int numberOfMatchedTokens = 0;
 
         for (Match match : matches) {
-            numberOfMatchedTokens += match.getLength();
+            numberOfMatchedTokens += match.length();
         }
 
         return numberOfMatchedTokens;

+ 1 - 1
jplag/src/main/java/de/jplag/JPlagResult.java

@@ -131,7 +131,7 @@ public class JPlagResult {
      * Note: Before, comparisons with a similarity below the given threshold were also included in the similarity matrix.
      */
     private int[] calculateSimilarityDistribution(List<JPlagComparison> comparisons) {
-        return calculateDistributionFor(comparisons, (JPlagComparison::similarity));
+        return calculateDistributionFor(comparisons, JPlagComparison::similarity);
     }
 
     private int[] calculateDistributionFor(List<JPlagComparison> comparisons, Function<JPlagComparison, Float> similarityExtractor) {

+ 7 - 42
jplag/src/main/java/de/jplag/Match.java

@@ -2,57 +2,22 @@ package de.jplag;
 
 /**
  * Represents two sections of two submissions that are similar.
+ * @param startOfFirst is the starting token index in the first submission.
+ * @param startOfSecond is the starting token index in the second submission.
+ * @param length is the length of these similar sections (number of tokens).
  */
-public class Match {
-
-    private final int startOfFirst;
-    private final int startOfSecond;
-    private final int length;
-
-    /**
-     * Creates a match.
-     * @param startOfFirst is the starting token index in the first submission.
-     * @param startOfSecond is the starting token index in the second submission.
-     * @param length is the length of these similar sections (number of tokens).
-     */
-    public Match(int startOfFirst, int startOfSecond, int length) {
-        this.startOfFirst = startOfFirst;
-        this.startOfSecond = startOfSecond;
-        this.length = length;
-    }
-
-    /**
-     * @return the starting index in the first submission.
-     */
-    public int getStartOfFirst() {
-        return startOfFirst;
-    }
-
-    /**
-     * @return the starting index in the second submission.
-     */
-    public int getStartOfSecond() {
-        return startOfSecond;
-    }
-
-    /**
-     * @return the length of the similar sections, meaning the number of tokens.
-     */
-    public int getLength() {
-        return length;
-    }
-
+public record Match(int startOfFirst, int startOfSecond, int length) {
     /**
      * Checks if two matches overlap.
      * @return true if they do.
      */
-    public final boolean overlap(int otherStartOfFirst, int otherStartOfSecond, int oLength) {
+    public boolean overlap(int otherStartOfFirst, int otherStartOfSecond, int otherLength) {
         if (startOfFirst < otherStartOfFirst) {
             if ((otherStartOfFirst - startOfFirst) < length) {
                 return true;
             }
         } else {
-            if ((startOfFirst - otherStartOfFirst) < oLength) {
+            if ((startOfFirst - otherStartOfFirst) < otherLength) {
                 return true;
             }
         }
@@ -60,7 +25,7 @@ public class Match {
         if (startOfSecond < otherStartOfSecond) {
             return (otherStartOfSecond - startOfSecond) < length;
         } else {
-            return (startOfSecond - otherStartOfSecond) < oLength;
+            return (startOfSecond - otherStartOfSecond) < otherLength;
         }
     }
 }

+ 1 - 1
jplag/src/main/java/de/jplag/Submission.java

@@ -187,7 +187,7 @@ public class Submission implements Comparable<Submission> {
      * String representation of the code files contained in this submission, annotated with all tokens.
      * @return the annotated code as string.
      */
-    public String getTokenAnnotatedSourcCode() {
+    public String getTokenAnnotatedSourceCode() {
         return TokenPrinter.printTokens(tokenList, files, submissionRootFile);
     }
 

+ 11 - 12
jplag/src/main/java/de/jplag/SubmissionSet.java

@@ -1,10 +1,7 @@
 package de.jplag;
 
-import static java.util.stream.Collectors.toList;
-
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Optional;
 import java.util.stream.Collectors;
 
 import org.slf4j.Logger;
@@ -31,16 +28,18 @@ public class SubmissionSet {
     /**
      * Base code submission if it exists.
      */
-    private final Optional<Submission> baseCodeSubmission;
+    private final Submission baseCodeSubmission;
+
     private final JPlagOptions options;
     private int errors = 0;
     private String currentSubmissionName;
 
     /**
      * @param submissions Submissions to check for plagiarism.
-     * @param baseCode Base code submission if it exists.
+     * @param baseCode Base code submission if it exists or {@code null}.
      */
-    public SubmissionSet(List<Submission> submissions, Optional<Submission> baseCode, JPlagOptions options) throws ExitException {
+    public SubmissionSet(List<Submission> submissions, Submission baseCode, JPlagOptions options)
+            throws ExitException {
         this.allSubmissions = submissions;
         this.baseCodeSubmission = baseCode;
         this.options = options;
@@ -53,7 +52,7 @@ public class SubmissionSet {
      * @return Whether a basecode is available for this collection.
      */
     public boolean hasBaseCode() {
-        return baseCodeSubmission.isPresent();
+        return baseCodeSubmission != null;
     }
 
     /**
@@ -63,10 +62,10 @@ public class SubmissionSet {
      * @see #hasBaseCode
      */
     public Submission getBaseCode() {
-        if (baseCodeSubmission.isEmpty()) {
+        if (baseCodeSubmission == null) {
             throw new AssertionError("Querying a non-existing basecode submission.");
         }
-        return baseCodeSubmission.get();
+        return baseCodeSubmission;
     }
 
     /**
@@ -97,14 +96,14 @@ public class SubmissionSet {
     }
 
     private List<Submission> filterInvalidSubmissions() {
-        return allSubmissions.stream().filter(Submission::hasErrors).collect(toList());
+        return allSubmissions.stream().filter(Submission::hasErrors).toList();
     }
 
     private void parseAllSubmissions() throws ExitException {
         try {
             parseSubmissions(allSubmissions);
-            if (baseCodeSubmission.isPresent()) {
-                parseBaseCodeSubmission(baseCodeSubmission.get()); // cannot use ifPresent because of throws declaration
+            if (baseCodeSubmission != null) {
+                parseBaseCodeSubmission(baseCodeSubmission);
             }
         } catch (OutOfMemoryError exception) {
             throw new SubmissionException("Out of memory during parsing of submission \"" + currentSubmissionName + "\"", exception);

+ 18 - 6
jplag/src/main/java/de/jplag/SubmissionSetBuilder.java

@@ -2,7 +2,16 @@ package de.jplag;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.*;
+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.function.Function;
 
 import org.slf4j.Logger;
@@ -66,7 +75,7 @@ public class SubmissionSetBuilder {
 
         // Merge everything in a submission set.
         List<Submission> submissions = new ArrayList<>(foundSubmissions.values());
-        return new SubmissionSet(submissions, baseCodeSubmission, options);
+        return new SubmissionSet(submissions, baseCodeSubmission.orElse(null), options);
     }
 
     /**
@@ -112,8 +121,9 @@ public class SubmissionSetBuilder {
         // former use can be removed without affecting the result of the checks.
         oldSubmissionDirectories.removeAll(commonRootdirectories);
         for (File rootDirectory : commonRootdirectories) {
-            logger.warn("Root directory \"" + rootDirectory.toString()
-                    + "\" is specified both for plagiarism checking and for prior submissions, will perform plagiarism checking only.");
+            logger.warn(
+                    "Root directory \"{}\" is specified both for plagiarism checking and for prior submissions, will perform plagiarism checking only.",
+                    rootDirectory);
         }
     }
 
@@ -122,7 +132,7 @@ public class SubmissionSetBuilder {
         // Extract the basecode submission if necessary.
         Optional<Submission> baseCodeSubmission = Optional.empty();
         if (options.hasBaseCode()) {
-            String baseCodeName = options.getBaseCodeSubmissionName().get();
+            String baseCodeName = options.getBaseCodeSubmissionName().orElseThrow();
             Submission baseCode = loadBaseCodeAsPath(baseCodeName);
             if (baseCode == null) {
                 int numberOfRootDirectories = submissionDirectories.size() + oldSubmissionDirectories.size();
@@ -131,13 +141,15 @@ public class SubmissionSetBuilder {
                 }
 
                 // There is one root directory, and the submissionDirectories variable has been checked to be non-empty.
-                // That set thus contains the the one and only root directory.
+                // That set thus contains the one and only root directory.
                 File rootDirectory = submissionDirectories.iterator().next();
 
                 // Single root-directory, try the legacy way of specifying basecode.
                 baseCode = loadBaseCodeViaName(baseCodeName, rootDirectory, foundSubmissions);
             }
+            // TODO DF: Here the method assumes that baseCode can be null. later, this is not assumed anymore
             baseCodeSubmission = Optional.ofNullable(baseCode);
+
             logger.info("Basecode directory \"{}\" will be used.", baseCode.getName());
 
             // Basecode may also be registered as a user submission. If so, remove the latter.

+ 1 - 3
jplag/src/main/java/de/jplag/clustering/ClusteringFactory.java

@@ -4,7 +4,6 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
-import java.util.stream.Collectors;
 
 import de.jplag.JPlagComparison;
 import de.jplag.Submission;
@@ -42,8 +41,7 @@ public class ClusteringFactory {
     }
 
     private static ClusteringResult<Submission> removeBadClusters(final ClusteringResult<Submission> clustering) {
-        List<Cluster<Submission>> filtered = clustering.getClusters().stream().filter(cluster -> !cluster.isBadCluster())
-                .collect(Collectors.toList());
+        List<Cluster<Submission>> filtered = clustering.getClusters().stream().filter(cluster -> !cluster.isBadCluster()).toList();
         return new ClusteringResult<>(filtered, clustering.getCommunityStrength());
     }
 }

+ 1 - 1
jplag/src/main/java/de/jplag/clustering/ClusteringResult.java

@@ -66,7 +66,7 @@ public class ClusteringResult<T> {
         }
         List<Cluster<Integer>> clusters = new ArrayList<>(clustering.size());
         float communityStrength = 0;
-        if (clustering.size() > 0) {
+        if (!clustering.isEmpty()) {
             RealMatrix percentagesOfSimilaritySums = new Array2DRowRealMatrix(clustering.size(), clustering.size());
             percentagesOfSimilaritySums = percentagesOfSimilaritySums.scalarMultiply(0);
             for (int i = 0; i < numberOfSubmissions; i++) {

+ 3 - 2
jplag/src/main/java/de/jplag/clustering/IntegerMapping.java

@@ -3,14 +3,15 @@ package de.jplag.clustering;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Creates a mapping of any set to integers
  */
 public class IntegerMapping<T> {
 
-    private HashMap<T, Integer> mapping;
-    private List<T> backMapping;
+    private final Map<T, Integer> mapping;
+    private final List<T> backMapping;
     private int size = 0;
 
     public IntegerMapping(int initialCapacity) {

+ 1 - 1
jplag/src/main/java/de/jplag/clustering/PreprocessorHelper.java

@@ -24,7 +24,7 @@ public class PreprocessorHelper {
         mapping = new IntegerMapping<>(connections.length);
         RealMatrix similarity = new Array2DRowRealMatrix(connections, true);
         for (int i = 0; i < similarity.getRowDimension(); i++) {
-            if (DoubleStream.of(similarity.getRow(i)).filter(x -> x > 0).findAny().isPresent()) {
+            if (DoubleStream.of(similarity.getRow(i)).anyMatch(x -> x > 0)) {
                 rowList.add(i);
                 mapping.map(i);
             }

+ 23 - 51
jplag/src/main/java/de/jplag/clustering/algorithm/AgglomerativeClustering.java

@@ -1,11 +1,6 @@
 package de.jplag.clustering.algorithm;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.PriorityQueue;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 
 import org.apache.commons.math3.linear.RealMatrix;
@@ -32,32 +27,29 @@ public class AgglomerativeClustering implements GenericClusteringAlgorithm {
         // clusters, those need to be ignored.
         PriorityQueue<ClusterConnection> similarities;
 
-        {
-            // initialization
+        // initialization
 
-            List<Cluster> initialClusters = new ArrayList<>(size);
-            for (int i = 0; i < size; i++) {
-                List<Integer> members = new ArrayList<>();
-                members.add(i);
-                Cluster cluster = new Cluster(members);
-                initialClusters.add(cluster);
-                clusters.add(cluster);
-            }
+        List<Cluster> initialClusters = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            List<Integer> members = new ArrayList<>();
+            members.add(i);
+            Cluster cluster = new Cluster(members);
+            initialClusters.add(cluster);
+            clusters.add(cluster);
+        }
 
-            List<ClusterConnection> initialSimilarities = new ArrayList<>(size * (size - 1) / 2);
+        List<ClusterConnection> initialSimilarities = new ArrayList<>(size * (size - 1) / 2);
 
-            for (int leftIndex = 0; leftIndex < initialClusters.size(); leftIndex++) {
-                Cluster leftCluster = initialClusters.get(leftIndex);
-                for (int rightIndex = leftIndex + 1; rightIndex < initialClusters.size(); rightIndex++) {
-                    Cluster rightCluster = initialClusters.get(rightIndex);
-                    initialSimilarities
-                            .add(new ClusterConnection(leftCluster, rightCluster, (float) similarityMatrix.getEntry(leftIndex, rightIndex)));
-                }
+        for (int leftIndex = 0; leftIndex < initialClusters.size(); leftIndex++) {
+            Cluster leftCluster = initialClusters.get(leftIndex);
+            for (int rightIndex = leftIndex + 1; rightIndex < initialClusters.size(); rightIndex++) {
+                Cluster rightCluster = initialClusters.get(rightIndex);
+                initialSimilarities.add(new ClusterConnection(leftCluster, rightCluster, (float) similarityMatrix.getEntry(leftIndex, rightIndex)));
             }
-
-            similarities = new PriorityQueue<>(initialSimilarities);
         }
 
+        similarities = new PriorityQueue<>(initialSimilarities);
+
         while (clusters.size() > 1) {
             ClusterConnection nearest = similarities.poll();
             // TODO Check that nearest is never null
@@ -70,8 +62,8 @@ public class AgglomerativeClustering implements GenericClusteringAlgorithm {
             }
             clusters.remove(nearest.left);
             clusters.remove(nearest.right);
-            nearest.left.getSubmissions().addAll(nearest.right.getSubmissions());
-            Cluster combined = new Cluster(nearest.left.getSubmissions());
+            nearest.left.submissions().addAll(nearest.right.submissions());
+            Cluster combined = new Cluster(nearest.left.submissions());
             for (Cluster otherCluster : clusters) {
                 float similarity = options.getAgglomerativeInterClusterSimilarity().clusterSimilarity(combined.submissions, otherCluster.submissions,
                         similarityMatrix);
@@ -80,21 +72,10 @@ public class AgglomerativeClustering implements GenericClusteringAlgorithm {
             clusters.add(combined);
         }
 
-        return clusters.stream().map(Cluster::getSubmissions).collect(Collectors.toList());
+        return clusters.stream().map(Cluster::submissions).collect(Collectors.toList());
     }
 
-    private static class ClusterConnection implements Comparable<ClusterConnection> {
-
-        private final Cluster left;
-        private final Cluster right;
-        private final float similarity;
-
-        public ClusterConnection(Cluster left, Cluster right, float similarity) {
-            this.left = left;
-            this.right = right;
-            this.similarity = similarity;
-        }
-
+    private record ClusterConnection(Cluster left, Cluster right, float similarity) implements Comparable<ClusterConnection> {
         @Override
         public int compareTo(ClusterConnection other) {
             return (int) Math.signum(other.similarity - similarity);
@@ -106,16 +87,7 @@ public class AgglomerativeClustering implements GenericClusteringAlgorithm {
      * Encapsulate a list in a class because we do not want hashing based on the members but on identity only. Also a
      * cluster need a different identity than the list because the lists are reused.
      */
-    private static class Cluster {
-        private final List<Integer> submissions;
-
-        public Cluster(List<Integer> submissions) {
-            this.submissions = submissions;
-        }
-
-        public List<Integer> getSubmissions() {
-            return submissions;
-        }
+    private record Cluster(List<Integer> submissions) {
     }
 
 }

+ 2 - 4
jplag/src/main/java/de/jplag/clustering/algorithm/BayesianOptimization.java

@@ -159,7 +159,7 @@ public class BayesianOptimization {
             } else {
                 // GPR
                 GaussianProcess gpr = fit(testedCoordinates, observations);
-                if (debug) {
+                if (debug && logger.isDebugEnabled()) {
                     logger.debug(gpr.toString(minima, maxima, 100, 25, 0));
                 }
                 // TODO Check that best is not null here
@@ -169,9 +169,7 @@ public class BayesianOptimization {
             OptimizationResult<T> result = objectiveFunction.apply(coordinates);
             result.params = coordinates;
             observations.add(result.getScore());
-            if (best == null) {
-                best = result;
-            } else if (result.score > best.score) {
+            if (best == null || result.score > best.score) {
                 best = result;
             }
         }

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

@@ -74,7 +74,7 @@ public class GaussianProcess {
      */
     public static GaussianProcess fit(List<RealVector> observedCoordinates, double[] observations, double noise, boolean normalize,
             double[] lengthScale) {
-        if (observedCoordinates.size() < 1)
+        if (observedCoordinates.isEmpty())
             throw new IllegalArgumentException("Observed coordinates are empty");
         if (observedCoordinates.size() != observations.length) {
             throw new IllegalArgumentException(MessageFormat.format("Observed coordinates and observations are of different dimensions {0} and {1}",

+ 3 - 3
jplag/src/main/java/de/jplag/clustering/algorithm/InterClusterSimilarity.java

@@ -1,7 +1,7 @@
 package de.jplag.clustering.algorithm;
 
 import java.util.List;
-import java.util.function.BiFunction;
+import java.util.function.BinaryOperator;
 
 import org.apache.commons.math3.linear.RealMatrix;
 
@@ -11,9 +11,9 @@ public enum InterClusterSimilarity {
     AVERAGE(0, Float::sum);
 
     private final float neutralElement;
-    private final BiFunction<Float, Float, Float> accumulator;
+    private final BinaryOperator<Float> accumulator;
 
-    InterClusterSimilarity(float neutralElement, BiFunction<Float, Float, Float> accumulator) {
+    InterClusterSimilarity(float neutralElement, BinaryOperator<Float> accumulator) {
         this.neutralElement = neutralElement;
         this.accumulator = accumulator;
     }

+ 2 - 2
jplag/src/main/java/de/jplag/clustering/algorithm/SpectralClustering.java

@@ -105,6 +105,7 @@ public class SpectralClustering implements GenericClusteringAlgorithm {
     private Collection<Collection<Integer>> cluster(int numberOfClusters, int dimension, List<Integer> eigenValueIds, EigenDecomposition ed) {
         RealMatrix concatenatedEigenVectors = new Array2DRowRealMatrix(dimension, numberOfClusters);
         concatenatedEigenVectors.walkInOptimizedOrder(new DefaultRealMatrixChangingVisitor() {
+            @Override
             public double visit(int row, int column, double value) {
                 int eigenVectorId = eigenValueIds.get(column);
                 RealVector eigenVector = ed.getEigenvector(eigenVectorId);
@@ -113,8 +114,7 @@ public class SpectralClustering implements GenericClusteringAlgorithm {
         });
 
         List<ClusterableEigenVector> normRows = IntStream.range(0, dimension).filter(i -> concatenatedEigenVectors.getRowVector(i).getNorm() > 0)
-                .mapToObj(row -> new ClusterableEigenVector(row, concatenatedEigenVectors.getRowVector(row).unitVector()))
-                .collect(Collectors.toList());
+                .mapToObj(row -> new ClusterableEigenVector(row, concatenatedEigenVectors.getRowVector(row).unitVector())).toList();
 
         Clusterer<ClusterableEigenVector> clusterer = new KMeansPlusPlusClusterer<>(numberOfClusters, options.getSpectralMaxKMeansIterationPerRun());
         List<? extends Cluster<ClusterableEigenVector>> clusters = clusterer.cluster(normRows);

+ 2 - 2
jplag/src/main/java/de/jplag/exceptions/ExitException.java

@@ -10,11 +10,11 @@ public abstract class ExitException extends Exception {
     @Serial
     private static final long serialVersionUID = 7091658804288889231L; // generated
 
-    public ExitException(String message) {
+    protected ExitException(String message) {
         super(message);
     }
 
-    public ExitException(String message, Throwable cause) {
+    protected ExitException(String message, Throwable cause) {
         super(message, cause);
     }
 }

+ 0 - 21
jplag/src/main/java/de/jplag/exceptions/ReportGenerationException.java

@@ -1,21 +0,0 @@
-package de.jplag.exceptions;
-
-import java.io.Serial;
-
-/**
- * Exceptions for problems with the report generation that lead to an preemptive exit.
- */
-public class ReportGenerationException extends ExitException {
-
-    @Serial
-    private static final long serialVersionUID = 628097693006117102L; // generated
-
-    public ReportGenerationException(String message) {
-        super(message);
-    }
-
-    public ReportGenerationException(String message, Throwable cause) {
-        super(message, cause);
-    }
-
-}

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

@@ -289,10 +289,10 @@ public class JPlagOptions {
 
     public void setSimilarityThreshold(float similarityThreshold) {
         if (similarityThreshold > 100) {
-            logger.warn("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) {
-            logger.warn("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;

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

@@ -36,7 +36,7 @@ public class JsonFactory {
                 jsonReports.add(mapper.writeValueAsString(comparisonReport));
             }
         } catch (JsonProcessingException e) {
-            logger.error("Error converting object to json " + e.getMessage());
+            logger.error("Error converting object to json " + e.getMessage(), e);
         }
         return jsonReports;
     }
@@ -50,7 +50,7 @@ public class JsonFactory {
             ObjectMapper mapper = new ObjectMapper();
             mapper.writeValue(Path.of(folderPath, "overview.json").toFile(), jPlagReport.overviewReport());
             for (ComparisonReport report : jPlagReport.comparisons()) {
-                String name = report.getFirstSubmissionId().concat("-").concat(report.getSecondSubmissionId()).concat(".json");
+                String name = report.firstSubmissionId().concat("-").concat(report.secondSubmissionId()).concat(".json");
                 mapper.writeValue(Path.of(folderPath, name).toFile(), report);
             }
         } catch (IOException e) {

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

@@ -5,8 +5,10 @@ import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
 import java.text.SimpleDateFormat;
-import java.util.*;
-import java.util.stream.Collectors;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -90,8 +92,7 @@ public class ReportObjectFactory {
     }
 
     private static List<Match> convertMatchesToReportMatches(JPlagResult result, JPlagComparison comparison, List<de.jplag.Match> matches) {
-        return matches.stream().map(match -> convertMatchToReportMatch(comparison, match, result.getOptions().getLanguage().usesIndex()))
-                .collect(Collectors.toList());
+        return matches.stream().map(match -> convertMatchToReportMatch(comparison, match, result.getOptions().getLanguage().usesIndex())).toList();
     }
 
     /**
@@ -132,7 +133,7 @@ public class ReportObjectFactory {
      * @return A list containing FilesOfSubmission DTOs.
      */
     private static List<FilesOfSubmission> getFilesForSubmission(Submission submission) {
-        return submission.getFiles().stream().map(file -> new FilesOfSubmission(file.getName(), readFileLines(file))).collect(Collectors.toList());
+        return submission.getFiles().stream().map(file -> new FilesOfSubmission(file.getName(), readFileLines(file))).toList();
     }
 
     /**
@@ -142,19 +143,19 @@ public class ReportObjectFactory {
      * @param usesIndex Indicates whether the language uses indexes.
      * @return A Match DTO.
      */
-    private static Match convertMatchToReportMatch(JPlagComparison comparison, de.jplag.Match match, Boolean usesIndex) {
+    private static Match convertMatchToReportMatch(JPlagComparison comparison, de.jplag.Match match, boolean usesIndex) {
         TokenList tokensFirst = comparison.getFirstSubmission().getTokenList();
         TokenList tokensSecond = comparison.getSecondSubmission().getTokenList();
-        Token startTokenFirst = tokensFirst.getToken(match.getStartOfFirst());
-        Token endTokenFirst = tokensFirst.getToken(match.getStartOfFirst() + match.getLength() - 1);
-        Token startTokenSecond = tokensSecond.getToken(match.getStartOfSecond());
-        Token endTokenSecond = tokensSecond.getToken(match.getStartOfSecond() + match.getLength() - 1);
+        Token startTokenFirst = tokensFirst.getToken(match.startOfFirst());
+        Token endTokenFirst = tokensFirst.getToken(match.startOfFirst() + match.length() - 1);
+        Token startTokenSecond = tokensSecond.getToken(match.startOfSecond());
+        Token endTokenSecond = tokensSecond.getToken(match.startOfSecond() + match.length() - 1);
 
         int startFirst = usesIndex ? startTokenFirst.getIndex() : startTokenFirst.getLine();
         int endFirst = usesIndex ? endTokenFirst.getIndex() : endTokenFirst.getLine();
         int startSecond = usesIndex ? startTokenSecond.getIndex() : startTokenSecond.getLine();
         int endSecond = usesIndex ? endTokenSecond.getIndex() : endTokenSecond.getLine();
-        int tokens = match.getLength();
+        int tokens = match.length();
 
         return new Match(startTokenFirst.getFile(), startTokenSecond.getFile(), startFirst, endFirst, startSecond, endSecond, tokens);
     }
@@ -167,7 +168,7 @@ public class ReportObjectFactory {
                 lines.add(line);
             }
         } catch (IOException exception) {
-            logger.error("Could not read file: " + exception.getMessage());
+            logger.error("Could not read file: " + exception.getMessage(), exception);
         }
         return lines;
     }
@@ -177,5 +178,4 @@ public class ReportObjectFactory {
         Date date = new Date();
         return dateFormat.format(date);
     }
-
 }

+ 4 - 44
jplag/src/main/java/de/jplag/reporting/reportobject/model/ComparisonReport.java

@@ -4,26 +4,10 @@ import java.util.List;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 
-public class ComparisonReport {
-
-    @JsonProperty("first_submission_id")
-    private final String firstSubmissionId;
-
-    @JsonProperty("second_submission_id")
-    private final String secondSubmissionId;
-
-    @JsonProperty("match_percentage")
-    private final float matchPercentage;
-
-    @JsonProperty("files_of_first_submission")
-    private final List<FilesOfSubmission> filesOfFirstSubmission;
-
-    @JsonProperty("files_of_second_submission")
-    private final List<FilesOfSubmission> filesOfSecondSubmission;
-
-    @JsonProperty("matches")
-    private final List<Match> matches;
-
+public record ComparisonReport(@JsonProperty("first_submission_id") String firstSubmissionId,
+        @JsonProperty("second_submission_id") String secondSubmissionId, @JsonProperty("match_percentage") float matchPercentage,
+        @JsonProperty("files_of_first_submission") List<FilesOfSubmission> filesOfFirstSubmission,
+        @JsonProperty("files_of_second_submission") List<FilesOfSubmission> filesOfSecondSubmission, @JsonProperty("matches") List<Match> matches) {
     public ComparisonReport(String firstSubmissionId, String secondSubmissionId, float matchPercentage,
             List<FilesOfSubmission> filesOfFirstSubmission, List<FilesOfSubmission> filesOfSecondSubmission, List<Match> matches) {
 
@@ -34,28 +18,4 @@ public class ComparisonReport {
         this.filesOfSecondSubmission = List.copyOf(filesOfSecondSubmission);
         this.matches = matches;
     }
-
-    public String getFirstSubmissionId() {
-        return firstSubmissionId;
-    }
-
-    public String getSecondSubmissionId() {
-        return secondSubmissionId;
-    }
-
-    public float getMatchPercentage() {
-        return matchPercentage;
-    }
-
-    public List<FilesOfSubmission> getFilesOfFirstSubmission() {
-        return filesOfFirstSubmission;
-    }
-
-    public List<FilesOfSubmission> getFilesOfSecondSubmission() {
-        return filesOfSecondSubmission;
-    }
-
-    public List<Match> getMatches() {
-        return matches;
-    }
 }

+ 2 - 2
jplag/src/main/java/de/jplag/reporting/reportobject/model/FilesOfSubmission.java

@@ -12,9 +12,9 @@ public class FilesOfSubmission {
     @JsonProperty("lines")
     private final List<String> lines;
 
-    public FilesOfSubmission(String fileName, List<String> file_code) {
+    public FilesOfSubmission(String fileName, List<String> fileCode) {
         this.fileName = fileName;
-        this.lines = List.copyOf(file_code);
+        this.lines = List.copyOf(fileCode);
     }
 
     public String getFileName() {

+ 3 - 60
jplag/src/main/java/de/jplag/reporting/reportobject/model/Match.java

@@ -2,64 +2,7 @@ package de.jplag.reporting.reportobject.model;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 
-public class Match {
-
-    @JsonProperty("first_file_name")
-    private final String firstFileName;
-
-    @JsonProperty("second_file_name")
-    private final String secondFileName;
-
-    @JsonProperty("start_in_first")
-    private final int startInFirst;
-
-    @JsonProperty("end_in_first")
-    private final int endInFirst;
-
-    @JsonProperty("start_in_second")
-    private final int startInSecond;
-
-    @JsonProperty("end_in_second")
-    private final int endInSecond;
-
-    @JsonProperty("tokens")
-    private final int tokens;
-
-    public Match(String firstFileName, String secondFileName, int startInFirst, int endInFirst, int startInSecond, int endInSecond, int tokens) {
-        this.firstFileName = firstFileName;
-        this.secondFileName = secondFileName;
-        this.startInFirst = startInFirst;
-        this.endInFirst = endInFirst;
-        this.startInSecond = startInSecond;
-        this.endInSecond = endInSecond;
-        this.tokens = tokens;
-    }
-
-    public String getFirstFileName() {
-        return firstFileName;
-    }
-
-    public String getSecondFileName() {
-        return secondFileName;
-    }
-
-    public int getStartInFirst() {
-        return startInFirst;
-    }
-
-    public int getEndInFirst() {
-        return endInFirst;
-    }
-
-    public int getStartInSecond() {
-        return startInSecond;
-    }
-
-    public int getEndInSecond() {
-        return endInSecond;
-    }
-
-    public int getTokens() {
-        return tokens;
-    }
+public record Match(@JsonProperty("first_file_name") String firstFileName, @JsonProperty("second_file_name") String secondFileName,
+        @JsonProperty("start_in_first") int startInFirst, @JsonProperty("end_in_first") int endInFirst,
+        @JsonProperty("start_in_second") int startInSecond, @JsonProperty("end_in_second") int endInSecond, @JsonProperty("tokens") int tokens) {
 }

+ 0 - 1
jplag/src/main/java/de/jplag/reporting/reportobject/model/TopComparison.java

@@ -4,5 +4,4 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 
 public record TopComparison(@JsonProperty("first_submission") String firstSubmission, @JsonProperty("second_submission") String secondSubmission,
         @JsonProperty("match_percentage") float matchPercentage) {
-
 }

+ 3 - 3
jplag/src/main/java/de/jplag/strategy/AbstractComparisonStrategy.java

@@ -19,9 +19,9 @@ public abstract class AbstractComparisonStrategy implements ComparisonStrategy {
 
     private final GreedyStringTiling greedyStringTiling;
 
-    protected JPlagOptions options;
+    protected final JPlagOptions options;
 
-    public AbstractComparisonStrategy(JPlagOptions options, GreedyStringTiling greedyStringTiling) {
+    protected AbstractComparisonStrategy(JPlagOptions options, GreedyStringTiling greedyStringTiling) {
         this.greedyStringTiling = greedyStringTiling;
         this.options = options;
     }
@@ -47,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);
-        logger.info("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);

+ 2 - 4
jplag/src/main/java/de/jplag/strategy/ComparisonMode.java

@@ -1,7 +1,5 @@
 package de.jplag.strategy;
 
-import static java.util.stream.Collectors.toList;
-
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Optional;
@@ -16,7 +14,7 @@ public enum ComparisonMode {
     private final String name;
     private final String description;
 
-    private ComparisonMode(String description) {
+    ComparisonMode(String description) {
         this.description = description;
         name = toString().toLowerCase();
     }
@@ -40,7 +38,7 @@ public enum ComparisonMode {
      * @return a collections of all mode names.
      */
     public static Collection<String> allNames() {
-        return Arrays.stream(values()).map(ComparisonMode::getName).collect(toList());
+        return Arrays.stream(values()).map(ComparisonMode::getName).toList();
     }
 
     /**

+ 1 - 1
jplag/src/main/java/de/jplag/strategy/NormalComparisonStrategy.java

@@ -28,7 +28,7 @@ public class NormalComparisonStrategy extends AbstractComparisonStrategy {
         List<JPlagComparison> comparisons = new ArrayList<>();
 
         for (SubmissionTuple tuple : buildComparisonTuples(submissions)) {
-            compareSubmissions(tuple.getLeft(), tuple.getRight(), withBaseCode).ifPresent(comparisons::add);
+            compareSubmissions(tuple.left(), tuple.right(), withBaseCode).ifPresent(comparisons::add);
         }
 
         long durationInMillis = System.currentTimeMillis() - timeBeforeStartInMillis;

+ 16 - 17
jplag/src/main/java/de/jplag/strategy/ParallelComparisonStrategy.java

@@ -3,19 +3,14 @@ package de.jplag.strategy;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.*;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
-import de.jplag.GreedyStringTiling;
-import de.jplag.JPlagComparison;
-import de.jplag.JPlagResult;
-import de.jplag.Submission;
-import de.jplag.SubmissionSet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import de.jplag.*;
 import de.jplag.options.JPlagOptions;
 
 /**
@@ -23,6 +18,8 @@ import de.jplag.options.JPlagOptions;
  * @author Timur Saglam
  */
 public class ParallelComparisonStrategy extends AbstractComparisonStrategy {
+    private static final Logger logger = LoggerFactory.getLogger("JPlag");
+
     private static final int TIMEOUT_IN_SECONDS = 5;
     private final ConcurrentMap<String, Lock> submissionLocks;
     private ExecutorService threadPool;
@@ -60,8 +57,9 @@ public class ParallelComparisonStrategy extends AbstractComparisonStrategy {
         while (successfulComparisons < tuples.size()) {
             try {
                 Thread.sleep(5);
-            } catch (InterruptedException exception) {
-                exception.printStackTrace();
+            } catch (InterruptedException e) {
+                logger.error(e.getMessage(), e);
+                Thread.currentThread().interrupt();
             }
         }
 
@@ -81,13 +79,13 @@ public class ParallelComparisonStrategy extends AbstractComparisonStrategy {
         return new Runnable() {
             @Override
             public void run() {
-                Lock leftLock = getOrCreateLock(tuple.getLeft().getName());
-                Lock rightLock = getOrCreateLock(tuple.getRight().getName());
+                Lock leftLock = getOrCreateLock(tuple.left().getName());
+                Lock rightLock = getOrCreateLock(tuple.right().getName());
                 boolean hasLeft = leftLock.tryLock();
                 boolean hasRight = hasLeft && rightLock.tryLock();
                 try {
                     if (hasLeft && hasRight) { // both locks acquired!
-                        compareSubmissions(tuple.getLeft(), tuple.getRight(), withBaseCode).ifPresent(comparisons::add);
+                        compareSubmissions(tuple.left(), tuple.right(), withBaseCode).ifPresent(comparisons::add);
                         synchronized (this) {
                             successfulComparisons++;
                         }
@@ -123,8 +121,9 @@ public class ParallelComparisonStrategy extends AbstractComparisonStrategy {
             if (!threadPool.awaitTermination(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS)) {
                 throw new IllegalStateException("Parallel comparison calculation timed out!");
             }
-        } catch (InterruptedException exception) {
-            throw new IllegalStateException("Thread pool interrupted during comparison: " + exception.getMessage());
+        } catch (InterruptedException e) {
+            logger.error(e.getMessage(), e);
+            Thread.currentThread().interrupt();
         }
     }
 

+ 2 - 15
jplag/src/main/java/de/jplag/strategy/SubmissionTuple.java

@@ -5,27 +5,14 @@ import de.jplag.Submission;
 /**
  * Tupel of source code submissions.
  */
-public class SubmissionTuple {
-    private final Submission left;
-    private final Submission right;
-
+public record SubmissionTuple(Submission left, Submission right) {
     /**
      * Creates a tupel from two submissions.
      */
-    public SubmissionTuple(Submission left, Submission right) {
+    public SubmissionTuple {
         if (left == null || right == null) {
             throw new IllegalArgumentException("Submissions cannot be null");
         }
-        this.left = left;
-        this.right = right;
-    }
-
-    public Submission getLeft() {
-        return left;
-    }
-
-    public Submission getRight() {
-        return right;
     }
 
     @Override

+ 0 - 1
jplag/src/main/resources/de/jplag/messages_de.properties

@@ -32,7 +32,6 @@ Report.Matches_for_X1_AND_X2=&Uuml;bereinstimmungen f&uuml;r {1_NAME} & {2_NAME}
 Report.Links=Links
 Report.INDEX=INDEX
 Report.HELP=HILFE
-Report.Distribution=Verteilung
 Report.Token_Distribution=Verteilung der Token
 
 Clusters.MIN_single_link=MIN, einfache Verkn&uuml;pfung

+ 0 - 1
jplag/src/main/resources/de/jplag/messages_en.properties

@@ -32,7 +32,6 @@ Report.Matches_for_X1_AND_X2=Matches for {1_NAME} & {2_NAME}
 Report.Links=Links
 Report.INDEX=INDEX
 Report.HELP=HELP
-Report.Distribution=Distribution
 Report.Token_Distribution=Token Distribution
 
 

+ 0 - 2
jplag/src/main/resources/de/jplag/messages_es.properties

@@ -3,7 +3,6 @@ Report.MatchesAvg=Coincidencias clasificadas por la semejanza media
 Report.MatchesMax=Coincidencias clasificadas por semejanza máxima
 Report.MatchesMin=Coincidencias clasificadas por semejanza mínima
 Report.average_similarity=semejanza media
-Report.maximum_similarity=semejanza máxima
 Report.maximum_similarity=semejanza mínima
 Report.WhatIsThis=¿Qué es esto?
 Report.Search_Results=Resultados de la Búsqueda
@@ -32,7 +31,6 @@ Report.Matches_for_X1_AND_X2=Coinicdencias para {1_NAME} & {2_NAME}
 Report.Links=Links
 Report.INDEX=INDICE
 Report.HELP=AYUDA
-Report.Distribution=Distribución
 Report.Token_Distribution=Distribución de símbolos
 
 Clusters.MIN_single_link=MIN, link único

+ 0 - 1
jplag/src/main/resources/de/jplag/messages_fr.properties

@@ -32,7 +32,6 @@ Report.Matches_for_X1_AND_X2=Ressemblance pour {1_NAME} & {2_NAME}
 Report.Links=Liens
 Report.INDEX=INDEX
 Report.HELP=AIDE
-Report.Distribution=Repartition
 Report.Token_Distribution=Repartition des tokens
 
 Clusters.MIN_single_link=MIN, single link

+ 0 - 1
jplag/src/main/resources/de/jplag/messages_pt.properties

@@ -32,7 +32,6 @@ Report.Matches_for_X1_AND_X2=Matches for {1_NAME} & {2_NAME}
 Report.Links=Links
 Report.INDEX=INDEX
 Report.HELP=HELP
-Report.Distribution=Distribution
 Report.Token_Distribution=Token Distribution
 
 Clusters.MIN_single_link=MIN, single link

+ 3 - 3
jplag/src/test/java/de/jplag/clustering/algorithm/BayesianOptimizationTest.java

@@ -6,10 +6,10 @@ import org.apache.commons.math3.linear.ArrayRealVector;
 import org.apache.commons.math3.linear.RealVector;
 import org.junit.jupiter.api.Test;
 
-public class BayesianOptimizationTest {
+class BayesianOptimizationTest {
 
     @Test
-    public void findParabolaMax() {
+    void findParabolaMax() {
         RealVector minima = new ArrayRealVector(new double[] {-20});
         RealVector maxima = new ArrayRealVector(new double[] {10});
         RealVector lengthScale = new ArrayRealVector(new double[] {5});
@@ -19,7 +19,7 @@ public class BayesianOptimizationTest {
             double val = v.getEntry(0);
             double result = -(val - maximumAt) * (val - maximumAt);
             result += 0.2 * (Math.random() - 0.5);
-            return new BayesianOptimization.OptimizationResult<Double>(result, result);
+            return new BayesianOptimization.OptimizationResult<>(result, result);
         }).getParams().getEntry(0);
         assertEquals(maximumAt, maximumPoint, 1);
     }

+ 1 - 1
jplag/src/test/java/de/jplag/clustering/algorithm/ClusteringData.java

@@ -38,7 +38,7 @@ public enum ClusteringData {
     private final Set<Set<Integer>> expected;
     private final ClusteringOptions options;
 
-    private ClusteringData(Supplier<RealMatrix> similarity, int[][] expected, ClusteringOptions.Builder options) {
+    ClusteringData(Supplier<RealMatrix> similarity, int[][] expected, ClusteringOptions.Builder options) {
         this.similarity = similarity.get();
         this.expected = makeSets(
                 Arrays.stream(expected).map(intArray -> Arrays.stream(intArray).boxed().collect(Collectors.toList())).collect(Collectors.toList()));

+ 1 - 1
jplag/src/test/java/de/jplag/clustering/preprocessors/PreprocessingTestBase.java

@@ -50,7 +50,7 @@ public class PreprocessingTestBase {
 
         assertTrue(usedOriginalIndices.stream().allMatch(index -> index >= 0 && index < original.getColumnDimension()), "original indices valid");
 
-        double[] columnNorms = IntStream.range(0, result.getColumnDimension()).mapToObj(index -> result.getColumn(index))
+        double[] columnNorms = IntStream.range(0, result.getColumnDimension()).mapToObj(result::getColumn)
                 .mapToDouble(array -> new ArrayRealVector(array, false).getNorm()).toArray();
 
         for (int i = 0; i < columnNorms.length; i++) {