/* * FindBugs - Find Bugs in Java programs * Copyright (C) 2003-2008 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 edu.umd.cs.findbugs.cloud; import java.io.BufferedReader; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.List; import edu.umd.cs.findbugs.BugAnnotation; import edu.umd.cs.findbugs.BugInstance; import edu.umd.cs.findbugs.BugRanker; import edu.umd.cs.findbugs.ClassAnnotation; import edu.umd.cs.findbugs.Project; import edu.umd.cs.findbugs.PropertyBundle; import edu.umd.cs.findbugs.SourceLineAnnotation; import edu.umd.cs.findbugs.ba.SourceFile; import edu.umd.cs.findbugs.charsets.UTF8; import edu.umd.cs.findbugs.cloud.Cloud.UserDesignation; import edu.umd.cs.findbugs.util.Util; /** * @author Keith */ public class BugFilingCommentHelper { private final Cloud cloud; private final String BUG_NOTE; private final String POSTMORTEM_NOTE; private final int POSTMORTEM_RANK; public BugFilingCommentHelper(Cloud cloud) { this.cloud = cloud; PropertyBundle properties = cloud.getPlugin().getProperties(); BUG_NOTE = properties.getProperty("findbugs.bugnote"); POSTMORTEM_NOTE = properties.getProperty("findbugs.postmortem.note"); POSTMORTEM_RANK = properties.getInt("findbugs.postmortem.maxRank", 4); } public String getBugReportSummary(BugInstance b) { return b.getMessageWithoutPrefix() + " in " + b.getPrimaryClass().getSourceFileName(); } public String getBugReportText(BugInstance b) { return getBugReportHead(b) + getBugReportSourceCode(b) + getLineTerminatedUserEvaluation(b) + getBugPatternExplanation(b) + getBugReportTail(b); } @SuppressWarnings("boxing") public String getBugReportSourceCode(BugInstance b) { StringWriter stringWriter = new StringWriter(); PrintWriter out = new PrintWriter(stringWriter); ClassAnnotation primaryClass = b.getPrimaryClass(); int firstLine = Integer.MAX_VALUE; int lastLine = Integer.MIN_VALUE; for (BugAnnotation a : b.getAnnotations()) if (a instanceof SourceLineAnnotation) { SourceLineAnnotation s = (SourceLineAnnotation) a; if (s.getClassName().equals(primaryClass.getClassName()) && s.getStartLine() > 0) { firstLine = Math.min(firstLine, s.getStartLine()); lastLine = Math.max(lastLine, s.getEndLine()); } } SourceLineAnnotation primarySource = primaryClass.getSourceLines(); if (primarySource.isSourceFileKnown() && firstLine >= 1 && firstLine <= lastLine && lastLine - firstLine < 50) { BufferedReader in = null; try { Project project = cloud.getBugCollection().getProject(); SourceFile sourceFile = project.getSourceFinder().findSourceFile(primarySource); in = UTF8.bufferedReader(sourceFile.getInputStream()); int lineNumber = 1; String commonWhiteSpace = null; List<SourceLine> source = new ArrayList<SourceLine>(); while (lineNumber <= lastLine + 4) { String txt = in.readLine(); if (txt == null) break; if (lineNumber >= firstLine - 4) { String trimmed = txt.trim(); if (trimmed.length() == 0) { if (lineNumber > lastLine) break; txt = trimmed; } source.add(new SourceLine(lineNumber, txt)); commonWhiteSpace = commonLeadingWhitespace(commonWhiteSpace, txt); } lineNumber++; } if (commonWhiteSpace == null) commonWhiteSpace = ""; out.println("\nRelevant source code:"); for (SourceLine s : source) { if (s.text.length() == 0) out.printf("%5d: %n", s.line); else out.printf("%5d: %s%n", s.line, s.text.substring(commonWhiteSpace.length())); } out.println(); } catch (IOException e) { assert true; } finally { Util.closeSilently(in); } out.close(); return stringWriter.toString(); } return ""; } public String getBugReportHead(BugInstance b) { StringWriter stringWriter = new StringWriter(); PrintWriter out = new PrintWriter(stringWriter); out.println("Bug report generated from FindBugs"); out.println(b.getMessageWithoutPrefix()); out.println(); ClassAnnotation primaryClass = b.getPrimaryClass(); for (BugAnnotation a : b.getAnnotations()) { if (a == primaryClass) out.println(a); else out.println(" " + a.toString(primaryClass)); } if (cloud.supportsSourceLinks()) { URL link = cloud.getSourceLink(b); if (link != null) { out.println(); out.println(cloud.getSourceLinkToolTip(b) + ": " + link); out.println(); } } if (BUG_NOTE != null) { out.println(BUG_NOTE); if (POSTMORTEM_NOTE != null && BugRanker.findRank(b) <= POSTMORTEM_RANK && cloud.getConsensusDesignation(b).score() >= 0) { out.println(POSTMORTEM_NOTE); } out.println(); } Collection<String> projects = cloud.getProjects(primaryClass.getClassName()); if (projects != null && !projects.isEmpty()) { String projectList = projects.toString(); projectList = projectList.substring(1, projectList.length() - 1); out.println("Possibly part of: " + projectList); out.println(); } out.close(); return stringWriter.toString(); } public String getBugPatternExplanation(BugInstance b) { String detailPlainText = b.getBugPattern().getDetailPlainText(); return "Bug pattern explanation:\n" + detailPlainText + "\n\n"; } public String getLineTerminatedUserEvaluation(BugInstance b) { UserDesignation designation = cloud.getUserDesignation(b); String result; if (designation != UserDesignation.UNCLASSIFIED) result = "Classified as: " + designation.toString() + "\n"; else result = ""; String eval = cloud.getUserEvaluation(b).trim(); if (eval.length() > 0) result = result + eval + "\n"; return result; } public String getBugReportTail(BugInstance b) { return "\nFindBugs issue identifier (do not modify or remove): " + b.getInstanceHash(); } // ================================= end of public methods // ==================================== private String commonLeadingWhitespace(String soFar, String txt) { if (txt.length() == 0) return soFar; if (soFar == null) return txt; soFar = Util.commonPrefix(soFar, txt); for (int i = 0; i < soFar.length(); i++) { if (!Character.isWhitespace(soFar.charAt(i))) return soFar.substring(0, i); } return soFar; } // ==================================== inner classes // ========================================= public static class SourceLine { public final int line; public final String text; public SourceLine(int line, String text) { this.line = line; this.text = text; } } }