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