Quellcode durchsuchen

fork & optimize: 从github上获取官方源码,并进行了一些优化(不过不保证完全没问题)

sky vor 3 Jahren
Ursprung
Commit
1c01e1de78
27 geänderte Dateien mit 327 neuen und 143 gelöschten Zeilen
  1. 1 1
      .mvn/jvm.config
  2. 4 0
      README.md
  3. 4 3
      core/src/main/java/de/jplag/GreedyStringTiling.java
  4. 11 0
      core/src/main/java/de/jplag/JPlag.java
  5. 53 24
      core/src/main/java/de/jplag/SubmissionSet.java
  6. 26 19
      core/src/main/java/de/jplag/SubmissionSetBuilder.java
  7. 4 0
      core/src/main/java/de/jplag/strategy/ParallelComparisonStrategy.java
  8. 6 6
      languages/cpp/src/main/java/de/jplag/cpp/Language.java
  9. 6 6
      languages/csharp/src/main/java/de/jplag/csharp/Language.java
  10. 14 3
      languages/emf-metamodel-dynamic/src/main/java/de/jplag/emf/dynamic/Language.java
  11. 10 10
      languages/emf-metamodel/src/main/java/de/jplag/emf/Language.java
  12. 6 6
      languages/golang/src/main/java/de/jplag/golang/Language.java
  13. 6 6
      languages/java/src/main/java/de/jplag/java/Language.java
  14. 6 6
      languages/kotlin/src/main/java/de/jplag/kotlin/Language.java
  15. 6 6
      languages/python-3/src/main/java/de/jplag/python3/Language.java
  16. 6 6
      languages/rlang/src/main/java/de/jplag/rlang/Language.java
  17. 6 6
      languages/rust/src/main/java/de/jplag/rust/Language.java
  18. 2 2
      languages/scala/src/main/scala/de/jplag/scala/Language.scala
  19. 6 6
      languages/scheme/src/main/java/de/jplag/scheme/Language.java
  20. 6 6
      languages/swift/src/main/java/de/jplag/swift/Language.java
  21. 6 6
      languages/text/src/main/java/de/jplag/text/Language.java
  22. 37 0
      lxs-test/pom.xml
  23. 66 0
      lxs-test/src/main/java/Main.java
  24. 12 0
      pom.xml
  25. 12 10
      report-viewer/package-lock.json
  26. 3 3
      report-viewer/src/components/CodePanel.vue
  27. 2 2
      report-viewer/src/components/MatchTable.vue

+ 1 - 1
.mvn/jvm.config

@@ -1 +1 @@
---add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
+--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED -Dmaven.javadoc.skip=true -Dfile.encoding=UTF-8 -Dmaven.test.skip=true

+ 4 - 0
README.md

@@ -12,6 +12,10 @@
 
 JPlag is a system that finds similarities among multiple sets of source code files. This way it can detect software plagiarism and collusion in software development. JPlag currently supports various programming languages, EMF metamodels, and natural language text.
 
+## Fork者描述
+
+1. 将token parser部分由串行改为了并行
+
 ## Supported Languages
 
 In the following, a list of all supported languages with their supported language version is provided. A language can be selected from the command line using the `-l <cli argument name>` argument.

+ 4 - 3
core/src/main/java/de/jplag/GreedyStringTiling.java

