1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package com.puppycrawl.tools.checkstyle.filters;
20
21 import com.google.common.collect.Lists;
22 import com.puppycrawl.tools.checkstyle.api.AuditEvent;
23 import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
24 import com.puppycrawl.tools.checkstyle.api.FileContents;
25 import com.puppycrawl.tools.checkstyle.api.Filter;
26 import com.puppycrawl.tools.checkstyle.api.TextBlock;
27 import com.puppycrawl.tools.checkstyle.api.Utils;
28 import com.puppycrawl.tools.checkstyle.checks.FileContentsHolder;
29 import java.lang.ref.WeakReference;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
36 import java.util.regex.PatternSyntaxException;
37 import org.apache.commons.beanutils.ConversionException;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 public class SuppressWithNearbyCommentFilter
76 extends AutomaticBean
77 implements Filter
78 {
79
80
81
82 public class Tag implements Comparable<Tag>
83 {
84
85 private final String text;
86
87
88 private int firstLine;
89
90
91 private int lastLine;
92
93
94 private Pattern tagCheckRegexp;
95
96
97 private Pattern tagMessageRegexp;
98
99
100
101
102
103
104
105
106 public Tag(String text, int line)
107 throws ConversionException
108 {
109 this.text = text;
110
111 tagCheckRegexp = checkRegexp;
112
113
114 String format = "";
115 try {
116 format = expandFrocomment(text, checkFormat, commentRegexp);
117 tagCheckRegexp = Pattern.compile(format);
118 if (messageFormat != null) {
119 format = expandFrocomment(
120 text, messageFormat, commentRegexp);
121 tagMessageRegexp = Pattern.compile(format);
122 }
123 int influence = 0;
124 if (influenceFormat != null) {
125 format = expandFrocomment(
126 text, influenceFormat, commentRegexp);
127 try {
128 if (format.startsWith("+")) {
129 format = format.substring(1);
130 }
131 influence = Integer.parseInt(format);
132 }
133 catch (final NumberFormatException e) {
134 throw new ConversionException(
135 "unable to parse influence from '" + text
136 + "' using " + influenceFormat, e);
137 }
138 }
139 if (influence >= 0) {
140 firstLine = line;
141 lastLine = line + influence;
142 }
143 else {
144 firstLine = line + influence;
145 lastLine = line;
146 }
147 }
148 catch (final PatternSyntaxException e) {
149 throw new ConversionException(
150 "unable to parse expanded comment " + format,
151 e);
152 }
153 }
154
155
156 public String getText()
157 {
158 return text;
159 }
160
161
162 public int getFirstLine()
163 {
164 return firstLine;
165 }
166
167
168 public int getLastLine()
169 {
170 return lastLine;
171 }
172
173
174
175
176
177
178
179
180
181
182 @Override
183 public int compareTo(Tag other)
184 {
185 if (firstLine == other.firstLine) {
186 return lastLine - other.lastLine;
187 }
188
189 return (firstLine - other.firstLine);
190 }
191
192
193
194
195
196
197
198 public boolean isMatch(AuditEvent event)
199 {
200 final int line = event.getLine();
201 if (line < firstLine) {
202 return false;
203 }
204 if (line > lastLine) {
205 return false;
206 }
207 final Matcher tagMatcher =
208 tagCheckRegexp.matcher(event.getSourceName());
209 if (tagMatcher.find()) {
210 return true;
211 }
212 if (tagMessageRegexp != null) {
213 final Matcher messageMatcher =
214 tagMessageRegexp.matcher(event.getMessage());
215 return messageMatcher.find();
216 }
217 return false;
218 }
219
220
221
222
223
224
225
226
227 private String expandFrocomment(
228 String comment,
229 String string,
230 Pattern regexp)
231 {
232 final Matcher matcher = regexp.matcher(comment);
233
234 if (!matcher.find()) {
235
236 return string;
237
238 }
239 String result = string;
240 for (int i = 0; i <= matcher.groupCount(); i++) {
241
242 result = result.replaceAll("\\$" + i, matcher.group(i));
243 }
244 return result;
245 }
246
247
248 @Override
249 public final String toString()
250 {
251 return "Tag[lines=[" + getFirstLine() + " to " + getLastLine()
252 + "]; text='" + getText() + "']";
253 }
254 }
255
256
257 private static final String DEFAULT_COMMENT_FORMAT =
258 "SUPPRESS CHECKSTYLE (\\w+)";
259
260
261 private static final String DEFAULT_CHECK_FORMAT = ".*";
262
263
264 private static final String DEFAULT_MESSAGE_FORMAT = null;
265
266
267 private static final String DEFAULT_INFLUENCE_FORMAT = "0";
268
269
270 private boolean checkC = true;
271
272
273 private boolean checkCPP = true;
274
275
276 private Pattern commentRegexp;
277
278
279 private String checkFormat;
280
281
282 private Pattern checkRegexp;
283
284
285 private String messageFormat;
286
287
288 private String influenceFormat;
289
290
291
292
293 private final List<Tag> tags = Lists.newArrayList();
294
295
296
297
298
299
300
301
302 private WeakReference<FileContents> fileContentsReference =
303 new WeakReference<FileContents>(null);
304
305
306
307
308
309
310 public SuppressWithNearbyCommentFilter()
311 {
312 if (DEFAULT_COMMENT_FORMAT != null) {
313 setCommentFormat(DEFAULT_COMMENT_FORMAT);
314 }
315 if (DEFAULT_CHECK_FORMAT != null) {
316 setCheckFormat(DEFAULT_CHECK_FORMAT);
317 }
318 if (DEFAULT_MESSAGE_FORMAT != null) {
319 setMessageFormat(DEFAULT_MESSAGE_FORMAT);
320 }
321 if (DEFAULT_INFLUENCE_FORMAT != null) {
322 setInfluenceFormat(DEFAULT_INFLUENCE_FORMAT);
323 }
324 }
325
326
327
328
329
330
331 public void setCommentFormat(String format)
332 throws ConversionException
333 {
334 try {
335 commentRegexp = Utils.getPattern(format);
336 }
337 catch (final PatternSyntaxException e) {
338 throw new ConversionException("unable to parse " + format, e);
339 }
340 }
341
342
343 public FileContents getFileContents()
344 {
345 return fileContentsReference.get();
346 }
347
348
349
350
351
352 public void setFileContents(FileContents fileContents)
353 {
354 fileContentsReference = new WeakReference<FileContents>(fileContents);
355 }
356
357
358
359
360
361
362 public void setCheckFormat(String format)
363 throws ConversionException
364 {
365 try {
366 checkRegexp = Utils.getPattern(format);
367 checkFormat = format;
368 }
369 catch (final PatternSyntaxException e) {
370 throw new ConversionException("unable to parse " + format, e);
371 }
372 }
373
374
375
376
377
378
379 public void setMessageFormat(String format)
380 throws ConversionException
381 {
382
383 try {
384 Utils.getPattern(format);
385 }
386 catch (final PatternSyntaxException e) {
387 throw new ConversionException("unable to parse " + format, e);
388 }
389 messageFormat = format;
390 }
391
392
393
394
395
396
397 public void setInfluenceFormat(String format)
398 throws ConversionException
399 {
400
401 try {
402 Utils.getPattern(format);
403 }
404 catch (final PatternSyntaxException e) {
405 throw new ConversionException("unable to parse " + format, e);
406 }
407 influenceFormat = format;
408 }
409
410
411
412
413
414
415 public void setCheckCPP(boolean checkCPP)
416 {
417 this.checkCPP = checkCPP;
418 }
419
420
421
422
423
424 public void setCheckC(boolean checkC)
425 {
426 this.checkC = checkC;
427 }
428
429
430 @Override
431 public boolean accept(AuditEvent event)
432 {
433 if (event.getLocalizedMessage() == null) {
434 return true;
435 }
436
437
438
439 final FileContents currentContents = FileContentsHolder.getContents();
440 if (currentContents == null) {
441
442
443 return true;
444 }
445 if (getFileContents() != currentContents) {
446 setFileContents(currentContents);
447 tagSuppressions();
448 }
449 for (final Iterator<Tag> iter = tags.iterator(); iter.hasNext();) {
450 final Tag tag = iter.next();
451 if (tag.isMatch(event)) {
452 return false;
453 }
454 }
455 return true;
456 }
457
458
459
460
461
462 private void tagSuppressions()
463 {
464 tags.clear();
465 final FileContents contents = getFileContents();
466 if (checkCPP) {
467 tagSuppressions(contents.getCppComments().values());
468 }
469 if (checkC) {
470 final Collection<List<TextBlock>> cComments =
471 contents.getCComments().values();
472 for (final List<TextBlock> element : cComments) {
473 tagSuppressions(element);
474 }
475 }
476 Collections.sort(tags);
477 }
478
479
480
481
482
483
484 private void tagSuppressions(Collection<TextBlock> comments)
485 {
486 for (final TextBlock comment : comments) {
487 final int startLineNo = comment.getStartLineNo();
488 final String[] text = comment.getText();
489 tagCommentLine(text[0], startLineNo);
490 for (int i = 1; i < text.length; i++) {
491 tagCommentLine(text[i], startLineNo + i);
492 }
493 }
494 }
495
496
497
498
499
500
501
502 private void tagCommentLine(String text, int line)
503 {
504 final Matcher matcher = commentRegexp.matcher(text);
505 if (matcher.find()) {
506 addTag(matcher.group(0), line);
507 }
508 }
509
510
511
512
513
514
515 private void addTag(String text, int line)
516 {
517 final Tag tag = new Tag(text, line);
518 tags.add(tag);
519 }
520 }