/*
 * FindBugs - Find bugs in Java programs
 * Copyright (C) 2004, University of Maryland
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs;

import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Class to pre-screen class files, so that only a subset are analyzed. This supports the -onlyAnalyze command line option. Modified February 2006 in four ways: a) don't break windows platform by hard-coding '/' as the directory separator b) store list of Matchers, not Patterns, so we don't keep instantiating Matchers c) fix suffix bug, so FooBar and Foo$Bar no longer match Bar d) addAllowedPackage() can now handle unicode chars in filenames, though we still may not be handling every case mentioned in section 7.2.1 of the JLS
Author:David Hovemeyer
See Also:
  • FindBugs
/** * Class to pre-screen class files, so that only a subset are analyzed. This * supports the -onlyAnalyze command line option. * * Modified February 2006 in four ways: a) don't break windows platform by * hard-coding '/' as the directory separator b) store list of Matchers, not * Patterns, so we don't keep instantiating Matchers c) fix suffix bug, so * FooBar and Foo$Bar no longer match Bar d) addAllowedPackage() can now handle * unicode chars in filenames, though we still may not be handling every case * mentioned in section 7.2.1 of the JLS * * @see FindBugs * @author David Hovemeyer */
public class ClassScreener implements IClassScreener { private static final boolean DEBUG = SystemProperties.getBoolean("findbugs.classscreener.debug");
regular expression fragment to match a directory separator. note: could use File.separatorChar instead, but that could be argued to be not general enough
/** * regular expression fragment to match a directory separator. note: could * use File.separatorChar instead, but that could be argued to be not * general enough */
private static final String SEP = "[/\\\\]"; // could include ':' for // classic macOS private static final String START = "(?:^|" + SEP + ")"; // (?:) is a // non-capturing // group
regular expression fragment to match a char of a class or package name. Actually, we just allow any char except a dot or a directory separator.
/** * regular expression fragment to match a char of a class or package name. * Actually, we just allow any char except a dot or a directory separator. */
private static final String JAVA_IDENTIFIER_PART = "[^./\\\\]"; private final LinkedList<Matcher> patternList;
Constructor. By default, the ClassScreener will match all class files. Once addAllowedClass() and addAllowedPackage() are called, the ClassScreener will only match the explicitly specified classes and packages.
/** * Constructor. By default, the ClassScreener will match <em>all</em> class * files. Once addAllowedClass() and addAllowedPackage() are called, the * ClassScreener will only match the explicitly specified classes and * packages. */
public ClassScreener() { this.patternList = new LinkedList<Matcher>(); }
replace the dots in a fully-qualified class/package name to a regular expression fragment that will match file names.
Params:
  • dotsName – such as "java.io" or "java.io.File"
Returns:regex fragment such as "java[/\\\\]io" (single backslash escaped twice)
/** * replace the dots in a fully-qualified class/package name to a regular * expression fragment that will match file names. * * @param dotsName * such as "java.io" or "java.io.File" * @return regex fragment such as "java[/\\\\]io" (single backslash escaped * twice) */
private static String dotsToRegex(String dotsName) { /* * oops, next line requires JDK 1.5 return dotsName.replace("$", * "\\$").replace(".", SEP); could use String.replaceAll(regex, repl) * but that can be problematic--javadoc says "Note that backslashes (\) * and dollar signs ($) in the replacement string may cause the results * to be different than if it were being treated as a literal * replacement" */ String tmp = dotsName.replace("$", "\\$"); return tmp.replace(".", SEP); // note: The original code used the \Q and \E regex quoting constructs // to escape $. }
Add the name of a class to be matched by the screener.
Params:
  • className – name of a class to be matched
/** * Add the name of a class to be matched by the screener. * * @param className * name of a class to be matched */
public void addAllowedClass(String className) { String classRegex = START + dotsToRegex(className) + ".class$"; if (DEBUG) { System.out.println("Class regex: " + classRegex); } patternList.add(Pattern.compile(classRegex).matcher("")); }
Add the name of a package to be matched by the screener. All class files that appear to be in the package should be matched.
Params:
  • packageName – name of the package to be matched
/** * Add the name of a package to be matched by the screener. All class files * that appear to be in the package should be matched. * * @param packageName * name of the package to be matched */
public void addAllowedPackage(String packageName) { if (packageName.endsWith(".")) { packageName = packageName.substring(0, packageName.length() - 1); } String packageRegex = START + dotsToRegex(packageName) + SEP + JAVA_IDENTIFIER_PART + "+.class$"; if (DEBUG) { System.out.println("Package regex: " + packageRegex); } patternList.add(Pattern.compile(packageRegex).matcher("")); }
Add the name of a prefix to be matched by the screener. All class files that appear to be in the package specified by the prefix, or a more deeply nested package, should be matched.
Params:
  • prefix – name of the prefix to be matched
/** * Add the name of a prefix to be matched by the screener. All class files * that appear to be in the package specified by the prefix, or a more * deeply nested package, should be matched. * * @param prefix * name of the prefix to be matched */
public void addAllowedPrefix(String prefix) { if (prefix.endsWith(".")) { prefix = prefix.substring(0, prefix.length() - 1); } if (DEBUG) { System.out.println("Allowed prefix: " + prefix); } String packageRegex = START + dotsToRegex(prefix) + SEP; if (DEBUG) { System.out.println("Prefix regex: " + packageRegex); } patternList.add(Pattern.compile(packageRegex).matcher("")); } /* * (non-Javadoc) * * @see edu.umd.cs.findbugs.IClassScreener#matches(java.lang.String) */ @Override public boolean matches(String fileName) { // Special case: if no classes or packages have been defined, // then the screener matches all class files. if (patternList.isEmpty()) { return true; } if (DEBUG) { System.out.println("Matching: " + fileName); } // Scan through list of regexes for (Matcher matcher : patternList) { if (DEBUG) { System.out.print("\tTrying [" + matcher.pattern()); } matcher.reset(fileName); if (matcher.find()) { if (DEBUG) { System.out.println("]: yes!"); } return true; } if (DEBUG) { System.out.println("]: no"); } } return false; } /* * (non-Javadoc) * * @see edu.umd.cs.findbugs.IClassScreener#vacuous() */ @Override public boolean vacuous() { return patternList.isEmpty(); } }