Browse Source

refactor baseCodeSubmissionName to baseCodeSubmissionDirectory and provided legacy initializers for baseCodeSubmissionName

Jan Wittler 4 years ago
parent
commit
104c7ded88

+ 6 - 4
cli/src/main/java/de/jplag/CLI.java

@@ -48,6 +48,7 @@ import org.slf4j.LoggerFactory;
 
 import de.jplag.clustering.ClusteringOptions;
 import de.jplag.clustering.Preprocessing;
+import de.jplag.exceptions.BasecodeException;
 import de.jplag.exceptions.ExitException;
 import de.jplag.logger.CollectedLoggerFactory;
 import de.jplag.options.JPlagOptions;
@@ -136,7 +137,7 @@ public final class CLI {
      * @param namespace encapsulates the parsed arguments in a {@link Namespace} format.
      * @return the newly built options.F
      */
-    public JPlagOptions buildOptionsFromArguments(Namespace namespace) {
+    public JPlagOptions buildOptionsFromArguments(Namespace namespace) throws BasecodeException {
         String fileSuffixString = SUFFIXES.getFrom(namespace);
         String[] fileSuffixes = new String[] {};
         if (fileSuffixString != null) {
@@ -156,9 +157,10 @@ public final class CLI {
         ClusteringOptions clusteringOptions = getClusteringOptions(namespace);
 
         return new JPlagOptions(language, MIN_TOKEN_MATCH.getFrom(namespace), submissionDirectories, oldSubmissionDirectories,
-                BASE_CODE.getFrom(namespace), SUBDIRECTORY.getFrom(namespace), Arrays.stream(fileSuffixes).toList(), EXCLUDE_FILE.getFrom(namespace),
-                JPlagOptions.DEFAULT_SIMILARITY_METRIC, SIMILARITY_THRESHOLD.getFrom(namespace), SHOWN_COMPARISONS.getFrom(namespace),
-                clusteringOptions, Verbosity.fromOption(VERBOSITY.getFrom(namespace)), DEBUG.getFrom(namespace));
+                (String) BASE_CODE.getFrom(namespace), SUBDIRECTORY.getFrom(namespace), Arrays.stream(fileSuffixes).toList(),
+                EXCLUDE_FILE.getFrom(namespace), JPlagOptions.DEFAULT_SIMILARITY_METRIC, SIMILARITY_THRESHOLD.getFrom(namespace),
+                SHOWN_COMPARISONS.getFrom(namespace), clusteringOptions, Verbosity.fromOption(VERBOSITY.getFrom(namespace)),
+                DEBUG.getFrom(namespace));
     }
 
     private static ClusteringOptions getClusteringOptions(Namespace namespace) {

+ 2 - 2
cli/src/test/java/de/jplag/cli/BaseCodeOptionTest.java

@@ -14,13 +14,13 @@ class BaseCodeOptionTest extends CommandLineInterfaceTest {
     @Test
     void testDefaultValue() {
         buildOptionsFromCLI(CURRENT_DIRECTORY);
-        assertNull(options.baseCodeSubmissionName());
+        assertNull(options.baseCodeSubmissionDirectory());
     }
 
     @Test
     void testCustomName() {
         String argument = buildArgument(CommandLineArgument.BASE_CODE, NAME);
         buildOptionsFromCLI(argument, CURRENT_DIRECTORY);
-        assertEquals(NAME, options.baseCodeSubmissionName());
+        assertEquals(NAME, options.baseCodeSubmissionDirectory().getName());
     }
 }

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

@@ -2,6 +2,7 @@ package de.jplag.cli;
 
 import static de.jplag.CommandLineArgument.ROOT_DIRECTORY;
 import static java.util.stream.Collectors.toSet;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 
 import java.util.Arrays;
 
@@ -9,6 +10,7 @@ import net.sourceforge.argparse4j.inf.Namespace;
 
 import de.jplag.CLI;
 import de.jplag.CommandLineArgument;
+import de.jplag.exceptions.BasecodeException;
 import de.jplag.options.JPlagOptions;
 
 /**
@@ -53,7 +55,11 @@ public abstract class CommandLineInterfaceTest {
         cli = new CLI();
         namespace = cli.parseArguments(arguments);
         System.out.println("Parsed arguments: " + parsedKeys(arguments));
-        options = cli.buildOptionsFromArguments(namespace);
+        try {
+            options = cli.buildOptionsFromArguments(namespace);
+        } catch (BasecodeException e) {
+            assertFalse(true);
+        }
     }
 
 }

+ 10 - 93
core/src/main/java/de/jplag/SubmissionSetBuilder.java

@@ -17,7 +17,6 @@ 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;
 import de.jplag.exceptions.SubmissionException;
@@ -70,11 +69,13 @@ public class SubmissionSetBuilder {
             processRootDirectoryEntries(oldDirectory, multipleRoots, foundSubmissions, false);
         }
 
-        Optional<Submission> baseCodeSubmission = loadBaseCode(submissionDirectories, oldSubmissionDirectories, foundSubmissions);
+        Optional<Submission> baseCodeSubmission = loadBaseCode(submissionDirectories, oldSubmissionDirectories);
+        baseCodeSubmission.ifPresent(baseSubmission -> {
+            foundSubmissions.remove(baseSubmission.getRoot());
+        });
 
         // Merge everything in a submission set.
         List<Submission> submissions = new ArrayList<>(foundSubmissions.values());
-
         return new SubmissionSet(submissions, baseCodeSubmission.orElse(null), options);
     }
 
@@ -126,102 +127,18 @@ public class SubmissionSetBuilder {
         }
     }
 
-    private Optional<Submission> loadBaseCode(Set<File> submissionDirectories, Set<File> oldSubmissionDirectories,
-            Map<File, Submission> foundSubmissions) throws ExitException {
+    private Optional<Submission> loadBaseCode(Set<File> submissionDirectories, Set<File> oldSubmissionDirectories) throws ExitException {
         if (!options.hasBaseCode()) {
             return Optional.empty();
         }
 
-        String baseCodeName = Optional.ofNullable(options.baseCodeSubmissionName()).orElseThrow();
-        Submission baseCode = loadBaseCodeAsPath(baseCodeName);
-        if (baseCode == null) {
-            int numberOfRootDirectories = submissionDirectories.size() + oldSubmissionDirectories.size();
-            if (numberOfRootDirectories > 1) {
-                throw new BasecodeException("The base code submission needs to be specified by path instead of by name!");
-            }
-
-            // There is one root directory, and the submissionDirectories variable has been checked to be non-empty.
-            // 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);
-        }
-
-        if (baseCode != null) {
-            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) {
-                logger.info("Submission \"{}\" is the specified basecode, it will be skipped during comparison.", removed.getName());
-            }
-        }
-        return Optional.ofNullable(baseCode);
-    }
-
-    /**
-     * Try to load the basecode under the assumption of being a path.
-     * @return Base code submission if the option value can be interpreted as global path, else {@code null}.
-     * @throws ExitException when the option value is a path with errors.
-     */
-    private Submission loadBaseCodeAsPath(String baseCodeName) throws ExitException {
-        File basecodeSubmission = new File(baseCodeName);
-        if (!basecodeSubmission.exists()) {
-            return null;
-        }
-
-        String errorMessage = isExcludedEntry(basecodeSubmission);
-        if (errorMessage != null) {
-            throw new BasecodeException(errorMessage); // Stating an excluded path as basecode isn't very useful.
-        }
-
-        try {
-            // Use an unlikely short name for the base code. If all is well, this name should not appear
-            // in the output since basecode matches are removed from it
-            return processSubmission(basecodeSubmission.getName(), basecodeSubmission, false);
-        } catch (SubmissionException exception) {
-            throw new BasecodeException(exception.getMessage(), exception); // Change thrown exception to basecode exception.
-        }
-    }
-
-    /**
-     * Try to load the basecode by looking up the basecode name in the root directory.
-     * @return the base code submission if a fitting subdirectory was found, else {@code null}.
-     * @throws ExitException when the option value is a sub-directory with errors.
-     */
-    private Submission loadBaseCodeViaName(String baseCodeName, File rootDirectory, Map<File, Submission> foundSubmissions) throws ExitException {
-        // Is the option value a single name after trimming spurious separators?
-        String name = baseCodeName;
-        while (name.startsWith(File.separator)) {
-            name = name.substring(1);
-        }
-        while (name.endsWith(File.separator)) {
-            name = name.substring(0, name.length() - 1);
-        }
-
-        // If it is not a name of a single sub-directory, bail out.
-        if (name.isEmpty() || name.contains(File.separator)) {
-            return null;
-        }
-
-        if (name.contains(".")) {
-            throw new BasecodeException("The basecode directory name \"" + name + "\" cannot contain dots!");
-        }
+        File baseCodeSubmissionDirectory = options.baseCodeSubmissionDirectory();
+        Submission baseCodeSubmission = processSubmission(baseCodeSubmissionDirectory.getName(), baseCodeSubmissionDirectory, false);
 
-        // Grab the basecode submission from the regular submissions.
-        File basecodePath = new File(rootDirectory, baseCodeName);
-
-        Submission baseCode = foundSubmissions
-                .get(makeCanonical(basecodePath, it -> new BasecodeException("Cannot compute canonical file path: " + basecodePath, it)));
-        if (baseCode == null) {
-            // No base code found at all, report an error.
-            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, warn about legacy usage.
-            logger.warn("Legacy use of the base code option found, please specify the basecode by path instead of by name!");
+        if (baseCodeSubmission != null) {
+            logger.info("Basecode directory \"{}\" will be used.", baseCodeSubmission.getName());
         }
-        return baseCode;
+        return Optional.ofNullable(baseCodeSubmission);
     }
 
     /**

+ 107 - 38
core/src/main/java/de/jplag/options/JPlagOptions.java

@@ -21,6 +21,7 @@ import org.slf4j.LoggerFactory;
 import de.jplag.JPlag;
 import de.jplag.Language;
 import de.jplag.clustering.ClusteringOptions;
+import de.jplag.exceptions.BasecodeException;
 
 /**
  * This record defines the options to configure {@link JPlag}.
@@ -29,7 +30,7 @@ import de.jplag.clustering.ClusteringOptions;
  * matching section. A smaller {@code <n>} increases the sensitivity but might lead to more false-positives.
  * @param submissionDirectories Directories with new submissions. These must be checked for plagiarism.
  * @param oldSubmissionDirectories Directories with old submissions to check against.
- * @param baseCodeSubmissionName Path name of the directory containing the base code.
+ * @param baseCodeSubmissionDirectory Directory containing the base code.
  * @param subdirectoryName Example: If the subdirectoryName is 'src', only the code inside submissionDir/src of each
  * submission will be used for comparison.
  * @param fileSuffixes List of file suffixes that should be included.
@@ -47,7 +48,7 @@ import de.jplag.clustering.ClusteringOptions;
  * @param debugParser If true, submissions that cannot be parsed will be stored in a separate directory.
  */
 public record JPlagOptions(Language language, Integer minimumTokenMatch, Set<File> submissionDirectories, Set<File> oldSubmissionDirectories,
-        String baseCodeSubmissionName, String subdirectoryName, List<String> fileSuffixes, String exclusionFileName,
+        File baseCodeSubmissionDirectory, String subdirectoryName, List<String> fileSuffixes, String exclusionFileName,
         SimilarityMetric similarityMetric, double similarityThreshold, int maximumNumberOfComparisons, ClusteringOptions clusteringOptions,
         Verbosity verbosity, boolean debugParser) {
 
@@ -60,12 +61,12 @@ public record JPlagOptions(Language language, Integer minimumTokenMatch, Set<Fil
     private static final Logger logger = LoggerFactory.getLogger(JPlag.class);
 
     public JPlagOptions(Language language, Set<File> submissionDirectories, Set<File> oldSubmissionDirectories) {
-        this(language, null, submissionDirectories, oldSubmissionDirectories, null, null, null, null, DEFAULT_SIMILARITY_METRIC,
+        this(language, null, submissionDirectories, oldSubmissionDirectories, (File) null, null, null, null, DEFAULT_SIMILARITY_METRIC,
                 DEFAULT_SIMILARITY_THRESHOLD, DEFAULT_SHOWN_COMPARISONS, new ClusteringOptions(), null, false);
     }
 
     public JPlagOptions(Language language, Integer minimumTokenMatch, Set<File> submissionDirectories, Set<File> oldSubmissionDirectories,
-            String baseCodeSubmissionName, String subdirectoryName, List<String> fileSuffixes, String exclusionFileName,
+            File baseCodeSubmissionDirectory, String subdirectoryName, List<String> fileSuffixes, String exclusionFileName,
             SimilarityMetric similarityMetric, double similarityThreshold, int maximumNumberOfComparisons, ClusteringOptions clusteringOptions,
             Verbosity verbosity, boolean debugParser) {
         this.language = language;
@@ -78,98 +79,98 @@ public record JPlagOptions(Language language, Integer minimumTokenMatch, Set<Fil
         this.exclusionFileName = exclusionFileName;
         this.submissionDirectories = submissionDirectories == null ? null : Collections.unmodifiableSet(submissionDirectories);
         this.oldSubmissionDirectories = oldSubmissionDirectories == null ? null : Collections.unmodifiableSet(oldSubmissionDirectories);
-        this.baseCodeSubmissionName = (baseCodeSubmissionName == null || baseCodeSubmissionName.isBlank()) ? null : baseCodeSubmissionName;
+        this.baseCodeSubmissionDirectory = baseCodeSubmissionDirectory;
         this.subdirectoryName = subdirectoryName;
         this.verbosity = verbosity;
         this.clusteringOptions = clusteringOptions;
     }
 
     public JPlagOptions withLanguageOption(Language language) {
-        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionName,
+        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionDirectory,
                 subdirectoryName, fileSuffixes, exclusionFileName, similarityMetric, similarityThreshold, maximumNumberOfComparisons,
                 clusteringOptions, verbosity, debugParser);
     }
 
     public JPlagOptions withDebugParser(boolean debugParser) {
-        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionName,
+        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionDirectory,
                 subdirectoryName, fileSuffixes, exclusionFileName, similarityMetric, similarityThreshold, maximumNumberOfComparisons,
                 clusteringOptions, verbosity, debugParser);
     }
 
     public JPlagOptions withFileSuffixes(List<String> fileSuffixes) {
-        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionName,
+        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionDirectory,
                 subdirectoryName, fileSuffixes, exclusionFileName, similarityMetric, similarityThreshold, maximumNumberOfComparisons,
                 clusteringOptions, verbosity, debugParser);
     }
 
     public JPlagOptions withSimilarityThreshold(double similarityThreshold) {
-        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionName,
+        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionDirectory,
                 subdirectoryName, fileSuffixes, exclusionFileName, similarityMetric, similarityThreshold, maximumNumberOfComparisons,
                 clusteringOptions, verbosity, debugParser);
     }
 
     public JPlagOptions withMaximumNumberOfComparisons(int maximumNumberOfComparisons) {
-        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionName,
+        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionDirectory,
                 subdirectoryName, fileSuffixes, exclusionFileName, similarityMetric, similarityThreshold, maximumNumberOfComparisons,
                 clusteringOptions, verbosity, debugParser);
     }
 
     public JPlagOptions withSimilarityMetric(SimilarityMetric similarityMetric) {
-        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionName,
+        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionDirectory,
                 subdirectoryName, fileSuffixes, exclusionFileName, similarityMetric, similarityThreshold, maximumNumberOfComparisons,
                 clusteringOptions, verbosity, debugParser);
     }
 
     public JPlagOptions withMinimumTokenMatch(Integer minimumTokenMatch) {
-        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionName,
+        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionDirectory,
                 subdirectoryName, fileSuffixes, exclusionFileName, similarityMetric, similarityThreshold, maximumNumberOfComparisons,
                 clusteringOptions, verbosity, debugParser);
     }
 
     public JPlagOptions withExclusionFileName(String exclusionFileName) {
-        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionName,
+        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionDirectory,
                 subdirectoryName, fileSuffixes, exclusionFileName, similarityMetric, similarityThreshold, maximumNumberOfComparisons,
                 clusteringOptions, verbosity, debugParser);
     }
 
     public JPlagOptions withSubmissionDirectories(Set<File> submissionDirectories) {
-        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionName,
+        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionDirectory,
                 subdirectoryName, fileSuffixes, exclusionFileName, similarityMetric, similarityThreshold, maximumNumberOfComparisons,
                 clusteringOptions, verbosity, debugParser);
     }
 
     public JPlagOptions withOldSubmissionDirectories(Set<File> oldSubmissionDirectories) {
-        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionName,
+        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionDirectory,
                 subdirectoryName, fileSuffixes, exclusionFileName, similarityMetric, similarityThreshold, maximumNumberOfComparisons,
                 clusteringOptions, verbosity, debugParser);
     }
 
