blob: 4ee824673a5c98e814b9845eb9224e50e35f3c77 [file] [log] [blame]
/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.util.regexfilter;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* This class implements filters that are configured with a sequence of regexes.
* Each regex in the sequence can be preceded by a + or a - to indicate whether
* it indicates that queries matching the regex should be included or excluded.
* Concrete subclasses indicate the default behaviors by overriding
* {@link #acceptByDefault()} and {@link #entriesArePositiveByDefault()}.
*/
public abstract class RegexFilter {
private final List<String> values;
private final ArrayList<Pattern> typePatterns;
private final ArrayList<Boolean> includeType;
public RegexFilter(TreeLogger logger, List<String> values)
throws UnableToCompleteException {
this.values = values;
int size = values.size();
typePatterns = new ArrayList<Pattern>(size);
includeType = new ArrayList<Boolean>(size);
// TODO investigate grouping multiple patterns into a single regex
for (String regex : values) {
// Patterns that don't start with [+-] are considered to be [-]
boolean include = entriesArePositiveByDefault();
// Ignore empty regexes
if (regex.length() == 0) {
logger.log(TreeLogger.ERROR, "Got empty blacklist entry");
throw new UnableToCompleteException();
}
char c = regex.charAt(0);
if (c == '+' || c == '-') {
regex = regex.substring(1); // skip initial character
include = (c == '+');
}
try {
Pattern p = Pattern.compile(regex);
typePatterns.add(p);
includeType.add(include);
if (logger.isLoggable(TreeLogger.DEBUG)) {
logger.log(TreeLogger.DEBUG, "Got filter entry '" + regex + "'");
}
} catch (PatternSyntaxException e) {
logger.log(TreeLogger.ERROR, "Got malformed filter entry '" + regex
+ "'");
throw new UnableToCompleteException();
}
}
}
public boolean isIncluded(TreeLogger logger, String query) {
logger = logger.branch(TreeLogger.DEBUG, "Considering query " + query);
// Process patterns in reverse order for early exit
int size = typePatterns.size();
for (int idx = size - 1; idx >= 0; idx--) {
if (logger.isLoggable(TreeLogger.DEBUG)) {
logger.log(TreeLogger.DEBUG, "Considering filter rule "
+ values.get(idx) + " for query " + query);
}
boolean include = includeType.get(idx);
Pattern pattern = typePatterns.get(idx);
if (pattern.matcher(query).matches()) {
if (include) {
if (logger.isLoggable(TreeLogger.DEBUG)) {
logger.log(TreeLogger.DEBUG, "Whitelisting " + query
+ " according to rule " + values.get(idx));
}
return true;
} else {
if (logger.isLoggable(TreeLogger.DEBUG)) {
logger.log(TreeLogger.DEBUG, "Blacklisting " + query
+ " according to rule " + values.get(idx));
}
return false;
}
}
}
// None of the patterns matched
return acceptByDefault();
}
/**
* If no pattern matches, whether the query should be considered as an accept.
*/
protected abstract boolean acceptByDefault();
/**
* If a pattern is not preceded by + or -, whether the query should be
* considered positive.
*/
protected abstract boolean entriesArePositiveByDefault();
}