1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.api;
21
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.regex.Pattern;
29
30 import com.puppycrawl.tools.checkstyle.grammar.CommentListener;
31 import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
32 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
33
34
35
36
37
38 public final class FileContents implements CommentListener {
39
40
41
42
43
44 private static final String MATCH_SINGLELINE_COMMENT_PAT = "^\\s*//.*$";
45
46 private static final Pattern MATCH_SINGLELINE_COMMENT = Pattern
47 .compile(MATCH_SINGLELINE_COMMENT_PAT);
48
49
50 private final FileText text;
51
52
53
54
55
56 private final Map<Integer, TextBlock> javadocComments = new HashMap<>();
57
58 private final Map<Integer, TextBlock> cppComments = new HashMap<>();
59
60
61
62
63
64 private final Map<Integer, List<TextBlock>> clangComments = new HashMap<>();
65
66
67
68
69
70
71 public FileContents(FileText text) {
72 this.text = new FileText(text);
73 }
74
75
76
77
78
79
80 public FileText getText() {
81 return new FileText(text);
82 }
83
84
85
86
87
88
89 public String[] getLines() {
90 return text.toLinesArray();
91 }
92
93
94
95
96
97
98
99 public String getLine(int index) {
100 return text.get(index);
101 }
102
103
104
105
106
107
108 public String getFileName() {
109 return text.getFile().toString();
110 }
111
112 @Override
113 public void reportSingleLineComment(String type, int startLineNo,
114 int startColNo) {
115 reportSingleLineComment(startLineNo, startColNo);
116 }
117
118
119
120
121
122
123
124 public void reportSingleLineComment(int startLineNo, int startColNo) {
125 final String line = line(startLineNo - 1);
126 final String[] txt = {line.substring(startColNo)};
127 final Comment comment = new Comment(txt, startColNo, startLineNo,
128 line.length() - 1);
129 cppComments.put(startLineNo, comment);
130 }
131
132 @Override
133 public void reportBlockComment(String type, int startLineNo,
134 int startColNo, int endLineNo, int endColNo) {
135 reportBlockComment(startLineNo, startColNo, endLineNo, endColNo);
136 }
137
138
139
140
141
142
143
144
145
146 public void reportBlockComment(int startLineNo, int startColNo,
147 int endLineNo, int endColNo) {
148 final String[] cComment = extractBlockComment(startLineNo, startColNo,
149 endLineNo, endColNo);
150 final Comment comment = new Comment(cComment, startColNo, endLineNo,
151 endColNo);
152
153
154 final List<TextBlock> entries = clangComments.computeIfAbsent(startLineNo,
155 empty -> new ArrayList<>());
156
157 entries.add(comment);
158
159
160 final String firstLine = line(startLineNo - 1);
161 if (firstLine.contains("/**") && !firstLine.contains("/**/")) {
162 javadocComments.put(endLineNo - 1, comment);
163 }
164 }
165
166
167
168
169
170
171
172
173
174
175 private String[] extractBlockComment(int startLineNo, int startColNo,
176 int endLineNo, int endColNo) {
177 final String[] returnValue;
178 if (startLineNo == endLineNo) {
179 returnValue = new String[1];
180 returnValue[0] = line(startLineNo - 1).substring(startColNo,
181 endColNo + 1);
182 }
183 else {
184 returnValue = new String[endLineNo - startLineNo + 1];
185 returnValue[0] = line(startLineNo - 1).substring(startColNo);
186 for (int i = startLineNo; i < endLineNo; i++) {
187 returnValue[i - startLineNo + 1] = line(i);
188 }
189 returnValue[returnValue.length - 1] = line(endLineNo - 1).substring(0,
190 endColNo + 1);
191 }
192 return returnValue;
193 }
194
195
196
197
198
199
200
201
202
203
204 private String line(int lineNo) {
205 return text.get(lineNo);
206 }
207
208
209
210
211
212
213
214
215 public TextBlock getJavadocBefore(int lineNoBefore) {
216
217 int lineNo = lineNoBefore - 2;
218
219
220 while (lineNo > 0 && (lineIsBlank(lineNo) || lineIsComment(lineNo)
221 || lineInsideBlockComment(lineNo + 1))) {
222 lineNo--;
223 }
224
225 return javadocComments.get(lineNo);
226 }
227
228
229
230
231
232
233
234
235
236
237 private boolean lineInsideBlockComment(int lineNo) {
238 final Collection<List<TextBlock>> values = clangComments.values();
239 return values.stream()
240 .flatMap(List::stream)
241 .filter(comment -> !javadocComments.containsValue(comment))
242 .anyMatch(comment -> {
243 final boolean lineInSideBlockComment = lineNo >= comment.getStartLineNo()
244 && lineNo <= comment.getEndLineNo();
245 boolean lineHasOnlyBlockComment = true;
246 if (comment.getStartLineNo() == comment.getEndLineNo()) {
247 final String line = line(comment.getStartLineNo() - 1).trim();
248 lineHasOnlyBlockComment = line.startsWith("/*") && line.endsWith("*/");
249 }
250 return lineInSideBlockComment && lineHasOnlyBlockComment;
251 });
252 }
253
254
255
256
257
258
259
260 public boolean lineIsBlank(int lineNo) {
261 return CommonUtil.isBlank(line(lineNo));
262 }
263
264
265
266
267
268
269
270
271 public boolean lineIsComment(int lineNo) {
272 return MATCH_SINGLELINE_COMMENT.matcher(line(lineNo)).matches();
273 }
274
275
276
277
278
279
280
281
282
283
284 public boolean hasIntersectionWithComment(int startLineNo,
285 int startColNo, int endLineNo, int endColNo) {
286 return hasIntersectionWithBlockComment(startLineNo, startColNo, endLineNo, endColNo)
287 || hasIntersectionWithSingleLineComment(startLineNo, startColNo, endLineNo,
288 endColNo);
289 }
290
291
292
293
294
295
296
297
298
299
300 private boolean hasIntersectionWithBlockComment(int startLineNo, int startColNo,
301 int endLineNo, int endColNo) {
302
303 final Collection<List<TextBlock>> values = clangComments.values();
304 return values.stream()
305 .flatMap(List::stream)
306 .anyMatch(comment -> comment.intersects(startLineNo, startColNo, endLineNo, endColNo));
307 }
308
309
310
311
312
313
314
315
316
317
318 private boolean hasIntersectionWithSingleLineComment(int startLineNo, int startColNo,
319 int endLineNo, int endColNo) {
320 boolean hasIntersection = false;
321
322 for (int lineNumber = startLineNo; lineNumber <= endLineNo;
323 lineNumber++) {
324 final TextBlock comment = cppComments.get(lineNumber);
325 if (comment != null && comment.intersects(startLineNo, startColNo,
326 endLineNo, endColNo)) {
327 hasIntersection = true;
328 break;
329 }
330 }
331 return hasIntersection;
332 }
333
334
335
336
337
338
339
340 public Map<Integer, TextBlock> getSingleLineComments() {
341 return Collections.unmodifiableMap(cppComments);
342 }
343
344
345
346
347
348
349
350
351 public Map<Integer, List<TextBlock>> getBlockComments() {
352 return Collections.unmodifiableMap(clangComments);
353 }
354
355
356
357
358
359
360
361
362 @Deprecated(since = "10.2")
363 public boolean inPackageInfo() {
364 return "package-info.java".equals(text.getFile().getName());
365 }
366 }