@@ -45,7 +45,7 @@ public class GreedyStringTiling {
      * @return the comparison of the submission with the base code submission.
      */
     public final JPlagComparison generateBaseCodeMarking(Submission submission, Submission baseCodeSubmission) {
-        JPlagComparison comparison = compare(submission, baseCodeSubmission);
+        JPlagComparison comparison = compare(submission, baseCodeSubmission); //
 
         List<Token> submissionTokenList = submission.getTokenList();
         Set<Token> baseCodeMarking = new HashSet<>();
@@ -95,8 +95,8 @@ public class GreedyStringTiling {
         List<Token> leftTokens = leftSubmission.getTokenList();
         List<Token> rightTokens = rightSubmission.getTokenList();
 
-        int[] leftValues = tokenValueListFromSubmission(leftSubmission);
-        int[] rightValues = tokenValueListFromSubmission(rightSubmission);
+        int[] leftValues = tokenValueListFromSubmission(leftSubmission); // 串行
+        int[] rightValues = tokenValueListFromSubmission(rightSubmission); // 串行
 
         // comparison uses <= because it is assumed that the last token is a pivot (FILE_END)
         if (leftTokens.size() <= minimumMatchLength || rightTokens.size() <= minimumMatchLength) {
@@ -208,6 +208,7 @@ public class GreedyStringTiling {
 
     /**
      * Converts the tokens of the submission to a list of values.
+     * 把tokens映射成数字
      * @param submission The submission from which to convert the tokens.
      */
     private int[] tokenValueListFromSubmission(Submission submission) {

+ 11 - 0
core/src/main/java/de/jplag/JPlag.java

@@ -10,6 +10,9 @@ import de.jplag.options.JPlagOptions;
 import de.jplag.strategy.ComparisonStrategy;
 import de.jplag.strategy.ParallelComparisonStrategy;
 
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+
 /**
  * This class coordinates the whole errorConsumer flow.
  */
@@ -39,8 +42,10 @@ public class JPlag {
      */
     public JPlagResult run() throws ExitException {
         // Parse and validate submissions.
+        long startTime1 = System.currentTimeMillis();
         SubmissionSetBuilder builder = new SubmissionSetBuilder(language, options);
         SubmissionSet submissionSet = builder.buildSubmissionSet();
+        long endTime1 = System.currentTimeMillis();
 
         int submissionCount = submissionSet.numberOfSubmissions();
         if (submissionCount < 2) {
@@ -48,10 +53,16 @@ public class JPlag {
         }
 
         // Compare valid submissions.
+
+        long startTime2= System.currentTimeMillis();
         JPlagResult result = comparisonStrategy.compareSubmissions(submissionSet);
+        long endTime2 = System.currentTimeMillis();
+
         if (logger.isInfoEnabled())
             logger.info("Total time for comparing submissions: {}", TimeUtil.formatDuration(result.getDuration()));
 
+        long period1 = endTime1 - startTime1;
+        long period2 = endTime2 - startTime2;
         result.setClusteringResult(ClusteringFactory.getClusterings(result.getAllComparisons(), options.clusteringOptions()));
 
         return result;

+ 53 - 24
core/src/main/java/de/jplag/SubmissionSet.java

@@ -2,6 +2,10 @@ package de.jplag;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 import org.slf4j.Logger;
@@ -16,8 +20,10 @@ import de.jplag.options.JPlagOptions;
  * Collection of all submissions and their basecode if it exists. Parses all submissions upon creation.
  */
 public class SubmissionSet {
+//    private static final int CPU_CORE_SIZE = 6;
     private static final Logger logger = LoggerFactory.getLogger(SubmissionSet.class);
-
+    private static final int CPU_CORE_SIZE = 6;
+    private static final ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(CPU_CORE_SIZE + 1);
     /**
      * Submissions to check for plagiarism.
      */
@@ -42,7 +48,11 @@ public class SubmissionSet {
         this.allSubmissions = submissions;
         this.baseCodeSubmission = baseCode;
         this.options = options;
+        long startTime_parseAllSubmissions = System.currentTimeMillis();
         parseAllSubmissions();
+        long endTime_parseAllSubmissions = System.currentTimeMillis();
+        threadPool.shutdown();
+        System.out.println("parseAllSubmissions:" + (endTime_parseAllSubmissions - startTime_parseAllSubmissions) + "ms");
         this.submissions = filterValidSubmissions();
         invalidSubmissions = filterInvalidSubmissions();
     }
@@ -137,37 +147,56 @@ public class SubmissionSet {
 
         long startTime = System.currentTimeMillis();
 
-        int tooShort = 0;
-        for (Submission submission : submissions) {
-            boolean ok;
-
-            logger.trace("------ Parsing submission: " + submission.getName());
-            currentSubmissionName = submission.getName();
-
-            if (!(ok = submission.parse(options.debugParser()))) {
-                errors++;
-            }
+        final AtomicInteger tooShort = new AtomicInteger(0);
+        final CountDownLatch countDownLatch = new CountDownLatch(submissions.size());
+        class SingleSubmissionParser implements Runnable {
 
-            if (submission.getTokenList() != null && submission.getNumberOfTokens() < options.minimumTokenMatch()) {
-                logger.error("Submission {} contains fewer tokens than minimum match length allows!", currentSubmissionName);
-                submission.setTokenList(null);
-                tooShort++;
-                ok = false;
-                submission.markAsErroneous();
+            private final Submission submission;
+            private final CountDownLatch countDownLatch;
+            public SingleSubmissionParser(Submission submission, CountDownLatch countDownLatch) {
+                this.submission = submission;
+                this.countDownLatch = countDownLatch;
             }
+            @Override
+            public void run() {
+                boolean ok;
+                logger.trace("------ Parsing submission: " + submission.getName());
+                currentSubmissionName = submission.getName();
+                if (!(ok = submission.parse(options.debugParser()))) {
+                    errors++;
+                }
+                if (submission.getTokenList() != null && submission.getNumberOfTokens() < options.minimumTokenMatch()) {
+                    logger.error("Submission {} contains fewer tokens than minimum match length allows!", currentSubmissionName);
+                    submission.setTokenList(null);
+                    tooShort.addAndGet(1);
+                    ok = false;
+                    submission.markAsErroneous();
+                }
+                if (ok) {
+                    logger.trace("OK -> Submission {} done", currentSubmissionName);
+                } else {
+                    logger.error("ERROR -> Submission {} removed", currentSubmissionName);
+                }
+                countDownLatch.countDown();
 
-            if (ok) {
-                logger.trace("OK");
-            } else {
-                logger.error("ERROR -> Submission {} removed", currentSubmissionName);
             }
         }
+        for (Submission submission : submissions) {
+            Runnable worker = new SingleSubmissionParser(submission, countDownLatch);
+            threadPool.execute(worker);
+        }
+        try {
+            countDownLatch.await();
+        } catch ( InterruptedException e) {
+            logger.error("ERROR -> SubmissionSet CountDownLatch error.\n{}", e.getMessage());
+        }
+
 
-        int validSubmissions = submissions.size() - errors - tooShort;
+        int validSubmissions = submissions.size() - errors - tooShort.get();
         logger.trace(validSubmissions + " submissions parsed successfully!");
         logger.trace(errors + " parser error" + (errors != 1 ? "s!" : "!"));
-        logger.trace(tooShort + " too short submission" + (tooShort != 1 ? "s!" : "!"));
-        printDetails(submissions, startTime, tooShort);
+        logger.trace(tooShort + " too short submission" + (tooShort.get() != 1 ? "s!" : "!"));
+        printDetails(submissions, startTime, tooShort.get());
     }
 
     private void printDetails(List<Submission> submissions, long startTime, int tooShort) {

+ 26 - 19
core/src/main/java/de/jplag/SubmissionSetBuilder.java

@@ -12,6 +12,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.ThreadPoolExecutor;
 import java.util.function.Function;
 
 import org.slf4j.Logger;
@@ -52,18 +53,18 @@ public class SubmissionSetBuilder {
      * @throws ExitException if the directory cannot be read.
      */
     public SubmissionSet buildSubmissionSet() throws ExitException {
-        Set<File> submissionDirectories = verifyRootDirectories(options.submissionDirectories(), true);
-        Set<File> oldSubmissionDirectories = verifyRootDirectories(options.oldSubmissionDirectories(), false);
-        checkForNonOverlappingRootDirectories(submissionDirectories, oldSubmissionDirectories);
+        Set<File> submissionDirectories = verifyRootDirectories(options.submissionDirectories(), true); // 检测根目录是否存在、根目录去重
+        Set<File> oldSubmissionDirectories = verifyRootDirectories(options.oldSubmissionDirectories(), false); // 同上
+        checkForNonOverlappingRootDirectories(submissionDirectories, oldSubmissionDirectories); // old目录中去掉交集
 
         // For backward compatibility, don't prefix submission names with their root directory
         // if there is only one root directory.
-        int numberOfRootDirectories = submissionDirectories.size() + oldSubmissionDirectories.size();
+        int numberOfRootDirectories = submissionDirectories.size() + oldSubmissionDirectories.size(); // 所有的根目录数量
         boolean multipleRoots = (numberOfRootDirectories > 1);
 
         // Collect valid looking entries from the root directories.
         Map<File, Submission> foundSubmissions = new HashMap<>();
-        for (File directory : submissionDirectories) {
+        for (File directory : submissionDirectories) { // 对于每个根目录,找到符合条件的二级目录,构造submission(二级目录往下递归),填充foundSubmissions
             processRootDirectoryEntries(directory, multipleRoots, foundSubmissions, true);
         }
         for (File oldDirectory : oldSubmissionDirectories) {
@@ -71,7 +72,7 @@ public class SubmissionSetBuilder {
         }
 
         Optional<Submission> baseCodeSubmission = loadBaseCode();
-        baseCodeSubmission.ifPresent(baseSubmission -> foundSubmissions.remove(baseSubmission.getRoot()));
+        baseCodeSubmission.ifPresent(baseSubmission -> foundSubmissions.remove(baseSubmission.getRoot())); // foundSubmissions里面去除掉baseSubmission
 
         // Merge everything in a submission set.
         List<Submission> submissions = new ArrayList<>(foundSubmissions.values());
@@ -111,14 +112,15 @@ public class SubmissionSetBuilder {
     private void checkForNonOverlappingRootDirectories(Set<File> submissionDirectories, Set<File> oldSubmissionDirectories) {
 
         Set<File> commonRootdirectories = new HashSet<>(submissionDirectories);
-        commonRootdirectories.retainAll(oldSubmissionDirectories);
-        if (commonRootdirectories.isEmpty()) {
+        commonRootdirectories.retainAll(oldSubmissionDirectories); // 新旧根目录取交集
+        if (commonRootdirectories.isEmpty()) { // 没有交集就不管
             return;
         }
 
         // As old submission directories are only read while new submission directories are both read and checked, the
         // former use can be removed without affecting the result of the checks.
-        oldSubmissionDirectories.removeAll(commonRootdirectories);
+        // 由于旧的提交目录只被读取,而新的提交目录同时被读取和检查,因此可以在不影响检查结果的情况下删除旧的使用
+        oldSubmissionDirectories.removeAll(commonRootdirectories); // 旧的根目录中去掉交集
         for (File rootDirectory : commonRootdirectories) {
             logger.warn(
                     "Root directory \"{}\" is specified both for plagiarism checking and for prior submissions, will perform plagiarism checking only.",
@@ -175,11 +177,11 @@ public class SubmissionSetBuilder {
      * @return Error message if the entry should be ignored.
      */
     private String isExcludedEntry(File submissionEntry) {
-        if (isFileExcluded(submissionEntry)) {
+        if (isFileExcluded(submissionEntry)) { // 排除掉excludedFileNames里面的文件/文件夹
             return "Exclude submission: " + submissionEntry.getName();
         }
 
-        if (submissionEntry.isFile() && !hasValidSuffix(submissionEntry)) {
+        if (submissionEntry.isFile() && !hasValidSuffix(submissionEntry)) { // 保留后缀匹配的文件
             return "Ignore submission with invalid suffix: " + submissionEntry.getName();
         }
         return null;
@@ -194,9 +196,9 @@ public class SubmissionSetBuilder {
      */
     private Submission processSubmission(String submissionName, File submissionFile, boolean isNew) throws ExitException {
 
-        if (submissionFile.isDirectory() && options.subdirectoryName() != null) {
+        if (submissionFile.isDirectory() && options.subdirectoryName() != null) { // 如指定了要处理的文件夹(比如src/)
             // Use subdirectory instead
-            submissionFile = new File(submissionFile, options.subdirectoryName());
+            submissionFile = new File(submissionFile, options.subdirectoryName()); // 则处理该三级文件夹
 
             if (!submissionFile.exists()) {
                 throw new SubmissionException(
@@ -208,6 +210,7 @@ public class SubmissionSetBuilder {
             }
         }
 
+        // 将文件路径转化为的绝对路径
         submissionFile = makeCanonical(submissionFile, it -> new SubmissionException("Cannot create submission: " + submissionName, it));
         return new Submission(submissionName, submissionFile, isNew, parseFilesRecursively(submissionFile), language);
     }
@@ -220,13 +223,17 @@ public class SubmissionSetBuilder {
      */
     private void processRootDirectoryEntries(File rootDirectory, boolean multipleRoots, Map<File, Submission> foundSubmissions, boolean isNew)
             throws ExitException {
-        for (String fileName : listSubmissionFiles(rootDirectory)) {
-            File submissionFile = new File(rootDirectory, fileName);
+        for (String fileName : listSubmissionFiles(rootDirectory)) { // 列出根目录下所有二级目录的文件、文件夹
+            File submissionFile = new File(rootDirectory, fileName); // 构造二级目录所代表的文件
 
-            String errorMessage = isExcludedEntry(submissionFile);
+            String errorMessage = isExcludedEntry(submissionFile); // 排除掉部分无效文件/文件夹(二级目录)
             if (errorMessage == null) {
-                String rootDirectoryPrefix = multipleRoots ? (rootDirectory.getName() + File.separator) : "";
+                String rootDirectoryPrefix = multipleRoots ? (rootDirectory.getName() + File.separator) : ""; // 拼接根目录
                 String submissionName = rootDirectoryPrefix + fileName;
+                /*
+                * submissionName: 二级目录/文件名(从根路径拼接的相对路径)
+                * submissionFile: 二级目录/文件
+                * */
                 Submission submission = processSubmission(submissionName, submissionFile, isNew);
                 foundSubmissions.put(submission.getRoot(), submission);
             } else {
@@ -265,11 +272,11 @@ public class SubmissionSetBuilder {
      * @return a list of nested files.
      */
     private Collection<File> parseFilesRecursively(File file) {
-        if (isFileExcluded(file)) {
+        if (isFileExcluded(file)) { // 排除文件/文件夹
             return Collections.emptyList();
         }
 
-        if (file.isFile() && hasValidSuffix(file)) {
+        if (file.isFile() && hasValidSuffix(file)) { // 保留后缀匹配的文件
             return Collections.singletonList(file);
         }
 

+ 4 - 0
core/src/main/java/de/jplag/strategy/ParallelComparisonStrategy.java

@@ -28,8 +28,12 @@ public class ParallelComparisonStrategy extends AbstractComparisonStrategy {
         }
 
         List<SubmissionTuple> tuples = buildComparisonTuples(submissionSet.getSubmissions());
+
+        long startTime= System.currentTimeMillis();
         List<JPlagComparison> comparisons = tuples.stream().parallel().map(tuple -> compareSubmissions(tuple.left(), tuple.right()))
                 .flatMap(Optional::stream).toList();
+        long endTime= System.currentTimeMillis();
+        System.out.println("compare:" + (endTime - startTime) + "ms" );
 
         long durationInMillis = System.currentTimeMillis() - timeBeforeStartInMillis;
         return new JPlagResult(comparisons, submissionSet, durationInMillis, options);

+ 6 - 6
languages/cpp/src/main/java/de/jplag/cpp/Language.java

@@ -13,11 +13,11 @@ import de.jplag.Token;
 public class Language implements de.jplag.Language {
     private static final String IDENTIFIER = "cpp";
 
-    private final Scanner scanner; // cpp code is scanned not parsed
-
-    public Language() {
-        scanner = new Scanner();
-    }
+//    private final Scanner scanner; // cpp code is scanned not parsed
+//
+//    public Language() {
+//        scanner = new Scanner();
+//    }
 
     @Override
     public String[] suffixes() {
@@ -41,6 +41,6 @@ public class Language implements de.jplag.Language {
 
     @Override
     public List<Token> parse(Set<File> files) throws ParsingException {
-        return this.scanner.scan(files);
+        return new Scanner().scan(files);
     }
 }

+ 6 - 6
languages/csharp/src/main/java/de/jplag/csharp/Language.java

@@ -20,11 +20,11 @@ public class Language implements de.jplag.Language {
     private static final String[] FILE_ENDINGS = new String[] {".cs", ".CS"};
     private static final int DEFAULT_MIN_TOKEN_MATCH = 8;
 
-    private final CSharpParserAdapter parser;
-
-    public Language() {
-        parser = new CSharpParserAdapter();
-    }
+//    private final CSharpParserAdapter parser;
+//
+//    public Language() {
+//        parser = new CSharpParserAdapter();
+//    }
 
     @Override
     public String[] suffixes() {
@@ -48,6 +48,6 @@ public class Language implements de.jplag.Language {
 
     @Override
     public List<Token> parse(Set<File> files) throws ParsingException {
-        return parser.parse(files);
+        return new CSharpParserAdapter().parse(files);
     }
 }

+ 14 - 3
languages/emf-metamodel-dynamic/src/main/java/de/jplag/emf/dynamic/Language.java

@@ -1,9 +1,15 @@
 package de.jplag.emf.dynamic;
 
+import de.jplag.ParsingException;
+import de.jplag.Token;
 import org.kohsuke.MetaInfServices;
 
 import de.jplag.emf.dynamic.parser.DynamicEcoreParser;
 
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
 /**
  * Language for EMF metamodels from the Eclipse Modeling Framework (EMF). This language is based on a dynamically
  * created token set instead of a hand-picked one.
@@ -16,9 +22,9 @@ public class Language extends de.jplag.emf.Language {
 
     private static final int DEFAULT_MIN_TOKEN_MATCH = 10;
 
-    public Language() {
-        super(new DynamicEcoreParser());
-    }
+//    public Language() {
+//        super(new DynamicEcoreParser());
+//    }
 
     @Override
     public String getName() {
@@ -34,4 +40,9 @@ public class Language extends de.jplag.emf.Language {
     public int minimumTokenMatch() {
         return DEFAULT_MIN_TOKEN_MATCH;
     }
+
+    @Override
+    public List<Token> parse(Set<File> files) throws ParsingException {
+        return new DynamicEcoreParser().parse(files);
+    }
 }

+ 10 - 10
languages/emf-metamodel/src/main/java/de/jplag/emf/Language.java

@@ -24,15 +24,15 @@ public class Language implements de.jplag.Language {
     private static final String IDENTIFIER = "emf";
     private static final int DEFAULT_MIN_TOKEN_MATCH = 6;
 
-    protected final EcoreParser parser;
-
-    public Language() {
-        this(new EcoreParser());
-    }
-
-    protected Language(EcoreParser parser) {
-        this.parser = parser;
-    }
+//    protected final EcoreParser parser;
+//
+//    public Language() {
+//        this(new EcoreParser());
+//    }
+//
+//    protected Language(EcoreParser parser) {
+//        this.parser = parser;
+//    }
 
     @Override
     public String[] suffixes() {
@@ -56,7 +56,7 @@ public class Language implements de.jplag.Language {
 
     @Override
     public List<Token> parse(Set<File> files) throws ParsingException {
-        return parser.parse(files);
+        return new EcoreParser().parse(files);
     }
 
     @Override

+ 6 - 6
languages/golang/src/main/java/de/jplag/golang/Language.java

@@ -16,11 +16,11 @@ public class Language implements de.jplag.Language {
     private static final String IDENTIFIER = "go";
     private static final int DEFAULT_MIN_TOKEN_MATCH = 8;
     private static final String[] FILE_EXTENSIONS = {".go"};
-    private final GoParserAdapter parserAdapter;
-
-    public Language() {
-        this.parserAdapter = new GoParserAdapter();
-    }
+//    private final GoParserAdapter parserAdapter;
+//
+//    public Language() {
+//        this.parserAdapter = new GoParserAdapter();
+//    }
 
     @Override
     public String[] suffixes() {
@@ -44,6 +44,6 @@ public class Language implements de.jplag.Language {
 
     @Override
     public List<Token> parse(Set<File> files) throws ParsingException {
-        return parserAdapter.parse(files);
+        return new GoParserAdapter().parse(files);
     }
 }

+ 6 - 6
languages/java/src/main/java/de/jplag/java/Language.java

@@ -16,11 +16,11 @@ import de.jplag.Token;
 public class Language implements de.jplag.Language {
     private static final String IDENTIFIER = "java";
 
-    private final Parser parser;
-
-    public Language() {
-        parser = new Parser();
-    }
+//    private final Parser parser;
+//
+//    public Language() {
+//        parser = new Parser();
+//    }
 
     @Override
     public String[] suffixes() {
@@ -44,6 +44,6 @@ public class Language implements de.jplag.Language {
 
     @Override
     public List<Token> parse(Set<File> files) throws ParsingException {
-        return this.parser.parse(files);
+        return new Parser().parse(files);
     }
 }

+ 6 - 6
languages/kotlin/src/main/java/de/jplag/kotlin/Language.java

@@ -19,11 +19,11 @@ public class Language implements de.jplag.Language {
     private static final String IDENTIFIER = "kotlin";
     private static final int DEFAULT_MIN_TOKEN_MATCH = 8;
     private static final String[] FILE_EXTENSIONS = {".kt"};
-    private final KotlinParserAdapter parserAdapter;
-
-    public Language() {
-        this.parserAdapter = new KotlinParserAdapter();
-    }
+//    private final KotlinParserAdapter parserAdapter;
+//
+//    public Language() {
+//        this.parserAdapter = new KotlinParserAdapter();
+//    }
 
     @Override
     public String[] suffixes() {
@@ -47,6 +47,6 @@ public class Language implements de.jplag.Language {
 
     @Override
     public List<Token> parse(Set<File> files) throws ParsingException {
-        return parserAdapter.parse(files);
+        return new KotlinParserAdapter().parse(files);
     }
 }

+ 6 - 6
languages/python-3/src/main/java/de/jplag/python3/Language.java

@@ -14,11 +14,11 @@ public class Language implements de.jplag.Language {
 
     private static final String IDENTIFIER = "python3";
 
-    private final Parser parser;
-
-    public Language() {
-        parser = new Parser();
-    }
+//    private final Parser parser;
+//
+//    public Language() {
+//        parser = new Parser();
+//    }
 
     @Override
     public String[] suffixes() {
@@ -42,6 +42,6 @@ public class Language implements de.jplag.Language {
 
     @Override
     public List<Token> parse(Set<File> files) throws ParsingException {
-        return this.parser.parse(files);
+        return new Parser().parse(files);
     }
 }

+ 6 - 6
languages/rlang/src/main/java/de/jplag/rlang/Language.java

@@ -19,11 +19,11 @@ public class Language implements de.jplag.Language {
     private static final String IDENTIFIER = "rlang";
     private static final int DEFAULT_MIN_TOKEN_MATCH = 8;
     private static final String[] FILE_EXTENSION = {".R", ".r"};
-    private final RParserAdapter parserAdapter;
-
-    public Language() {
-        this.parserAdapter = new RParserAdapter();
-    }
+//    private final RParserAdapter parserAdapter;
+//
+//    public Language() {
+//        this.parserAdapter = new RParserAdapter();
+//    }
 
     @Override
     public String[] suffixes() {
@@ -47,6 +47,6 @@ public class Language implements de.jplag.Language {
 
     @Override
     public List<Token> parse(Set<File> files) throws ParsingException {
-        return parserAdapter.parse(files);
+        return new RParserAdapter().parse(files);
     }
 }

+ 6 - 6
languages/rust/src/main/java/de/jplag/rust/Language.java

@@ -20,11 +20,11 @@ public class Language implements de.jplag.Language {
     private static final String IDENTIFIER = "rust";
     private static final int MINIMUM_TOKEN_MATCH = 8;
 
-    private final RustParserAdapter parserAdapter;
-
-    public Language() {
-        this.parserAdapter = new RustParserAdapter();
-    }
+//    private final RustParserAdapter parserAdapter;
+//
+//    public Language() {
+//        this.parserAdapter = new RustParserAdapter();
+//    }
 
     @Override
     public String[] suffixes() {
@@ -48,6 +48,6 @@ public class Language implements de.jplag.Language {
 
     @Override
     public List<Token> parse(Set<File> files) throws ParsingException {
-        return parserAdapter.parse(files);
+        return new RustParserAdapter().parse(files);
     }
 }

+ 2 - 2
languages/scala/src/main/scala/de/jplag/scala/Language.scala

@@ -9,7 +9,7 @@ import java.util
 import scala.jdk.CollectionConverters.{SeqHasAsJava, SetHasAsScala}
 
 class Language extends de.jplag.Language {
-  private val parser = new Parser
+//  private val parser = new Parser
   private final val fileExtensions = Array(".scala", ".sc")
 
   override def suffixes: Array[String] = fileExtensions
@@ -20,5 +20,5 @@ class Language extends de.jplag.Language {
 
   override def minimumTokenMatch = 8
 
-  override def parse(files: util.Set[File]): java.util.List[Token] = this.parser.parse(files.asScala.toSet).asJava
+  override def parse(files: util.Set[File]): java.util.List[Token] = (new Parser).parse(files.asScala.toSet).asJava
 }

+ 6 - 6
languages/scheme/src/main/java/de/jplag/scheme/Language.java

@@ -13,11 +13,11 @@ import de.jplag.Token;
 public class Language implements de.jplag.Language {
 
     private static final String IDENTIFIER = "scheme";
-    private final de.jplag.scheme.Parser parser;
-
-    public Language() {
-        parser = new Parser();
-    }
+//    private final de.jplag.scheme.Parser parser;
+//
+//    public Language() {
+//        parser = new Parser();
+//    }
 
     @Override
     public String[] suffixes() {
@@ -41,6 +41,6 @@ public class Language implements de.jplag.Language {
 
     @Override
     public List<Token> parse(Set<File> files) throws ParsingException {
-        return this.parser.parse(files);
+        return new Parser().parse(files);
     }
 }

+ 6 - 6
languages/swift/src/main/java/de/jplag/swift/Language.java

@@ -20,11 +20,11 @@ public class Language implements de.jplag.Language {
     private static final String NAME = "Swift Parser";
     private static final int DEFAULT_MIN_TOKEN_MATCH = 8;
     private static final String[] FILE_EXTENSIONS = {".swift"};
-    private final SwiftParserAdapter parserAdapter;
-
-    public Language() {
-        this.parserAdapter = new SwiftParserAdapter();
-    }
+//    private final SwiftParserAdapter parserAdapter;
+//
+//    public Language() {
+//        this.parserAdapter = new SwiftParserAdapter();
+//    }
 
     @Override
     public String[] suffixes() {
@@ -48,6 +48,6 @@ public class Language implements de.jplag.Language {
 
     @Override
     public List<Token> parse(Set<File> files) throws ParsingException {
-        return parserAdapter.parse(files);
+        return new SwiftParserAdapter().parse(files);
     }
 }

+ 6 - 6
languages/text/src/main/java/de/jplag/text/Language.java

@@ -18,11 +18,11 @@ import de.jplag.Token;
 public class Language implements de.jplag.Language {
 
     private static final String IDENTIFIER = "text";
-    private final ParserAdapter parserAdapter;
-
-    public Language() {
-        parserAdapter = new ParserAdapter();
-    }
+//    private final ParserAdapter parserAdapter;
+//
+//    public Language() {
+//        parserAdapter = new ParserAdapter();
+//    }
 
     @Override
     public String[] suffixes() {
@@ -46,6 +46,6 @@ public class Language implements de.jplag.Language {
 
     @Override
     public List<Token> parse(Set<File> files) throws ParsingException {
-        return parserAdapter.parse(files);
+        return new ParserAdapter().parse(files);
     }
 }

+ 37 - 0
lxs-test/pom.xml

@@ -0,0 +1,37 @@
+<?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">
+    <parent>
+        <artifactId>aggregator</artifactId>
+        <groupId>de.jplag</groupId>
+        <version>4.1.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>lxs-test</artifactId>
+
+    <properties>
+        <maven.compiler.source>17</maven.compiler.source>
+        <maven.compiler.target>17</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>de.jplag</groupId>
+            <artifactId>jplag</artifactId>
+            <version>4.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>de.jplag</groupId>
+            <artifactId>java</artifactId>
+            <version>4.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <version>RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+</project>

+ 66 - 0
lxs-test/src/main/java/Main.java

@@ -0,0 +1,66 @@
+import de.jplag.JPlag;
+import de.jplag.JPlagResult;
+import de.jplag.Language;
+import de.jplag.clustering.ClusteringOptions;
+import de.jplag.exceptions.ExitException;
+import de.jplag.options.JPlagOptions;
+import de.jplag.reporting.reportobject.ReportObjectFactory;
+
+import java.io.File;
+import java.util.Set;
+
+public class Main {
+    public static void main(String[] args) {
+        test2();
+        test3();
+    }
+
+    public static void test1() {
+
+        System.out.println(123123123);
+        Language language = new de.jplag.java.Language();
+        Set<File> submissionDirectories = Set.of(
+                new File("D:\\study\\研究生毕业设计\\proj")
+        );
+        File baseCode = new File("D:\\study\\研究生毕业设计\\proj\\SEECII-ERP");
+        JPlagOptions options = new JPlagOptions(language, submissionDirectories, Set.of())
+                .withBaseCodeSubmissionDirectory(baseCode)
+                .withMinimumTokenMatch(7).withClusteringOptions(new ClusteringOptions().withEnabled(false));
+
+        JPlag jplag = new JPlag(options);
+        try {
+            JPlagResult result = jplag.run();
+
+            // Optional
+            ReportObjectFactory reportObjectFactory = new ReportObjectFactory();
+            reportObjectFactory.createAndSaveReport(result, "D:\\study\\研究生毕业设计\\code\\lalala0");
+        } catch (ExitException e) {
+            // error handling here
+        }
+        System.out.println(456456456);
+    }
+
+    public static void test2() {
+        Language language = new de.jplag.java.Language();
+        Set<File> submissionDirectories = Set.of(
+                new File("D:\\study\\研究生毕业设计\\projtest")
+        );
+//        File baseCode = new File("D:\\study\\研究生毕业设计\\proj\\SEECII-ERP");
+        JPlagOptions options = new JPlagOptions(language, submissionDirectories, Set.of())
+//                .withBaseCodeSubmissionDirectory(baseCode)
+                .withMinimumTokenMatch(7).withClusteringOptions(new ClusteringOptions().withEnabled(false));
+
+        JPlag jplag = new JPlag(options);
+        try {
+            JPlagResult result = jplag.run();
+
+            // Optional
+            ReportObjectFactory reportObjectFactory = new ReportObjectFactory();
+            reportObjectFactory.createAndSaveReport(result, "D:\\study\\研究生毕业设计\\code\\lalala");
+        } catch (Exception e) {
+            // error handling here
+        }
+    }
+
+    public static void test3() {}
+}

+ 12 - 0
pom.xml

@@ -4,6 +4,9 @@
     <groupId>de.jplag</groupId>
     <artifactId>aggregator</artifactId>
     <version>${revision}</version>
+    <modules>
+        <module>language-cli</module>
+    </modules>
     <packaging>pom</packaging>
 
     <name>JPlag Plagiarism Detector</name>
@@ -274,6 +277,15 @@
                     <artifactId>maven-deploy-plugin</artifactId>
                     <version>3.0.0</version>
                 </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>3.8.1</version>
+                    <configuration>
+                        <source>17</source>
+                        <target>17</target>
+                    </configuration>
+                </plugin>
             </plugins>
         </pluginManagement>
         <plugins>

+ 12 - 10
report-viewer/package-lock.json

@@ -5,7 +5,6 @@
   "requires": true,
   "packages": {
     "": {
-      "name": "report-viewer",
       "version": "0.1.0",
       "dependencies": {
         "@highlightjs/vue-plugin": "^2.1.0",
@@ -3514,6 +3513,8 @@
       "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz",
       "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==",
       "dev": true,
+      "optional": true,
+      "peer": true,
       "dependencies": {
         "fast-deep-equal": "^3.1.1",
         "json-schema-traverse": "^1.0.0",
@@ -3529,7 +3530,9 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
       "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
-      "dev": true
+      "dev": true,
+      "optional": true,
+      "peer": true
     },
     "node_modules/ajv-keywords": {
       "version": "3.5.2",
@@ -15620,7 +15623,6 @@
       "integrity": "sha512-yl+5qhpjd8e1G4cMXfORkkBlvtPCIgmRf3IYCWYDKIQ7m+PPa5iTm4feiNmCMD6yGqQWMhhK/7M3oWGL9boKwg==",
       "dev": true,
       "requires": {
-        "@babel/core": "^7.12.16",
         "@babel/helper-compilation-targets": "^7.12.16",
         "@babel/helper-module-imports": "^7.12.13",
         "@babel/plugin-proposal-class-properties": "^7.12.13",
@@ -15633,7 +15635,6 @@
         "@vue/babel-plugin-jsx": "^1.0.3",
         "@vue/babel-preset-jsx": "^1.1.2",
         "babel-plugin-dynamic-import-node": "^2.3.3",
-        "core-js": "^3.8.3",
         "core-js-compat": "^3.8.3",
         "semver": "^7.3.4"
       },
@@ -16392,15 +16393,14 @@
       "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
       "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
       "dev": true,
-      "requires": {
-        "ajv": "^8.0.0"
-      },
+      "requires": {},
       "dependencies": {
         "ajv": {
-          "version": "8.10.0",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz",
+          "version": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz",
           "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==",
           "dev": true,
+          "optional": true,
+          "peer": true,
           "requires": {
             "fast-deep-equal": "^3.1.1",
             "json-schema-traverse": "^1.0.0",
@@ -16412,7 +16412,9 @@
           "version": "1.0.0",
           "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
           "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
-          "dev": true
+          "dev": true,
+          "optional": true,
+          "peer": true
         }
       }
     },

+ 3 - 3
report-viewer/src/components/CodePanel.vue

@@ -31,9 +31,9 @@
           v-for="(line, index) in lines"
           :id="String(panelId).concat(title).concat(index)"
           :key="index"
-          :color="coloringArray[index]"
-          :is-first="isFirst[index]"
-          :is-last="isLast[index]"
+          :color="coloringArray[index + 1]"
+          :is-first="isFirst[index + 1]"
+          :is-last="isLast[index + 1]"
           :line-number="index + 1"
           :text="line"
           :visible="collapse"

+ 2 - 2
report-viewer/src/components/MatchTable.vue

@@ -24,13 +24,13 @@
         <td>
           <div class="td-content">
             <p>{{ match.firstFile }}</p>
-            <p>({{ match.startInFirst + 1 }} - {{ match.endInFirst + 1 }})</p>
+            <p>({{ match.startInFirst }} - {{ match.endInFirst }})</p>
           </div>
         </td>
         <td>
           <div class="td-content">
             <p>{{ match.secondFile }}</p>
-            <p>({{ match.startInSecond + 1 }} - {{ match.endInSecond + 1 }})</p>
+            <p>({{ match.startInSecond }} - {{ match.endInSecond }})</p>
           </div>
         </td>
         <td>{{ match.tokens }}</td>