View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
5    */
6   package mockit.coverage.reporting.sourceFiles;
7   
8   import edu.umd.cs.findbugs.annotations.NonNull;
9   import edu.umd.cs.findbugs.annotations.Nullable;
10  
11  import java.io.IOException;
12  
13  import mockit.coverage.data.FileCoverageData;
14  import mockit.coverage.dataItems.PerFileDataCoverage;
15  import mockit.coverage.reporting.OutputFile;
16  import mockit.coverage.reporting.dataCoverage.DataCoverageOutput;
17  import mockit.coverage.reporting.lineCoverage.LineCoverageOutput;
18  import mockit.coverage.reporting.parsing.FileParser;
19  import mockit.coverage.reporting.parsing.LineElement;
20  import mockit.coverage.reporting.parsing.LineParser;
21  
22  import org.checkerframework.checker.index.qual.NonNegative;
23  
24  /**
25   * Generates an HTML page containing line-by-line coverage information for a single source file.
26   */
27  public final class FileCoverageReport {
28      @NonNull
29      private final InputFile inputFile;
30      @NonNull
31      private final OutputFile output;
32      @NonNull
33      private final FileParser fileParser;
34      @NonNull
35      private final NeutralOutput neutralOutput;
36      @NonNull
37      private final LineCoverageOutput lineCoverage;
38      @Nullable
39      private final DataCoverageOutput dataCoverage;
40  
41      public FileCoverageReport(@NonNull String outputDir, @NonNull InputFile inputFile,
42              @NonNull FileCoverageData fileData, boolean withCallPoints) throws IOException {
43          this.inputFile = inputFile;
44          output = new OutputFile(outputDir, inputFile.filePath);
45          fileParser = new FileParser();
46          neutralOutput = new NeutralOutput(output);
47          lineCoverage = new LineCoverageOutput(output, fileData.getLineCoverageData(), withCallPoints);
48          dataCoverage = createDataCoverageOutput(fileData);
49      }
50  
51      @Nullable
52      private static DataCoverageOutput createDataCoverageOutput(@NonNull FileCoverageData fileData) {
53          PerFileDataCoverage dataCoverageInfo = fileData.dataCoverageInfo;
54          return dataCoverageInfo.hasFields() ? new DataCoverageOutput(dataCoverageInfo) : null;
55      }
56  
57      public void generate() throws IOException {
58          try {
59              writeHeader();
60              writeFormattedSourceLines();
61              writeFooter();
62          } finally {
63              inputFile.close();
64              output.close();
65          }
66      }
67  
68      private void writeHeader() {
69          output.writeCommonHeader(inputFile.getSourceFileName());
70          output.println("  <table>");
71          output.println("    <caption>" + inputFile.getSourceFilePath() + "</caption>");
72      }
73  
74      private void writeFormattedSourceLines() throws IOException {
75          LineParser lineParser = fileParser.lineParser;
76          String line;
77  
78          while ((line = inputFile.nextLine()) != null) {
79              boolean lineWithCodeElements = fileParser.parseCurrentLine(line);
80  
81              if (lineWithCodeElements && dataCoverage != null) {
82                  dataCoverage.writeCoverageInfoIfLineStartsANewFieldDeclaration(fileParser);
83              }
84  
85              if (!neutralOutput.writeLineWithoutCoverageInfo(lineParser)) {
86                  writeOpeningOfNewLine(lineParser.getNumber());
87  
88                  if (!lineCoverage.writeLineWithCoverageInfo(lineParser)) {
89                      writeLineWithoutCoverageInfo(lineParser.getInitialElement());
90                  }
91  
92                  output.println("    </tr>");
93              }
94          }
95      }
96  
97      private void writeOpeningOfNewLine(@NonNegative int lineNumber) {
98          output.println("    <tr>");
99          output.write("      <td>");
100         output.print(lineNumber);
101         output.write("</td>");
102     }
103 
104     private void writeLineWithoutCoverageInfo(@NonNull LineElement initialElement) {
105         output.println("<td></td>");
106         output.write("      <td><pre class='");
107         output.write(initialElement.isComment() ? "cm'>" : "pp'>");
108         output.write(initialElement.toString());
109         output.println("</pre></td>");
110     }
111 
112     private void writeFooter() {
113         output.println("  </table>");
114         output.writeCommonFooter();
115     }
116 }