/* * 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 static de.tobject.findbugs.marker.FindBugsMarker.*; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; 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 org.eclipse.jface.preference.IPreferenceStore; import de.tobject.findbugs.FindbugsPlugin; import de.tobject.findbugs.marker.FindBugsMarker.MarkerConfidence; import edu.umd.cs.findbugs.AppVersion; import edu.umd.cs.findbugs.DetectorFactory; import edu.umd.cs.findbugs.SortedBugCollection; 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 SortedBugCollection collection; private final List<MarkerParameter> mpList; private final IProject project; public MarkerReporter(List<MarkerParameter> mpList, SortedBugCollection 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(); IPreferenceStore store = FindbugsPlugin.getPluginPreferences(project); for (MarkerParameter mp : mpList) { if (mp.markerType == null) { continue; } if (!MarkerUtil.shouldDisplayWarning(mp.bug, filterSettings)) { continue; } updateMarkerSeverity(store, mp); // This triggers resource update on IResourceChangeListener's (BugTreeView) addMarker(mp); } } private static void updateMarkerSeverity(IPreferenceStore store, MarkerParameter mp) { String markerSeverityStr = store.getString(mp.markerType); mp.markerSeverity = MarkerSeverity.get(markerSeverityStr).value; } private void addMarker(MarkerParameter mp) throws CoreException { Map<String, Object> attributes = createMarkerAttributes(mp); if(attributes.isEmpty()){ collection.remove(mp.bug); return; } IResource markerTarget = mp.resource.getMarkerTarget(); IMarker[] existingMarkers = markerTarget.findMarkers(mp.markerType, true, IResource.DEPTH_ZERO); // 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(mp.markerType); newMarker.setAttributes(attributes); } private static @CheckForNull IMarker findSameBug(Map<String, Object> attributes, IMarker[] existingMarkers) throws CoreException { Object bugId = attributes.get(UNIQUE_ID); if (bugId == null) { return null; } Object line = attributes.get(PRIMARY_LINE); for (IMarker marker : existingMarkers) { Object idAttribute = marker.getAttribute(UNIQUE_ID); if (bugId.equals(idAttribute)) { // Fix for issue 3054146: we filter too much. Different bugs // from the same method and same type have equal hashes, as they // do not include source line info to the hash calculation // So we must compare source lines to to avoid too much // filtering. Object primaryLine = marker.getAttribute(PRIMARY_LINE); if ((line == null && primaryLine == null) || (line != null && line.equals(primaryLine))) { return marker; } } } return null; } /** * @param mp * @return attributes map which should be assigned to the given marker. If the map is empty, * the marker shouldn't be generated */ @Nonnull 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(PRIMARY_LINE, mp.primaryLine); attributes.put(BUG_TYPE, mp.bug.getType()); attributes.put(PATTERN_TYPE, mp.bug.getAbbrev()); attributes.put(RANK, Integer.valueOf(mp.bug.getBugRank())); attributes.put(PRIO_AKA_CONFIDENCE, MarkerConfidence.getConfidence(mp.bug.getPriority()).name()); long seqNum = mp.bug.getFirstVersion(); if (seqNum == 0) { attributes.put(FIRST_VERSION, "-1"); } else { AppVersion theVersion = collection.getAppVersionFromSequenceNumber(seqNum); if (theVersion == null) { attributes.put(FIRST_VERSION, "Cannot find AppVersion: seqnum=" + seqNum + "; collection seqnum=" + collection.getSequenceNumber()); } else { attributes.put(FIRST_VERSION, Long.toString(theVersion.getTimestamp())); } } try { attributes.put(IMarker.MESSAGE, getMessage(mp)); } catch (RuntimeException e) { FindbugsPlugin.getDefault().logException(e, "Error generating msg for " + mp.bug.getType() + ", attributes: " + attributes); attributes.clear(); return attributes; } attributes.put(IMarker.SEVERITY, mp.markerSeverity); // 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(UNIQUE_ID, uniqueId); } // Set unique id of the plugin, so we can easily refer back // to it later: for example, when the user group markers by plugin. DetectorFactory detectorFactory = mp.bug.getDetectorFactory(); if(detectorFactory != null) { String pluginId = detectorFactory.getPlugin().getPluginId(); if (pluginId != null) { attributes.put(DETECTOR_PLUGIN_ID, pluginId); } else { attributes.clear(); return attributes; } } else { attributes.clear(); return attributes; } IJavaElement javaElt = mp.resource.getCorespondingJavaElement(); if (javaElt != null) { attributes.put(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; } private static String getMessage(MarkerParameter mp) { String message = mp.bug.getMessageWithoutPrefix(); message += " ["+ mp.bug.getBugRankCategory() + "(" + mp.bug.getBugRank() + "), " + MarkerConfidence.getConfidence(mp.bug.getPriority()) + " confidence]"; return message; } }