/* * 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.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; 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.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.jobs.ISchedulingRule; 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.jface.viewers.StructuredSelection; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.texteditor.ITextEditor; import de.tobject.findbugs.FindBugsJob; import de.tobject.findbugs.FindbugsPlugin; import de.tobject.findbugs.builder.WorkItem; import de.tobject.findbugs.view.explorer.BugGroup; 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.DetectorFactoryCollection; import edu.umd.cs.findbugs.FieldAnnotation; import edu.umd.cs.findbugs.PackageMemberAnnotation; import edu.umd.cs.findbugs.Plugin; import edu.umd.cs.findbugs.SortedBugCollection; import edu.umd.cs.findbugs.SourceLineAnnotation; import edu.umd.cs.findbugs.config.ProjectFilterSettings; import edu.umd.cs.findbugs.findbugsmarker.FindBugsMarker; /** * 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; /** * don't instantiate an utility class */ private MarkerUtil() { super(); } /** * Create an Eclipse marker for given BugInstance. * * @param javaProject * the project * @param monitor */ public static void createMarkers(IJavaProject javaProject, BugCollection theCollection, ISchedulingRule rule, 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 rule, IWorkspace.AVOID_UPDATE, monitor); } 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("NOT found resource for a BUG in 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()); } } /** * 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(BugInstance bugInstance, ProjectFilterSettings filterSettings) { 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 { // TODO in case we removed some of previously available // detectors, we should // throw away bugs reported by them // 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, project, monitor); } }; job.scheduleInteractive(); } public static @CheckForNull BugCode findBugCodeForMarker(IMarker marker) { try { Object bugCode = marker.getAttribute(FindBugsMarker.PATTERN_TYPE); if (bugCode instanceof String) { return DetectorFactoryCollection.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 DetectorFactoryCollection.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 @CheckForNull Plugin findDetectorPluginFor(IMarker marker) { try { Object pluginId = marker.getAttribute(FindBugsMarker.DETECTOR_PLUGIN_ID); if (pluginId instanceof String) { return DetectorFactoryCollection.instance().getPluginById((String) pluginId); } } catch (CoreException e) { FindbugsPlugin.getDefault().logException(e, "Marker does not contain valid plugin 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; } public static class BugCollectionAndInstance { public BugCollection getBugCollection() { return bugCollection; } public BugInstance getBugInstance() { return bugInstance; } public BugCollectionAndInstance(BugCollection bugCollection, BugInstance bugInstance) { this.bugCollection = bugCollection; this.bugInstance = bugInstance; } final BugCollection bugCollection; final BugInstance bugInstance; } /** * 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) { BugCollectionAndInstance bci = findBugCollectionAndInstanceForMarker(marker); if (bci == null) { return null; } return bci.bugInstance; } /** * Find the BugCollectionAndInstance 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 BugCollectionAndInstance findBugCollectionAndInstanceForMarker(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 new BugCollectionAndInstance(bugCollection, 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 ITextSelection) { IEditorPart editor = FindbugsPlugin.getActiveWorkbenchWindow().getActivePage().getActiveEditor(); if (!(editor instanceof ITextEditor)) { return null; } IMarker marker = MarkerUtil.getMarkerFromEditor((ITextSelection) selection, (ITextEditor) editor); if (marker != null) { selection = new StructuredSelection(marker); } else { selection = new StructuredSelection(); } } 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; } }