/*
* FindBugs - Find bugs in Java programs
* Copyright (C) 2003-2005 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.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import edu.umd.cs.findbugs.ba.AnalysisContext;
/**
* A DetectorFactory is responsible for creating instances of Detector objects
* and for maintaining meta-information about the detector class.
*
* @author David Hovemeyer
* @see Detector
*/
public class DetectorFactory {
private static final boolean DEBUG_JAVA_VERSION = SystemProperties.getBoolean("findbugs.debug.javaversion");
private static final Class<?>[] constructorArgTypes = new Class<?>[]{BugReporter.class};
static class ReflectionDetectorCreator {
private Class<?> detectorClass;
private Method setAnalysisContext;
ReflectionDetectorCreator(Class<?> detectorClass) {
this.detectorClass = detectorClass;
try {
setAnalysisContext = detectorClass.getDeclaredMethod(
"setAnalysisContext", new Class[]{AnalysisContext.class});
} catch (NoSuchMethodException e) {
// Ignore
}
}
public Detector createDetector(BugReporter bugReporter) {
try {
Constructor<?> constructor =
detectorClass.getConstructor(constructorArgTypes);
Detector detector = (Detector) constructor.newInstance(new Object[]{bugReporter});
if (setAnalysisContext != null) {
setAnalysisContext.invoke(
detector,
new Object[]{AnalysisContext.currentAnalysisContext()});
}
return detector;
} catch (Exception e) {
throw new RuntimeException("Could not instantiate " + detectorClass.getName() +
" as Detector", e);
}
}
public Detector2 createDetector2(BugReporter bugReporter) {
if (Detector2.class.isAssignableFrom(detectorClass)) {
try {
Constructor<?> constructor =
detectorClass.getConstructor(constructorArgTypes);
return (Detector2) constructor.newInstance(new Object[]{bugReporter});
} catch (Exception e) {
throw new RuntimeException("Could not instantiate " + detectorClass.getName() +
" as Detector2", e);
}
}
if (Detector.class.isAssignableFrom(detectorClass)) {
if (NonReportingDetector.class.isAssignableFrom(detectorClass))
return new NonReportingDetectorToDetector2Adapter(createDetector(bugReporter));
return new DetectorToDetector2Adapter(
createDetector(bugReporter));
}
throw new RuntimeException("Class " + detectorClass.getName() + " is not a detector class");
}
public Class<?> getDetectorClass() {
return detectorClass;
}
}
private Plugin plugin;
private final ReflectionDetectorCreator detectorCreator;
private int positionSpecifiedInPluginDescriptor;
private boolean defEnabled;
private final String speed;
private final String reports;
private final String requireJRE;
private String detailHTML;
private int priorityAdjustment;
private boolean enabledButNonReporting;
private boolean hidden;
/**
* Constructor.
*
* @param plugin the Plugin the Detector is part of
* @param detectorClass the Class object of the Detector
* @param enabled true if the Detector is enabled by default, false if disabled
* @param speed a string describing roughly how expensive the analysis performed
* by the detector is; suggested values are "fast", "moderate", and "slow"
* @param reports comma separated list of bug pattern codes reported
* by the detector; empty if unknown
* @param requireJRE string describing JRE version required to run the
* the detector: e.g., "1.5"
*/
public DetectorFactory(Plugin plugin,
Class<?> detectorClass, boolean enabled, String speed, String reports,
String requireJRE) {
this.plugin = plugin;
this.detectorCreator = new ReflectionDetectorCreator(detectorClass);
this.defEnabled = enabled;
this.speed = speed;
this.reports = reports;
this.requireJRE = requireJRE;
this.priorityAdjustment = 0;
this.hidden = false;
}
/**
* Set the overall position in which this detector was specified
* in the plugin descriptor.
*
* @param positionSpecifiedInPluginDescriptor position in plugin descriptor
*/
public void setPositionSpecifiedInPluginDescriptor(
int positionSpecifiedInPluginDescriptor) {
this.positionSpecifiedInPluginDescriptor = positionSpecifiedInPluginDescriptor;
}
/**
* Get the overall position in which this detector was specified
* in the plugin descriptor.
*
* @return position in plugin descriptor
*/
public int getPositionSpecifiedInPluginDescriptor() {
return positionSpecifiedInPluginDescriptor;
}
/**
* Get the Plugin that this Detector is part of.
*
* @return the Plugin this Detector is part of
*/
public Plugin getPlugin() {
return plugin;
}
/**
* Determine whether the detector class is a subtype of the given class (or interface).
*
* @param otherClass a class or interface
* @return true if the detector class is a subtype of the given class or interface
*/
public boolean isDetectorClassSubtypeOf(Class<?> otherClass) {
return otherClass.isAssignableFrom(detectorCreator.getDetectorClass());
}
/**
* Return whether or not this DetectorFactory produces detectors
* which report warnings.
*
* @return true if the created Detectors report warnings, false if not
*/
public boolean isReportingDetector() {
return !isDetectorClassSubtypeOf(TrainingDetector.class)
&& !isDetectorClassSubtypeOf(FirstPassDetector.class);
}
/**
* Check to see if we are running on a recent-enough JRE for
* this detector to be enabled.
*
* @return true if the current JRE is recent enough to run the Detector,
* false if it is too old
*/
public boolean isEnabledForCurrentJRE() {
if (requireJRE.equals(""))
return true;
try {
JavaVersion requiredVersion = new JavaVersion(requireJRE);
JavaVersion runtimeVersion = JavaVersion.getRuntimeVersion();
if (DEBUG_JAVA_VERSION) {
System.out.println(
"Checking JRE version for " + getShortName() +
" (requires " + requiredVersion +
", running on " + runtimeVersion + ")");
}
boolean enabledForCurrentJRE = runtimeVersion.isSameOrNewerThan(requiredVersion);
if (DEBUG_JAVA_VERSION) {
System.out.println("\t==> " + enabledForCurrentJRE);
}
return enabledForCurrentJRE;
} catch (JavaVersionException e) {
if (DEBUG_JAVA_VERSION) {
System.out.println("Couldn't check Java version: " + e.toString());
e.printStackTrace(System.out);
}
return false;
}
}
/**
* Set visibility of the factory (to GUI dialogs to configure detectors).
* Invisible detectors are those that are needed behind the scenes,
* but shouldn't be explicitly enabled or disabled by the user.
*
* @param hidden true if this factory should be hidden, false if not
*/
public void setHidden(boolean hidden) {
this.hidden = hidden;
}
/**
* Get visibility of the factory (to GUI dialogs to configure detectors).
*/
public boolean isHidden() {
return hidden;
}
/**
* Is this factory enabled by default
*/
public boolean isDefaultEnabled() {
return defEnabled;
}
/**
* Set the priority adjustment for the detector produced by this factory.
*
* @param priorityAdjustment the priority adjustment
*/
public void setPriorityAdjustment(int priorityAdjustment) {
this.priorityAdjustment = priorityAdjustment;
}
public void setEnabledButNonReporting(boolean notReporting) {
this.enabledButNonReporting = notReporting;
}
/**
* Get the priority adjustment for the detector produced by this factory.
*
* @return the priority adjustment
*/
public int getPriorityAdjustment() {
if (enabledButNonReporting) return 100;
return priorityAdjustment;
}
/**
* Get the speed of the Detector produced by this factory.
*/
public String getSpeed() {
return speed;
}
/**
* Get list of bug pattern codes reported by the detector: blank if unknown.
*/
public String getReportedBugPatternCodes() {
return reports;
}
/**
* Get set of all BugPatterns this detector reports.
* An empty set means that we don't know what kind of
* bug patterns might be reported.
*/
public Set<BugPattern> getReportedBugPatterns() {
Set<BugPattern> result = new TreeSet<BugPattern>();
StringTokenizer tok = new StringTokenizer(reports, ",");
while (tok.hasMoreTokens()) {
String type = tok.nextToken();
BugPattern bugPattern = I18N.instance().lookupBugPattern(type);
if (bugPattern != null)
result.add(bugPattern);
}
return result;
}
/**
* Get an HTML document describing the Detector.
*/
public String getDetailHTML() {
return detailHTML;
}
/**
* Set the HTML document describing the Detector.
*/
public void setDetailHTML(String detailHTML) {
this.detailHTML = detailHTML;
}
/**
* Create a Detector instance.
* This method is only guaranteed to work for
* old-style detectors using the BCEL bytecode framework.
*
* @param bugReporter the BugReporter to be used to report bugs
* @return the Detector
* @deprecated Use createDetector2 in new code
*/
@Deprecated
public Detector create(BugReporter bugReporter) {
return detectorCreator.createDetector(bugReporter);
}
/**
* Create a Detector2 instance.
*
* @param bugReporter the BugReporter to be used to report bugs
* @return the Detector2
*/
public Detector2 createDetector2(BugReporter bugReporter) {
return detectorCreator.createDetector2(bugReporter);
}
/**
* Get the short name of the Detector.
* This is the name of the detector class without the package qualification.
*/
public String getShortName() {
String className = detectorCreator.getDetectorClass().getName();
int endOfPkg = className.lastIndexOf('.');
if (endOfPkg >= 0)
className = className.substring(endOfPkg + 1);
return className;
}
/**
* Get the full name of the detector.
* This is the name of the detector class, with package qualification.
*/
public String getFullName() {
return detectorCreator.getDetectorClass().getName();
}
}
// vim:ts=4