/* * 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 } } } } } }