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.checks.regexp;
021
022import java.util.regex.Matcher;
023import java.util.regex.Pattern;
024
025import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
026import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
027import com.puppycrawl.tools.checkstyle.api.DetailAST;
028import com.puppycrawl.tools.checkstyle.api.FileContents;
029import com.puppycrawl.tools.checkstyle.api.FileText;
030import com.puppycrawl.tools.checkstyle.api.LineColumn;
031import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
032
033/**
034 * <p>
035 * Checks that a specified pattern exists, exists less than
036 * a set number of times, or does not exist in the file.
037 * </p>
038 * <p>
039 * This check combines all the functionality provided by
040 * <a href="https://checkstyle.org/config_header.html#RegexpHeader">RegexpHeader</a>
041 * except supplying the regular expression from a file.
042 * </p>
043 * <p>
044 * It differs from them in that it works in multiline mode. Its regular expression
045 * can span multiple lines and it checks this against the whole file at once.
046 * The others work in singleline mode. Their single or multiple regular expressions
047 * can only span one line. They check each of these against each line in the file in turn.
048 * </p>
049 * <p>
050 * <b>Note:</b> Because of the different mode of operation there may be some
051 * changes in the regular expressions used to achieve a particular end.
052 * </p>
053 * <p>
054 * In multiline mode...
055 * </p>
056 * <ul>
057 * <li>
058 * {@code ^} means the beginning of a line, as opposed to beginning of the input.
059 * </li>
060 * <li>
061 * For beginning of the input use {@code \A}.
062 * </li>
063 * <li>
064 * {@code $} means the end of a line, as opposed to the end of the input.
065 * </li>
066 * <li>
067 * For end of input use {@code \Z}.
068 * </li>
069 * <li>
070 * Each line in the file is terminated with a line feed character.
071 * </li>
072 * </ul>
073 * <p>
074 * <b>Note:</b> Not all regular expression engines are created equal.
075 * Some provide extra functions that others do not and some elements
076 * of the syntax may vary. This check makes use of the
077 * <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/regex/package-summary.html">
078 * java.util.regex package</a>; please check its documentation for details
079 * of how to construct a regular expression to achieve a particular goal.
080 * </p>
081 * <p>
082 * <b>Note:</b> When entering a regular expression as a parameter in
083 * the XML config file you must also take into account the XML rules. e.g.
084 * if you want to match a &lt; symbol you need to enter &amp;lt;.
085 * The regular expression should be entered on one line.
086 * </p>
087 * <ul>
088 * <li>
089 * Property {@code format} - Specify the pattern to match against.
090 * Type is {@code java.util.regex.Pattern}.
091 * Default value is {@code "^$"}.
092 * </li>
093 * <li>
094 * Property {@code message} - Specify message which is used to notify about
095 * violations, if empty then the default (hard-coded) message is used.
096 * Type is {@code java.lang.String}.
097 * Default value is {@code null}.
098 * </li>
099 * <li>
100 * Property {@code illegalPattern} - Control whether the pattern is required or illegal.
101 * Type is {@code boolean}.
102 * Default value is {@code false}.
103 * </li>
104 * <li>
105 * Property {@code duplicateLimit} - Control whether to check for duplicates
106 * of a required pattern, any negative value means no checking for duplicates,
107 * any positive value is used as the maximum number of allowed duplicates,
108 * if the limit is exceeded violations will be logged.
109 * Type is {@code int}.
110 * Default value is {@code 0}.
111 * </li>
112 * <li>
113 * Property {@code errorLimit} - Specify the maximum number of violations before
114 * the check will abort.
115 * Type is {@code int}.
116 * Default value is {@code 100}.
117 * </li>
118 * <li>
119 * Property {@code ignoreComments} - Control whether to ignore matches found within comments.
120 * Type is {@code boolean}.
121 * Default value is {@code false}.
122 * </li>
123 * </ul>
124 * <p>
125 * To configure the check:
126 * </p>
127 * <p>
128 * The following examples are mainly copied from the other 3 checks mentioned above,
129 * to show how the same results can be achieved using this check in place of them.
130 * </p>
131 * <p>
132 * <b>To use like Required Regexp check:</b>
133 * </p>
134 * <p>
135 * An example of how to configure the check to make sure a copyright statement
136 * is included in the file:
137 * </p>
138 * <p>
139 * The statement.
140 * </p>
141 * <pre>
142 * // This code is copyrighted
143 * </pre>
144 * <p>
145 * The check.
146 * </p>
147 * <pre>
148 * &lt;module name="Regexp"&gt;
149 *   &lt;property name="format" value="// This code is copyrighted"/&gt;
150 * &lt;/module&gt;
151 * </pre>
152 * <p>
153 * Your statement may be multiline.
154 * </p>
155 * <pre>
156 * // This code is copyrighted
157 * // (c) MyCompany
158 * </pre>
159 * <p>
160 * Then the check would be.
161 * </p>
162 * <pre>
163 * &lt;module name="Regexp"&gt;
164 *   &lt;property name="format" value="// This code is copyrighted\n// \(c\) MyCompany"/&gt;
165 * &lt;/module&gt;
166 * </pre>
167 * <p>
168 * <b>Note:</b> To search for parentheses () in a regular expression you must
169 * escape them like \(\). This is required by the regexp engine, otherwise it will
170 * think they are special instruction characters.
171 * </p>
172 * <p>
173 * And to make sure it appears only once:
174 * </p>
175 * <pre>
176 * &lt;module name="Regexp"&gt;
177 *   &lt;property name="format" value="// This code is copyrighted\n// \(c\) MyCompany"/&gt;
178 *   &lt;property name="duplicateLimit" value="0"/&gt;
179 * &lt;/module&gt;
180 * </pre>
181 * <p>
182 * It can also be useful to attach a meaningful message to the check:
183 * </p>
184 * <pre>
185 * &lt;module name="Regexp"&gt;
186 *   &lt;property name="format" value="// This code is copyrighted\n// \(c\) MyCompany"/&gt;
187 *   &lt;property name="message" value="Copyright"/&gt;
188 * &lt;/module&gt;
189 * </pre>
190 * <p>
191 * <b>To use like illegal regexp check:</b>
192 * </p>
193 * <p>
194 * An example of how to configure the check to make sure there are no calls to
195 * {@code System.out.println}:
196 * </p>
197 * <pre>
198 * &lt;module name="Regexp"&gt;
199 *   &lt;!-- . matches any character, so we need to escape it and use \. to match dots. --&gt;
200 *   &lt;property name="format" value="System\.out\.println"/&gt;
201 *   &lt;property name="illegalPattern" value="true"/&gt;
202 * &lt;/module&gt;
203 * </pre>
204 * <p>
205 * You may want to make the above check ignore comments, like this:
206 * </p>
207 * <pre>
208 * &lt;module name="Regexp"&gt;
209 *   &lt;property name="format" value="System\.out\.println"/&gt;
210 *   &lt;property name="illegalPattern" value="true"/&gt;
211 *   &lt;property name="ignoreComments" value="true"/&gt;
212 * &lt;/module&gt;
213 * </pre>
214 * <p>
215 * An example of how to configure the check to find trailing whitespace at the end of a line:
216 * </p>
217 * <pre>
218 * &lt;module name="Regexp"&gt;
219 *   &lt;property name="format" value="[ \t]+$"/&gt;
220 *   &lt;property name="illegalPattern" value="true"/&gt;
221 *   &lt;property name="message" value="Trailing whitespace"/&gt;
222 * &lt;/module&gt;
223 * </pre>
224 * <p>
225 * An example of how to configure the check to find case-insensitive occurrences of "debug":
226 * </p>
227 * <pre>
228 * &lt;module name="Regexp"&gt;
229 *   &lt;property name="format" value="(?i)debug"/&gt;
230 *   &lt;property name="illegalPattern" value="true"/&gt;
231 * &lt;/module&gt;
232 * </pre>
233 * <p>
234 * <b>Note:</b> The (?i) at the beginning of the regular expression tells the
235 * regexp engine to ignore the case.
236 * </p>
237 * <p>
238 * There is also a feature to limit the number of violations reported.
239 * When the limit is reached the check aborts with a message reporting that
240 * the limit has been reached. The default limit setting is 100,
241 * but this can be change as shown in the following example.
242 * </p>
243 * <pre>
244 * &lt;module name="Regexp"&gt;
245 *   &lt;property name="format" value="(?i)debug"/&gt;
246 *   &lt;property name="illegalPattern" value="true"/&gt;
247 *   &lt;property name="errorLimit" value="1000"/&gt;
248 * &lt;/module&gt;
249 * </pre>
250 * <p>
251 * <b>To use like <a href="https://checkstyle.org/config_header.html#RegexpHeader">
252 * RegexpHeader</a>:</b>
253 * </p>
254 * <p>
255 * To configure the check to verify that each file starts with the following multiline header.
256 * </p>
257 * <p>
258 * Note the following:
259 * </p>
260 * <ul>
261 * <li>
262 * \A means the start of the file.
263 * </li>
264 * <li>
265 * The date can be any 4 digit number.
266 * </li>
267 * </ul>
268 * <pre>
269 * // Copyright (C) 2004 MyCompany
270 * // All rights reserved
271 * </pre>
272 * <pre>
273 * &lt;module name="Regexp"&gt;
274 *   &lt;property
275 *     name="format"
276 *     value="\A// Copyright \(C\) \d\d\d\d MyCompany\n// All rights reserved"/&gt;
277 * &lt;/module&gt;
278 * </pre>
279 * <p>
280 * A more complex example. Note how the import and javadoc multilines are handled,
281 * there can be any number of them.
282 * </p>
283 * <pre>
284 * ///////////////////////////////////////////////////////////////////////
285 * // checkstyle:
286 * // Checks Java source code for adherence to a set of rules.
287 * // Copyright (C) 2004  Oliver Burn
288 * // Last modification by $Author A.N.Other$
289 * ///////////////////////////////////////////////////////////////////////
290 *
291 * package com.puppycrawl.checkstyle;
292 *
293 * import java.util.thing1;
294 * import java.util.thing2;
295 * import java.util.thing3;
296 *
297 * &#47;**
298 * * javadoc line 1
299 * * javadoc line 2
300 * * javadoc line 3
301 * *&#47;
302 * </pre>
303 * <pre>
304 * &lt;module name="Regexp"&gt;
305 *   &lt;property
306 *     name="format"
307 *     value="\A/{71}\n// checkstyle:\n// Checks Java source code for
308 *     adherence to a set of rules\.\n// Copyright \(C\) \d\d\d\d  Oliver Burn\n
309 *     // Last modification by \$Author.*\$\n/{71}\n\npackage [\w\.]*;\n\n
310 *     (import [\w\.]*;\n)*\n/\*\*\n( \*[^/]*\n)* \*&#47;"/&gt;
311 * &lt;/module&gt;
312 * </pre>
313 * <p>
314 * <b>More examples:</b>
315 * </p>
316 * <p>
317 * The next 2 examples deal with the following example Java source file:
318 * </p>
319 * <pre>
320 * &#47;*
321 * * PID.java
322 * *
323 * * Copyright (c) 2001 ACME
324 * * 123 Some St.
325 * * Somewhere.
326 * *
327 * * This software is the confidential and proprietary information of ACME.
328 * * ("Confidential Information"). You shall not disclose such
329 * * Confidential Information and shall use it only in accordance with
330 * * the terms of the license agreement you entered into with ACME.
331 * *
332 * * $Log: config_misc.xml,v $
333 * * Revision 1.7  2007/01/16 12:16:35  oburn
334 * * Removing all reference to mailing lists
335 * *
336 * * Revision 1.6  2005/12/25 16:13:10  o_sukhodolsky
337 * * Fix for rfe 1248106 (TYPECAST is now accepted by NoWhitespaceAfter)
338 * *
339 * * Fix for rfe 953266 (thanks to Paul Guyot (pguyot) for submitting patch)
340 * * IllegalType can be configured to accept some abstract classes which
341 * * matches to regexp of illegal type names (property legalAbstractClassNames)
342 * *
343 * * TrailingComment now can be configured to accept some trailing comments
344 * * (such as NOI18N) (property legalComment, rfe 1385344).
345 * *
346 * * Revision 1.5  2005/11/06 11:54:12  oburn
347 * * Incorporate excellent patch [ 1344344 ] Consolidation of regexp checks.
348 * *
349 * * Revision 1.3.8.1  2005/10/11 14:26:32  someone
350 * * Fix for bug 251.  The broken bit is fixed
351 * *&#47;
352 *
353 * package com.acme.tools;
354 *
355 * import com.acme.thing1;
356 * import com.acme.thing2;
357 * import com.acme.thing3;
358 *
359 * &#47;**
360 * *
361 * * &lt;P&gt;
362 * *   &lt;I&gt;This software is the confidential and proprietary information of
363 * *   ACME (&lt;B&gt;"Confidential Information"&lt;/B&gt;). You shall not
364 * *   disclose such Confidential Information and shall use it only in
365 * *   accordance with the terms of the license agreement you entered into
366 * *   with ACME.&lt;/I&gt;
367 * * &lt;/P&gt;
368 * *
369 * * &amp;#169; copyright 2002 ACME
370 * *
371 * * &#64;author   Some Body
372 * *&#47;
373 * public class PID extends StateMachine implements WebObject.Constants {
374 *
375 * &#47;** javadoc. *&#47;
376 * public static final int A_SETPOINT = 1;
377 * .
378 * .
379 * .
380 * } // class PID
381 * </pre>
382 * <p>
383 * This checks for the presence of the header, the first 16 lines.
384 * </p>
385 * <p>
386 * Note the following:
387 * </p>
388 * <ul>
389 * <li>
390 * Line 2 and 13 contain the file name. These are checked to make sure they
391 * are the same, and that they match the class name.
392 * </li>
393 * <li>
394 * The date can be any 4 digit number.
395 * </li>
396 * </ul>
397 * <pre>
398 * &lt;module name="Regexp"&gt;
399 *   &lt;property
400 *     name="format"
401 *     value="\A/\*\n \* (\w*)\.java\n \*\n \* Copyright \(c\)
402 *     \d\d\d\d ACME\n \* 123 Some St\.\n \* Somewhere\.\n \*\n
403 *     \* This software is the confidential and proprietary information
404 *     of ACME\.\n \* \(&amp;quot;Confidential Information&amp;quot;\)\. You
405 *     shall not disclose such\n \* Confidential Information and shall
406 *     use it only in accordance with\n \* the terms of the license
407 *     agreement you entered into with ACME\.\n \*\n
408 *     \* \$Log: config_misc\.xml,v $
409 *     \* Revision 1\.7  2007/01/16 12:16:35  oburn
410 *     \* Removing all reference to mailing lists
411 *     \* \
412 *     \* Revision 1.6  2005/12/25 16:13:10  o_sukhodolsky
413 *     \* Fix for rfe 1248106 \(TYPECAST is now accepted by NoWhitespaceAfter\)
414 *     \* \
415 *     \* Fix for rfe 953266 \(thanks to Paul Guyot \(pguyot\) for submitting patch\)
416 *     \* IllegalType can be configured to accept some abstract classes which
417 *     \* matches to regexp of illegal type names \(property legalAbstractClassNames\)
418 *     \*
419 *     \* TrailingComment now can be configured to accept some trailing comments
420 *     \* \(such as NOI18N\) \(property legalComment, rfe 1385344\).
421 *     \*
422 *     \* Revision 1.5  2005/11/06 11:54:12  oburn
423 *     \* Incorporate excellent patch \[ 1344344 \] Consolidation of regexp checks.
424 *     \* \\n(.*\n)*([\w|\s]*( class | interface )\1)"/&gt;
425 *   &lt;property name="message" value="Correct header not found"/&gt;
426 * &lt;/module&gt;
427 * </pre>
428 * <p>
429 * This checks for the presence of a copyright notice within the class javadoc, lines 24 to 37.
430 * </p>
431 * <pre>
432 * &lt;module name="Regexp"&gt;
433 *   &lt;property
434 *     name="format"
435 *     value="(/\*\*\n)( \*.*\n)*( \* &lt;P&gt;\n \*   &lt;I&gt;
436 *     This software is the confidential and proprietary information of\n
437 *     \*   ACME \(&lt;B&gt;&amp;quot;Confidential Information&amp;quot;&lt;/B&gt;
438 *     \)\. You shall not\n \*   disclose such Confidential Information
439 *     and shall use it only in\n \*   accordance with the terms of the
440 *     license agreement you entered into\n \*   with ACME\.&lt;/I&gt;\n
441 *     \* &lt;/P&gt;\n \*\n \* &amp;#169; copyright \d\d\d\d ACME\n
442 *     \*\n \* &#64;author .*)(\n\s\*.*)*&#47;\n[\w|\s]*( class | interface )"/&gt;
443 *   &lt;property name="message"
444 *     value="Copyright in class/interface Javadoc"/&gt;
445 *   &lt;property name="duplicateLimit" value="0"/&gt;
446 * &lt;/module&gt;
447 * </pre>
448 * <p>
449 * <b>Note:</b> To search for things that mean something in XML, like &lt;
450 * you need to escape them like &amp;lt;. This is required so the XML parser
451 * does not act on them, but instead passes the correct character to the regexp engine.
452 * </p>
453 * <p>
454 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
455 * </p>
456 * <p>
457 * Violation Message Keys:
458 * </p>
459 * <ul>
460 * <li>
461 * {@code duplicate.regexp}
462 * </li>
463 * <li>
464 * {@code illegal.regexp}
465 * </li>
466 * <li>
467 * {@code required.regexp}
468 * </li>
469 * </ul>
470 *
471 * @since 4.0
472 */
473@FileStatefulCheck
474public class RegexpCheck extends AbstractCheck {
475
476    /**
477     * A key is pointing to the warning message text in "messages.properties"
478     * file.
479     */
480    public static final String MSG_ILLEGAL_REGEXP = "illegal.regexp";
481
482    /**
483     * A key is pointing to the warning message text in "messages.properties"
484     * file.
485     */
486    public static final String MSG_REQUIRED_REGEXP = "required.regexp";
487
488    /**
489     * A key is pointing to the warning message text in "messages.properties"
490     * file.
491     */
492    public static final String MSG_DUPLICATE_REGEXP = "duplicate.regexp";
493
494    /** Default duplicate limit. */
495    private static final int DEFAULT_DUPLICATE_LIMIT = -1;
496
497    /** Default error report limit. */
498    private static final int DEFAULT_ERROR_LIMIT = 100;
499
500    /** Error count exceeded message. */
501    private static final String ERROR_LIMIT_EXCEEDED_MESSAGE =
502        "The error limit has been exceeded, "
503        + "the check is aborting, there may be more unreported errors.";
504
505    /**
506     * Specify message which is used to notify about violations,
507     * if empty then the default (hard-coded) message is used.
508     */
509    private String message;
510
511    /** Control whether to ignore matches found within comments. */
512    private boolean ignoreComments;
513
514    /** Control whether the pattern is required or illegal. */
515    private boolean illegalPattern;
516
517    /** Specify the maximum number of violations before the check will abort. */
518    private int errorLimit = DEFAULT_ERROR_LIMIT;
519
520    /**
521     * Control whether to check for duplicates of a required pattern,
522     * any negative value means no checking for duplicates,
523     * any positive value is used as the maximum number of allowed duplicates,
524     * if the limit is exceeded violations will be logged.
525     */
526    private int duplicateLimit;
527
528    /** Boolean to say if we should check for duplicates. */
529    private boolean checkForDuplicates;
530
531    /** Tracks number of matches made. */
532    private int matchCount;
533
534    /** Tracks number of errors. */
535    private int errorCount;
536
537    /** Specify the pattern to match against. */
538    private Pattern format = Pattern.compile("^$", Pattern.MULTILINE);
539
540    /** The matcher. */
541    private Matcher matcher;
542
543    /**
544     * Setter to specify message which is used to notify about violations,
545     * if empty then the default (hard-coded) message is used.
546     *
547     * @param message custom message which should be used in report.
548     */
549    public void setMessage(String message) {
550        this.message = message;
551    }
552
553    /**
554     * Setter to control whether to ignore matches found within comments.
555     *
556     * @param ignoreComments True if comments should be ignored.
557     */
558    public void setIgnoreComments(boolean ignoreComments) {
559        this.ignoreComments = ignoreComments;
560    }
561
562    /**
563     * Setter to control whether the pattern is required or illegal.
564     *
565     * @param illegalPattern True if pattern is not allowed.
566     */
567    public void setIllegalPattern(boolean illegalPattern) {
568        this.illegalPattern = illegalPattern;
569    }
570
571    /**
572     * Setter to specify the maximum number of violations before the check will abort.
573     *
574     * @param errorLimit the number of errors to report.
575     */
576    public void setErrorLimit(int errorLimit) {
577        this.errorLimit = errorLimit;
578    }
579
580    /**
581     * Setter to control whether to check for duplicates of a required pattern,
582     * any negative value means no checking for duplicates,
583     * any positive value is used as the maximum number of allowed duplicates,
584     * if the limit is exceeded violations will be logged.
585     *
586     * @param duplicateLimit negative values mean no duplicate checking,
587     *     any positive value is used as the limit.
588     */
589    public void setDuplicateLimit(int duplicateLimit) {
590        this.duplicateLimit = duplicateLimit;
591        checkForDuplicates = duplicateLimit > DEFAULT_DUPLICATE_LIMIT;
592    }
593
594    /**
595     * Setter to specify the pattern to match against.
596     *
597     * @param pattern the new pattern
598     */
599    public final void setFormat(Pattern pattern) {
600        format = CommonUtil.createPattern(pattern.pattern(), Pattern.MULTILINE);
601    }
602
603    @Override
604    public int[] getDefaultTokens() {
605        return getRequiredTokens();
606    }
607
608    @Override
609    public int[] getAcceptableTokens() {
610        return getRequiredTokens();
611    }
612
613    @Override
614    public int[] getRequiredTokens() {
615        return CommonUtil.EMPTY_INT_ARRAY;
616    }
617
618    @Override
619    public void beginTree(DetailAST rootAST) {
620        matcher = format.matcher(getFileContents().getText().getFullText());
621        matchCount = 0;
622        errorCount = 0;
623        findMatch();
624    }
625
626    /** Recursive method that finds the matches. */
627    private void findMatch() {
628        final boolean foundMatch = matcher.find();
629        if (foundMatch) {
630            final FileText text = getFileContents().getText();
631            final LineColumn start = text.lineColumn(matcher.start());
632            final int startLine = start.getLine();
633
634            final boolean ignore = isIgnore(startLine, text, start);
635
636            if (!ignore) {
637                matchCount++;
638                if (illegalPattern || checkForDuplicates
639                        && matchCount - 1 > duplicateLimit) {
640                    errorCount++;
641                    logMessage(startLine);
642                }
643            }
644            if (canContinueValidation(ignore)) {
645                findMatch();
646            }
647        }
648        else if (!illegalPattern && matchCount == 0) {
649            logMessage(0);
650        }
651    }
652
653    /**
654     * Check if we can stop validation.
655     *
656     * @param ignore flag
657     * @return true is we can continue
658     */
659    private boolean canContinueValidation(boolean ignore) {
660        return errorCount <= errorLimit - 1
661                && (ignore || illegalPattern || checkForDuplicates);
662    }
663
664    /**
665     * Detect ignore situation.
666     *
667     * @param startLine position of line
668     * @param text file text
669     * @param start line column
670     * @return true is that need to be ignored
671     */
672    private boolean isIgnore(int startLine, FileText text, LineColumn start) {
673        final LineColumn end;
674        if (matcher.end() == 0) {
675            end = text.lineColumn(0);
676        }
677        else {
678            end = text.lineColumn(matcher.end() - 1);
679        }
680        boolean ignore = false;
681        if (ignoreComments) {
682            final FileContents theFileContents = getFileContents();
683            final int startColumn = start.getColumn();
684            final int endLine = end.getLine();
685            final int endColumn = end.getColumn();
686            ignore = theFileContents.hasIntersectionWithComment(startLine,
687                startColumn, endLine, endColumn);
688        }
689        return ignore;
690    }
691
692    /**
693     * Displays the right message.
694     *
695     * @param lineNumber the line number the message relates to.
696     */
697    private void logMessage(int lineNumber) {
698        String msg;
699
700        if (message == null || message.isEmpty()) {
701            msg = format.pattern();
702        }
703        else {
704            msg = message;
705        }
706
707        if (errorCount >= errorLimit) {
708            msg = ERROR_LIMIT_EXCEEDED_MESSAGE + msg;
709        }
710
711        if (illegalPattern) {
712            log(lineNumber, MSG_ILLEGAL_REGEXP, msg);
713        }
714        else {
715            if (lineNumber > 0) {
716                log(lineNumber, MSG_DUPLICATE_REGEXP, msg);
717            }
718            else {
719                log(lineNumber, MSG_REQUIRED_REGEXP, msg);
720            }
721        }
722    }
723
724}