/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.util.filter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* A filter which uses a regular expression to match Strings. Invalid regular
* expressions will match nothing.
* <p>
* Because regular expression matching is slow, and a common usage is to match
* some sort of relative file path, the regular expression is checked to see if
* it can be evaluated using much faster calls to
* {@link String#endsWith(String)}.
*/
public class RegexStringFilter implements Filter<String> {
/**
* Matches regular expressions begin with an optional {@code ^}, then
* {@code .*}, then a literal path, with an optional file extension, and
* finally an optional {@code $} at the end. The {@code .} in the extension
* may or may not be preceded by a {@code \} escape. The literal path
* portion is determine by the absence of any of the following characters:
* <code>\ [ ( . * ? + | { $</code>
*
* There are two capturing groups in the expression. The first is for the
* literal path. The second is for the file extension, without the escaping.
* The concatenation of these two captures creates the {@link String} which
* can be used with {@link String#endsWith(String)}.
*
* For ease of reference, the non-Java escaped form of this pattern is:
* <code>\^?\.\*([^\\\[\(\.\*\?\+\|\{\$]+)(?:\\?(\.\w+))?\$?</code>
*/
private static final Pattern ENDS_WITH = Pattern
.compile("\\^?\\.\\*([^\\\\\\[\\(\\.\\*\\?\\+\\|\\{\\$]+)(?:\\\\?(\\.\\w+))?\\$?");
protected String regex;
protected Pattern pattern;
protected String endsWith;
public RegexStringFilter(String regex) {
this.regex = regex;
optimize();
}
public String getRegex() {
return this.regex;
}
public String getEndsWith() {
return this.endsWith;
}
protected void optimize() {
final Matcher matcher = ENDS_WITH.matcher(this.regex);
if (matcher.matches()) {
final String literalPath = matcher.group(1);
final String fileExtension = matcher.group(2);
if (fileExtension != null) {
this.endsWith = literalPath + fileExtension;
} else {
this.endsWith = literalPath;
}
} else {
try {
this.pattern = Pattern.compile(this.regex);
} catch (PatternSyntaxException e) {
// If the regular expression is invalid, then pattern will be
// null.
}
}
}
@Override
public boolean filter(String obj) {
if (this.endsWith != null) {
return obj.endsWith(this.endsWith);
} else if (this.pattern != null) {
return this.pattern.matcher(obj).matches();
} else {
// The regular expression must have been bad, so it will match
// nothing.
return false;
}
}
@Override
public String toString() {
return "matches " + this.regex;
}
}