001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2023 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018///////////////////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.filters;
021
022import java.lang.ref.WeakReference;
023import java.util.ArrayList;
024import java.util.Collection;
025import java.util.List;
026import java.util.Objects;
027import java.util.regex.Matcher;
028import java.util.regex.Pattern;
029import java.util.regex.PatternSyntaxException;
030
031import com.puppycrawl.tools.checkstyle.PropertyType;
032import com.puppycrawl.tools.checkstyle.TreeWalkerAuditEvent;
033import com.puppycrawl.tools.checkstyle.TreeWalkerFilter;
034import com.puppycrawl.tools.checkstyle.XdocsPropertyType;
035import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
036import com.puppycrawl.tools.checkstyle.api.FileContents;
037import com.puppycrawl.tools.checkstyle.api.TextBlock;
038import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
039
040/**
041 * <p>
042 * Filter {@code SuppressWithNearbyCommentFilter} uses nearby comments to suppress audit events.
043 * </p>
044 * <p>
045 * Rationale: Same as {@code SuppressionCommentFilter}.
046 * Whereas the SuppressionCommentFilter uses matched pairs of filters to turn
047 * on/off comment matching, {@code SuppressWithNearbyCommentFilter} uses single comments.
048 * This requires fewer lines to mark a region, and may be aesthetically preferable in some contexts.
049 * </p>
050 * <p>
051 * Attention: This filter may only be specified within the TreeWalker module
052 * ({@code &lt;module name="TreeWalker"/&gt;}) and only applies to checks which are also
053 * defined within this module. To filter non-TreeWalker checks like {@code RegexpSingleline},
054 * a <a href="https://checkstyle.org/config_filters.html#SuppressWithPlainTextCommentFilter">
055 * SuppressWithPlainTextCommentFilter</a> or similar filter must be used.
056 * </p>
057 * <p>
058 * SuppressWithNearbyCommentFilter can suppress Checks that have
059 * Treewalker as parent module.
060 * </p>
061 * <ul>
062 * <li>
063 * Property {@code commentFormat} - Specify comment pattern to trigger filter to begin suppression.
064 * Type is {@code java.util.regex.Pattern}.
065 * Default value is {@code "SUPPRESS CHECKSTYLE (\w+)"}.
066 * </li>
067 * <li>
068 * Property {@code checkFormat} - Specify check pattern to suppress.
069 * Type is {@code java.util.regex.Pattern}.
070 * Default value is {@code ".*"}.
071 * </li>
072 * <li>
073 * Property {@code messageFormat} - Define message pattern to suppress.
074 * Type is {@code java.util.regex.Pattern}.
075 * Default value is {@code null}.
076 * </li>
077 * <li>
078 * Property {@code idFormat} - Specify check ID pattern to suppress.
079 * Type is {@code java.util.regex.Pattern}.
080 * Default value is {@code null}.
081 * </li>
082 * <li>
083 * Property {@code influenceFormat} - Specify negative/zero/positive value that
084 * defines the number of lines preceding/at/following the suppression comment.
085 * Type is {@code java.lang.String}.
086 * Default value is {@code "0"}.
087 * </li>
088 * <li>
089 * Property {@code checkCPP} - Control whether to check C++ style comments ({@code //}).
090 * Type is {@code boolean}.
091 * Default value is {@code true}.
092 * </li>
093 * <li>
094 * Property {@code checkC} - Control whether to check C style comments ({@code &#47;* ... *&#47;}).
095 * Type is {@code boolean}.
096 * Default value is {@code true}.
097 * </li>
098 * </ul>
099 * <p>
100 * To configure a filter to suppress audit events for <i>check</i> on any line
101 * with a comment {@code SUPPRESS CHECKSTYLE <i>check</i>}:
102 * </p>
103 * <pre>
104 * &lt;module name=&quot;SuppressWithNearbyCommentFilter&quot;/&gt;
105 * </pre>
106 * <pre>
107 * private int [] array; // SUPPRESS CHECKSTYLE
108 * </pre>
109 * <p>
110 * To configure a filter to suppress all audit events on any line containing
111 * the comment {@code CHECKSTYLE IGNORE THIS LINE}:
112 * </p>
113 * <pre>
114 * &lt;module name=&quot;SuppressWithNearbyCommentFilter&quot;&gt;
115 *   &lt;property name=&quot;commentFormat&quot; value=&quot;CHECKSTYLE IGNORE THIS LINE&quot;/&gt;
116 *   &lt;property name=&quot;checkFormat&quot; value=&quot;.*&quot;/&gt;
117 *   &lt;property name=&quot;influenceFormat&quot; value=&quot;0&quot;/&gt;
118 * &lt;/module&gt;
119 * </pre>
120 * <pre>
121 * public static final int lowerCaseConstant; // CHECKSTYLE IGNORE THIS LINE
122 * </pre>
123 * <p>
124 * To configure a filter so that {@code // OK to catch (Throwable|Exception|RuntimeException) here}
125 * permits the current and previous line to avoid generating an IllegalCatch audit event:
126 * </p>
127 * <pre>
128 * &lt;module name=&quot;SuppressWithNearbyCommentFilter&quot;&gt;
129 *   &lt;property name=&quot;commentFormat&quot; value=&quot;OK to catch (\w+) here&quot;/&gt;
130 *   &lt;property name=&quot;checkFormat&quot; value=&quot;IllegalCatchCheck&quot;/&gt;
131 *   &lt;property name=&quot;messageFormat&quot; value=&quot;$1&quot;/&gt;
132 *   &lt;property name=&quot;influenceFormat&quot; value=&quot;-1&quot;/&gt;
133 * &lt;/module&gt;
134 * </pre>
135 * <pre>
136 * . . .
137 * catch (RuntimeException re) {
138 * // OK to catch RuntimeException here
139 * }
140 * catch (Throwable th) { ... }
141 * . . .
142 * </pre>
143 * <p>
144 * To configure a filter so that {@code CHECKSTYLE IGNORE <i>check</i> FOR NEXT
145 * <i>var</i> LINES} avoids triggering any audits for the given check for
146 * the current line and the next <i>var</i> lines (for a total of <i>var</i>+1 lines):
147 * </p>
148 * <pre>
149 * &lt;module name=&quot;SuppressWithNearbyCommentFilter&quot;&gt;
150 *   &lt;property name=&quot;commentFormat&quot;
151 *       value=&quot;CHECKSTYLE IGNORE (\w+) FOR NEXT (\d+) LINES&quot;/&gt;
152 *   &lt;property name=&quot;checkFormat&quot; value=&quot;$1&quot;/&gt;
153 *   &lt;property name=&quot;influenceFormat&quot; value=&quot;$2&quot;/&gt;
154 * &lt;/module&gt;
155 * </pre>
156 * <pre>
157 * static final int lowerCaseConstant; // CHECKSTYLE IGNORE ConstantNameCheck FOR NEXT 3 LINES
158 * static final int lowerCaseConstant1;
159 * static final int lowerCaseConstant2;
160 * static final int lowerCaseConstant3;
161 * static final int lowerCaseConstant4; // will warn here
162 * </pre>
163 * <p>
164 * To configure a filter to avoid any audits on code like:
165 * </p>
166 * <pre>
167 * &lt;module name=&quot;SuppressWithNearbyCommentFilter&quot;&gt;
168 *   &lt;property name=&quot;commentFormat&quot;
169 *     value=&quot;ALLOW (\\w+) ON PREVIOUS LINE&quot;/&gt;
170 *   &lt;property name=&quot;checkFormat&quot; value=&quot;$1&quot;/&gt;
171 *   &lt;property name=&quot;influenceFormat&quot; value=&quot;-1&quot;/&gt;
172 * &lt;/module&gt;
173 * </pre>
174 * <pre>
175 * private int D2;
176 * // ALLOW MemberName ON PREVIOUS LINE
177 * . . .
178 * </pre>
179 * <p>
180 * To configure a filter to allow suppress one or more Checks (separated by "|")
181 * and demand comment no less than 14 symbols:
182 * </p>
183 * <pre>
184 * &lt;module name="SuppressWithNearbyCommentFilter"&gt;
185 *   &lt;property name="commentFormat"
186 *     value="@cs\.suppress \[(\w+(\|\w+)*)\] \w[-\.'`,:;\w ]{14,}"/&gt;
187 *   &lt;property name="checkFormat" value="$1"/&gt;
188 *   &lt;property name="influenceFormat" value="1"/&gt;
189 * &lt;/module&gt;
190 * </pre>
191 * <pre>
192 * public static final int [] array; // @cs.suppress [ConstantName|NoWhitespaceAfter] A comment here
193 * </pre>
194 * <p>
195 * It is possible to specify an ID of checks, so that it can be leveraged by
196 * the SuppressWithNearbyCommentFilter to skip validations. The following examples show how to skip
197 * validations near code that has comment like {@code // @cs-: &lt;ID/&gt; (reason)},
198 * where ID is the ID of checks you want to suppress.
199 * </p>
200 * <p>
201 * Examples of Checkstyle checks configuration:
202 * </p>
203 * <pre>
204 * &lt;module name="RegexpSinglelineJava"&gt;
205 *   &lt;property name="id" value="ignore"/&gt;
206 *   &lt;property name="format" value="^.*@Ignore\s*$"/&gt;
207 *   &lt;property name="message" value="@Ignore should have a reason."/&gt;
208 * &lt;/module&gt;
209 *
210 * &lt;module name="RegexpSinglelineJava"&gt;
211 *   &lt;property name="id" value="systemout"/&gt;
212 *   &lt;property name="format" value="^.*System\.(out|err).*$"/&gt;
213 *   &lt;property name="message" value="Don't use System.out/err, use SLF4J instead."/&gt;
214 * &lt;/module&gt;
215 * </pre>
216 * <p>
217 * Example of SuppressWithNearbyCommentFilter configuration (idFormat which is set to
218 * '$1' points that ID of the checks is in the first group of commentFormat regular expressions):
219 * </p>
220 * <pre>
221 * &lt;module name="SuppressWithNearbyCommentFilter"&gt;
222 *   &lt;property name="commentFormat" value="@cs-: (\w+) \(.*\)"/&gt;
223 *   &lt;property name="idFormat" value="$1"/&gt;
224 *   &lt;property name="influenceFormat" value="0"/&gt;
225 * &lt;/module&gt;
226 * </pre>
227 * <pre>
228 * &#64;Ignore // @cs-: ignore (test has not been implemented yet)
229 * &#64;Test
230 * public void testMethod() { }
231 *
232 * public static void foo() {
233 *   System.out.println("Debug info."); // @cs-: systemout (should not fail RegexpSinglelineJava)
234 * }
235 * </pre>
236 * <p>
237 * Example of how to configure the check to suppress more than one checks.
238 * The influence format is specified in the second regexp group.
239 * </p>
240 * <pre>
241 * &lt;module name="SuppressWithNearbyCommentFilter"&gt;
242 *   &lt;property name="commentFormat" value="@cs-\: ([\w\|]+) influence (\d+)"/&gt;
243 *   &lt;property name="checkFormat" value="$1"/&gt;
244 *   &lt;property name="influenceFormat" value="$2"/&gt;
245 * &lt;/module&gt;
246 * </pre>
247 * <pre>
248 * // @cs-: ClassDataAbstractionCoupling influence 2
249 * // @cs-: MagicNumber influence 4
250 * &#64;Service // no violations from ClassDataAbstractionCoupling here
251 * &#64;Transactional
252 * public class UserService {
253 *   private int value = 10022; // no violations from MagicNumber here
254 * }
255 * </pre>
256 * <p>
257 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
258 * </p>
259 *
260 * @since 5.0
261 */
262public class SuppressWithNearbyCommentFilter
263    extends AutomaticBean
264    implements TreeWalkerFilter {
265
266    /** Format to turn checkstyle reporting off. */
267    private static final String DEFAULT_COMMENT_FORMAT =
268        "SUPPRESS CHECKSTYLE (\\w+)";
269
270    /** Default regex for checks that should be suppressed. */
271    private static final String DEFAULT_CHECK_FORMAT = ".*";
272
273    /** Default regex for lines that should be suppressed. */
274    private static final String DEFAULT_INFLUENCE_FORMAT = "0";
275
276    /** Tagged comments. */
277    private final List<Tag> tags = new ArrayList<>();
278
279    /** Control whether to check C style comments ({@code &#47;* ... *&#47;}). */
280    private boolean checkC = true;
281
282    /** Control whether to check C++ style comments ({@code //}). */
283    // -@cs[AbbreviationAsWordInName] We can not change it as,
284    // check's property is a part of API (used in configurations).
285    private boolean checkCPP = true;
286
287    /** Specify comment pattern to trigger filter to begin suppression. */
288    private Pattern commentFormat = Pattern.compile(DEFAULT_COMMENT_FORMAT);
289
290    /** Specify check pattern to suppress. */
291    @XdocsPropertyType(PropertyType.PATTERN)
292    private String checkFormat = DEFAULT_CHECK_FORMAT;
293
294    /** Define message pattern to suppress. */
295    @XdocsPropertyType(PropertyType.PATTERN)
296    private String messageFormat;
297
298    /** Specify check ID pattern to suppress. */
299    @XdocsPropertyType(PropertyType.PATTERN)
300    private String idFormat;
301
302    /**
303     * Specify negative/zero/positive value that defines the number of lines
304     * preceding/at/following the suppression comment.
305     */
306    private String influenceFormat = DEFAULT_INFLUENCE_FORMAT;
307
308    /**
309     * References the current FileContents for this filter.
310     * Since this is a weak reference to the FileContents, the FileContents
311     * can be reclaimed as soon as the strong references in TreeWalker
312     * are reassigned to the next FileContents, at which time filtering for
313     * the current FileContents is finished.
314     */
315    private WeakReference<FileContents> fileContentsReference = new WeakReference<>(null);
316
317    /**
318     * Setter to specify comment pattern to trigger filter to begin suppression.
319     *
320     * @param pattern a pattern.
321     */
322    public final void setCommentFormat(Pattern pattern) {
323        commentFormat = pattern;
324    }
325
326    /**
327     * Returns FileContents for this filter.
328     *
329     * @return the FileContents for this filter.
330     */
331    private FileContents getFileContents() {
332        return fileContentsReference.get();
333    }
334
335    /**
336     * Set the FileContents for this filter.
337     *
338     * @param fileContents the FileContents for this filter.
339     * @noinspection WeakerAccess
340     * @noinspectionreason WeakerAccess - we avoid 'protected' when possible
341     */
342    public void setFileContents(FileContents fileContents) {
343        fileContentsReference = new WeakReference<>(fileContents);
344    }
345
346    /**
347     * Setter to specify check pattern to suppress.
348     *
349     * @param format a {@code String} value
350     */
351    public final void setCheckFormat(String format) {
352        checkFormat = format;
353    }
354
355    /**
356     * Setter to define message pattern to suppress.
357     *
358     * @param format a {@code String} value
359     */
360    public void setMessageFormat(String format) {
361        messageFormat = format;
362    }
363
364    /**
365     * Setter to specify check ID pattern to suppress.
366     *
367     * @param format a {@code String} value
368     */
369    public void setIdFormat(String format) {
370        idFormat = format;
371    }
372
373    /**
374     * Setter to specify negative/zero/positive value that defines the number
375     * of lines preceding/at/following the suppression comment.
376     *
377     * @param format a {@code String} value
378     */
379    public final void setInfluenceFormat(String format) {
380        influenceFormat = format;
381    }
382
383    /**
384     * Setter to control whether to check C++ style comments ({@code //}).
385     *
386     * @param checkCpp {@code true} if C++ comments are checked.
387     */
388    // -@cs[AbbreviationAsWordInName] We can not change it as,
389    // check's property is a part of API (used in configurations).
390    public void setCheckCPP(boolean checkCpp) {
391        checkCPP = checkCpp;
392    }
393
394    /**
395     * Setter to control whether to check C style comments ({@code &#47;* ... *&#47;}).
396     *
397     * @param checkC {@code true} if C comments are checked.
398     */
399    public void setCheckC(boolean checkC) {
400        this.checkC = checkC;
401    }
402
403    @Override
404    protected void finishLocalSetup() {
405        // No code by default
406    }
407
408    @Override
409    public boolean accept(TreeWalkerAuditEvent event) {
410        boolean accepted = true;
411
412        if (event.getViolation() != null) {
413            // Lazy update. If the first event for the current file, update file
414            // contents and tag suppressions
415            final FileContents currentContents = event.getFileContents();
416
417            if (getFileContents() != currentContents) {
418                setFileContents(currentContents);
419                tagSuppressions();
420            }
421            if (matchesTag(event)) {
422                accepted = false;
423            }
424        }
425        return accepted;
426    }
427
428    /**
429     * Whether current event matches any tag from {@link #tags}.
430     *
431     * @param event TreeWalkerAuditEvent to test match on {@link #tags}.
432     * @return true if event matches any tag from {@link #tags}, false otherwise.
433     */
434    private boolean matchesTag(TreeWalkerAuditEvent event) {
435        boolean result = false;
436        for (final Tag tag : tags) {
437            if (tag.isMatch(event)) {
438                result = true;
439                break;
440            }
441        }
442        return result;
443    }
444
445    /**
446     * Collects all the suppression tags for all comments into a list and
447     * sorts the list.
448     */
449    private void tagSuppressions() {
450        tags.clear();
451        final FileContents contents = getFileContents();
452        if (checkCPP) {
453            tagSuppressions(contents.getSingleLineComments().values());
454        }
455        if (checkC) {
456            final Collection<List<TextBlock>> cComments =
457                contents.getBlockComments().values();
458            cComments.forEach(this::tagSuppressions);
459        }
460    }
461
462    /**
463     * Appends the suppressions in a collection of comments to the full
464     * set of suppression tags.
465     *
466     * @param comments the set of comments.
467     */
468    private void tagSuppressions(Collection<TextBlock> comments) {
469        for (final TextBlock comment : comments) {
470            final int startLineNo = comment.getStartLineNo();
471            final String[] text = comment.getText();
472            tagCommentLine(text[0], startLineNo);
473            for (int i = 1; i < text.length; i++) {
474                tagCommentLine(text[i], startLineNo + i);
475            }
476        }
477    }
478
479    /**
480     * Tags a string if it matches the format for turning
481     * checkstyle reporting on or the format for turning reporting off.
482     *
483     * @param text the string to tag.
484     * @param line the line number of text.
485     */
486    private void tagCommentLine(String text, int line) {
487        final Matcher matcher = commentFormat.matcher(text);
488        if (matcher.find()) {
489            addTag(matcher.group(0), line);
490        }
491    }
492
493    /**
494     * Adds a comment suppression {@code Tag} to the list of all tags.
495     *
496     * @param text the text of the tag.
497     * @param line the line number of the tag.
498     */
499    private void addTag(String text, int line) {
500        final Tag tag = new Tag(text, line, this);
501        tags.add(tag);
502    }
503
504    /**
505     * A Tag holds a suppression comment and its location.
506     */
507    private static final class Tag {
508
509        /** The text of the tag. */
510        private final String text;
511
512        /** The first line where warnings may be suppressed. */
513        private final int firstLine;
514
515        /** The last line where warnings may be suppressed. */
516        private final int lastLine;
517
518        /** The parsed check regexp, expanded for the text of this tag. */
519        private final Pattern tagCheckRegexp;
520
521        /** The parsed message regexp, expanded for the text of this tag. */
522        private final Pattern tagMessageRegexp;
523
524        /** The parsed check ID regexp, expanded for the text of this tag. */
525        private final Pattern tagIdRegexp;
526
527        /**
528         * Constructs a tag.
529         *
530         * @param text the text of the suppression.
531         * @param line the line number.
532         * @param filter the {@code SuppressWithNearbyCommentFilter} with the context
533         * @throws IllegalArgumentException if unable to parse expanded text.
534         */
535        private Tag(String text, int line, SuppressWithNearbyCommentFilter filter) {
536            this.text = text;
537
538            // Expand regexp for check and message
539            // Does not intern Patterns with Utils.getPattern()
540            String format = "";
541            try {
542                format = CommonUtil.fillTemplateWithStringsByRegexp(
543                        filter.checkFormat, text, filter.commentFormat);
544                tagCheckRegexp = Pattern.compile(format);
545                if (filter.messageFormat == null) {
546                    tagMessageRegexp = null;
547                }
548                else {
549                    format = CommonUtil.fillTemplateWithStringsByRegexp(
550                            filter.messageFormat, text, filter.commentFormat);
551                    tagMessageRegexp = Pattern.compile(format);
552                }
553                if (filter.idFormat == null) {
554                    tagIdRegexp = null;
555                }
556                else {
557                    format = CommonUtil.fillTemplateWithStringsByRegexp(
558                            filter.idFormat, text, filter.commentFormat);
559                    tagIdRegexp = Pattern.compile(format);
560                }
561                format = CommonUtil.fillTemplateWithStringsByRegexp(
562                        filter.influenceFormat, text, filter.commentFormat);
563
564                final int influence = parseInfluence(format, filter.influenceFormat, text);
565
566                if (influence >= 1) {
567                    firstLine = line;
568                    lastLine = line + influence;
569                }
570                else {
571                    firstLine = line + influence;
572                    lastLine = line;
573                }
574            }
575            catch (final PatternSyntaxException ex) {
576                throw new IllegalArgumentException(
577                    "unable to parse expanded comment " + format, ex);
578            }
579        }
580
581        /**
582         * Gets influence from suppress filter influence format param.
583         *
584         * @param format          influence format to parse
585         * @param influenceFormat raw influence format
586         * @param text            text of the suppression
587         * @return parsed influence
588         * @throws IllegalArgumentException when unable to parse int in format
589         */
590        private static int parseInfluence(String format, String influenceFormat, String text) {
591            try {
592                return Integer.parseInt(format);
593            }
594            catch (final NumberFormatException ex) {
595                throw new IllegalArgumentException("unable to parse influence from '" + text
596                        + "' using " + influenceFormat, ex);
597            }
598        }
599
600        @Override
601        public boolean equals(Object other) {
602            if (this == other) {
603                return true;
604            }
605            if (other == null || getClass() != other.getClass()) {
606                return false;
607            }
608            final Tag tag = (Tag) other;
609            return Objects.equals(firstLine, tag.firstLine)
610                    && Objects.equals(lastLine, tag.lastLine)
611                    && Objects.equals(text, tag.text)
612                    && Objects.equals(tagCheckRegexp, tag.tagCheckRegexp)
613                    && Objects.equals(tagMessageRegexp, tag.tagMessageRegexp)
614                    && Objects.equals(tagIdRegexp, tag.tagIdRegexp);
615        }
616
617        @Override
618        public int hashCode() {
619            return Objects.hash(text, firstLine, lastLine, tagCheckRegexp, tagMessageRegexp,
620                    tagIdRegexp);
621        }
622
623        /**
624         * Determines whether the source of an audit event
625         * matches the text of this tag.
626         *
627         * @param event the {@code TreeWalkerAuditEvent} to check.
628         * @return true if the source of event matches the text of this tag.
629         */
630        public boolean isMatch(TreeWalkerAuditEvent event) {
631            return isInScopeOfSuppression(event)
632                    && isCheckMatch(event)
633                    && isIdMatch(event)
634                    && isMessageMatch(event);
635        }
636
637        /**
638         * Checks whether the {@link TreeWalkerAuditEvent} is in the scope of the suppression.
639         *
640         * @param event {@link TreeWalkerAuditEvent} instance.
641         * @return true if the {@link TreeWalkerAuditEvent} is in the scope of the suppression.
642         */
643        private boolean isInScopeOfSuppression(TreeWalkerAuditEvent event) {
644            final int line = event.getLine();
645            return line >= firstLine && line <= lastLine;
646        }
647
648        /**
649         * Checks whether {@link TreeWalkerAuditEvent} source name matches the check format.
650         *
651         * @param event {@link TreeWalkerAuditEvent} instance.
652         * @return true if the {@link TreeWalkerAuditEvent} source name matches the check format.
653         */
654        private boolean isCheckMatch(TreeWalkerAuditEvent event) {
655            final Matcher checkMatcher = tagCheckRegexp.matcher(event.getSourceName());
656            return checkMatcher.find();
657        }
658
659        /**
660         * Checks whether the {@link TreeWalkerAuditEvent} module ID matches the ID format.
661         *
662         * @param event {@link TreeWalkerAuditEvent} instance.
663         * @return true if the {@link TreeWalkerAuditEvent} module ID matches the ID format.
664         */
665        private boolean isIdMatch(TreeWalkerAuditEvent event) {
666            boolean match = true;
667            if (tagIdRegexp != null) {
668                if (event.getModuleId() == null) {
669                    match = false;
670                }
671                else {
672                    final Matcher idMatcher = tagIdRegexp.matcher(event.getModuleId());
673                    match = idMatcher.find();
674                }
675            }
676            return match;
677        }
678
679        /**
680         * Checks whether the {@link TreeWalkerAuditEvent} message matches the message format.
681         *
682         * @param event {@link TreeWalkerAuditEvent} instance.
683         * @return true if the {@link TreeWalkerAuditEvent} message matches the message format.
684         */
685        private boolean isMessageMatch(TreeWalkerAuditEvent event) {
686            boolean match = true;
687            if (tagMessageRegexp != null) {
688                final Matcher messageMatcher = tagMessageRegexp.matcher(event.getMessage());
689                match = messageMatcher.find();
690            }
691            return match;
692        }
693
694        @Override
695        public String toString() {
696            return "Tag[text='" + text + '\''
697                    + ", firstLine=" + firstLine
698                    + ", lastLine=" + lastLine
699                    + ", tagCheckRegexp=" + tagCheckRegexp
700                    + ", tagMessageRegexp=" + tagMessageRegexp
701                    + ", tagIdRegexp=" + tagIdRegexp
702                    + ']';
703        }
704
705    }
706
707}