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