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 -> isLineBlockComment(lineNo, comment));
243 }
244
245
246
247
248
249
250
251
252
253 private boolean isLineBlockComment(int lineNo, TextBlock comment) {
254 final boolean lineInSideBlockComment = lineNo >= comment.getStartLineNo()
255 && lineNo <= comment.getEndLineNo();
256 boolean lineHasOnlyBlockComment = true;
257 final String startLine = line(comment.getStartLineNo() - 1).trim();
258 if (!startLine.startsWith("/*")) {
259 lineHasOnlyBlockComment = false;
260 }
261
262 final String endLine = line(comment.getEndLineNo() - 1).trim();
263 if (!endLine.endsWith("*/")) {
264 lineHasOnlyBlockComment = false;
265 }
266 return lineInSideBlockComment && lineHasOnlyBlockComment;
267 }
268
269
270
271
272
273
274
275 public boolean lineIsBlank(int lineNo) {
276 return CommonUtil.isBlank(line(lineNo));
277 }
278
279
280
281
282
283
284
285
286 public boolean lineIsComment(int lineNo) {
287 return MATCH_SINGLELINE_COMMENT.matcher(line(lineNo)).matches();
288 }
289
290
291
292
293
294
295
296
297
298
299 public boolean hasIntersectionWithComment(int startLineNo,
300 int startColNo, int endLineNo, int endColNo) {
301 return hasIntersectionWithBlockComment(startLineNo, startColNo, endLineNo, endColNo)
302 || hasIntersectionWithSingleLineComment(startLineNo, startColNo, endLineNo,
303 endColNo);
304 }
305
306
307
308
309
310
311
312
313
314
315 private boolean hasIntersectionWithBlockComment(int startLineNo, int startColNo,
316 int endLineNo, int endColNo) {
317
318 final Collection<List<TextBlock>> values = clangComments.values();
319 return values.stream()
320 .flatMap(List::stream)
321 .anyMatch(comment -> comment.intersects(startLineNo, startColNo, endLineNo, endColNo));
322 }
323
324
325
326
327
328
329
330
331
332
333 private boolean hasIntersectionWithSingleLineComment(int startLineNo, int startColNo,
334 int endLineNo, int endColNo) {
335 boolean hasIntersection = false;
336
337 for (int lineNumber = startLineNo; lineNumber <= endLineNo;
338 lineNumber++) {
339 final TextBlock comment = cppComments.get(lineNumber);
340 if (comment != null && comment.intersects(startLineNo, startColNo,
341 endLineNo, endColNo)) {
342 hasIntersection = true;
343 break;
344 }
345 }
346 return hasIntersection;
347 }
348
349
350
351
352
353
354
355 public Map<Integer, TextBlock> getSingleLineComments() {
356 return Collections.unmodifiableMap(cppComments);
357 }
358
359
360
361
362
363
364
365
366 public Map<Integer, List<TextBlock>> getBlockComments() {
367 return Collections.unmodifiableMap(clangComments);
368 }
369
370
371
372
373
374
375
376
377 @Deprecated(since = "10.2")
378 public boolean inPackageInfo() {
379 return "package-info.java".equals(text.getFile().getName());
380 }
381 }