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.lineCoverage;
7   
8   import static java.lang.Character.isWhitespace;
9   
10  import edu.umd.cs.findbugs.annotations.NonNull;
11  import edu.umd.cs.findbugs.annotations.Nullable;
12  
13  import java.util.List;
14  
15  import mockit.coverage.CallPoint;
16  import mockit.coverage.lines.BranchCoverageData;
17  import mockit.coverage.lines.LineCoverageData;
18  import mockit.coverage.lines.LineSegmentData;
19  import mockit.coverage.reporting.ListOfCallPoints;
20  import mockit.coverage.reporting.parsing.LineElement;
21  import mockit.coverage.reporting.parsing.LineParser;
22  
23  import org.checkerframework.checker.index.qual.NonNegative;
24  
25  final class LineSegmentsFormatter {
26      @Nullable
27      private final ListOfCallPoints listOfCallPoints;
28      @NonNull
29      private final StringBuilder formattedLine;
30  
31      // Helper fields:
32      private int lineNumber;
33      @NonNegative
34      private int segmentIndex;
35      @Nullable
36      private LineElement element;
37  
38      LineSegmentsFormatter(boolean withCallPoints, @NonNull StringBuilder formattedLine) {
39          listOfCallPoints = withCallPoints ? new ListOfCallPoints() : null;
40          this.formattedLine = formattedLine;
41      }
42  
43      void formatSegments(@NonNull LineParser lineParser, @NonNull LineCoverageData lineData) {
44          lineNumber = lineParser.getNumber();
45  
46          List<BranchCoverageData> branchData = lineData.getBranches();
47          int numSegments = lineData.getNumberOfSegments();
48  
49          element = lineParser.getInitialElement().appendUntilNextCodeElement(formattedLine);
50  
51          segmentIndex = 0;
52          appendUntilNextBranchingPoint(lineData);
53  
54          while (element != null && segmentIndex < numSegments) {
55              LineSegmentData segmentData = segmentIndex == 0 ? lineData : branchData.get(segmentIndex - 1);
56              element = element.appendUntilNextCodeElement(formattedLine);
57              appendUntilNextBranchingPoint(segmentData);
58          }
59  
60          if (element != null) {
61              element.appendAllBefore(formattedLine, null);
62          }
63  
64          formattedLine.append("</pre>");
65  
66          if (listOfCallPoints != null) {
67              formattedLine.append(listOfCallPoints.getContents());
68          }
69      }
70  
71      private void appendUntilNextBranchingPoint(@NonNull LineSegmentData segmentData) {
72          if (element != null) {
73              LineElement firstElement = element;
74              element = element.findNextBranchingPoint();
75  
76              appendToFormattedLine(segmentData, firstElement);
77  
78              if (element != null && element.isBranchingElement()) {
79                  formattedLine.append(element.getText());
80                  element = element.getNext();
81              }
82          }
83      }
84  
85      private void appendToFormattedLine(@NonNull LineSegmentData segmentData, @NonNull LineElement firstElement) {
86          if (firstElement != element) {
87              appendStartTag(segmentData);
88              firstElement.appendAllBefore(formattedLine, element);
89              appendEndTag(segmentData);
90  
91              segmentIndex++;
92          }
93      }
94  
95      private void appendStartTag(@NonNull LineSegmentData segmentData) {
96          formattedLine.append("<span id='l").append(lineNumber).append('s').append(segmentIndex);
97          formattedLine.append("' title='Executions: ").append(segmentData.getExecutionCount()).append("' ");
98  
99          if (segmentData.isCovered()) {
100             if (segmentData.containsCallPoints()) {
101                 formattedLine.append("class='cvd cp' onclick='sh(this,").append(segmentIndex).append(")'>");
102             } else {
103                 formattedLine.append("class='cvd'>");
104             }
105         } else {
106             formattedLine.append("class='uncvd'>");
107         }
108     }
109 
110     private void appendEndTag(@NonNull LineSegmentData segmentData) {
111         int i = formattedLine.length() - 1;
112 
113         while (isWhitespace(formattedLine.charAt(i))) {
114             i--;
115         }
116 
117         formattedLine.insert(i + 1, "</span>");
118 
119         if (listOfCallPoints != null) {
120             List<CallPoint> callPoints = segmentData.getCallPoints();
121             listOfCallPoints.insertListOfCallPoints(callPoints);
122         }
123     }
124 }