Selaa lähdekoodia

add support for multiple parsing exceptions in one submission by wrapping them within another parsing exception

Jan Wittler 4 vuotta sitten
vanhempi
commit
b72c4e505e

+ 26 - 0
language-api/src/main/java/de/jplag/ParsingException.java

@@ -2,6 +2,8 @@ package de.jplag;
 
 
 import java.io.File;
 import java.io.File;
 import java.io.Serial;
 import java.io.Serial;
+import java.util.Collection;
+import java.util.stream.Collectors;
 
 
 /**
 /**
  * An exception to throw if any error occurred while parsing files in a language frontend.
  * An exception to throw if any error occurred while parsing files in a language frontend.
@@ -50,6 +52,30 @@ public class ParsingException extends Exception {
         super(constructMessage(file, reason), cause);
         super(constructMessage(file, reason), cause);
     }
     }
 
 
+    /**
+     * Creates a new parsing exception which wraps the provided exceptions. If no exception to wrap is provided, null is
+     * returned. If only one exception is provided, it is returned.
+     * @param exceptions the collection of exceptions to wrap.
+     * @return a new parsing exception wrapping the provided exceptions, <code>null</code> if no exceptions are provided, or
+     * the provided exception if only one was provided.
+     */
+    public static ParsingException wrappingExceptions(Collection<ParsingException> exceptions) {
+        switch (exceptions.size()) {
+            case 0:
+                return null;
+            case 1:
+                return exceptions.iterator().next();
+            default: {
+                String message = exceptions.stream().map(ParsingException::getMessage).collect(Collectors.joining("\n"));
+                return new ParsingException(message);
+            }
+        }
+    }
+
+    private ParsingException(String message) {
+        super(message);
+    }
+
     private static String constructMessage(File file, String reason) {
     private static String constructMessage(File file, String reason) {
         StringBuilder messageBuilder = new StringBuilder();
         StringBuilder messageBuilder = new StringBuilder();
         messageBuilder.append("failed to parse '%s'".formatted(file));
         messageBuilder.append("failed to parse '%s'".formatted(file));

+ 16 - 16
languages/java/src/main/java/de/jplag/java/JavacAdapter.java

@@ -3,12 +3,12 @@ package de.jplag.java;
 import java.io.File;
 import java.io.File;
 import java.io.IOException;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
 import java.util.Set;
 import java.util.Set;
 
 
-import javax.tools.Diagnostic;
 import javax.tools.DiagnosticCollector;
 import javax.tools.DiagnosticCollector;
 import javax.tools.JavaCompiler;
 import javax.tools.JavaCompiler;
 import javax.tools.JavaCompiler.CompilationTask;
 import javax.tools.JavaCompiler.CompilationTask;
@@ -34,6 +34,7 @@ public class JavacAdapter {
     public void parseFiles(Set<File> files, final Parser parser) throws ParsingException {
     public void parseFiles(Set<File> files, final Parser parser) throws ParsingException {
         var listener = new DiagnosticCollector<>();
         var listener = new DiagnosticCollector<>();
 
 
+        List<ParsingException> parsingExceptions = new ArrayList<>();
         try (final StandardJavaFileManager fileManager = javac.getStandardFileManager(listener, null, StandardCharsets.UTF_8)) {
         try (final StandardJavaFileManager fileManager = javac.getStandardFileManager(listener, null, StandardCharsets.UTF_8)) {
             var javaFiles = fileManager.getJavaFileObjectsFromFiles(files);
             var javaFiles = fileManager.getJavaFileObjectsFromFiles(files);
 
 
@@ -47,15 +48,16 @@ public class JavacAdapter {
                 final LineMap map = ast.getLineMap();
                 final LineMap map = ast.getLineMap();
                 var scanner = new TokenGeneratingTreeScanner(file, parser, map, positions, ast);
                 var scanner = new TokenGeneratingTreeScanner(file, parser, map, positions, ast);
                 ast.accept(scanner, null);
                 ast.accept(scanner, null);
-                if (scanner.getParsingException() != null) {
-                    throw scanner.getParsingException();
-                }
+                parsingExceptions.addAll(scanner.getParsingExceptions());
                 parser.add(Token.fileEnd(file));
                 parser.add(Token.fileEnd(file));
             }
             }
         } catch (IOException exception) {
         } catch (IOException exception) {
             throw new ParsingException(null, exception.getMessage(), exception);
             throw new ParsingException(null, exception.getMessage(), exception);
         }
         }
-        processErrors(parser.logger, listener);
+        parsingExceptions.addAll(processErrors(parser.logger, listener));
+        if (!parsingExceptions.isEmpty()) {
+            throw ParsingException.wrappingExceptions(parsingExceptions);
+        }
     }
     }
 
 
     private Iterable<? extends CompilationUnitTree> executeCompilationTask(final CompilationTask task, Logger logger) {
     private Iterable<? extends CompilationUnitTree> executeCompilationTask(final CompilationTask task, Logger logger) {
@@ -68,18 +70,16 @@ public class JavacAdapter {
         return abstractSyntaxTrees;
         return abstractSyntaxTrees;
     }
     }
 
 
-    private void processErrors(Logger logger, DiagnosticCollector<Object> listener) throws ParsingException {
-        for (Diagnostic<?> diagnosticItem : listener.getDiagnostics()) {
-            if (diagnosticItem.getKind() == javax.tools.Diagnostic.Kind.ERROR) {
-                File file = null;
-                if (diagnosticItem.getSource() instanceof JavaFileObject) {
-                    JavaFileObject fileObject = (JavaFileObject) diagnosticItem.getSource();
-                    file = new File(fileObject.toUri());
-                }
-                logger.error("{}", diagnosticItem);
-                throw new ParsingException(file, diagnosticItem.getMessage(Locale.getDefault()));
+    private List<ParsingException> processErrors(Logger logger, DiagnosticCollector<Object> listener) {
+        return listener.getDiagnostics().stream().filter(it -> it.getKind() == javax.tools.Diagnostic.Kind.ERROR).map(diagnosticItem -> {
+            File file = null;
+            if (diagnosticItem.getSource() instanceof JavaFileObject) {
+                JavaFileObject fileObject = (JavaFileObject) diagnosticItem.getSource();
+                file = new File(fileObject.toUri());
             }
             }
-        }
+            logger.error("{}", diagnosticItem);
+            return new ParsingException(file, diagnosticItem.getMessage(Locale.getDefault()));
+        }).toList();
     }
     }
 
 
 }
 }

+ 6 - 4
languages/java/src/main/java/de/jplag/java/TokenGeneratingTreeScanner.java

@@ -1,6 +1,8 @@
 package de.jplag.java;
 package de.jplag.java;
 
 
 import java.io.File;
 import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
 
 
 import com.sun.source.tree.AnnotationTree;
 import com.sun.source.tree.AnnotationTree;
 import com.sun.source.tree.AssertTree;
 import com.sun.source.tree.AssertTree;
@@ -53,7 +55,7 @@ final class TokenGeneratingTreeScanner extends TreeScanner<Object, Object> {
     private final SourcePositions positions;
     private final SourcePositions positions;
     private final CompilationUnitTree ast;
     private final CompilationUnitTree ast;
 
 
-    private ParsingException parsingException;
+    private List<ParsingException> parsingExceptions = new ArrayList<>();
 
 
     public TokenGeneratingTreeScanner(File file, Parser parser, LineMap map, SourcePositions positions, CompilationUnitTree ast) {
     public TokenGeneratingTreeScanner(File file, Parser parser, LineMap map, SourcePositions positions, CompilationUnitTree ast) {
         this.file = file;
         this.file = file;
@@ -63,8 +65,8 @@ final class TokenGeneratingTreeScanner extends TreeScanner<Object, Object> {
         this.ast = ast;
         this.ast = ast;
     }
     }
 
 
-    public ParsingException getParsingException() {
-        return parsingException;
+    public List<ParsingException> getParsingExceptions() {
+        return parsingExceptions;
     }
     }
 
 
     /**
     /**
@@ -401,7 +403,7 @@ final class TokenGeneratingTreeScanner extends TreeScanner<Object, Object> {
 
 
     @Override
     @Override
     public Object visitErroneous(ErroneousTree node, Object p) {
     public Object visitErroneous(ErroneousTree node, Object p) {
-        parsingException = new ParsingException(file, "error while visiting %s".formatted(node));
+        parsingExceptions.add(new ParsingException(file, "error while visiting %s".formatted(node)));
         return super.visitErroneous(node, p);
         return super.visitErroneous(node, p);
     }
     }