001: /*
002: * FindBugs - Find Bugs in Java programs
003: * Copyright (C) 2006, University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307, USA
018: */
019:
020: package edu.umd.cs.findbugs.gui2;
021:
022: import java.awt.event.ActionEvent;
023: import java.awt.event.ActionListener;
024: import java.awt.event.WindowAdapter;
025: import java.awt.event.WindowEvent;
026: import java.io.IOException;
027:
028: import javax.swing.BoxLayout;
029: import javax.swing.JButton;
030: import javax.swing.JLabel;
031: import javax.swing.JOptionPane;
032: import javax.swing.JProgressBar;
033: import javax.swing.SwingUtilities;
034:
035: import edu.umd.cs.findbugs.BugCollection;
036: import edu.umd.cs.findbugs.FindBugsProgress;
037: import edu.umd.cs.findbugs.Project;
038: import edu.umd.cs.findbugs.annotations.NonNull;
039:
040: @SuppressWarnings("serial")
041: //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.
042: /**
043: *Creating an instance of this class runs a FindBugs analysis, and pops up a nice progress window
044: */
045: public final class AnalyzingDialog extends FBDialog implements
046: FindBugsProgress {
047: private volatile boolean analysisFinished = false;
048: @NonNull
049: private Project project;
050: private AnalysisCallback callback;
051: private AnalysisThread analysisThread = new AnalysisThread();
052:
053: private int count;
054: private int goal;
055:
056: private JLabel statusLabel;
057: private JProgressBar progressBar;
058: private JButton cancelButton;
059:
060: public AnalyzingDialog(@NonNull
061: final Project project, final boolean changeSettings) {
062: this (project, new AnalysisCallback() {
063: public void analysisFinished(BugCollection results) {
064: if (changeSettings)
065: ProjectSettings.newInstance();
066: MainFrame instance = MainFrame.getInstance();
067: instance.setProjectAndBugCollection(project, results);
068: }
069:
070: public void analysisInterrupted() {
071: }
072: }, false);
073: }
074:
075: /**
076: *
077: * @param project The Project to analyze
078: * @param callback contains what to do if the analysis is interrupted and what to do if it finishes normally
079: * @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.
080: */
081: public AnalyzingDialog(@NonNull
082: Project project, AnalysisCallback callback, boolean joinThread) {
083: if (project == null)
084: throw new NullPointerException("null project");
085: this .project = project;
086: this .callback = callback;
087: initComponents();
088: MainFrame.getInstance().showWaitCard();
089: analysisThread.start();
090: if (joinThread)
091: try {
092: analysisThread.join();
093: } catch (InterruptedException e) {
094: }
095: }
096:
097: public void initComponents() {
098: statusLabel = new JLabel(" ");
099: progressBar = new JProgressBar();
100: progressBar.setStringPainted(true);
101: cancelButton = new JButton(edu.umd.cs.findbugs.L10N
102: .getLocalString("dlg.cancel_btn", "Cancel"));
103: cancelButton.addActionListener(new ActionListener() {
104: public void actionPerformed(ActionEvent evt) {
105: cancel();
106: }
107: });
108: setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
109: addWindowListener(new WindowAdapter() {
110: @Override
111: public void windowClosed(WindowEvent evt) {
112: cancel();
113: }
114: });
115:
116: SwingUtilities.invokeLater(new Runnable() {
117: public void run() {
118: setLayout(new BoxLayout(getContentPane(),
119: BoxLayout.Y_AXIS));
120: add(statusLabel);
121: add(progressBar);
122: add(cancelButton);
123: statusLabel.setAlignmentX(CENTER_ALIGNMENT);
124: progressBar.setAlignmentX(CENTER_ALIGNMENT);
125: cancelButton.setAlignmentX(CENTER_ALIGNMENT);
126: pack();
127: setSize(300, getHeight());
128: setLocationRelativeTo(MainFrame.getInstance());
129: setResizable(false);
130: setModal(true);//Why was this set to false before?
131: setVisible(true);
132: }
133: });
134: }
135:
136: private void cancel() {
137: if (!analysisFinished) {
138: analysisThread.interrupt();
139: setVisible(false);
140: // TODO there should be a call to dispose() here, but it seems to cause repainting issues
141: }
142: }
143:
144: private void updateStage(String stage) {
145: statusLabel.setText(stage);
146: }
147:
148: private void incrementCount() {
149: count++;
150: SwingUtilities.invokeLater(new Runnable() {
151: public void run() {
152: progressBar.setString(count + "/" + goal);
153: progressBar.setValue(count);
154: }
155: });
156: }
157:
158: private void updateCount(final int count, final int goal) {
159: this .count = count;
160: this .goal = goal;
161: SwingUtilities.invokeLater(new Runnable() {
162: public void run() {
163: progressBar.setString(count + "/" + goal);
164: progressBar.setValue(count);
165: progressBar.setMaximum(goal);
166: }
167: });
168: }
169:
170: public void finishArchive() {
171: incrementCount();
172: }
173:
174: public void finishClass() {
175: incrementCount();
176: }
177:
178: public void finishPerClassAnalysis() {
179: updateStage(edu.umd.cs.findbugs.L10N.getLocalString(
180: "progress.finishing_analysis", "Finishing analysis..."));
181: }
182:
183: public void reportNumberOfArchives(int numArchives) {
184: updateStage(edu.umd.cs.findbugs.L10N.getLocalString(
185: "progress.scanning_archives", "Scanning archives..."));
186: updateCount(0, numArchives);
187: }
188:
189: public void startAnalysis(int numClasses) {
190: updateStage(edu.umd.cs.findbugs.L10N.getLocalString(
191: "progress.analyzing_classes", "Analyzing classes..."));
192: updateCount(0, numClasses);
193: }
194:
195: private class AnalysisThread extends Thread {
196: {
197: // Give the analysis thread lower priority than the UI
198: setPriority(NORM_PRIORITY - 1);
199: }
200:
201: @Override
202: public void run() {
203: if (project == null)
204: throw new NullPointerException("null project");
205:
206: BugCollection data;
207: try {
208: data = BugLoader.doAnalysis(project,
209: AnalyzingDialog.this );
210: } catch (InterruptedException e) {
211: callback.analysisInterrupted();
212: // We don't have to clean up the dialog because the
213: // cancel button handler does this already.
214: return;
215: } catch (IOException e) {
216: callback.analysisInterrupted();
217: scheduleDialogCleanup();
218: scheduleErrorDialog("Analysis failed", e.getMessage());
219: return;
220: }
221:
222: // Analysis succeeded
223: analysisFinished = true;
224: scheduleDialogCleanup();
225: callback.analysisFinished(data);
226: MainFrame.getInstance().newProject();
227: }
228:
229: private void scheduleDialogCleanup() {
230: SwingUtilities.invokeLater(new Runnable() {
231: /* (non-Javadoc)
232: * @see java.lang.Runnable#run()
233: */
234: public void run() {
235: AnalyzingDialog.this .setVisible(false);
236: }
237: });
238: }
239:
240: private void scheduleErrorDialog(final String title,
241: final String message) {
242: SwingUtilities.invokeLater(new Runnable() {
243: /* (non-Javadoc)
244: * @see java.lang.Runnable#run()
245: */
246: public void run() {
247: JOptionPane.showMessageDialog(MainFrame
248: .getInstance(), message, title,
249: JOptionPane.ERROR_MESSAGE);
250: }
251: });
252: }
253: }
254:
255: /* (non-Javadoc)
256: * @see edu.umd.cs.findbugs.FindBugsProgress#predictPassCount(int[])
257: */
258: public void predictPassCount(int[] classesPerPass) {
259: // TODO Auto-generated method stub
260:
261: }
262: }
|