/* * FindBugs - Find Bugs in Java programs * Copyright (C) 2006, 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.gui2; import static java.util.Objects.requireNonNull; import java.awt.Dimension; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.net.URL; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.xml.parsers.ParserConfigurationException; import org.dom4j.DocumentException; import org.xml.sax.SAXException; import edu.umd.cs.findbugs.BugCollection; import edu.umd.cs.findbugs.BugCollectionBugReporter; import edu.umd.cs.findbugs.BugReporter; import edu.umd.cs.findbugs.DetectorFactoryCollection; import edu.umd.cs.findbugs.FindBugs2; import edu.umd.cs.findbugs.FindBugsProgress; import edu.umd.cs.findbugs.IFindBugsEngine; import edu.umd.cs.findbugs.Priorities; import edu.umd.cs.findbugs.Project; import edu.umd.cs.findbugs.SortedBugCollection; import edu.umd.cs.findbugs.SystemProperties; import edu.umd.cs.findbugs.config.UserPreferences; import edu.umd.cs.findbugs.filter.Filter; import edu.umd.cs.findbugs.filter.LastVersionMatcher; import edu.umd.cs.findbugs.workflow.Update; /** * Everything having to do with loading bugs should end up here. * * @author Dan * */ public class BugLoader { private static UserPreferences preferencesSingleton = UserPreferences.createDefaultUserPreferences(); /** * Get UserPreferences singleton. This should only be used if there is a * single set of user preferences to be used for all projects. * * @return the UserPreferences */ static UserPreferences getUserPreferences() { return preferencesSingleton; } /** * Performs an analysis and returns the BugSet created * * @param p * The Project to run the analysis on * @param progressCallback * the progressCallBack is supposed to be supplied by analyzing * dialog, FindBugs supplies progress information while it runs * the analysis * @return the bugs found * @throws InterruptedException * @throws IOException */ public static BugCollection doAnalysis(@Nonnull Project p, FindBugsProgress progressCallback) throws IOException, InterruptedException { StringWriter stringWriter = new StringWriter(); BugCollectionBugReporter pcb = new BugCollectionBugReporter(p, new PrintWriter(stringWriter, true)); pcb.setPriorityThreshold(Priorities.LOW_PRIORITY); IFindBugsEngine fb = createEngine(p, pcb); fb.setUserPreferences(getUserPreferences()); fb.setProgressCallback(progressCallback); fb.setProjectName(p.getProjectName()); fb.execute(); String warnings = stringWriter.toString(); if (warnings.length() > 0) { JTextArea tp = new JTextArea(warnings); tp.setEditable(false); JScrollPane pane = new JScrollPane(tp); pane.setPreferredSize(new Dimension(600, 400)); JOptionPane.showMessageDialog(MainFrame.getInstance(), pane, "Analysis errors", JOptionPane.WARNING_MESSAGE); } return pcb.getBugCollection(); } /** * Create the IFindBugsEngine that will be used to analyze the application. * * @param p * the Project * @param pcb * the PrintCallBack * @return the IFindBugsEngine */ private static IFindBugsEngine createEngine(@Nonnull Project p, BugReporter pcb) { FindBugs2 engine = new FindBugs2(); engine.setBugReporter(pcb); engine.setProject(p); engine.setDetectorFactoryCollection(DetectorFactoryCollection.instance()); // // Honor -effort option if one was given on the command line. // engine.setAnalysisFeatureSettings(Driver.getAnalysisSettingList()); return engine; } public static @CheckForNull SortedBugCollection loadBugs(MainFrame mainFrame, Project project, File source) { if (!source.isFile() || !source.canRead()) { JOptionPane.showMessageDialog(mainFrame, "Unable to read " + source); return null; } SortedBugCollection col = new SortedBugCollection(project); try { col.readXML(source); if (col.hasDeadBugs()) { addDeadBugMatcher(col); } } catch (Exception e) { e.printStackTrace(); JOptionPane.showMessageDialog(mainFrame, "Could not read " + source + "; " + e.getMessage()); } MainFrame.getInstance().setProjectAndBugCollectionInSwingThread(project, col); return col; } public static @CheckForNull SortedBugCollection loadBugs(MainFrame mainFrame, Project project, URL url) { SortedBugCollection col = new SortedBugCollection(project); try { if (MainFrame.GUI2_DEBUG) { System.out.println("loading from: " + url); JOptionPane.showMessageDialog(mainFrame, "loading from: " + url); } col.readXML(url); if (MainFrame.GUI2_DEBUG) { System.out.println("finished reading: " + url); JOptionPane.showMessageDialog(mainFrame, "loaded: " + url); } addDeadBugMatcher(col); } catch (Throwable e) { String msg = SystemProperties.getOSDependentProperty("findbugs.unableToLoadViaURL"); if (msg == null) { msg = e.getMessage(); } else { try { msg = String.format(msg, url); } catch (Exception e2) { msg = e.getMessage(); } } JOptionPane.showMessageDialog(mainFrame, "Could not read " + url + "\n" + msg); if (SystemProperties.getBoolean("findbugs.failIfUnableToLoadViaURL")) { System.exit(1); } } MainFrame.getInstance().setProjectAndBugCollectionInSwingThread(project, col); return col; } static void addDeadBugMatcher(BugCollection bugCollection) { if (bugCollection == null || !bugCollection.hasDeadBugs()) { return; } Filter suppressionMatcher = bugCollection.getProject().getSuppressionFilter(); suppressionMatcher.softAdd(LastVersionMatcher.DEAD_BUG_MATCHER); } public static @CheckForNull Project loadProject(MainFrame mainFrame, File f) { try { Project project = Project.readXML(f); project.setGuiCallback(mainFrame.getGuiCallback()); return project; } catch (IOException e) { JOptionPane.showMessageDialog(mainFrame, "Could not read " + f + "; " + e.getMessage()); } catch (SAXException | ParserConfigurationException e) { JOptionPane.showMessageDialog(mainFrame, "Could not read project from " + f + "; " + e.getMessage()); } return null; } private BugLoader() { throw new UnsupportedOperationException(); } /** * TODO: This really needs to be rewritten such that they don't have to * choose ALL xmls in one fel swoop. I'm thinking something more like new * project wizard's functionality. -Dan * * Merges bug collection histories from xmls selected by the user. Right now * all xmls must be in the same folder and he must select all of them at * once Makes use of FindBugs's mergeCollection method in the Update class * of the workflow package * * @return the merged collecction of bugs */ public static BugCollection combineBugHistories() { try { FBFileChooser chooser = new FBFileChooser(); chooser.setFileFilter(new FindBugsAnalysisFileFilter()); // chooser.setCurrentDirectory(GUISaveState.getInstance().getStarterDirectoryForLoadBugs()); // This is done by FBFileChooser. chooser.setMultiSelectionEnabled(true); chooser.setDialogTitle(edu.umd.cs.findbugs.L10N.getLocalString("dlg.choose_xmls_ttl", "Choose All XML's To Combine")); if (chooser.showOpenDialog(MainFrame.getInstance()) == JFileChooser.CANCEL_OPTION) { return null; } SortedBugCollection conglomeration = new SortedBugCollection(); conglomeration.readXML(chooser.getSelectedFiles()[0]); Update update = new Update(); for (int x = 1; x < chooser.getSelectedFiles().length; x++) { File f = chooser.getSelectedFiles()[x]; SortedBugCollection col = new SortedBugCollection(); col.readXML(f); conglomeration = (SortedBugCollection) update.mergeCollections(conglomeration, col, false, false);// False // means // dont // show // dead // bugs } return conglomeration; } catch (IOException e) { Debug.println(e); return null; } catch (DocumentException e) { Debug.println(e); return null; } } /** * Does what it says it does, hit apple r (control r on pc) and the analysis * is redone using the current project * * @param p * @return the bugs from the reanalysis, or null if cancelled */ public static @CheckForNull BugCollection doAnalysis(@Nonnull Project p) { requireNonNull(p, "null project"); RedoAnalysisCallback ac = new RedoAnalysisCallback(); AnalyzingDialog.show(p, ac, true); if (ac.finished) { return ac.getBugCollection(); } else { return null; } } /** * Does what it says it does, hit apple r (control r on pc) and the analysis * is redone using the current project * * @param p * @return the bugs from the reanalysis, or null if canceled */ public static @CheckForNull BugCollection redoAnalysisKeepComments(@Nonnull Project p) { requireNonNull(p, "null project"); BugCollection current = MainFrame.getInstance().getBugCollection(); Update update = new Update(); RedoAnalysisCallback ac = new RedoAnalysisCallback(); AnalyzingDialog.show(p, ac, true); if (!ac.finished) { return null; } if (current == null) { current = ac.getBugCollection(); } else { current = update.mergeCollections(current, ac.getBugCollection(), true, false); if (current.hasDeadBugs()) { addDeadBugMatcher(current); } } return current; } /** just used to know how the new analysis went */ private static class RedoAnalysisCallback implements AnalysisCallback { BugCollection getBugCollection() { return justAnalyzed; } BugCollection justAnalyzed; volatile boolean finished; @Override public void analysisFinished(BugCollection b) { justAnalyzed = b; finished = true; } @Override public void analysisInterrupted() { finished = false; } } }