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  }