-    public JPlagOptions withBaseCodeSubmissionName(String baseCodeSubmissionName) {
-        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionName,
+    public JPlagOptions withBaseCodeSubmissionDirectory(File baseCodeSubmissionDirectory) {
+        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionDirectory,
                 subdirectoryName, fileSuffixes, exclusionFileName, similarityMetric, similarityThreshold, maximumNumberOfComparisons,
                 clusteringOptions, verbosity, debugParser);
     }
 
     public JPlagOptions withSubdirectoryName(String subdirectoryName) {
-        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionName,
+        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionDirectory,
                 subdirectoryName, fileSuffixes, exclusionFileName, similarityMetric, similarityThreshold, maximumNumberOfComparisons,
                 clusteringOptions, verbosity, debugParser);
     }
 
     public JPlagOptions withVerbosity(Verbosity verbosity) {
-        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionName,
+        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionDirectory,
                 subdirectoryName, fileSuffixes, exclusionFileName, similarityMetric, similarityThreshold, maximumNumberOfComparisons,
                 clusteringOptions, verbosity, debugParser);
     }
 
     public JPlagOptions withClusteringOptions(ClusteringOptions clusteringOptions) {
-        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionName,
+        return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionDirectory,
                 subdirectoryName, fileSuffixes, exclusionFileName, similarityMetric, similarityThreshold, maximumNumberOfComparisons,
                 clusteringOptions, verbosity, debugParser);
     }
 
     public boolean hasBaseCode() {
-        return baseCodeSubmissionName != null;
+        return baseCodeSubmissionDirectory != null;
     }
 
     public Set<String> excludedFiles() {
@@ -184,23 +185,6 @@ public record JPlagOptions(Language language, Integer minimumTokenMatch, Set<Fil
         return fileSuffixes == null ? null : Collections.unmodifiableList(fileSuffixes);
     }
 
-    /**
-     * Path name of the directory containing the base code.<br>
-     * For backwards compatibility it may also be a directory name inside the root directory. Condition for the latter is
-     * <ul>
-     * <li>Specified path does not exist.</li>
-     * <li>Name has not have a separator character after trimming them from both ends (leaving at least a one-character
-     * name).</li>
-     * <li>A submission with the specified name exists in the root directory.</li>
-     * </ul>
-     * It's an error if a string has been provided, but it is neither an existing path nor does it fulfill all the
-     * conditions of the compatibility fallback listed above.
-     */
-    @Override
-    public String baseCodeSubmissionName() {
-        return baseCodeSubmissionName;
-    }
-
     @Override
     public Integer minimumTokenMatch() {
         var language = language();
@@ -241,4 +225,89 @@ public record JPlagOptions(Language language, Integer minimumTokenMatch, Set<Fil
     private Integer normalizeMinimumTokenMatch(Integer minimumTokenMatch) {
         return (minimumTokenMatch != null && minimumTokenMatch < 1) ? Integer.valueOf(1) : minimumTokenMatch;
     }
+
+    /**
+     * Creates new options to configure {@link JPlag}.
+     * @param language Language to use when parsing the submissions.
+     * @param minimumTokenMatch Tunes the comparison sensitivity by adjusting the minimum token required to be counted as
+     * matching section. A smaller {@code <n>} increases the sensitivity but might lead to more false-positives.
+     * @param submissionDirectories Directories with new submissions. These must be checked for plagiarism.
+     * @param oldSubmissionDirectories Directories with old submissions to check against.
+     * @param baseCodeSubmissionName Path name of the directory containing the base code.
+     * @param subdirectoryName Example: If the subdirectoryName is 'src', only the code inside submissionDir/src of each
+     * submission will be used for comparison.
+     * @param fileSuffixes List of file suffixes that should be included.
+     * @param exclusionFileName Name of the file that contains the names of files to exclude from comparison.
+     * @param similarityMetric The similarity metric determines how the minimum similarity threshold required for a
+     * comparison (of two submissions) is calculated. This affects which comparisons are stored and thus make it into the
+     * result object.
+     * @param similarityThreshold Percentage value (must be between 0 and 100). Comparisons (of submissions pairs) with a
+     * similarity below this threshold will be ignored. The default value of 0 allows all matches to be stored. This affects
+     * which comparisons are stored and thus make it into the result object. See also {@link #similarityMetric()}.
+     * @param maximumNumberOfComparisons The maximum number of comparisons that will be shown in the generated report. If
+     * set to {@link #SHOW_ALL_COMPARISONS} all comparisons will be shown.
+     * @param clusteringOptions Clustering options
+     * @param verbosity Level of output verbosity.
+     * @param debugParser If true, submissions that cannot be parsed will be stored in a separate directory.
+     * @deprecated Use the default initializer with @{{@link #baseCodeSubmissionDirectory} instead.
+     */
+    @Deprecated
+    public JPlagOptions(Language language, Integer minimumTokenMatch, Set<File> submissionDirectories, Set<File> oldSubmissionDirectories,
+            String baseCodeSubmissionName, String subdirectoryName, List<String> fileSuffixes, String exclusionFileName,
+            SimilarityMetric similarityMetric, double similarityThreshold, int maximumNumberOfComparisons, ClusteringOptions clusteringOptions,
+            Verbosity verbosity, boolean debugParser) throws BasecodeException {
+        this(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories,
+                convertLegacyBaseCodeToFile(baseCodeSubmissionName, submissionDirectories), subdirectoryName, fileSuffixes, exclusionFileName,
+                similarityMetric, similarityThreshold, maximumNumberOfComparisons, clusteringOptions, verbosity, debugParser);
+    }
+
+    /**
+     * Creates a new options instance with the provided base code submission name
+     * @param baseCodeSubmissionName the path or name of the base code submission
+     * @return a new options instance with the provided base code submission name
+     * @deprecated Use @{{@link #withBaseCodeSubmissionDirectory} instead.
+     */
+    @Deprecated
+    public JPlagOptions withBaseCodeSubmissionName(String baseCodeSubmissionName) {
+        try {
+            return new JPlagOptions(language, minimumTokenMatch, submissionDirectories, oldSubmissionDirectories, baseCodeSubmissionName,
+                    subdirectoryName, fileSuffixes, exclusionFileName, similarityMetric, similarityThreshold, maximumNumberOfComparisons,
+                    clusteringOptions, verbosity, debugParser);
+        } catch (BasecodeException e) {
+            throw new IllegalArgumentException(e.getMessage(), e.getCause());
+        }
+    }
+
+    @Deprecated
+    private static File convertLegacyBaseCodeToFile(String baseCodeSubmissionName, Set<File> submissionDirectories) throws BasecodeException {
+        if (baseCodeSubmissionName == null) {
+            return null;
+        }
+        File baseCodeAsAbsolutePath = new File(baseCodeSubmissionName);
+        if (baseCodeAsAbsolutePath.exists()) {
+            return baseCodeAsAbsolutePath;
+        } else {
+            if (submissionDirectories.size() != 1) {
+                throw new BasecodeException(
+                        "Legacy use of the base code option is only supported for one root directory! Please migrate to the path-based API.");
+            }
+            String normalizedName = baseCodeSubmissionName;
+            while (normalizedName.startsWith(File.separator)) {
+                normalizedName = normalizedName.substring(1);
+            }
+            while (normalizedName.endsWith(File.separator)) {
+                normalizedName = normalizedName.substring(0, normalizedName.length() - 1);
+            }
+            if (normalizedName.isEmpty() || normalizedName.contains(File.separator)) {
+                throw new BasecodeException(
+                        "The basecode directory name \"" + normalizedName + "\" cannot contain dots! Please migrate to the path-based API.");
+            }
+            if (normalizedName.contains(".")) {
+                throw new BasecodeException(
+                        "The basecode directory name \"" + normalizedName + "\" cannot contain dots! Please migrate to the path-based API.");
+            }
+            File submissionDirectory = submissionDirectories.iterator().next();
+            return new File(submissionDirectory, baseCodeSubmissionName);
+        }
+    }
 }

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

@@ -138,7 +138,7 @@ public class ReportObjectFactory {
         folders.addAll(result.getOptions().submissionDirectories());
         folders.addAll(result.getOptions().oldSubmissionDirectories());
 
-        String baseCodePath = result.getOptions().hasBaseCode() ? result.getOptions().baseCodeSubmissionName() : "";
+        String baseCodePath = result.getOptions().hasBaseCode() ? result.getOptions().baseCodeSubmissionDirectory().getName() : "";
         ClusteringResultMapper clusteringResultMapper = new ClusteringResultMapper(submissionToIdFunction);
 
         OverviewReport overviewReport = new OverviewReport(folders.stream().map(File::getPath).toList(), // submissionFolderPath

+ 1 - 1
core/src/test/java/de/jplag/BaseCodeTest.java

@@ -63,6 +63,6 @@ public class BaseCodeTest extends TestBase {
 
     @Test
     void testBasecodeUserSubmissionWithDots() {
-        assertThrows(BasecodeException.class, () -> runJPlag("basecode", it -> it.withBaseCodeSubmissionName("base.ext")));
+        assertThrows(IllegalArgumentException.class, () -> runJPlag("basecode", it -> it.withBaseCodeSubmissionName("base.ext")));
     }
 }