/* * FindBugs Eclipse Plug-in. * Copyright (C) 2003 - 2004, Peter Friese * Copyright (C) 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.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.IJavaElement; import de.tobject.findbugs.FindbugsPlugin; import de.tobject.findbugs.marker.FindBugsMarker; import edu.umd.cs.findbugs.AppVersion; import edu.umd.cs.findbugs.BugCollection; import edu.umd.cs.findbugs.BugInstance; import edu.umd.cs.findbugs.Priorities; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.config.ProjectFilterSettings; import edu.umd.cs.findbugs.config.UserPreferences; /** * Creates a FindBugs marker in a runnable window. */ public class MarkerReporter implements IWorkspaceRunnable { private final BugCollection collection; private static final boolean EXPERIMENTAL_BUGS = false; private final List<MarkerParameter> mpList; private final IProject project; public MarkerReporter(List<MarkerParameter> mpList, BugCollection theCollection, IProject project) { this.mpList = mpList; this.collection = theCollection; this.project = project; } public void run(IProgressMonitor monitor) throws CoreException { UserPreferences userPrefs = FindbugsPlugin.getUserPreferences(project); ProjectFilterSettings filterSettings = userPrefs.getFilterSettings(); for (MarkerParameter mp : mpList) { if(!MarkerUtil.shouldDisplayWarning(project, mp.bug, filterSettings)){ continue; } String markerType = getMarkerType(mp.bug); if(markerType == null) { continue; } // This triggers resource update on IResourceChangeListener's (BugTreeView) addMarker(markerType, mp); } } private void addMarker(String markerType, MarkerParameter mp) throws CoreException { IResource markerTarget = mp.resource.getMarkerTarget(); IMarker[] existingMarkers = markerTarget.findMarkers(markerType, true, IResource.DEPTH_ZERO); Map<String, Object> attributes = createMarkerAttributes(mp); // XXX Workaround for bug 2785257 (has to be solved better) // see http://sourceforge.net/tracker/?func=detail&atid=614693&aid=2785257&group_id=96405 // currently we can't run FB only on a subset of classes related to the specific // source folder if source folders have same class output directory. // In this case the classes from BOTH source folders are examined by FB and // new markers can be created for issues which are already reported. // Therefore here we check if a marker with SAME bug id is already known, // and if yes, delete it (replacing with newer one) if(existingMarkers.length > 0){ IMarker oldMarker = findSameBug(attributes, existingMarkers); if(oldMarker != null){ oldMarker.delete(); } } IMarker newMarker = markerTarget.createMarker(markerType); setAttributes(newMarker, attributes); } private @CheckForNull IMarker findSameBug(Map<String, Object> attributes, IMarker[] existingMarkers) throws CoreException { Object bugId = attributes.get(FindBugsMarker.UNIQUE_ID); if (bugId == null) { return null; } for (IMarker marker : existingMarkers) { Object idAttribute = marker.getAttribute(FindBugsMarker.UNIQUE_ID); if (bugId.equals(idAttribute)) { return marker; } } return null; } /** * @param bug * @return null if marker shouldn't be generated */ private String getMarkerType(BugInstance bug) { String markerType; switch (bug.getPriority()) { case Priorities.HIGH_PRIORITY: markerType = FindBugsMarker.NAME_HIGH; break; case Priorities.NORMAL_PRIORITY: markerType = FindBugsMarker.NAME_NORMAL; break; case Priorities.LOW_PRIORITY: markerType = FindBugsMarker.NAME_LOW; break; case Priorities.EXP_PRIORITY: if (!EXPERIMENTAL_BUGS) { return null; } markerType = FindBugsMarker.NAME_EXPERIMENTAL; break; case Priorities.IGNORE_PRIORITY: FindbugsPlugin.getDefault().logError("Bug with ignore priority "); return null; default: FindbugsPlugin.getDefault().logError( "Bug with unknown priority " + bug.getPriority()); return null; } return markerType; } /** * @param mp * @return attributes map which should be assigned to the given marker */ private Map<String, Object> createMarkerAttributes(MarkerParameter mp) { Map<String, Object> attributes = new HashMap<String, Object>(23); attributes.put(IMarker.LINE_NUMBER, mp.startLine); attributes.put(FindBugsMarker.PRIMARY_LINE, mp.primaryLine); attributes.put(FindBugsMarker.BUG_TYPE, mp.bug.getType()); attributes.put(FindBugsMarker.PATTERN_TYPE, mp.bug.getAbbrev()); long seqNum = mp.bug.getFirstVersion(); if(seqNum == 0) { attributes.put(FindBugsMarker.FIRST_VERSION, "-1"); } else { AppVersion theVersion = collection.getAppVersionFromSequenceNumber(seqNum); if (theVersion == null) { attributes.put(FindBugsMarker.FIRST_VERSION, "Cannot find AppVersion: seqnum=" + seqNum + "; collection seqnum=" + collection.getSequenceNumber()); } else { attributes.put(FindBugsMarker.FIRST_VERSION, Long .toString(theVersion.getTimestamp())); } } try { attributes.put(IMarker.MESSAGE, mp.bug.getAbridgedMessage()); } catch (RuntimeException e) { FindbugsPlugin.getDefault().logException(e, "Error generating msg for " + mp.bug.getType()); attributes.put(IMarker.MESSAGE, "??? " + mp.bug.getType()); } attributes.put(IMarker.SEVERITY, Integer.valueOf(IMarker.SEVERITY_WARNING)); attributes.put(FindBugsMarker.PRIORITY_TYPE, mp.bug.getPriorityTypeString()); switch (mp.bug.getPriority()) { case Priorities.HIGH_PRIORITY: attributes.put(IMarker.PRIORITY, Integer.valueOf(IMarker.PRIORITY_HIGH)); break; case Priorities.NORMAL_PRIORITY: attributes.put(IMarker.PRIORITY, Integer.valueOf(IMarker.PRIORITY_NORMAL)); break; default: attributes.put(IMarker.PRIORITY, Integer.valueOf(IMarker.PRIORITY_LOW)); break; } // Set unique id of warning, so we can easily refer back // to it later: for example, when the user classifies the warning. String uniqueId = mp.bug.getInstanceHash(); if (uniqueId != null) { attributes.put(FindBugsMarker.UNIQUE_ID, uniqueId); } IJavaElement javaElt = mp.resource.getCorespondingJavaElement(); if(javaElt != null){ attributes.put(FindBugsMarker.UNIQUE_JAVA_ID, javaElt.getHandleIdentifier()); // Eclipse markers model doesn't allow to have markers // attached to the (non-resource) part of the resource (like jar entry inside the jar) // TODO we should add annotations to opened class file editors to show (missing) // markers for single class file inside the jar. Otherwise we will show markers // in the bug explorer view but NOT inside the class file editor } return attributes; } /** * Set all the attributes to marker in one 'workspace transaction' * @param marker non null * @throws CoreException */ private void setAttributes(IMarker marker, Map<String, Object> attributes) throws CoreException { marker.setAttributes(attributes); } }