package org.checkerframework.framework.test.diagnostics; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; import java.io.Reader; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import javax.tools.JavaFileObject; /** * Reads a file that has serialized javac diagnostics and returns either a list of * TestDiagnosticLines or TestDiagnostics. This file might either: * * <ul> * <li>a Java file, which is read by creating a {@code JavaDiagnosticReader} with the {@link * #JAVA_COMMENT_CODEC} * <li>a "Diagnostic" file, which is read by creating a {@code JavaDiagnosticReader} with a {@link * #DIAGNOSTIC_FILE_CODEC} * </ul> */ public class JavaDiagnosticReader implements Iterator<TestDiagnosticLine> { // This class begins with the most common static helper methods that are used to read diagnostics /** * Reads the entire input file using the given codec and returns the resulting line. * * @param toRead the file (Java or Diagnostics format) to read * @param codec a codec corresponding to the file type being read * @param omitEmptyDiagnostics whether or not lines that do not contain any diagnostics should * be reported as empty TestDiagnosticLines * @return the List of TestDiagnosticLines from the input file */ public static List<TestDiagnosticLine> readDiagnostics( File toRead, DiagnosticCodec codec, boolean omitEmptyDiagnostics) { List<TestDiagnosticLine> lines = new ArrayList<>(); JavaDiagnosticReader reader = new JavaDiagnosticReader(toRead, codec); while (reader.hasNext()) { TestDiagnosticLine line = reader.next(); if (!omitEmptyDiagnostics || line.hasDiagnostics()) { lines.add(line); } } reader.close(); return lines; } /** * Reads diagnostic lines from the comments of the input Java file. * * @param toRead a Java File * @param omitEmptyDiagnostics whether or not lines that do not contain any diagnostics should * be reported as empty TestDiagnosticLines * @return the List of TestDiagnosticLines from the input file */ public static List<TestDiagnosticLine> readDiagnostics( File toRead, boolean omitEmptyDiagnostics) { return readDiagnostics(toRead, JAVA_COMMENT_CODEC, omitEmptyDiagnostics); } /** * Reads diagnostic lines from the comments of a set of Java file. * * @param toRead java files to read using the JAVA_COMMENT_CODEC * @param omitEmptyDiagnostics whether or not lines that do not contain any diagnostics should * be reported as empty TestDiagnosticLines * @return the List of TestDiagnosticLines from the input Jav afiles */ public static List<TestDiagnosticLine> readDiagnosticLines( Iterable<File> toRead, boolean omitEmptyDiagnostics) { List<TestDiagnosticLine> lines = new ArrayList<>(); for (File file : toRead) { lines.addAll(readDiagnostics(file, omitEmptyDiagnostics)); } return lines; } /** * Reads diagnostics from the comments of a set of Java file. * * @param toRead java files to read using the JAVA_COMMENT_CODEC * @param omitEmptyDiagnostics whether or not lines that do not contain any diagnostics should * be reported as empty TestDiagnosticLines * @return the List of TestDiagnostics (not lines) from the files ToRead */ public static List<TestDiagnostic> readDiagnostics( Iterable<File> toRead, boolean omitEmptyDiagnostics) { List<TestDiagnosticLine> lines = readDiagnosticLines(toRead, omitEmptyDiagnostics); List<TestDiagnostic> diagnostics = new ArrayList<TestDiagnostic>((int) (lines.size() + lines.size() * 0.1)); for (TestDiagnosticLine line : lines) { diagnostics.addAll(line.getDiagnostics()); } return diagnostics; } /** * Reads diagnostic lines from the comments of a set of Java file. * * @param toRead the Java files to read using the JAVA_COMMENT_CODEC * @param omitEmptyDiagnostics whether or not lines that do not contain any diagnostics should * be reported as empty TestDiagnosticLines * @return the List of TestDiagnostics (not lines) from the files ToRead */ public static List<TestDiagnosticLine> readDiagnosticsJfo( JavaFileObject toRead, boolean omitEmptyDiagnostics) { List<TestDiagnosticLine> lines = new ArrayList<>(); JavaDiagnosticReader reader = new JavaDiagnosticReader(toRead, JAVA_COMMENT_CODEC); while (reader.hasNext()) { TestDiagnosticLine line = reader.next(); if (!omitEmptyDiagnostics || line.hasDiagnostics()) { lines.add(line); } } reader.close(); return lines; } /** * Reads diagnostic lines from the comments of a set of Java file. * * @param toRead the Java files to read using the JAVA_COMMENT_CODEC * @param omitEmptyDiagnostics whether or not lines that do not contain any diagnostics should * be reported as empty TestDiagnosticLines * @return the List of TestDiagnosticLines from the input Jav afiles */ public static List<TestDiagnosticLine> readExpectedDiagnosticLinesJfo( Iterable<? extends JavaFileObject> toRead, boolean omitEmptyDiagnostics) { List<TestDiagnosticLine> lines = new ArrayList<>(); for (JavaFileObject file : toRead) { lines.addAll(readDiagnosticsJfo(file, omitEmptyDiagnostics)); } return lines; } /** * Reads diagnostics from the comments of a set of Java file. * * @param toRead the Java files to read using the JAVA_COMMENT_CODEC * @param omitEmptyDiagnostics whether or not lines that do not contain any diagnostics should * be reported as empty TestDiagnosticLines * @return the List of TestDiagnostics (not lines) */ public static List<TestDiagnostic> readExpectedDiagnosticsJfo( Iterable<? extends JavaFileObject> toRead, boolean omitEmptyDiagnostics) { List<TestDiagnosticLine> lines = readExpectedDiagnosticLinesJfo(toRead, omitEmptyDiagnostics); List<TestDiagnostic> diagnostics = new ArrayList<TestDiagnostic>((int) (lines.size() + lines.size() * 0.1)); for (TestDiagnosticLine line : lines) { diagnostics.addAll(line.getDiagnostics()); } return diagnostics; } /** * Reads diagnostic lines line-by-line from the input Diagnostic file. * * @param toRead a Diagnostic File * @param omitEmptyDiagnostics whether or not lines that do not contain any diagnostics should * be reported as empty TestDiagnosticLines * @return the List of TestDiagnosticLines from the input file */ public static List<TestDiagnosticLine> readDiagnosticFile( File toRead, boolean omitEmptyDiagnostics) { return readDiagnostics(toRead, DIAGNOSTIC_FILE_CODEC, omitEmptyDiagnostics); } /** * Reads diagnostic lines line-by-line from the input Diagnostic files. * * @param toRead a set of Diagnostic Files * @param omitEmptyDiagnostics whether or not lines that do not contain any diagnostics should * be reported as empty TestDiagnosticLines * @return the List of TestDiagnosticLines from the input files */ public static List<TestDiagnosticLine> readDiagnosticFileLines( Iterable<? extends File> toRead, boolean omitEmptyDiagnostics) { List<TestDiagnosticLine> lines = new ArrayList<>(); for (File file : toRead) { lines.addAll(readDiagnosticFile(file, omitEmptyDiagnostics)); } return lines; } /** * Reads diagnostics line-by-line from the input Diagnostic files. * * @param toRead a set of Diagnostic Files * @param omitEmptyDiagnostics whether or not lines that do not contain any diagnostics should * be reported as empty TestDiagnosticLines * @return the List of TestDiagnosticLines from the input files */ public static List<TestDiagnostic> readDiagnosticFiles( Iterable<? extends File> toRead, boolean omitEmptyDiagnostics) { List<TestDiagnosticLine> lines = readDiagnosticFileLines(toRead, omitEmptyDiagnostics); List<TestDiagnostic> diagnostics = new ArrayList<TestDiagnostic>((int) (lines.size() + lines.size() * 0.1)); for (TestDiagnosticLine line : lines) { diagnostics.addAll(line.getDiagnostics()); } return diagnostics; } /** Instances of DiagnosticCodec represent the various formats diagnostic strings can take */ public interface DiagnosticCodec { public TestDiagnosticLine convertLine(String filename, long lineNumber, String line); } /** Interprets a string that was written as a comment in a Java file */ public static DiagnosticCodec JAVA_COMMENT_CODEC = new DiagnosticCodec() { @Override public TestDiagnosticLine convertLine( String filename, long lineNumber, String line) { return TestDiagnosticUtils.fromJavaSourceLine(filename, line, lineNumber); } }; /** Interprets a string that was written as a line in a Diagnostic File */ public static DiagnosticCodec DIAGNOSTIC_FILE_CODEC = new DiagnosticCodec() { @Override public TestDiagnosticLine convertLine( String filename, long lineNumber, String line) { return TestDiagnosticUtils.fromDiagnosticFileLine(line); } }; public final File toRead; public final JavaFileObject toReadFileObject; public final DiagnosticCodec codec; private final String filename; private boolean initialized = false; private boolean closed = false; private LineNumberReader reader = null; public String nextLine = null; public int nextLineNumber = -1; public JavaDiagnosticReader(File toRead, DiagnosticCodec codec) { this.toRead = toRead; this.filename = shortFileName(toRead.getAbsolutePath()); this.toReadFileObject = null; this.codec = codec; } public JavaDiagnosticReader(JavaFileObject toRead, DiagnosticCodec codec) { this.toRead = null; this.toReadFileObject = toRead; this.codec = codec; this.filename = shortFileName(toRead.getName()); } private String shortFileName(String name) { int index = name.lastIndexOf(File.separator); return name.substring(index + 1, name.length()); } private void init() throws IOException { if (!initialized && !closed) { initialized = true; Reader fileReader = (toRead != null) ? new FileReader(toRead) : toReadFileObject.openReader(true); reader = new LineNumberReader(fileReader); advance(); } } @Override public boolean hasNext() { if (closed) { return false; } try { init(); } catch (IOException e) { throw new RuntimeException(e); } return nextLine != null; } @Override public void remove() { throw new UnsupportedOperationException( "Cannot remove elements using JavaDiagnosticFileReader."); } @Override public TestDiagnosticLine next() { try { init(); if (nextLine == null) { throw new NoSuchElementException(); } else if (closed) { throw new RuntimeException("Reader has been closed: " + toRead.getAbsolutePath()); } String current = nextLine; int currentLineNumber = nextLineNumber; advance(); if (nextLine == null) { close(); } return codec.convertLine(filename, currentLineNumber, current); } catch (IOException e) { throw new RuntimeException(e); } } protected void advance() throws IOException { nextLine = reader.readLine(); nextLineNumber = reader.getLineNumber(); } public void close() { try { if (initialized) { reader.close(); } closed = true; } catch (IOException e) { throw new RuntimeException(e); } } }