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 lineNo--;
222 }
223
224 return javadocComments.get(lineNo);
225 }
226
227
228
229
230
231
232
233 public boolean lineIsBlank(int lineNo) {
234 return CommonUtil.isBlank(line(lineNo));
235 }
236
237
238
239
240
241
242
243
244 public boolean lineIsComment(int lineNo) {
245 return MATCH_SINGLELINE_COMMENT.matcher(line(lineNo)).matches();
246 }
247
248
249
250
251
252
253
254
255
256
257 public boolean hasIntersectionWithComment(int startLineNo,
258 int startColNo, int endLineNo, int endColNo) {
259 return hasIntersectionWithBlockComment(startLineNo, startColNo, endLineNo, endColNo)
260 || hasIntersectionWithSingleLineComment(startLineNo, startColNo, endLineNo,
261 endColNo);
262 }
263
264
265
266
267
268
269
270
271
272
273 private boolean hasIntersectionWithBlockComment(int startLineNo, int startColNo,
274 int endLineNo, int endColNo) {
275
276 final Collection<List<TextBlock>> values = clangComments.values();
277 return values.stream()
278 .flatMap(List::stream)
279 .anyMatch(comment -> comment.intersects(startLineNo, startColNo, endLineNo, endColNo));
280 }
281
282
283
284
285
286
287
288
289
290
291 private boolean hasIntersectionWithSingleLineComment(int startLineNo, int startColNo,
292 int endLineNo, int endColNo) {
293 boolean hasIntersection = false;
294
295 for (int lineNumber = startLineNo; lineNumber <= endLineNo;
296 lineNumber++) {
297 final TextBlock comment = cppComments.get(lineNumber);
298 if (comment != null && comment.intersects(startLineNo, startColNo,
299 endLineNo, endColNo)) {
300 hasIntersection = true;
301 break;
302 }
303 }
304 return hasIntersection;
305 }
306
307
308
309
310
311
312
313 public Map<Integer, TextBlock> getSingleLineComments() {
314 return Collections.unmodifiableMap(cppComments);
315 }
316
317
318
319
320
321
322
323
324 public Map<Integer, List<TextBlock>> getBlockComments() {
325 return Collections.unmodifiableMap(clangComments);
326 }
327
328
329
330
331
332
333
334
335 @Deprecated(since = "10.2")
336 public boolean inPackageInfo() {
337 return "package-info.java".equals(text.getFile().getName());
338 }
339 }