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.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 single-line 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 < symbol you need to enter &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 * <module name="Regexp"> 149 * <property name="format" value="// This code is copyrighted"/> 150 * </module> 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 * <module name="Regexp"> 164 * <property name="format" value="// This code is copyrighted\n// \(c\) MyCompany"/> 165 * </module> 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 * <module name="Regexp"> 177 * <property name="format" value="// This code is copyrighted\n// \(c\) MyCompany"/> 178 * <property name="duplicateLimit" value="0"/> 179 * </module> 180 * </pre> 181 * <p> 182 * It can also be useful to attach a meaningful message to the check: 183 * </p> 184 * <pre> 185 * <module name="Regexp"> 186 * <property name="format" value="// This code is copyrighted\n// \(c\) MyCompany"/> 187 * <property name="message" value="Copyright"/> 188 * </module> 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 * <module name="Regexp"> 199 * <!-- . matches any character, so we need to escape it and use \. to match dots. --> 200 * <property name="format" value="System\.out\.println"/> 201 * <property name="illegalPattern" value="true"/> 202 * </module> 203 * </pre> 204 * <p> 205 * You may want to make the above check ignore comments, like this: 206 * </p> 207 * <pre> 208 * <module name="Regexp"> 209 * <property name="format" value="System\.out\.println"/> 210 * <property name="illegalPattern" value="true"/> 211 * <property name="ignoreComments" value="true"/> 212 * </module> 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 * <module name="Regexp"> 219 * <property name="format" value="[ \t]+$"/> 220 * <property name="illegalPattern" value="true"/> 221 * <property name="message" value="Trailing whitespace"/> 222 * </module> 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 * <module name="Regexp"> 229 * <property name="format" value="(?i)debug"/> 230 * <property name="illegalPattern" value="true"/> 231 * </module> 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 * <module name="Regexp"> 245 * <property name="format" value="(?i)debug"/> 246 * <property name="illegalPattern" value="true"/> 247 * <property name="errorLimit" value="1000"/> 248 * </module> 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 * <module name="Regexp"> 274 * <property 275 * name="format" 276 * value="\A// Copyright \(C\) \d\d\d\d MyCompany\n// All rights reserved"/> 277 * </module> 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 * /** 298 * * javadoc line 1 299 * * javadoc line 2 300 * * javadoc line 3 301 * */ 302 * </pre> 303 * <pre> 304 * <module name="Regexp"> 305 * <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)* \*/"/> 311 * </module> 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 * /* 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 * */ 352 * 353 * package com.acme.tools; 354 * 355 * import com.acme.thing1; 356 * import com.acme.thing2; 357 * import com.acme.thing3; 358 * 359 * /** 360 * * 361 * * <P> 362 * * <I>This software is the confidential and proprietary information of 363 * * ACME (<B>"Confidential Information"</B>). 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.</I> 367 * * </P> 368 * * 369 * * &#169; copyright 2002 ACME 370 * * 371 * * @author Some Body 372 * */ 373 * public class PID extends StateMachine implements WebObject.Constants { 374 * 375 * /** javadoc. */ 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 * <module name="Regexp"> 399 * <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 \* \(&quot;Confidential Information&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)"/> 425 * <property name="message" value="Correct header not found"/> 426 * </module> 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 * <module name="Regexp"> 433 * <property 434 * name="format" 435 * value="(/\*\*\n)( \*.*\n)*( \* <P>\n \* <I> 436 * This software is the confidential and proprietary information of\n 437 * \* ACME \(<B>&quot;Confidential Information&quot;</B> 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\.</I>\n 441 * \* </P>\n \*\n \* &#169; copyright \d\d\d\d ACME\n 442 * \*\n \* @author .*)(\n\s\*.*)*/\n[\w|\s]*( class | interface )"/> 443 * <property name="message" 444 * value="Copyright in class/interface Javadoc"/> 445 * <property name="duplicateLimit" value="0"/> 446 * </module> 447 * </pre> 448 * <p> 449 * <b>Note:</b> To search for things that mean something in XML, like < 450 * you need to escape them like &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 // suppress deprecation until https://github.com/checkstyle/checkstyle/issues/11166 619 @SuppressWarnings("deprecation") 620 @Override 621 public void beginTree(DetailAST rootAST) { 622 matcher = format.matcher(getFileContents().getText().getFullText()); 623 matchCount = 0; 624 errorCount = 0; 625 findMatch(); 626 } 627 628 /** Recursive method that finds the matches. */ 629 // suppress deprecation until https://github.com/checkstyle/checkstyle/issues/11166 630 @SuppressWarnings("deprecation") 631 private void findMatch() { 632 final boolean foundMatch = matcher.find(); 633 if (foundMatch) { 634 final FileText text = getFileContents().getText(); 635 final LineColumn start = text.lineColumn(matcher.start()); 636 final int startLine = start.getLine(); 637 638 final boolean ignore = isIgnore(startLine, text, start); 639 640 if (!ignore) { 641 matchCount++; 642 if (illegalPattern || checkForDuplicates 643 && matchCount - 1 > duplicateLimit) { 644 errorCount++; 645 logMessage(startLine); 646 } 647 } 648 if (canContinueValidation(ignore)) { 649 findMatch(); 650 } 651 } 652 else if (!illegalPattern && matchCount == 0) { 653 logMessage(0); 654 } 655 } 656 657 /** 658 * Check if we can stop validation. 659 * 660 * @param ignore flag 661 * @return true is we can continue 662 */ 663 private boolean canContinueValidation(boolean ignore) { 664 return errorCount <= errorLimit - 1 665 && (ignore || illegalPattern || checkForDuplicates); 666 } 667 668 /** 669 * Detect ignore situation. 670 * 671 * @param startLine position of line 672 * @param text file text 673 * @param start line column 674 * @return true is that need to be ignored 675 */ 676 // suppress deprecation until https://github.com/checkstyle/checkstyle/issues/11166 677 @SuppressWarnings("deprecation") 678 private boolean isIgnore(int startLine, FileText text, LineColumn start) { 679 final LineColumn end; 680 if (matcher.end() == 0) { 681 end = text.lineColumn(0); 682 } 683 else { 684 end = text.lineColumn(matcher.end() - 1); 685 } 686 boolean ignore = false; 687 if (ignoreComments) { 688 final FileContents theFileContents = getFileContents(); 689 final int startColumn = start.getColumn(); 690 final int endLine = end.getLine(); 691 final int endColumn = end.getColumn(); 692 ignore = theFileContents.hasIntersectionWithComment(startLine, 693 startColumn, endLine, endColumn); 694 } 695 return ignore; 696 } 697 698 /** 699 * Displays the right message. 700 * 701 * @param lineNumber the line number the message relates to. 702 */ 703 private void logMessage(int lineNumber) { 704 String msg; 705 706 if (message == null || message.isEmpty()) { 707 msg = format.pattern(); 708 } 709 else { 710 msg = message; 711 } 712 713 if (errorCount >= errorLimit) { 714 msg = ERROR_LIMIT_EXCEEDED_MESSAGE + msg; 715 } 716 717 if (illegalPattern) { 718 log(lineNumber, MSG_ILLEGAL_REGEXP, msg); 719 } 720 else { 721 if (lineNumber > 0) { 722 log(lineNumber, MSG_DUPLICATE_REGEXP, msg); 723 } 724 else { 725 log(lineNumber, MSG_REQUIRED_REGEXP, msg); 726 } 727 } 728 } 729 730}