/*
* 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 java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import edu.umd.cs.findbugs.BugCollection;
import edu.umd.cs.findbugs.FindBugsProgress;
import edu.umd.cs.findbugs.Project;
import edu.umd.cs.findbugs.annotations.NonNull;
@SuppressWarnings("serial")
//Note: Don't remove the final, if anyone extends this class, bad things could happen, since a thread is started in this class's constructor.
/**
*Creating an instance of this class runs a FindBugs analysis, and pops up a nice progress window
*/
public final class AnalyzingDialog extends FBDialog implements FindBugsProgress
{
private volatile boolean analysisFinished = false;
@NonNull private Project project;
private AnalysisCallback callback;
private AnalysisThread analysisThread = new AnalysisThread();
private int count;
private int goal;
private JLabel statusLabel;
private JProgressBar progressBar;
private JButton cancelButton;
public AnalyzingDialog(@NonNull final Project project, final boolean changeSettings)
{
this(project, new AnalysisCallback()
{
public void analysisFinished(BugCollection results)
{
if (changeSettings)
ProjectSettings.newInstance();
MainFrame instance = MainFrame.getInstance();
assert results.getProject() == project;
instance.setBugCollection(results);
}
public void analysisInterrupted() {
MainFrame instance = MainFrame.getInstance();
instance.updateProjectAndBugCollection(null, null, null);
}
}, false);
}
/**
*
* @param project The Project to analyze
* @param callback contains what to do if the analysis is interrupted and what to do if it finishes normally
* @param joinThread Whether or not this constructor should return before the analysis is complete. If true, the constructor does not return until the analysis is either finished or interrupted.
*/
public AnalyzingDialog(@NonNull Project project, AnalysisCallback callback, boolean joinThread)
{
if (project == null)
throw new NullPointerException("null project");
this.project = project;
this.callback = callback;
initComponents();
MainFrame.getInstance().showWaitCard();
analysisThread.start();
if (joinThread)
try {analysisThread.join();}
catch (InterruptedException e) {}
}
public void initComponents()
{
statusLabel = new JLabel(" ");
progressBar = new JProgressBar();
progressBar.setStringPainted(true);
cancelButton = new JButton(edu.umd.cs.findbugs.L10N.getLocalString("dlg.cancel_btn", "Cancel"));
cancelButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
cancel();
}
});
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter()
{
@Override
public void windowClosed(WindowEvent evt)
{
cancel();
}
});
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
add(statusLabel);
add(progressBar);
add(cancelButton);
statusLabel.setAlignmentX(CENTER_ALIGNMENT);
progressBar.setAlignmentX(CENTER_ALIGNMENT);
cancelButton.setAlignmentX(CENTER_ALIGNMENT);
pack();
setSize(300, getHeight());
setLocationRelativeTo(MainFrame.getInstance());
setResizable(false);
setModal(true);//Why was this set to false before?
setVisible(true);
}
});
}
private void cancel()
{
if (!analysisFinished)
{
analysisThread.interrupt();
setVisible(false);
// TODO there should be a call to dispose() here, but it seems to cause repainting issues
}
}
private void updateStage(String stage)
{
statusLabel.setText(stage);
}
private void incrementCount()
{
count++;
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
progressBar.setString(count + "/" + goal);
progressBar.setValue(count);
}
});
}
private void updateCount(final int count, final int goal)
{
this.count = count;
this.goal = goal;
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
progressBar.setString(count + "/" + goal);
progressBar.setValue(count);
progressBar.setMaximum(goal);
}
});
}
public void finishArchive()
{
incrementCount();
}
public void finishClass()
{
incrementCount();
}
public void finishPerClassAnalysis()
{
updateStage(edu.umd.cs.findbugs.L10N.getLocalString("progress.finishing_analysis", "Finishing analysis..."));
}
public void reportNumberOfArchives(int numArchives)
{
updateStage(edu.umd.cs.findbugs.L10N.getLocalString("progress.scanning_archives", "Scanning archives..."));
updateCount(0, numArchives);
}
int pass = 0;
public void startAnalysis(int numClasses)
{
pass++;
String localString = edu.umd.cs.findbugs.L10N.getLocalString("progress.analyzing_classes", "Analyzing classes...");
updateStage(localString + ", pass " + pass + "/" + classesPerPass.length);
updateCount(0, numClasses);
}
private class AnalysisThread extends Thread
{
{
// Give the analysis thread its (possibly user-defined) priority.
// The default is a slightly lower priority than the UI.
setPriority(Driver.getPriority());
}
@Override
public void run()
{
if (project == null) throw new NullPointerException("null project");
BugCollection data;
try {
data = BugLoader.doAnalysis(project, AnalyzingDialog.this);
} catch (InterruptedException e) {
callback.analysisInterrupted();
// We don't have to clean up the dialog because the
// cancel button handler does this already.
return;
} catch (IOException e) {
callback.analysisInterrupted();
scheduleDialogCleanup();
scheduleErrorDialog("Analysis failed", e.getMessage());
return;
}
// Analysis succeeded
analysisFinished = true;
scheduleDialogCleanup();
callback.analysisFinished(data);
MainFrame.getInstance().newProject();
}
private void scheduleDialogCleanup() {
SwingUtilities.invokeLater(new Runnable() {
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
AnalyzingDialog.this.setVisible(false);
}
});
}
private void scheduleErrorDialog(final String title, final String message) {
SwingUtilities.invokeLater(new Runnable() {
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
JOptionPane.showMessageDialog(
MainFrame.getInstance(),
message,
title,
JOptionPane.ERROR_MESSAGE);
}
});
}
}
int [] classesPerPass;
/* (non-Javadoc)
* @see edu.umd.cs.findbugs.FindBugsProgress#predictPassCount(int[])
*/
public void predictPassCount(int[] classesPerPass) {
this.classesPerPass = classesPerPass;
}
public void startArchive(String name) {
// noop
}
}