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