/*
* FindBugs - Find bugs in Java programs
* Copyright (C) 2003-2008 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.io.File;
import java.io.IOException;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.dom4j.DocumentException;
import edu.umd.cs.findbugs.annotations.SuppressWarnings;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.AnalysisFeatures;
import edu.umd.cs.findbugs.config.AnalysisFeatureSetting;
import edu.umd.cs.findbugs.config.CommandLine.HelpRequestedException;
import edu.umd.cs.findbugs.filter.Filter;
import edu.umd.cs.findbugs.filter.FilterException;
/**
* Static methods and fields useful for working with instances of
* IFindBugsEngine.
*
* This class was previously the main driver for FindBugs analyses, but has been
* replaced by {@link FindBugs2 FindBugs2}.
*
* @author Bill Pugh
* @author David Hovemeyer
*/
public abstract class FindBugs {
/**
* Analysis settings for -effort:min.
*/
public static final AnalysisFeatureSetting[] MIN_EFFORT = new AnalysisFeatureSetting[] {
new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, true),
new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, false),
new AnalysisFeatureSetting(AnalysisFeatures.MERGE_SIMILAR_WARNINGS, true),
new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, false),
new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, false),
new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, false),
new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, false),
new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, false),
new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), };
/**
* Analysis settings for -effort:less.
*/
public static final AnalysisFeatureSetting[] LESS_EFFORT = new AnalysisFeatureSetting[] {
new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
new AnalysisFeatureSetting(AnalysisFeatures.MERGE_SIMILAR_WARNINGS, true),
new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, false),
new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, false),
new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, false),
new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), };
/**
* Analysis settings for -effort:default.
*/
public static final AnalysisFeatureSetting[] DEFAULT_EFFORT = new AnalysisFeatureSetting[] {
new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
new AnalysisFeatureSetting(AnalysisFeatures.MERGE_SIMILAR_WARNINGS, true),
new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, true),
new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, true),
new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, true),
new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), };
/**
* Analysis settings for -effort:more.
*/
public static final AnalysisFeatureSetting[] MORE_EFFORT = new AnalysisFeatureSetting[] {
new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
new AnalysisFeatureSetting(AnalysisFeatures.MERGE_SIMILAR_WARNINGS, true),
new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, true),
new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, true),
new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, true),
new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), };
/**
* Analysis settings for -effort:max.
*/
public static final AnalysisFeatureSetting[] MAX_EFFORT = new AnalysisFeatureSetting[] {
new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
new AnalysisFeatureSetting(AnalysisFeatures.MERGE_SIMILAR_WARNINGS, true),
new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, false),
new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, true),
new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, true),
new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, true),
new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, true), };
/**
* Debug tracing.
*/
public static final boolean DEBUG = Boolean.getBoolean("findbugs.debug");
/**
* FindBugs home directory.
*/
private static String home = System.getProperty("findbugs.home");
public static boolean noAnalysis = Boolean.getBoolean("findbugs.noAnalysis");
/**
* Disable analysis within FindBugs. Turns off loading of bug detectors.
*/
public static void setNoAnalysis() {
noAnalysis = true;
}
public static final Logger LOGGER = Logger.getLogger(FindBugs.class.getPackage().getName());
static {
LOGGER.setLevel(Level.WARNING);
}
/**
* Known URL protocols. Filename URLs that do not have an explicit protocol
* are assumed to be files.
*/
static public final Set<String> knownURLProtocolSet = new HashSet<String>();
static {
knownURLProtocolSet.add("file");
knownURLProtocolSet.add("http");
knownURLProtocolSet.add("https");
knownURLProtocolSet.add("jar");
}
/**
* Set the FindBugs home directory.
*/
public static void setHome(String home) {
FindBugs.home = home;
}
/**
* Get the FindBugs home directory.
*/
public static String getHome() {
return home;
}
/**
* Configure training databases.
*
* @param findBugs
* the IFindBugsEngine to configure
* @throws IOException
*/
public static void configureTrainingDatabases(IFindBugsEngine findBugs) throws IOException {
if (findBugs.emitTrainingOutput()) {
String trainingOutputDir = findBugs.getTrainingOutputDir();
if (!new File(trainingOutputDir).isDirectory())
throw new IOException("Training output directory " + trainingOutputDir + " does not exist");
AnalysisContext.currentAnalysisContext().setDatabaseOutputDir(trainingOutputDir);
// XXX: hack
System.setProperty("findbugs.checkreturn.savetraining", new File(trainingOutputDir, "checkReturn.db").getPath());
}
if (findBugs.useTrainingInput()) {
String trainingInputDir = findBugs.getTrainingInputDir();
if (!new File(trainingInputDir).isDirectory())
throw new IOException("Training input directory " + trainingInputDir + " does not exist");
AnalysisContext.currentAnalysisContext().setDatabaseInputDir(trainingInputDir);
AnalysisContext.currentAnalysisContext().loadInterproceduralDatabases();
// XXX: hack
System.setProperty("findbugs.checkreturn.loadtraining", new File(trainingInputDir, "checkReturn.db").getPath());
} else {
AnalysisContext.currentAnalysisContext().loadDefaultInterproceduralDatabases();
}
}
/**
* Determines whether or not given DetectorFactory should be enabled.
*
* @param findBugs
* the IFindBugsEngine
* @param factory
* the DetectorFactory
* @param rankThreshold
* TODO
* @return true if the DetectorFactory should be enabled, false otherwise
*/
public static boolean isDetectorEnabled(IFindBugsEngine findBugs, DetectorFactory factory, int rankThreshold) {
if (!findBugs.getUserPreferences().isDetectorEnabled(factory))
return false;
if (!factory.isEnabledForCurrentJRE())
return false;
// Slow first pass detectors are usually disabled, but may be explicitly
// enabled
if (!AnalysisContext.currentAnalysisContext().getBoolProperty(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS)
&& factory.isDetectorClassSubtypeOf(InterproceduralFirstPassDetector.class))
return false;
int maxRank = Integer.MAX_VALUE;
Set<BugPattern> reportedBugPatterns = factory.getReportedBugPatterns();
boolean isNonReportingDetector = factory.isDetectorClassSubtypeOf(FirstPassDetector.class);
if (!isNonReportingDetector && !reportedBugPatterns.isEmpty()) {
for (BugPattern b : reportedBugPatterns) {
int rank = BugRanker.findRank(b, factory);
if (maxRank > rank)
maxRank = rank;
}
if (maxRank > rankThreshold) {
if (false) {
System.out.println("Detector " + factory.getShortName() + " has max rank " + maxRank + ", disabling");
System.out.println("Reports : " + reportedBugPatterns);
}
return false;
}
}
// Training detectors are enabled if, and only if, we are emitting
// training output
boolean isTrainingDetector = factory.isDetectorClassSubtypeOf(TrainingDetector.class);
if (findBugs.emitTrainingOutput()) {
return isTrainingDetector || isNonReportingDetector;
}
if (isTrainingDetector)
return false;
return true;
}
/**
* Process -bugCategories option.
*
* @param categories
* comma-separated list of bug categories
*
* @return Set of categories to be used
*/
public static Set<String> handleBugCategories(String categories) {
// Parse list of bug categories
Set<String> categorySet = new HashSet<String>();
StringTokenizer tok = new StringTokenizer(categories, ",");
while (tok.hasMoreTokens()) {
categorySet.add(tok.nextToken());
}
return categorySet;
}
/**
* Process the command line.
*
* @param commandLine
* the TextUICommandLine object which will parse the command line
* @param argv
* the command line arguments
* @param findBugs
* the IFindBugsEngine to configure
* @throws IOException
* @throws FilterException
*/
public static void processCommandLine(TextUICommandLine commandLine, String[] argv, IFindBugsEngine findBugs)
throws IOException, FilterException {
// Expand option files in command line.
// An argument beginning with "@" is treated as specifying
// the name of an option file.
// Each line of option files are treated as a single argument.
// Blank lines and comment lines (beginning with "#")
// are ignored.
try {
argv = commandLine.expandOptionFiles(argv, true, true);
} catch (HelpRequestedException e) {
showHelp(commandLine);
}
int argCount = 0;
try {
argCount = commandLine.parse(argv);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
showHelp(commandLine);
} catch (HelpRequestedException e) {
showHelp(commandLine);
}
Project project = commandLine.getProject();
for (int i = argCount; i < argv.length; ++i)
project.addFile(argv[i]);
commandLine.handleXArgs();
if (project.getFileCount() == 0) {
System.out.println("No files to be analyzed");
showHelp(commandLine);
}
commandLine.configureEngine(findBugs);
}
/**
* Show -help message.
*
* @param commandLine
*/
@SuppressWarnings("DM_EXIT")
public static void showHelp(TextUICommandLine commandLine) {
showSynopsis();
ShowHelp.showGeneralOptions();
FindBugs.showCommandLineOptions(commandLine);
System.exit(1);
}
/**
* Given a fully-configured IFindBugsEngine and the TextUICommandLine used
* to configure it, execute the analysis.
*
* @param findBugs
* a fully-configured IFindBugsEngine
* @param commandLine
* the TextUICommandLine used to configure the IFindBugsEngine
*/
@SuppressWarnings("DM_EXIT")
public static void runMain(IFindBugsEngine findBugs, TextUICommandLine commandLine) throws IOException {
try {
findBugs.execute();
} catch (InterruptedException e) {
assert false; // should not occur
checkExitCodeFail(commandLine, e);
throw new RuntimeException(e);
} catch (RuntimeException e) {
checkExitCodeFail(commandLine, e);
throw e;
} catch (IOException e) {
checkExitCodeFail(commandLine, e);
throw e;
}
int bugCount = findBugs.getBugCount();
int missingClassCount = findBugs.getMissingClassCount();
int errorCount = findBugs.getErrorCount();
if (!commandLine.quiet() || commandLine.setExitCode()) {
if (bugCount > 0)
System.err.println("Warnings generated: " + bugCount);
if (missingClassCount > 0)
System.err.println("Missing classes: " + missingClassCount);
if (errorCount > 0)
System.err.println("Analysis errors: " + errorCount);
}
if (commandLine.setExitCode()) {
int exitCode = 0;
System.err.println("Calculating exit code...");
if (errorCount > 0) {
exitCode |= ExitCodes.ERROR_FLAG;
System.err.println("Setting 'errors encountered' flag (" + ExitCodes.ERROR_FLAG + ")");
}
if (missingClassCount > 0) {
exitCode |= ExitCodes.MISSING_CLASS_FLAG;
System.err.println("Setting 'missing class' flag (" + ExitCodes.MISSING_CLASS_FLAG + ")");
}
if (bugCount > 0) {
exitCode |= ExitCodes.BUGS_FOUND_FLAG;
System.err.println("Setting 'bugs found' flag (" + ExitCodes.BUGS_FOUND_FLAG + ")");
}
System.err.println("Exit code set to: " + exitCode);
System.exit(exitCode);
}
}
/**
* @param commandLine
* @param e
*/
private static void checkExitCodeFail(TextUICommandLine commandLine, Exception e) {
if (commandLine.setExitCode()) {
e.printStackTrace(System.err);
System.exit(ExitCodes.ERROR_FLAG);
}
}
/**
* Print command line options synopses to stdout.
*/
public static void showCommandLineOptions() {
showCommandLineOptions(new TextUICommandLine());
}
/**
* Print command line options synopses to stdout.
*
* @param commandLine
* the TextUICommandLine whose options should be printed
*/
public static void showCommandLineOptions(TextUICommandLine commandLine) {
commandLine.printUsage(System.out);
}
/**
* Show the overall FindBugs command synopsis.
*/
public static void showSynopsis() {
System.out
.println("Usage: findbugs [general options] -textui [command line options...] [jar/zip/class files, directories...]");
}
/**
* Configure the (bug instance) Filter for the given DelegatingBugReporter.
*
* @param bugReporter
* a DelegatingBugReporter
* @param filterFileName
* filter file name
* @param include
* true if the filter is an include filter, false if it's an
* exclude filter
* @throws java.io.IOException
* @throws edu.umd.cs.findbugs.filter.FilterException
*/
public static BugReporter configureFilter(BugReporter bugReporter, String filterFileName, boolean include)
throws IOException, FilterException {
Filter filter = new Filter(filterFileName);
return new FilterBugReporter(bugReporter, filter, include);
}
/**
* Configure a baseline bug instance filter.
*
* @param bugReporter
* a DelegatingBugReporter
* @param baselineFileName
* filename of baseline Filter
* @throws java.io.IOException
* @throws org.dom4j.DocumentException
*/
public static BugReporter configureBaselineFilter(BugReporter bugReporter, String baselineFileName) throws IOException,
DocumentException {
return new ExcludingHashesBugReporter(bugReporter, baselineFileName);
}
/**
* Configure the BugCollection (if the BugReporter being used is
* constructing one).
*
* @param findBugs
* the IFindBugsEngine
*/
public static void configureBugCollection(IFindBugsEngine findBugs) {
BugCollection bugs = findBugs.getBugReporter().getBugCollection();
if (bugs != null) {
bugs.setReleaseName(findBugs.getReleaseName());
Project project = findBugs.getProject();
String projectName = project.getProjectName();
if (projectName == null) {
projectName = findBugs.getProjectName();
project.setProjectName(projectName);
}
long timestamp = project.getTimestamp();
if (FindBugs.validTimestamp(timestamp)) {
bugs.setTimestamp(timestamp);
bugs.getProjectStats().setTimestamp(timestamp);
}
}
}
/** Date of release of Java 1.0 */
public final static long MINIMUM_TIMESTAMP = new GregorianCalendar(1996, 0, 23).getTime().getTime();
/**
* @param timestamp
* @return
*/
public static boolean validTimestamp(long timestamp) {
return timestamp > MINIMUM_TIMESTAMP;
}
}
// vim:ts=4