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