View Javadoc

    1   /**
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.util.filter;
    5
    6   import java.util.regex.Matcher;
    7   import java.util.regex.Pattern;
    8   import java.util.regex.PatternSyntaxException;
    9
    10  /**
    11   * A filter which uses a regular expression to match Strings. Invalid regular
    12   * expressions will match nothing.
    13   * <p>
    14   * Because regular expression matching is slow, and a common usage is to match
    15   * some sort of relative file path, the regular expression is checked to see if
    16   * it can be evaluated using much faster calls to
    17   * {@link String#endsWith(String)}.
    18   */
    19  public class RegexStringFilter implements Filter<String> {
    20      /**
    21       * Matches regular expressions begin with an optional {@code ^}, then
    22       * {@code .*}, then a literal path, with an optional file extension, and
    23       * finally an optional {@code $} at the end. The {@code .} in the extension
    24       * may or may not be preceded by a {@code \} escape. The literal path
    25       * portion is determine by the absence of any of the following characters:
    26       * <code>\ [ ( . * ? + | { $</code>
    27       *
    28       * There are two capturing groups in the expression. The first is for the
    29       * literal path. The second is for the file extension, without the escaping.
    30       * The concatenation of these two captures creates the {@link String} which
    31       * can be used with {@link String#endsWith(String)}.
    32       *
    33       * For ease of reference, the non-Java escaped form of this pattern is:
    34       * <code>\^?\.\*([^\\\[\(\.\*\?\+\|\{\$]+)(?:\\?(\.\w+))?\$?</code>
    35       */
    36      private static final Pattern ENDS_WITH = Pattern
    37  	    .compile("\\^?\\.\\*([^\\\\\\[\\(\\.\\*\\?\\+\\|\\{\\$]+)(?:\\\\?(\\.\\w+))?\\$?");
    38
    39      protected String regex;
    40      protected Pattern pattern;
    41      protected String endsWith;
    42
    43      public RegexStringFilter(String regex) {
    44  	this.regex = regex;
    45  	optimize();
    46      }
    47
    48      public String getRegex() {
    49  	return this.regex;
    50      }
    51
    52      public String getEndsWith() {
    53  	return this.endsWith;
    54      }
    55
    56      protected void optimize() {
    57  	final Matcher matcher = ENDS_WITH.matcher(this.regex);
    58  	if (matcher.matches()) {
    59  	    final String literalPath = matcher.group(1);
    60  	    final String fileExtension = matcher.group(2);
    61  	    if (fileExtension != null) {
    62  		this.endsWith = literalPath + fileExtension;
    63  	    } else {
    64  		this.endsWith = literalPath;
    65  	    }
    66  	} else {
    67  	    try {
    68  		this.pattern = Pattern.compile(this.regex);
    69  	    } catch (PatternSyntaxException e) {
    70  		// If the regular expression is invalid, then pattern will be null.
    71  	    }
    72  	}
    73      }
    74
    75      public boolean filter(String obj) {
    76  	if (this.endsWith != null) {
    77  	    return obj.endsWith(this.endsWith);
    78  	} else if (this.pattern != null) {
    79  	    return this.pattern.matcher(obj).matches();
    80  	} else {
    81  	    // The regular expression must have been bad, so it will match nothing.
    82  	    return false;
    83  	}
    84      }
    85
    86      @Override
    87      public String toString() {
    88  	return "matches " + this.regex;
    89      }
    90  }