1
2
3
4
5 package mockit.coverage.reporting.parsing;
6
7 import edu.umd.cs.findbugs.annotations.NonNull;
8 import edu.umd.cs.findbugs.annotations.Nullable;
9
10 import mockit.coverage.reporting.parsing.LineElement.ElementType;
11
12 import org.checkerframework.checker.index.qual.NonNegative;
13
14
15
16
17
18
19 public final class LineParser {
20 private static final String SEPARATORS = ".,;()";
21
22 @NonNegative
23 private int lineNum;
24 private String line;
25 @Nullable
26 private LineElement initialElement;
27 private boolean inComments;
28
29
30 @Nullable
31 private LineElement currentElement;
32 @NonNegative
33 private int lineLength;
34 private int startPos;
35 private boolean inCodeElement;
36 @NonNegative
37 private int pos;
38 private int currChar;
39
40 @NonNegative
41 public int getNumber() {
42 return lineNum;
43 }
44
45 public boolean isInComments() {
46 return inComments;
47 }
48
49 public boolean isBlankLine() {
50 int n = line.length();
51
52 for (int i = 0; i < n; i++) {
53 char c = line.charAt(i);
54
55 if (!Character.isWhitespace(c)) {
56 return false;
57 }
58 }
59
60 return true;
61 }
62
63 @NonNull
64 public LineElement getInitialElement() {
65 assert initialElement != null;
66 return initialElement;
67 }
68
69 boolean parse(@NonNull String lineToParse) {
70 lineNum++;
71 initialElement = null;
72 currentElement = null;
73 line = lineToParse;
74 lineLength = lineToParse.length();
75 startPos = inComments ? 0 : -1;
76 inCodeElement = false;
77
78 for (pos = 0; pos < lineLength; pos++) {
79 currChar = lineToParse.codePointAt(pos);
80
81 if (parseComment()) {
82 break;
83 }
84
85 parseSeparatorsAndCode();
86 }
87
88 if (startPos >= 0) {
89 addFinalElement();
90 } else if (initialElement == null) {
91 initialElement = new LineElement(ElementType.SEPARATOR, "");
92 return false;
93 }
94
95 return !inComments && !isBlankLine();
96 }
97
98 private void parseSeparatorsAndCode() {
99 boolean separator = isSeparator();
100
101 if (separator && !inCodeElement) {
102 startNewElementIfNotYetStarted();
103 } else if (!separator && !inCodeElement) {
104 if (startPos >= 0) {
105 addElement();
106 }
107
108 inCodeElement = true;
109 startPos = pos;
110 } else if (separator) {
111 addElement();
112 inCodeElement = false;
113 startPos = pos;
114 }
115 }
116
117 private boolean isSeparator() {
118 return Character.isWhitespace(currChar) || SEPARATORS.indexOf(currChar) >= 0;
119 }
120
121 private void startNewElementIfNotYetStarted() {
122 if (startPos < 0) {
123 startPos = pos;
124 }
125 }
126
127 private boolean parseComment() {
128 if (inComments && parseUntilEndOfLineOrEndOfComment()) {
129 return true;
130 }
131
132 while (currChar == '/' && pos < lineLength - 1) {
133 int c2 = line.codePointAt(pos + 1);
134
135 if (c2 == '/') {
136 endCodeElementIfPending();
137 startNewElementIfNotYetStarted();
138 inComments = true;
139 addFinalElement();
140 inComments = false;
141 startPos = -1;
142 return true;
143 }
144 if (c2 != '*') {
145 break;
146 }
147 endCodeElementIfPending();
148 startNewElementIfNotYetStarted();
149 inComments = true;
150 pos += 2;
151
152 if (parseUntilEndOfLineOrEndOfComment()) {
153 return true;
154 }
155 }
156
157 return false;
158 }
159
160 private void endCodeElementIfPending() {
161 if (inCodeElement) {
162 addElement();
163 startPos = pos;
164 inCodeElement = false;
165 }
166 }
167
168 private boolean parseUntilEndOfLineOrEndOfComment() {
169 while (pos < lineLength) {
170 currChar = line.codePointAt(pos);
171
172 if (currChar == '*' && pos < lineLength - 1 && line.codePointAt(pos + 1) == '/') {
173 pos += 2;
174 addElement();
175 startPos = -1;
176 inComments = false;
177 break;
178 }
179
180 pos++;
181 }
182
183 if (pos < lineLength) {
184 currChar = line.codePointAt(pos);
185 return false;
186 }
187
188 return true;
189 }
190
191 private void addFinalElement() {
192 String text = line.substring(startPos);
193 addElement(text);
194 }
195
196 private void addElement() {
197 String text = pos > 0 ? line.substring(startPos, pos) : line.substring(startPos);
198 addElement(text);
199 }
200
201 private void addElement(@NonNull String text) {
202 ElementType type;
203
204 if (inComments) {
205 type = ElementType.COMMENT;
206 } else if (inCodeElement) {
207 type = ElementType.CODE;
208 } else {
209 type = ElementType.SEPARATOR;
210 }
211
212 LineElement newElement = new LineElement(type, text);
213
214 if (initialElement == null) {
215 initialElement = newElement;
216 } else {
217 assert currentElement != null;
218 currentElement.setNext(newElement);
219 }
220
221 currentElement = newElement;
222 }
223 }