/*
* FindBugs Eclipse Plug-in.
* Copyright (C) 2003 - 2004, Peter Friese
* Copyright (C) 2004-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 de.tobject.findbugs.reporter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IOpenable;
import org.eclipse.jdt.core.IParent;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.compiler.IScanner;
import org.eclipse.jdt.core.compiler.ITerminalSymbols;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.internal.core.CompilationUnit;
import org.eclipse.jdt.internal.core.JavaElement;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IEditorPart;
import de.tobject.findbugs.FindBugsJob;
import de.tobject.findbugs.FindbugsPlugin;
import de.tobject.findbugs.builder.WorkItem;
import de.tobject.findbugs.marker.FindBugsMarker;
import de.tobject.findbugs.view.explorer.BugGroup;
import de.tobject.findbugs.view.explorer.IFilterGui;
import edu.umd.cs.findbugs.BugCode;
import edu.umd.cs.findbugs.BugCollection;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugPattern;
import edu.umd.cs.findbugs.ClassAnnotation;
import edu.umd.cs.findbugs.FieldAnnotation;
import edu.umd.cs.findbugs.I18N;
import edu.umd.cs.findbugs.PackageMemberAnnotation;
import edu.umd.cs.findbugs.SortedBugCollection;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.config.ProjectFilterSettings;
/**
* Utility methods for converting FindBugs BugInstance objects
* into Eclipse markers.
*
* @author Peter Friese
* @author David Hovemeyer
*/
public final class MarkerUtil {
final static Pattern fullName = Pattern.compile("^(.+?)(([$+][0-9].*)?)");
private static final IMarker[] EMPTY = new IMarker[0];
private static final int START_LINE_OF_ENCLOSING_TYPE = 1;
private static final List<IMarkerFilter> markerFilters = new ArrayList<IMarkerFilter>();
private static final Map<String,FilterData> filterMap =
new HashMap<String, FilterData>();
/**
* Data class
*/
public static class FilterData
{
public final IMarkerFilter filter;
public boolean enabled = false;
public IFilterGui filterGui = null;
/**
* cTor
* @param xiFilter
* @param xiFilterStatus
*/
public FilterData(IMarkerFilter xiFilter) {
filter = xiFilter;
}
}
/**
* don't instantiate an utility class
*/
private MarkerUtil() {
super();
}
/**
* Associate the GUI element with a filter
* @param xiFilterID
* @param filterGui
*/
public synchronized static void setFilterGui(String xiFilterID, IFilterGui filterGui)
{
filterMap.get(xiFilterID).filterGui = filterGui;
}
/**
* Enable a set of filters
* @param ids
*/
public synchronized static void enableFilters(String[] ids)
{
for (String id : ids)
{
FilterData filterdata = filterMap.get(id);
if (filterdata != null)
{
filterdata.enabled = true;
if (filterdata.filterGui != null)
{
filterdata.filterGui.setChecked(true);
}
filterdata.filter.resetFilter();
addMarkerFilter(filterdata.filter);
}
}
}
/**
* Disable a set of filters
* @param ids
*/
public synchronized static void disableFilters(String[] ids)
{
for (String id : ids)
{
FilterData filterdata = filterMap.get(id);
if (filterdata != null)
{
filterdata.enabled = false;
if (filterdata.filterGui != null)
{
filterdata.filterGui.setChecked(false);
}
removeMarkerFilter(filterdata.filter);
}
}
}
/**
* @return List of all active filter IDs
*/
public static List<String> getFilterIDs()
{
return new ArrayList<String>(filterMap.keySet());
}
/**
* @param xiFilterID
* @return Name of the filter with the given name.
*/
public static String getFilterName(String xiFilterID) {
return filterMap.get(xiFilterID).filter.getName();
}
/**
* @param xiFilterID
* @return True if the filterID is enabled
*/
public synchronized static boolean isFilterEnabled(String xiFilterID) {
return filterMap.get(xiFilterID).enabled;
}
/**
* Add the filter to the filter ID map
* @param filterID
* @param xiFilter
*/
public static void putInFilterMap(String filterID, FilterData xiFilter) {
filterMap.put(filterID, xiFilter);
}
/**
* Clear filter ID map and active filters list
*/
public static void clearExtensionFilters()
{
filterMap.clear();
markerFilters.clear();
}
/**
* Public method visible from other plugins to allow Marker Filters to be added.
* @param filter Filter to add.
*/
private static void addMarkerFilter(IMarkerFilter filter) {
if (!markerFilters.contains(filter))
{
markerFilters.add(filter);
}
}
/**
* Public method visible from other plugins to allow Marker Filters to be removed.
* @param filter Filter to removed.
*/
private static void removeMarkerFilter(IMarkerFilter filter) {
markerFilters.remove(filter);
}
/**
* Create an Eclipse marker for given BugInstance.
*
* @param javaProject the project
* @param monitor
*/
public static void createMarkers(IJavaProject javaProject, BugCollection theCollection,
IProgressMonitor monitor) {
if(monitor.isCanceled()){
return;
}
List<MarkerParameter> bugParameters = createBugParameters(javaProject, theCollection,
monitor);
if(monitor.isCanceled()){
return;
}
IProject project = javaProject.getProject();
try {
project.getWorkspace().run(
new MarkerReporter(bugParameters, theCollection, project), // action
project, // scheduling rule (null if there are no scheduling restrictions)
0, // flags (could specify IWorkspace.AVOID_UPDATE)
monitor); // progress monitor (null if progress reporting is not desired)
} catch (CoreException e) {
FindbugsPlugin.getDefault().logException(e, "Core exception on add marker");
}
}
/**
* As a side-effect this method updates missing line information for some bugs stored
* in the given bug collection
* @param project
* @param theCollection
* @return never null
*/
public static List<MarkerParameter> createBugParameters(IJavaProject project,
BugCollection theCollection, IProgressMonitor monitor) {
List<MarkerParameter> bugParameters = new ArrayList<MarkerParameter>();
if (project == null) {
FindbugsPlugin.getDefault().logException(
new NullPointerException("project is null"), "project is null");
return bugParameters;
}
Iterator<BugInstance> iterator = theCollection.iterator();
while (iterator.hasNext() && !monitor.isCanceled()) {
BugInstance bug = iterator.next();
MarkerParameter mp = createMarkerParameter(project, bug);
if(mp != null){
bugParameters.add(mp);
}
}
return bugParameters;
}
private static MarkerParameter createMarkerParameter(IJavaProject project, BugInstance bug) {
IJavaElement type = null;
WorkItem resource = null;
try {
type = getJavaElement(bug, project);
if(type != null) {
resource = new WorkItem(type);
}
} catch (JavaModelException e1) {
FindbugsPlugin.getDefault().logException(
e1, "Could not find Java type for FindBugs warning");
}
if (resource == null) {
if (Reporter.DEBUG) {
reportNoResourceFound(bug);
}
return null;
}
// default - first class line
int primaryLine = bug.getPrimarySourceLineAnnotation().getStartLine();
// FindBugs needs originally generated primary line in order to find the bug again.
// Sometimes this primary line is <= 0, which causes Eclipse editor to ignore it
// So we check if we can replace the "wrong" primary line with a "better" start line
// If not, we just provide two values - one for Eclipse, another for FindBugs itself.
int startLine = -1;
if (primaryLine <= 0) {
FieldAnnotation primaryField = bug.getPrimaryField();
if (primaryField != null && primaryField.getSourceLines() != null) {
startLine = primaryField.getSourceLines().getStartLine();
if(startLine < 0){
// We have to provide line number, otherwise editor wouldn't show it
startLine = 1;
}
} else {
// We have to provide line number, otherwise editor wouldn't show it
startLine = 1;
}
}
MarkerParameter parameter;
if(startLine > 0){
parameter = new MarkerParameter(bug, resource, startLine, primaryLine);
} else {
parameter = new MarkerParameter(bug, resource, primaryLine, primaryLine);
}
if (Reporter.DEBUG) {
System.out.println("Creating marker for "
+ resource.getPath() + ": line "
+ parameter.primaryLine
+ bug.getMessage());
}
return parameter;
}
private static void reportNoResourceFound(BugInstance bug) {
String className = null;
String packageName = null;
ClassAnnotation primaryClass = bug.getPrimaryClass();
if (primaryClass != null) {
className = primaryClass.getClassName();
packageName = primaryClass.getPackageName();
}
if (Reporter.DEBUG) {
System.out.println("BUG in class: " //$NON-NLS-1$
+ packageName + "." //$NON-NLS-1$
+ className + ": \n\t" //$NON-NLS-1$
+ bug.getMessage() + " / Annotation: " //$NON-NLS-1$
+ bug.getAnnotationText() + " / Source Line: " //$NON-NLS-1$
+ bug.getPrimarySourceLineAnnotation());
}
System.out.println("NOT found resource for a BUG in class: "
+ packageName + "."
+ className + ": \n\t"
+ bug.getMessage() + " / Annotation: "
+ bug.getAnnotationText() + " / Source Line: "
+ bug.getPrimarySourceLineAnnotation());
}
/**
* Get the underlying resource (Java class) for given BugInstance.
*
* @param bug the BugInstance
* @param project the project
* @return the IResource representing the Java class
*/
private static @CheckForNull
IJavaElement getJavaElement(BugInstance bug, IJavaProject project) throws JavaModelException {
SourceLineAnnotation primarySourceLineAnnotation = bug.getPrimarySourceLineAnnotation();
PackageMemberAnnotation packageAnnotation = null;
String packageName = null;
String qualifiedClassName = null;
if (primarySourceLineAnnotation == null) {
packageAnnotation = bug.getPrimaryClass();
if (packageAnnotation != null) {
packageName = packageAnnotation.getPackageName();
qualifiedClassName = packageAnnotation.getClassName();
}
} else {
packageName = primarySourceLineAnnotation.getPackageName();
qualifiedClassName = primarySourceLineAnnotation.getClassName();
}
if (qualifiedClassName == null) {
return null;
}
if (Reporter.DEBUG) {
System.out.println("Looking up class: " + packageName + ", " + qualifiedClassName);
}
Matcher m = fullName.matcher(qualifiedClassName);
IType type;
String innerName = null;
if (m.matches() && m.group(2).length() > 0) {
String outerQualifiedClassName = m.group(1).replace('$','.');
innerName = m.group(2).substring(1);
// second argument is required to find also secondary types
type = project.findType(outerQualifiedClassName, (IProgressMonitor)null);
/*
* code below only points to the first line of inner class
* even if this is not a class bug but field bug
*/
if(type != null && !hasLineInfo(primarySourceLineAnnotation)) {
completeInnerClassInfo(qualifiedClassName, innerName, type, bug);
}
} else {
// second argument is required to find also secondary types
type = project.findType(qualifiedClassName.replace('$','.'), (IProgressMonitor)null);
// for inner classes, some detectors does not properly report source lines:
// instead of reporting the first line of inner class, they report first line of parent class
// in this case we will try to fix this here and point to the right start line
if(type != null && type.isMember()){
if(!hasLineInfo(primarySourceLineAnnotation)) {
completeInnerClassInfo(qualifiedClassName, type.getElementName(), type, bug);
}
}
}
// reassign it as it may be changed for inner classes
primarySourceLineAnnotation = bug.getPrimarySourceLineAnnotation();
int startLine;
/*
* Eclipse can help us find the line number for fields => we trying to add line
* info for fields here
*/
if (primarySourceLineAnnotation != null) {
startLine = primarySourceLineAnnotation.getStartLine();
if(startLine <= 0 && bug.getPrimaryField() != null){
completeFieldInfo(qualifiedClassName, innerName, type, bug);
}
} else {
if(bug.getPrimaryField() != null){
completeFieldInfo(qualifiedClassName, innerName, type, bug);
}
}
return type;
}
private static boolean hasLineInfo(SourceLineAnnotation annotation) {
return annotation != null && annotation.getStartLine() > 0;
}
private static void completeFieldInfo(String qualifiedClassName, String innerName,
IType type, BugInstance bug) {
FieldAnnotation field = bug.getPrimaryField();
if (field == null || type == null) {
return;
}
IField ifield = type.getField(field.getFieldName());
ISourceRange sourceRange = null;
IScanner scanner = null;
JavaModelException ex = null;
try {
sourceRange = ifield.getNameRange();
} catch (JavaModelException e) {
ex = e;
}
try {
// second try...
if (sourceRange == null) {
sourceRange = ifield.getSourceRange();
}
scanner = initScanner(type, sourceRange);
} catch (JavaModelException e) {
String message = "Can not complete field annotation " + field + " for the field: "
+ ifield + " in class: " + qualifiedClassName + ", type "
+ type + ", bug " + bug;
if(ex != null){
// report only first one
e = ex;
}
FindbugsPlugin.getDefault().logMessage(IStatus.WARNING, message, e);
}
if(scanner == null || sourceRange == null){
return;
}
int lineNbr = scanner.getLineNumber(sourceRange.getOffset());
lineNbr = lineNbr <= 0 ? 1 : lineNbr;
String sourceFileStr = getSourceFileHint(type, qualifiedClassName);
field.setSourceLines(new SourceLineAnnotation(qualifiedClassName, sourceFileStr,
lineNbr, lineNbr, 0, 0));
}
private static String getSourceFileHint(IType type, String qualifiedClassName) {
String sourceFileStr = "";
IJavaElement primaryElement = type.getPrimaryElement();
if(primaryElement != null){
return primaryElement.getElementName() + ".java";
}
return sourceFileStr;
}
private static void completeInnerClassInfo(String qualifiedClassName,
String innerName, @NonNull IType type, BugInstance bug) throws JavaModelException {
int lineNbr = findChildSourceLine(type, innerName, bug);
// should be always first line, if not found
lineNbr = lineNbr <= 0 ? 1 : lineNbr;
String sourceFileStr = getSourceFileHint(type, qualifiedClassName);
if (sourceFileStr != null && sourceFileStr.length() > 0) {
bug.addSourceLine(new SourceLineAnnotation(qualifiedClassName, sourceFileStr,
lineNbr, lineNbr, 0, 0));
}
}
/**
* @return start line of given type, or 1 if line could not be found
*/
private static int getLineStart(IType source) throws JavaModelException {
ISourceRange range = source.getNameRange();
if(range == null){
range = source.getSourceRange();
}
IScanner scanner = initScanner(source, range);
if(scanner != null && range != null) {
return scanner.getLineNumber(range.getOffset());
}
return START_LINE_OF_ENCLOSING_TYPE;
}
/**
* @param source must be not null
* @param range can be null
* @return may return null, otherwise an initialized scanner which may answer which
* source offset index belongs to which source line
* @throws JavaModelException
*/
private static IScanner initScanner(IType source, ISourceRange range) throws JavaModelException {
if(range == null){
return null;
}
char[] charContent = getContent(source);
if(charContent == null){
return null;
}
IScanner scanner = ToolFactory.createScanner(false, false, false, true);
scanner.setSource(charContent);
int offset = range.getOffset();
try {
while (scanner.getNextToken() != ITerminalSymbols.TokenNameEOF) {
// do nothing, just wait for the end of stream
if(offset <= scanner.getCurrentTokenEndPosition()){
break;
}
}
} catch (InvalidInputException e) {
FindbugsPlugin.getDefault().logException(e,
"Could not init scanner for type: " + source);
}
return scanner;
}
@SuppressWarnings("restriction")
private static char[] getContent(IType source) throws JavaModelException {
char [] charContent = null;
IOpenable op = source.getOpenable();
if (op instanceof CompilationUnit) {
charContent = ((CompilationUnit)(op)).getContents();
}
if(charContent == null){
String content = source.getSource();
if(content != null){
charContent = content.toCharArray();
}
}
return charContent;
}
private static int findChildSourceLine(IType parentType, String name, BugInstance bug)
throws JavaModelException {
if (parentType == null) {
return -1;
}
char firstChar = name.charAt(0);
boolean firstIsDigit = Character.isDigit(firstChar);
if (!firstIsDigit) {
return findInnerClassSourceLine(parentType, name);
}
boolean innerFromMember = firstIsDigit && name.length() > 1
&& !Character.isDigit(name.charAt(1));
if(innerFromMember){
return findInnerClassSourceLine(parentType, name.substring(1));
}
return findInnerAnonymousClassSourceLine(parentType, name);
}
private static int findInnerAnonymousClassSourceLine(IJavaElement parentType,
String innerName) throws JavaModelException {
IType anon = JdtUtils.findAnonymous((IType) parentType, innerName);
if(anon != null) {
return getLineStart(anon);
}
return START_LINE_OF_ENCLOSING_TYPE;
}
private static int findInnerClassSourceLine(IJavaElement parentType,
String name) throws JavaModelException {
String elemName = parentType.getElementName();
if (name.equals(elemName)) {
if (parentType instanceof IType) {
return getLineStart((IType) parentType);
}
}
if (parentType instanceof IParent) {
IJavaElement[] children = ((IParent) parentType).getChildren();
for (int i = 0; i < children.length; i++) {
// recursive call
int line = findInnerClassSourceLine(children[i], name);
if (line > 0) {
return line;
}
}
}
return START_LINE_OF_ENCLOSING_TYPE;
}
/**
* Remove all FindBugs problem markers for given resource. If the given resource is
* project, will also clear bug collection.
*
* @param res the resource
*/
public static void removeMarkers(IResource res) throws CoreException {
// remove any markers added by our builder
// This triggers resource update on IResourceChangeListener's (BugTreeView)
res.deleteMarkers(FindBugsMarker.NAME, true, IResource.DEPTH_INFINITE);
if(res instanceof IProject){
IProject project = (IProject) res;
FindbugsPlugin.clearBugCollection(project);
}
}
/**
* Given current active bug category set, minimum warning priority,
* and previous user classification, return whether or not a warning
* (bug instance) should be displayed using a marker.
*
* @param bugInstance the warning
* @param filterSettings project filter settings
* @return true if the warning should be displayed, false if not
*/
public static boolean shouldDisplayWarning(IProject project, BugInstance bugInstance, ProjectFilterSettings filterSettings) {
boolean isFiltered = false;
// Consult all filters provided by extensions
for (IMarkerFilter mFilter : markerFilters)
{
isFiltered |= mFilter.isFiltered(project, bugInstance);
}
if (isFiltered) {
return false;
}
return filterSettings.displayWarning(bugInstance);
}
/**
* Attempt to redisplay FindBugs problem markers for
* given project.
*
* @param javaProject the project
*/
public static void redisplayMarkers(final IJavaProject javaProject) {
final IProject project = javaProject.getProject();
FindBugsJob job = new FindBugsJob("Refreshing FindBugs markers", project){
@Override
protected void runWithProgress(IProgressMonitor monitor) throws CoreException {
// Get the saved bug collection for the project
SortedBugCollection bugs = FindbugsPlugin.getBugCollection(project,
monitor);
// Remove old markers
project.deleteMarkers(FindBugsMarker.NAME, true, IResource.DEPTH_INFINITE);
// Display warnings
createMarkers(javaProject, bugs, monitor);
}
};
job.scheduleInteractive();
}
public static @CheckForNull BugCode findBugCodeForMarker(IMarker marker) {
try {
Object bugCode = marker.getAttribute(FindBugsMarker.PATTERN_TYPE);
if(bugCode instanceof String){
return I18N.instance().getBugCode((String) bugCode);
}
} catch (CoreException e) {
FindbugsPlugin.getDefault().logException(e, "Marker does not contain bug code");
return null;
}
return null;
}
public static @CheckForNull BugPattern findBugPatternForMarker(IMarker marker) {
try {
Object patternId = marker.getAttribute(FindBugsMarker.BUG_TYPE);
if(patternId instanceof String){
return I18N.instance().lookupBugPattern((String) patternId);
}
} catch (CoreException e) {
FindbugsPlugin.getDefault().logException(e, "Marker does not contain pattern id");
return null;
}
return null;
}
public static @CheckForNull IJavaElement findJavaElementForMarker(IMarker marker) {
try {
Object elementId = marker.getAttribute(FindBugsMarker.UNIQUE_JAVA_ID);
if(elementId instanceof String){
return JavaCore.create((String) elementId);
}
} catch (CoreException e) {
FindbugsPlugin.getDefault().logException(e, "Marker does not contain valid java element id");
return null;
}
return null;
}
public static Set<IMarker> findMarkerForJavaElement(IJavaElement elt, IMarker[] possibleCandidates, boolean recursive) {
String id = elt.getHandleIdentifier();
Set<IMarker> markers = new HashSet<IMarker>();
for (IMarker marker : possibleCandidates) {
try {
Object elementId = marker.getAttribute(FindBugsMarker.UNIQUE_JAVA_ID);
// UNIQUE_JAVA_ID exists first since 1.3.9 as FB attribute
if(!(elementId instanceof String)){
continue;
}
String stringId = (String) elementId;
if(!recursive){
if(stringId.equals(id)){
// exact match
markers.add(marker);
} else if(isDirectChild(id, stringId)){
// direct child: class in the package, but not in the sub-package
markers.add(marker);
}
} else if(stringId.startsWith(id)){
markers.add(marker);
}
} catch (CoreException e) {
FindbugsPlugin.getDefault().logException(e, "Marker does not contain valid java element id");
continue;
}
}
return markers;
}
/**
*
* @param parentId
* java element id of a parent element
* @param childId
* java element id of possible child
* @return true if the second string represents a java element which is a direct child
* of the parent element.
*/
@SuppressWarnings("restriction")
private static boolean isDirectChild(String parentId, String childId) {
return childId.startsWith(parentId)
&& (childId.length() > (parentId.length() + 1))
// if there is NOT a class file separator, then it's not a direct child
&& childId.charAt(parentId.length()) == JavaElement.JEM_CLASSFILE;
}
/**
* Find the BugInstance associated with given FindBugs marker.
*
* @param marker a FindBugs marker
* @return the BugInstance associated with the marker,
* or null if we can't find the BugInstance
*/
public static @CheckForNull BugInstance findBugInstanceForMarker(IMarker marker) {
IResource resource = marker.getResource();
IProject project = resource.getProject();
if (project == null) {
// Also shouldn't happen.
FindbugsPlugin.getDefault().logError("No project for warning marker");
return null;
}
if (!isFindBugsMarker(marker)) {
// log disabled because otherwise each selection in problems view generates
// 6 new errors (we need refactor all bug views to get rid of this).
// FindbugsPlugin.getDefault().logError("Selected marker is not a FindBugs marker");
// FindbugsPlugin.getDefault().logError(marker.getType());
// FindbugsPlugin.getDefault().logError(FindBugsMarker.NAME);
return null;
}
// We have a FindBugs marker. Get the corresponding BugInstance.
String bugId = marker.getAttribute(FindBugsMarker.UNIQUE_ID, null);
if (bugId == null) {
FindbugsPlugin.getDefault().logError("Marker does not contain unique id for warning");
return null;
}
try {
BugCollection bugCollection = FindbugsPlugin.getBugCollection(project, null);
if (bugCollection == null) {
FindbugsPlugin.getDefault().logError("Could not get BugCollection for FindBugs marker");
return null;
}
String bugType = (String) marker.getAttribute(FindBugsMarker.BUG_TYPE);
Integer primaryLineNumber = (Integer)marker.getAttribute(FindBugsMarker.PRIMARY_LINE);
// compatibility
if(primaryLineNumber == null){
primaryLineNumber = Integer.valueOf(getEditorLine(marker));
}
if (bugType == null) {
FindbugsPlugin.getDefault()
.logError("Could not get find attributes for marker " + marker
+ ": (" + bugId + ", " + primaryLineNumber + ")");
return null;
}
BugInstance bug = bugCollection.findBug(bugId, bugType, primaryLineNumber.intValue());
return bug;
} catch (CoreException e) {
FindbugsPlugin.getDefault().logException(e, "Could not get BugInstance for FindBugs marker");
return null;
}
}
private static int getEditorLine(IMarker marker) {
return marker.getAttribute(IMarker.LINE_NUMBER, -1);
}
/**
* Fish an IMarker out of given selection.
*
* @param selection the selection
* @return the selected IMarker, or null if we can't find an IMarker
* in the selection
*/
public static Set<IMarker> getMarkerFromSelection(ISelection selection) {
Set<IMarker> markers = new HashSet<IMarker>();
if(!(selection instanceof IStructuredSelection)){
return markers;
}
IStructuredSelection sSelection = (IStructuredSelection) selection;
for (Iterator<?> iter = sSelection.iterator(); iter.hasNext();) {
Object next = iter.next();
markers.addAll(getMarkers(next));
}
return markers;
}
public static Set<IMarker> getMarkers(Object obj){
Set<IMarker> markers = new HashSet<IMarker>();
if(obj instanceof IMarker){
IMarker marker = (IMarker) obj;
if (isFindBugsMarker(marker)) {
markers.add(marker);
}
} else if (obj instanceof BugGroup){
BugGroup group = (BugGroup) obj;
markers.addAll(group.getAllMarkers());
} else if (obj instanceof IResource){
IResource res = (IResource) obj;
IMarker[] markers2 = MarkerUtil.getAllMarkers(res);
for (IMarker marker : markers2) {
markers.add(marker);
}
} else if (obj instanceof IJavaElement) {
markers.addAll(new WorkItem((IJavaElement) obj).getMarkers(true));
} else if (obj instanceof IAdaptable){
IAdaptable adapter = (IAdaptable) obj;
IMarker marker = (IMarker) adapter.getAdapter(IMarker.class);
if(marker == null){
IResource resource = (IResource) adapter.getAdapter(IResource.class);
if(resource == null){
return markers;
}
IMarker[] markers2 = getMarkers(resource, IResource.DEPTH_INFINITE);
markers.addAll(Arrays.asList(markers2));
} else if (isFindBugsMarker(marker)) {
markers.add(marker);
}
}
return markers;
}
/**
* Tries to retrieve right bug marker for given selection. If there are many markers
* for given editor, and text selection doesn't match any of them, return null. If
* there is only one marker for given editor, returns this marker in any case.
*
* @param selection
* @param editor
* @return may return null
*/
public static IMarker getMarkerFromEditor(ITextSelection selection, IEditorPart editor) {
IResource resource = (IResource) editor.getEditorInput().getAdapter(IFile.class);
IMarker[] allMarkers;
if(resource != null){
allMarkers = getMarkers(resource, IResource.DEPTH_ZERO);
} else {
IClassFile classFile = (IClassFile) editor.getEditorInput().getAdapter(IClassFile.class);
if(classFile == null){
return null;
}
Set<IMarker> markers = getMarkers(classFile.getType());
allMarkers = markers.toArray(new IMarker[markers.size()]);
}
// if editor contains only one FB marker, do some cheating and always return it.
if(allMarkers.length == 1) {
return allMarkers[0];
}
// +1 because it counts real lines, but editor shows lines + 1
int startLine = selection.getStartLine() + 1;
for (IMarker marker : allMarkers) {
int line = getEditorLine(marker);
if(startLine == line){
return marker;
}
}
return null;
}
public static IMarker getMarkerFromSingleSelection(ISelection selection) {
if(!(selection instanceof IStructuredSelection)){
return null;
}
IStructuredSelection sSelection = (IStructuredSelection) selection;
if(sSelection.size() != 1){
return null;
}
Object next = sSelection.getFirstElement();
if(next instanceof IMarker){
IMarker marker = (IMarker) next;
if (!isFindBugsMarker(marker)) {
return null;
}
return marker;
} else if (next instanceof BugGroup){
return null;
} else if (next instanceof IResource){
return null;
} else if (next instanceof IAdaptable){
IAdaptable adapter = (IAdaptable) next;
IMarker marker = (IMarker) adapter.getAdapter(IMarker.class);
if (!isFindBugsMarker(marker)) {
return null;
}
return marker;
}
return null;
}
public static boolean isFindBugsMarker(IMarker marker) {
try {
return marker != null && marker.exists() && marker.isSubtypeOf(FindBugsMarker.NAME);
} catch (CoreException e) {
FindbugsPlugin.getDefault().logException(e,
"Exception while checking FindBugs type on marker.");
}
return false;
}
/**
* Retrieves all the FB markers from given resource and all its descendants
* @param fileOrFolder
* @return never null (empty array if nothing there or exception happens).
* Exception will be logged
*/
public static IMarker[] getAllMarkers(IResource fileOrFolder){
return getMarkers(fileOrFolder, IResource.DEPTH_INFINITE);
}
/**
* Retrieves all the FB markers from given resource and all its descendants
* @param fileOrFolder
* @return never null (empty array if nothing there or exception happens).
* Exception will be logged
*/
public static IMarker[] getMarkers(IResource fileOrFolder, int depth){
try {
return fileOrFolder.findMarkers(FindBugsMarker.NAME, true, depth);
} catch (CoreException e) {
FindbugsPlugin.getDefault().logException(e,
"Cannot collect FindBugs warnings from: " + fileOrFolder);
}
return EMPTY;
}
/**
* @param marker might be null
* @param bugIdToFilter might be null
* @return true if marker should be filtered
*/
public static boolean isFiltered(IMarker marker, Set<String> bugIdToFilter) {
if(marker == null){
return true;
}
if(bugIdToFilter == null){
return false;
}
String pattern = marker.getAttribute(FindBugsMarker.BUG_TYPE, "not found");
String patternType = marker.getAttribute(FindBugsMarker.PATTERN_TYPE, "not found");
for (String badId : bugIdToFilter) {
if(badId.equals(patternType) || badId.equals(pattern)){
return true;
}
}
return false;
}
/**
* Reset all filters provided by extensions.
*/
public static void resetMarkerFilters() {
for (IMarkerFilter filters : markerFilters) {
filters.resetFilter();
}
}
/**
* Reload all markers to allow changes to the active filter set to be applied.
*/
public static void redisplayAllMarkers() {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IProject[] projects = workspace.getRoot().getProjects();
if (projects != null) {
for (IProject project : projects) {
if (FindbugsPlugin.bugCollectionExists(project)) {
try {
boolean javaNature = project.isNatureEnabled(JavaCore.NATURE_ID);
if (javaNature) {
IJavaProject jproject = JavaCore.create(project);
if (jproject.isOpen()) {
MarkerUtil.redisplayMarkers(jproject);
}
}
} catch (CoreException e) {
// Throw away
}
}
}
}
}
}