0001: /*
0002: * FindBugs - Find Bugs in Java programs
0003: * Copyright (C) 2006, University of Maryland
0004: *
0005: * This library is free software; you can redistribute it and/or
0006: * modify it under the terms of the GNU Lesser General Public
0007: * License as published by the Free Software Foundation; either
0008: * version 2.1 of the License, or (at your option) any later version.
0009: *
0010: * This library is distributed in the hope that it will be useful,
0011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0013: * Lesser General Public License for more details.
0014: *
0015: * You should have received a copy of the GNU Lesser General Public
0016: * License along with this library; if not, write to the Free Software
0017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307, USA
0018: */
0019:
0020: package edu.umd.cs.findbugs.gui2;
0021:
0022: import java.awt.BorderLayout;
0023: import java.awt.CardLayout;
0024: import java.awt.Color;
0025: import java.awt.Component;
0026: import java.awt.Container;
0027: import java.awt.Cursor;
0028: import java.awt.Dimension;
0029: import java.awt.EventQueue;
0030: import java.awt.Font;
0031: import java.awt.GridBagConstraints;
0032: import java.awt.GridBagLayout;
0033: import java.awt.GridLayout;
0034: import java.awt.Insets;
0035: import java.awt.Toolkit;
0036: import java.awt.event.ActionEvent;
0037: import java.awt.event.ActionListener;
0038: import java.awt.event.ComponentAdapter;
0039: import java.awt.event.ComponentEvent;
0040: import java.awt.event.KeyEvent;
0041: import java.awt.event.MouseAdapter;
0042: import java.awt.event.MouseEvent;
0043: import java.awt.event.MouseListener;
0044: import java.awt.event.WindowAdapter;
0045: import java.awt.event.WindowEvent;
0046: import java.io.File;
0047: import java.io.FileInputStream;
0048: import java.io.FileNotFoundException;
0049: import java.io.FileOutputStream;
0050: import java.io.IOException;
0051: import java.io.InputStream;
0052: import java.lang.reflect.Method;
0053: import java.net.MalformedURLException;
0054: import java.net.URL;
0055: import java.util.ArrayList;
0056: import java.util.Iterator;
0057: import java.util.zip.GZIPInputStream;
0058:
0059: import javax.swing.Action;
0060: import javax.swing.ActionMap;
0061: import javax.swing.BorderFactory;
0062: import javax.swing.Box;
0063: import javax.swing.ImageIcon;
0064: import javax.swing.JButton;
0065: import javax.swing.JEditorPane;
0066: import javax.swing.JFileChooser;
0067: import javax.swing.JLabel;
0068: import javax.swing.JMenu;
0069: import javax.swing.JMenuBar;
0070: import javax.swing.JMenuItem;
0071: import javax.swing.JOptionPane;
0072: import javax.swing.JPanel;
0073: import javax.swing.JPopupMenu;
0074: import javax.swing.JScrollPane;
0075: import javax.swing.JSplitPane;
0076: import javax.swing.JTable;
0077: import javax.swing.JTextField;
0078: import javax.swing.JToolTip;
0079: import javax.swing.JTree;
0080: import javax.swing.KeyStroke;
0081: import javax.swing.SwingUtilities;
0082: import javax.swing.UIManager;
0083: import javax.swing.border.BevelBorder;
0084: import javax.swing.event.TreeModelEvent;
0085: import javax.swing.event.TreeSelectionEvent;
0086: import javax.swing.event.TreeSelectionListener;
0087: import javax.swing.filechooser.FileFilter;
0088: import javax.swing.plaf.FontUIResource;
0089: import javax.swing.plaf.basic.BasicTreeUI;
0090: import javax.swing.table.DefaultTableModel;
0091: import javax.swing.table.JTableHeader;
0092: import javax.swing.text.JTextComponent;
0093: import javax.swing.text.TextAction;
0094: import javax.swing.text.html.HTMLEditorKit;
0095: import javax.swing.text.html.StyleSheet;
0096: import javax.swing.tree.TreePath;
0097: import javax.swing.tree.TreeSelectionModel;
0098:
0099: import edu.umd.cs.findbugs.BugAnnotation;
0100: import edu.umd.cs.findbugs.BugCollection;
0101: import edu.umd.cs.findbugs.BugInstance;
0102: import edu.umd.cs.findbugs.ClassAnnotation;
0103: import edu.umd.cs.findbugs.FieldAnnotation;
0104: import edu.umd.cs.findbugs.FindBugsDisplayFeatures;
0105: import edu.umd.cs.findbugs.I18N;
0106: import edu.umd.cs.findbugs.MethodAnnotation;
0107: import edu.umd.cs.findbugs.PackageMemberAnnotation;
0108: import edu.umd.cs.findbugs.Project;
0109: import edu.umd.cs.findbugs.SortedBugCollection;
0110: import edu.umd.cs.findbugs.SourceLineAnnotation;
0111: import edu.umd.cs.findbugs.SystemProperties;
0112: import edu.umd.cs.findbugs.annotations.CheckForNull;
0113: import edu.umd.cs.findbugs.annotations.NonNull;
0114: import edu.umd.cs.findbugs.ba.AnalysisContext;
0115: import edu.umd.cs.findbugs.ba.SourceFinder;
0116: import edu.umd.cs.findbugs.filter.Filter;
0117: import edu.umd.cs.findbugs.filter.LastVersionMatcher;
0118: import edu.umd.cs.findbugs.filter.Matcher;
0119: import edu.umd.cs.findbugs.gui.ConsoleLogger;
0120: import edu.umd.cs.findbugs.gui.LogSync;
0121: import edu.umd.cs.findbugs.gui.Logger;
0122: import edu.umd.cs.findbugs.gui2.BugTreeModel.TreeModification;
0123: import edu.umd.cs.findbugs.sourceViewer.NavigableTextPane;
0124:
0125: @SuppressWarnings("serial")
0126: /*
0127: * This is where it all happens... seriously... all of it...
0128: * All the menus are set up, all the listeners, all the frames, dockable window functionality
0129: * There is no one style used, no one naming convention, its all just kinda here. This is another one of those
0130: * classes where no one knows quite why it works.
0131: */
0132: /**
0133: * The MainFrame is just that, the main application window where just about everything happens.
0134: */
0135: public class MainFrame extends FBFrame implements LogSync {
0136: static JButton newButton(String key, String name) {
0137: JButton b = new JButton();
0138: edu.umd.cs.findbugs.L10N.localiseButton(b, key, name, false);
0139: return b;
0140: }
0141:
0142: static JMenuItem newJMenuItem(String key, String string, int vkF) {
0143: JMenuItem m = new JMenuItem();
0144: edu.umd.cs.findbugs.L10N.localiseButton(m, key, string, false);
0145: m.setMnemonic(vkF);
0146: return m;
0147:
0148: }
0149:
0150: static JMenuItem newJMenuItem(String key, String string) {
0151: JMenuItem m = new JMenuItem();
0152: edu.umd.cs.findbugs.L10N.localiseButton(m, key, string, true);
0153: return m;
0154:
0155: }
0156:
0157: static JMenu newJMenu(String key, String string) {
0158: JMenu m = new JMenu();
0159: edu.umd.cs.findbugs.L10N.localiseButton(m, key, string, true);
0160: return m;
0161: }
0162:
0163: JTree tree;
0164: private BasicTreeUI treeUI;
0165: boolean userInputEnabled;
0166:
0167: static boolean isMacLookAndFeel() {
0168: return UIManager.getLookAndFeel().getClass().getName()
0169: .startsWith("apple");
0170: }
0171:
0172: static final String DEFAULT_SOURCE_CODE_MSG = edu.umd.cs.findbugs.L10N
0173: .getLocalString("msg.nosource_txt", "No available source");
0174:
0175: static final int COMMENTS_TAB_STRUT_SIZE = 5;
0176: static final int COMMENTS_MARGIN = 5;
0177: static final int SEARCH_TEXT_FIELD_SIZE = 32;
0178:
0179: static final String TITLE_START_TXT = "FindBugs: ";
0180:
0181: private JTextField sourceSearchTextField = new JTextField(
0182: SEARCH_TEXT_FIELD_SIZE);
0183: private JButton findButton = newButton("button.find", "Find");
0184: private JButton findNextButton = newButton("button.findNext",
0185: "Find Next");
0186: private JButton findPreviousButton = newButton("button.findPrev",
0187: "Find Previous");
0188:
0189: public static final boolean DEBUG = SystemProperties
0190: .getBoolean("gui2.debug");
0191:
0192: static final boolean MAC_OS_X = SystemProperties.getProperty(
0193: "os.name").toLowerCase().startsWith("mac os x");
0194: final static String WINDOW_MODIFIED = "windowModified";
0195:
0196: NavigableTextPane sourceCodeTextPane = new NavigableTextPane();
0197: private JScrollPane sourceCodeScrollPane;
0198:
0199: final CommentsArea comments;
0200: private SorterTableColumnModel sorter;
0201: private JTableHeader tableheader;
0202: private JLabel statusBarLabel = new JLabel();
0203:
0204: private JPanel summaryTopPanel;
0205: private final HTMLEditorKit htmlEditorKit = new HTMLEditorKit();
0206: private final JEditorPane summaryHtmlArea = new JEditorPane();
0207: private JScrollPane summaryHtmlScrollPane = new JScrollPane(
0208: summaryHtmlArea);
0209:
0210: final private FindBugsLayoutManagerFactory findBugsLayoutManagerFactory;
0211: final private FindBugsLayoutManager guiLayout;
0212:
0213: /* To change this value must use setProjectChanged(boolean b).
0214: * This is because saveProjectItemMenu is dependent on it for when
0215: * saveProjectMenuItem should be enabled.
0216: */
0217: boolean projectChanged = false;
0218: final private JMenuItem reconfigMenuItem = newJMenuItem(
0219: "menu.reconfig", "Reconfigure...", KeyEvent.VK_F);
0220: private JMenuItem redoAnalysis;
0221: BugLeafNode currentSelectedBugLeaf;
0222: BugAspects currentSelectedBugAspects;
0223: private JPopupMenu bugPopupMenu;
0224: private JPopupMenu branchPopupMenu;
0225: private static MainFrame instance;
0226: private RecentMenu recentMenuCache;
0227: private JMenu recentMenu;
0228: private JMenuItem preferencesMenuItem;
0229: private Project curProject = new Project();
0230: private JScrollPane treeScrollPane;
0231: SourceFinder sourceFinder;
0232: private Object lock = new Object();
0233: private boolean newProject = false;
0234:
0235: private Class<?> osxAdapter;
0236: private Method osxPrefsEnableMethod;
0237:
0238: private Logger logger = new ConsoleLogger(this );
0239: SourceCodeDisplay displayer = new SourceCodeDisplay(this );
0240:
0241: private SaveType saveType = SaveType.NOT_KNOWN;
0242: FBFileChooser saveOpenFileChooser;
0243: FBFileChooser filterOpenFileChooser;
0244:
0245: @CheckForNull
0246: private File saveFile = null;
0247:
0248: enum SaveReturn {
0249: SAVE_SUCCESSFUL, SAVE_IO_EXCEPTION, SAVE_ERROR
0250: };
0251:
0252: JMenuItem saveMenuItem = newJMenuItem("menu.save_item", "Save",
0253: KeyEvent.VK_S);
0254:
0255: static void makeInstance(FindBugsLayoutManagerFactory factory) {
0256: if (instance != null)
0257: throw new IllegalStateException();
0258: instance = new MainFrame(factory);
0259: instance.initializeGUI();
0260: }
0261:
0262: /**
0263: * @param string
0264: * @param vkF
0265: * @return
0266: */
0267:
0268: static MainFrame getInstance() {
0269: if (instance == null)
0270: throw new IllegalStateException();
0271: return instance;
0272: }
0273:
0274: private void initializeGUI() {
0275: SwingUtilities.invokeLater(new InitializeGUI());
0276: }
0277:
0278: private MainFrame(FindBugsLayoutManagerFactory factory) {
0279: this .findBugsLayoutManagerFactory = factory;
0280: this .guiLayout = factory.getInstance(this );
0281: this .comments = new CommentsArea(this );
0282: FindBugsDisplayFeatures.setAbridgedMessages(true);
0283: }
0284:
0285: /**
0286: * Show About
0287: */
0288: void about() {
0289: AboutDialog dialog = new AboutDialog(this , logger, true);
0290: dialog.setSize(600, 554);
0291: dialog.setLocationRelativeTo(this );
0292: dialog.setVisible(true);
0293: }
0294:
0295: /**
0296: * Show Preferences
0297: */
0298: void preferences() {
0299: saveComments(currentSelectedBugLeaf, currentSelectedBugAspects);
0300: PreferencesFrame.getInstance().setLocationRelativeTo(
0301: MainFrame.this );
0302: PreferencesFrame.getInstance().setVisible(true);
0303: }
0304:
0305: /**
0306: * enable/disable preferences menu
0307: */
0308: void enablePreferences(boolean b) {
0309: preferencesMenuItem.setEnabled(b);
0310: if (MAC_OS_X) {
0311: if (osxPrefsEnableMethod != null) {
0312: Object args[] = { Boolean.valueOf(b) };
0313: try {
0314: osxPrefsEnableMethod.invoke(osxAdapter, args);
0315: } catch (Exception e) {
0316: System.err
0317: .println("Exception while enabling Preferences menu: "
0318: + e);
0319: }
0320: }
0321: }
0322: }
0323:
0324: /**
0325: * This method is called when the application is closing. This is either by
0326: * the exit menuItem or by clicking on the window's system menu.
0327: */
0328: void callOnClose() {
0329: comments.saveComments(currentSelectedBugLeaf,
0330: currentSelectedBugAspects);
0331: if (projectChanged) {
0332: int value = JOptionPane
0333: .showConfirmDialog(
0334: MainFrame.this ,
0335: edu.umd.cs.findbugs.L10N.getLocalString(
0336: "msg.you_are_closing_txt",
0337: "You are closing")
0338: + " "
0339: + edu.umd.cs.findbugs.L10N
0340: .getLocalString(
0341: "msg.without_saving_txt",
0342: "without saving. Do you want to save?"),
0343: edu.umd.cs.findbugs.L10N.getLocalString(
0344: "msg.confirm_save_txt",
0345: "Do you want to save?"),
0346: JOptionPane.YES_NO_CANCEL_OPTION,
0347: JOptionPane.QUESTION_MESSAGE);
0348:
0349: if (value == JOptionPane.CANCEL_OPTION
0350: || value == JOptionPane.CLOSED_OPTION)
0351: return;
0352: else if (value == JOptionPane.YES_OPTION) {
0353:
0354: if (saveFile == null) {
0355: if (!saveAs())
0356: return;
0357: } else
0358: save();
0359: }
0360: }
0361:
0362: GUISaveState.getInstance().setPreviousComments(
0363: comments.prevCommentsList);
0364: guiLayout.saveState();
0365: GUISaveState.getInstance().setFrameBounds(getBounds());
0366: GUISaveState.getInstance().save();
0367:
0368: System.exit(0);
0369: }
0370:
0371: /*
0372: * A lot of if(false) here is for switching from special cases based on localSaveType
0373: * to depending on the SaveType.forFile(f) method. Can delete when sure works.
0374: */
0375: JMenuItem createRecentItem(final File f,
0376: final SaveType localSaveType) {
0377: if (DEBUG)
0378: System.out.println("createRecentItem(" + f + ", "
0379: + localSaveType + ")");
0380: String name = f.getName();
0381:
0382: final JMenuItem item = new JMenuItem(name);
0383: item.addActionListener(new ActionListener() {
0384: public void actionPerformed(ActionEvent e) {
0385: try {
0386: setCursor(new Cursor(Cursor.WAIT_CURSOR));
0387:
0388: if (!f.exists()) {
0389: JOptionPane
0390: .showMessageDialog(
0391: null,
0392: edu.umd.cs.findbugs.L10N
0393: .getLocalString(
0394: "msg.proj_not_found",
0395: "This project can no longer be found"));
0396: GUISaveState.getInstance().fileNotFound(f);
0397: return;
0398: }
0399: GUISaveState.getInstance().fileReused(f);//Move to front in GUISaveState, so it will be last thing to be removed from the list
0400:
0401: MainFrame.this .recentMenuCache.addRecentFile(f);
0402:
0403: if (!f.exists())
0404: throw new IllegalStateException(
0405: "User used a recent projects menu item that didn't exist.");
0406:
0407: //Moved this outside of the thread, and above the line saveFile=f.getParentFile()
0408: //Since if this save goes on in the thread below, there is no way to stop the save from
0409: //overwriting the files we are about to load.
0410: if (curProject != null && projectChanged) {
0411: int response = JOptionPane
0412: .showConfirmDialog(
0413: MainFrame.this ,
0414: edu.umd.cs.findbugs.L10N
0415: .getLocalString(
0416: "dlg.save_current_changes",
0417: "The current project has been changed, Save current changes?"),
0418: edu.umd.cs.findbugs.L10N
0419: .getLocalString(
0420: "dlg.save_changes",
0421: "Save Changes?"),
0422: JOptionPane.YES_NO_CANCEL_OPTION,
0423: JOptionPane.WARNING_MESSAGE);
0424:
0425: if (response == JOptionPane.YES_OPTION) {
0426: if (saveFile != null)
0427: save();
0428: else
0429: saveAs();
0430: } else if (response == JOptionPane.CANCEL_OPTION)
0431: return;
0432: //IF no, do nothing.
0433: }
0434:
0435: SaveType st = SaveType.forFile(f);
0436: boolean result = true;
0437: switch (st) {
0438: case PROJECT:
0439: openProject(f);
0440: break;
0441: case XML_ANALYSIS:
0442: result = openAnalysis(f, st);
0443: break;
0444: case FBP_FILE:
0445: result = openFBPFile(f);
0446: break;
0447: case FBA_FILE:
0448: result = openFBAFile(f);
0449: break;
0450: default:
0451: error("Wrong file type in recent menu item.");
0452: }
0453:
0454: if (!result) {
0455: JOptionPane
0456: .showMessageDialog(
0457: MainFrame.getInstance(),
0458: "There was an error in opening the file",
0459: "Recent Menu Opening Error",
0460: JOptionPane.WARNING_MESSAGE);
0461: }
0462: } finally {
0463: setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
0464: setSaveType(localSaveType);
0465: }
0466: }
0467: });
0468: item.setFont(item.getFont().deriveFont(Driver.getFontSize()));
0469: return item;
0470: }
0471:
0472: BugCollection bugCollection;
0473:
0474: @SwingThread
0475: void setProjectAndBugCollection(Project project, @CheckForNull
0476: BugCollection bugCollection) {
0477: Filter suppressionMatcher = project.getSuppressionFilter();
0478: if (suppressionMatcher != null) {
0479: suppressionMatcher
0480: .softAdd(LastVersionMatcher.DEAD_BUG_MATCHER);
0481: }
0482: // setRebuilding(false);
0483: if (bugCollection == null) {
0484: showTreeCard();
0485: } else {
0486: curProject = project;
0487: this .bugCollection = bugCollection;
0488: displayer.clearCache();
0489:
0490: BugTreeModel model = (BugTreeModel) getTree().getModel();
0491: setSourceFinder(new SourceFinder());
0492: getSourceFinder().setSourceBaseList(
0493: project.getSourceDirList());
0494: BugSet bs = new BugSet(bugCollection);
0495: model.getOffListenerList();
0496: model.changeSet(bs);
0497: if (bs.size() == 0 && bs.sizeUnfiltered() > 0) {
0498: warnUserOfFilters();
0499: }
0500: updateStatusBar();
0501: }
0502: PreferencesFrame.getInstance().updateFilterPanel();
0503: setProjectChanged(false);
0504: reconfigMenuItem.setEnabled(true);
0505: newProject();
0506: clearSourcePane();
0507: clearSummaryTab();
0508:
0509: /* This is here due to a threading issue. It can only be called after
0510: * curProject has been changed. Since this method is called by both open methods
0511: * it is put here.*/
0512: changeTitle();
0513: }
0514:
0515: void updateProjectAndBugCollection(Project project,
0516: BugCollection bugCollection, BugTreeModel previousModel) {
0517: setRebuilding(false);
0518: if (bugCollection != null) {
0519: displayer.clearCache();
0520: BugSet bs = new BugSet(bugCollection);
0521: //Dont clear data, the data's correct, just get the tree off the listener lists.
0522: ((BugTreeModel) tree.getModel()).getOffListenerList();
0523: ((BugTreeModel) tree.getModel()).changeSet(bs);
0524: //curProject=BugLoader.getLoadedProject();
0525: setProjectChanged(true);
0526: }
0527: setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
0528: }
0529:
0530: /**
0531: * Changes the title based on curProject and saveFile.
0532: *
0533: */
0534: public void changeTitle() {
0535: String name = curProject.getProjectName();
0536: if (name == null && saveFile != null)
0537: name = saveFile.getAbsolutePath();
0538: if (name == null)
0539: name = Project.UNNAMED_PROJECT;
0540: MainFrame.this .setTitle(TITLE_START_TXT + name);
0541: }
0542:
0543: /**
0544: * Creates popup menu for bugs on tree.
0545: * @return
0546: */
0547: private JPopupMenu createBugPopupMenu() {
0548: JPopupMenu popupMenu = new JPopupMenu();
0549:
0550: JMenuItem filterMenuItem = newJMenuItem(
0551: "menu.filterBugsLikeThis", "Filter bugs like this");
0552:
0553: filterMenuItem.addActionListener(new ActionListener() {
0554: public void actionPerformed(ActionEvent evt) {
0555: saveComments(currentSelectedBugLeaf,
0556: currentSelectedBugAspects);
0557: new NewFilterFromBug(currentSelectedBugLeaf.getBug());
0558:
0559: setProjectChanged(true);
0560: MainFrame.getInstance().getTree().setSelectionRow(0);//Selects the top of the Jtree so the CommentsArea syncs up.
0561: }
0562: });
0563:
0564: popupMenu.add(filterMenuItem);
0565:
0566: JMenu changeDesignationMenu = newJMenu(
0567: "menu.changeDesignation", "Change bug designation");
0568:
0569: int i = 0;
0570: int keyEvents[] = { KeyEvent.VK_1, KeyEvent.VK_2,
0571: KeyEvent.VK_3, KeyEvent.VK_4, KeyEvent.VK_5,
0572: KeyEvent.VK_6, KeyEvent.VK_7, KeyEvent.VK_8,
0573: KeyEvent.VK_9 };
0574: for (String key : I18N.instance().getUserDesignationKeys(true)) {
0575: String name = I18N.instance().getUserDesignation(key);
0576: comments.addDesignationItem(changeDesignationMenu, name,
0577: keyEvents[i++]);
0578: }
0579:
0580: popupMenu.add(changeDesignationMenu);
0581:
0582: return popupMenu;
0583: }
0584:
0585: /**
0586: * Creates the branch pop up menu that ask if the user wants
0587: * to hide all the bugs in that branch.
0588: * @return
0589: */
0590: private JPopupMenu createBranchPopUpMenu() {
0591: JPopupMenu popupMenu = new JPopupMenu();
0592:
0593: JMenuItem filterMenuItem = newJMenuItem("menu.filterTheseBugs",
0594: "Filter these bugs");
0595:
0596: filterMenuItem.addActionListener(new ActionListener() {
0597: public void actionPerformed(ActionEvent evt) {
0598: //TODO This code does a smarter version of filtering that is only possible for branches, and does so correctly
0599: //However, it is still somewhat of a hack, because if we ever add more tree listeners than simply the bugtreemodel,
0600: //They will not be called by this code. Using FilterActivity to notify all listeners will however destroy any
0601: //benefit of using the smarter deletion method.
0602: saveComments(currentSelectedBugLeaf,
0603: currentSelectedBugAspects);
0604: int startCount;
0605: TreePath path = MainFrame.getInstance().getTree()
0606: .getSelectionPath();
0607: TreePath deletePath = path;
0608: startCount = ((BugAspects) (path.getLastPathComponent()))
0609: .getCount();
0610: int count = ((BugAspects) (path.getParentPath()
0611: .getLastPathComponent())).getCount();
0612: while (count == startCount) {
0613: deletePath = deletePath.getParentPath();
0614: if (deletePath.getParentPath() == null)//We are at the top of the tree, don't let this be removed, rebuild tree from root.
0615: {
0616: Matcher m = currentSelectedBugAspects
0617: .getMatcher();
0618: Filter suppressionFilter = MainFrame
0619: .getInstance().getProject()
0620: .getSuppressionFilter();
0621: suppressionFilter.addChild(m);
0622: PreferencesFrame.getInstance()
0623: .updateFilterPanel();
0624: FilterActivity.notifyListeners(
0625: FilterListener.Action.FILTERING, null);
0626: return;
0627: }
0628: count = ((BugAspects) (deletePath.getParentPath()
0629: .getLastPathComponent())).getCount();
0630: }
0631: /*
0632: deletePath should now be a path to the highest
0633: ancestor branch with the same number of elements
0634: as the branch to be deleted
0635: in other words, the branch that we actually have
0636: to remove in order to correctly remove the selected branch.
0637: */
0638: BugTreeModel model = MainFrame.getInstance()
0639: .getBugTreeModel();
0640: TreeModelEvent event = new TreeModelEvent(this ,
0641: deletePath.getParentPath(),
0642: new int[] { model.getIndexOfChild(
0643: deletePath.getParentPath()
0644: .getLastPathComponent(),
0645: deletePath.getLastPathComponent()) },
0646: new Object[] { deletePath
0647: .getLastPathComponent() });
0648: Matcher m = currentSelectedBugAspects.getMatcher();
0649: Filter suppressionFilter = MainFrame.getInstance()
0650: .getProject().getSuppressionFilter();
0651: suppressionFilter.addChild(m);
0652: PreferencesFrame.getInstance().updateFilterPanel();
0653: model.sendEvent(event, TreeModification.REMOVE);
0654: // FilterActivity.notifyListeners(FilterListener.Action.FILTERING, null);
0655:
0656: setProjectChanged(true);
0657:
0658: MainFrame.getInstance().getTree().setSelectionRow(0);//Selects the top of the Jtree so the CommentsArea syncs up.
0659: }
0660: });
0661:
0662: popupMenu.add(filterMenuItem);
0663:
0664: JMenu changeDesignationMenu = newJMenu(
0665: "menu.changeDesignation", "Change bug designation");
0666:
0667: int i = 0;
0668: int keyEvents[] = { KeyEvent.VK_1, KeyEvent.VK_2,
0669: KeyEvent.VK_3, KeyEvent.VK_4, KeyEvent.VK_5,
0670: KeyEvent.VK_6, KeyEvent.VK_7, KeyEvent.VK_8,
0671: KeyEvent.VK_9 };
0672: for (String key : I18N.instance().getUserDesignationKeys(true)) {
0673: String name = I18N.instance().getUserDesignation(key);
0674: addDesignationItem(changeDesignationMenu, name,
0675: keyEvents[i++]);
0676: }
0677:
0678: popupMenu.add(changeDesignationMenu);
0679:
0680: return popupMenu;
0681: }
0682:
0683: /**
0684: * Creates the MainFrame's menu bar.
0685: * @return the menu bar for the MainFrame
0686: */
0687: protected JMenuBar createMainMenuBar() {
0688: JMenuBar menuBar = new JMenuBar();
0689:
0690: //Create JMenus for menuBar.
0691: JMenu fileMenu = newJMenu("menu.file_menu", "File");
0692: fileMenu.setMnemonic(KeyEvent.VK_F);
0693: JMenu editMenu = newJMenu("menu.edit_menu", "Edit");
0694: editMenu.setMnemonic(KeyEvent.VK_E);
0695:
0696: //Edit fileMenu JMenu object.
0697: JMenuItem newProjectMenuItem = newJMenuItem("menu.new_item",
0698: "New Project", KeyEvent.VK_N);
0699: JMenuItem openMenuItem = newJMenuItem("menu.open_item",
0700: "Open...", KeyEvent.VK_O);
0701: recentMenu = newJMenu("menu.recent", "Recent");
0702: recentMenuCache = new RecentMenu(recentMenu);
0703: JMenuItem saveAsMenuItem = newJMenuItem("menu.saveas_item",
0704: "Save As...", KeyEvent.VK_A);
0705: redoAnalysis = newJMenuItem("menu.rerunAnalysis",
0706: "Redo Analysis", KeyEvent.VK_R);
0707: JMenuItem importFilter = newJMenuItem("menu.importFilter_item",
0708: "Import filter...");
0709: JMenuItem exportFilter = newJMenuItem("menu.exportFilter_item",
0710: "Export filter...");
0711:
0712: JMenuItem exitMenuItem = null;
0713: if (!MAC_OS_X) {
0714: exitMenuItem = newJMenuItem("menu.exit", "Exit",
0715: KeyEvent.VK_X);
0716: exitMenuItem.addActionListener(new ActionListener() {
0717: public void actionPerformed(ActionEvent evt) {
0718: callOnClose();
0719: }
0720: });
0721: }
0722: JMenu windowMenu = guiLayout.createWindowMenu();
0723:
0724: attachAcceleratorKey(newProjectMenuItem, KeyEvent.VK_N);
0725:
0726: newProjectMenuItem.addActionListener(new ActionListener() {
0727: public void actionPerformed(ActionEvent evt) {
0728: newProjectMenu();
0729: }
0730: });
0731:
0732: reconfigMenuItem.setEnabled(false);
0733: attachAcceleratorKey(reconfigMenuItem, KeyEvent.VK_F);
0734: reconfigMenuItem.addActionListener(new ActionListener() {
0735: public void actionPerformed(ActionEvent evt) {
0736: saveComments(currentSelectedBugLeaf,
0737: currentSelectedBugAspects);
0738: new NewProjectWizard(curProject);
0739: }
0740: });
0741:
0742: JMenuItem mergeMenuItem = newJMenuItem("menu.mergeAnalysis",
0743: "Merge Analysis...");
0744:
0745: mergeMenuItem.setEnabled(true);
0746: mergeMenuItem.addActionListener(new ActionListener() {
0747: public void actionPerformed(ActionEvent evt) {
0748: mergeAnalysis();
0749: }
0750: });
0751:
0752: redoAnalysis.setEnabled(false);
0753: attachAcceleratorKey(redoAnalysis, KeyEvent.VK_R);
0754: redoAnalysis.addActionListener(new ActionListener() {
0755: public void actionPerformed(ActionEvent evt) {
0756: redoAnalysis();
0757: }
0758: });
0759:
0760: openMenuItem.setEnabled(true);
0761: attachAcceleratorKey(openMenuItem, KeyEvent.VK_O);
0762: openMenuItem.addActionListener(new ActionListener() {
0763: public void actionPerformed(ActionEvent evt) {
0764: open();
0765: }
0766: });
0767:
0768: saveAsMenuItem.addActionListener(new ActionListener() {
0769: public void actionPerformed(ActionEvent evt) {
0770: saveAs();
0771: }
0772: });
0773: exportFilter.addActionListener(new ActionListener() {
0774: public void actionPerformed(ActionEvent evt) {
0775: exportFilter();
0776: }
0777: });
0778: importFilter.addActionListener(new ActionListener() {
0779: public void actionPerformed(ActionEvent evt) {
0780: importFilter();
0781: }
0782: });
0783: saveMenuItem.setEnabled(false);
0784: attachAcceleratorKey(saveMenuItem, KeyEvent.VK_S);
0785: saveMenuItem.addActionListener(new ActionListener() {
0786: public void actionPerformed(ActionEvent evt) {
0787: save();
0788: }
0789: });
0790:
0791: fileMenu.add(newProjectMenuItem);
0792: fileMenu.add(reconfigMenuItem);
0793: fileMenu.addSeparator();
0794:
0795: fileMenu.add(openMenuItem);
0796: fileMenu.add(recentMenu);
0797: fileMenu.addSeparator();
0798: fileMenu.add(importFilter);
0799: fileMenu.add(exportFilter);
0800: fileMenu.addSeparator();
0801: fileMenu.add(saveAsMenuItem);
0802: fileMenu.add(saveMenuItem);
0803:
0804: fileMenu.addSeparator();
0805: fileMenu.add(redoAnalysis);
0806: // fileMenu.add(mergeMenuItem);
0807:
0808: //TODO: This serves no purpose but to test something
0809: if (false) {
0810: JMenuItem temp = new JMenuItem("Temp");
0811: temp.addActionListener(new ActionListener() {
0812: public void actionPerformed(ActionEvent arg0) {
0813: System.out.println("Current Project Name: "
0814: + curProject.getProjectName());
0815: }
0816: });
0817: fileMenu.add(temp);
0818: }
0819:
0820: if (exitMenuItem != null) {
0821: fileMenu.addSeparator();
0822: fileMenu.add(exitMenuItem);
0823: }
0824:
0825: menuBar.add(fileMenu);
0826:
0827: //Edit editMenu Menu object.
0828: JMenuItem cutMenuItem = new JMenuItem(new CutAction());
0829: JMenuItem copyMenuItem = new JMenuItem(new CopyAction());
0830: JMenuItem pasteMenuItem = new JMenuItem(new PasteAction());
0831: preferencesMenuItem = newJMenuItem("menu.preferences_menu",
0832: "Filters/Suppressions...");
0833: JMenuItem sortMenuItem = newJMenuItem("menu.sortConfiguration",
0834: "Sort Configuration...");
0835: JMenuItem goToLineMenuItem = newJMenuItem("menu.gotoLine",
0836: "Go to line...");
0837:
0838: attachAcceleratorKey(cutMenuItem, KeyEvent.VK_X);
0839: attachAcceleratorKey(copyMenuItem, KeyEvent.VK_C);
0840: attachAcceleratorKey(pasteMenuItem, KeyEvent.VK_V);
0841:
0842: preferencesMenuItem.addActionListener(new ActionListener() {
0843: public void actionPerformed(ActionEvent evt) {
0844: preferences();
0845: }
0846: });
0847:
0848: sortMenuItem.addActionListener(new ActionListener() {
0849: public void actionPerformed(ActionEvent evt) {
0850: saveComments(currentSelectedBugLeaf,
0851: currentSelectedBugAspects);
0852: SorterDialog.getInstance().setLocationRelativeTo(
0853: MainFrame.this );
0854: SorterDialog.getInstance().setVisible(true);
0855: }
0856: });
0857:
0858: attachAcceleratorKey(goToLineMenuItem, KeyEvent.VK_L);
0859: goToLineMenuItem.addActionListener(new ActionListener() {
0860: public void actionPerformed(ActionEvent evt) {
0861: guiLayout.makeSourceVisible();
0862: try {
0863: int num = Integer
0864: .parseInt(JOptionPane
0865: .showInputDialog(
0866: MainFrame.this ,
0867: "",
0868: edu.umd.cs.findbugs.L10N
0869: .getLocalString(
0870: "dlg.go_to_line_lbl",
0871: "Go To Line")
0872: + ":",
0873: JOptionPane.QUESTION_MESSAGE));
0874: displayer.showLine(num);
0875: } catch (NumberFormatException e) {
0876: }
0877: }
0878: });
0879:
0880: editMenu.add(cutMenuItem);
0881: editMenu.add(copyMenuItem);
0882: editMenu.add(pasteMenuItem);
0883: editMenu.addSeparator();
0884: editMenu.add(goToLineMenuItem);
0885: editMenu.addSeparator();
0886: //editMenu.add(selectAllMenuItem);
0887: // editMenu.addSeparator();
0888: if (!MAC_OS_X) {
0889: // Preferences goes in Findbugs menu and is handled by OSXAdapter
0890: editMenu.add(preferencesMenuItem);
0891: }
0892: editMenu.add(sortMenuItem);
0893:
0894: menuBar.add(editMenu);
0895:
0896: if (windowMenu != null)
0897: menuBar.add(windowMenu);
0898:
0899: final ActionMap map = tree.getActionMap();
0900:
0901: JMenu navMenu = newJMenu("menu.navigation", "Navigation");
0902:
0903: addNavItem(map, navMenu, "menu.expand", "Expand", "expand",
0904: KeyEvent.VK_RIGHT);
0905: addNavItem(map, navMenu, "menu.collapse", "Collapse",
0906: "collapse", KeyEvent.VK_LEFT);
0907: addNavItem(map, navMenu, "menu.up", "Up", "selectPrevious",
0908: KeyEvent.VK_UP);
0909: addNavItem(map, navMenu, "menu.down", "Down", "selectNext",
0910: KeyEvent.VK_DOWN);
0911:
0912: menuBar.add(navMenu);
0913:
0914: JMenu designationMenu = newJMenu("menu.designation",
0915: "Designation");
0916: int i = 0;
0917: int keyEvents[] = { KeyEvent.VK_1, KeyEvent.VK_2,
0918: KeyEvent.VK_3, KeyEvent.VK_4, KeyEvent.VK_5,
0919: KeyEvent.VK_6, KeyEvent.VK_7, KeyEvent.VK_8,
0920: KeyEvent.VK_9 };
0921: for (String key : I18N.instance().getUserDesignationKeys(true)) {
0922: String name = I18N.instance().getUserDesignation(key);
0923: addDesignationItem(designationMenu, name, keyEvents[i++]);
0924: }
0925: menuBar.add(designationMenu);
0926:
0927: if (!MAC_OS_X) {
0928: // On Mac, 'About' appears under Findbugs menu, so no need for it here
0929: JMenu helpMenu = newJMenu("menu.help_menu", "Help");
0930: JMenuItem aboutItem = newJMenuItem("menu.about_item",
0931: "About FindBugs");
0932: helpMenu.add(aboutItem);
0933:
0934: aboutItem
0935: .addActionListener(new java.awt.event.ActionListener() {
0936: public void actionPerformed(
0937: java.awt.event.ActionEvent evt) {
0938: about();
0939: }
0940: });
0941: menuBar.add(helpMenu);
0942: }
0943: return menuBar;
0944: }
0945:
0946: /**
0947: * @param map
0948: * @param navMenu
0949: */
0950: private void addNavItem(final ActionMap map, JMenu navMenu,
0951: String menuNameKey, String menuNameDefault,
0952: String actionName, int keyEvent) {
0953: JMenuItem toggleItem = newJMenuItem(menuNameKey,
0954: menuNameDefault);
0955: toggleItem
0956: .addActionListener(treeActionAdapter(map, actionName));
0957: attachAcceleratorKey(toggleItem, keyEvent);
0958: navMenu.add(toggleItem);
0959: }
0960:
0961: ActionListener treeActionAdapter(ActionMap map, String actionName) {
0962: final Action selectPrevious = map.get(actionName);
0963: return new ActionListener() {
0964: public void actionPerformed(ActionEvent e) {
0965: e.setSource(tree);
0966: selectPrevious.actionPerformed(e);
0967: }
0968: };
0969: }
0970:
0971: static void attachAcceleratorKey(JMenuItem item, int keystroke) {
0972: attachAcceleratorKey(item, keystroke, 0);
0973: }
0974:
0975: static void attachAcceleratorKey(JMenuItem item, int keystroke,
0976: int additionalMask) {
0977: // As far as I know, Mac is the only platform on which it is normal
0978: // practice to use accelerator masks such as Shift and Alt, so
0979: // if we're not running on Mac, just ignore them
0980: if (!MAC_OS_X && additionalMask != 0) {
0981: return;
0982: }
0983:
0984: item.setAccelerator(KeyStroke.getKeyStroke(keystroke, Toolkit
0985: .getDefaultToolkit().getMenuShortcutKeyMask()
0986: | additionalMask));
0987: }
0988:
0989: void newProject() {
0990: clearSourcePane();
0991: redoAnalysis.setEnabled(true);
0992:
0993: if (newProject) {
0994: setProjectChanged(true);
0995: // setTitle(TITLE_START_TXT + Project.UNNAMED_PROJECT);
0996: saveFile = null;
0997: saveMenuItem.setEnabled(false);
0998: reconfigMenuItem.setEnabled(true);
0999: newProject = false;
1000: }
1001: }
1002:
1003: /**
1004: * This method is for when the user wants to open a file.
1005: */
1006: private void importFilter() {
1007: boolean loading = true;
1008: filterOpenFileChooser.setDialogTitle(edu.umd.cs.findbugs.L10N
1009: .getLocalString("dlg.importFilter_ttl",
1010: "Import and merge filter..."));
1011:
1012: boolean retry = true;
1013: boolean alreadyExists = true;
1014: File f = null;
1015: while (retry) {
1016: retry = false;
1017:
1018: int value = filterOpenFileChooser
1019: .showOpenDialog(MainFrame.this );
1020:
1021: if (value != JFileChooser.APPROVE_OPTION)
1022: return;
1023:
1024: f = filterOpenFileChooser.getSelectedFile();
1025:
1026: if (!f.exists()) {
1027: JOptionPane.showMessageDialog(filterOpenFileChooser,
1028: "No such file", "Invalid File",
1029: JOptionPane.WARNING_MESSAGE);
1030: retry = true;
1031: continue;
1032: }
1033: Filter filter;
1034: try {
1035: filter = Filter.parseFilter(f.getPath());
1036: } catch (IOException e) {
1037: JOptionPane.showMessageDialog(filterOpenFileChooser,
1038: "Could not load filter.");
1039: retry = true;
1040: continue;
1041: }
1042: projectChanged = true;
1043: if (curProject.getSuppressionFilter() == null) {
1044: curProject.setSuppressionFilter(filter);
1045: } else {
1046: for (Matcher m : filter.getChildren())
1047: curProject.getSuppressionFilter().addChild(m);
1048: }
1049: PreferencesFrame.getInstance().updateFilterPanel();
1050: }
1051:
1052: }
1053:
1054: /**
1055: * This method is for when the user wants to open a file.
1056: */
1057: private void open() {
1058: saveComments(currentSelectedBugLeaf, currentSelectedBugAspects);
1059:
1060: if (projectChanged) {
1061: int response = JOptionPane
1062: .showConfirmDialog(
1063: MainFrame.this ,
1064: edu.umd.cs.findbugs.L10N
1065: .getLocalString(
1066: "dlg.save_current_changes",
1067: "The current project has been changed, Save current changes?"),
1068: edu.umd.cs.findbugs.L10N
1069: .getLocalString("dlg.save_changes",
1070: "Save Changes?"),
1071: JOptionPane.YES_NO_CANCEL_OPTION,
1072: JOptionPane.WARNING_MESSAGE);
1073:
1074: if (response == JOptionPane.YES_OPTION) {
1075: if (saveFile != null)
1076: save();
1077: else
1078: saveAs();
1079: } else if (response == JOptionPane.CANCEL_OPTION)
1080: return;
1081: //IF no, do nothing.
1082: }
1083:
1084: boolean loading = true;
1085: SaveType fileType = SaveType.NOT_KNOWN;
1086: tryAgain: while (loading) {
1087: int value = saveOpenFileChooser
1088: .showOpenDialog(MainFrame.this );
1089: if (value != JFileChooser.APPROVE_OPTION)
1090: return;
1091:
1092: loading = false;
1093: fileType = convertFilterToType(saveOpenFileChooser
1094: .getFileFilter());
1095: final File f = saveOpenFileChooser.getSelectedFile();
1096:
1097: if (!fileType.isValid(f)) {
1098: JOptionPane
1099: .showMessageDialog(
1100: saveOpenFileChooser,
1101: "That file is not compatible with the choosen file type",
1102: "Invalid File",
1103: JOptionPane.WARNING_MESSAGE);
1104: loading = true;
1105: continue;
1106: }
1107:
1108: switch (fileType) {
1109: case PROJECT:
1110: File xmlFile = OriginalGUI2ProjectFile
1111: .fileContainingXMLData(f);
1112:
1113: if (!xmlFile.exists()) {
1114: JOptionPane
1115: .showMessageDialog(
1116: saveOpenFileChooser,
1117: edu.umd.cs.findbugs.L10N
1118: .getLocalString(
1119: "dlg.no_xml_data_lbl",
1120: "This directory does not contain saved bug XML data, please choose a different directory."));
1121: loading = true;
1122: continue tryAgain;
1123: }
1124:
1125: openProject(f);
1126: break;
1127: case XML_ANALYSIS:
1128: if (!f.getName().endsWith(".xml")) {
1129: JOptionPane
1130: .showMessageDialog(
1131: saveOpenFileChooser,
1132: edu.umd.cs.findbugs.L10N
1133: .getLocalString(
1134: "dlg.not_xml_data_lbl",
1135: "This is not a saved bug XML data file."));
1136: loading = true;
1137: continue tryAgain;
1138: }
1139:
1140: if (!openAnalysis(f, fileType)) {
1141: //TODO: Deal if something happens when loading analysis
1142: JOptionPane
1143: .showMessageDialog(saveOpenFileChooser,
1144: "An error occurred while trying to load the analysis.");
1145: loading = true;
1146: continue tryAgain;
1147: }
1148: break;
1149: case FBP_FILE:
1150: if (!openFBPFile(f)) {
1151: //TODO: Deal if something happens when loading analysis
1152: JOptionPane
1153: .showMessageDialog(saveOpenFileChooser,
1154: "An error occurred while trying to load the analysis.");
1155: loading = true;
1156: continue tryAgain;
1157: }
1158: break;
1159: case FBA_FILE:
1160: if (!openFBAFile(f)) {
1161: //TODO: Deal if something happens when loading analysis
1162: JOptionPane
1163: .showMessageDialog(saveOpenFileChooser,
1164: "An error occurred while trying to load the analysis.");
1165: loading = true;
1166: continue tryAgain;
1167: }
1168: break;
1169: }
1170: }
1171: }
1172:
1173: /**
1174: * Method called to open FBA file.
1175: * @param f
1176: * @return
1177: */
1178: private boolean openFBAFile(File f) {
1179: return openAnalysis(f, SaveType.FBA_FILE);
1180: }
1181:
1182: /**
1183: * Method called to open FBP file.
1184: * @param f
1185: * @return
1186: */
1187: private boolean openFBPFile(File f) {
1188: if (!f.exists() || !f.canRead()) {
1189: return false;
1190: }
1191:
1192: prepareForFileLoad(f, SaveType.FBP_FILE);
1193:
1194: loadProjectFromFile(f);
1195:
1196: return true;
1197:
1198: }
1199:
1200: private boolean exportFilter() {
1201: if (curProject == null
1202: || curProject.getSuppressionFilter() == null) {
1203: JOptionPane.showMessageDialog(MainFrame.this ,
1204: edu.umd.cs.findbugs.L10N.getLocalString(
1205: "dlg.no_filter", "There is no filter"));
1206: return false;
1207: }
1208:
1209: filterOpenFileChooser.setDialogTitle(edu.umd.cs.findbugs.L10N
1210: .getLocalString("dlg.exportFilter_ttl",
1211: "Export filter..."));
1212:
1213: boolean retry = true;
1214: boolean alreadyExists = true;
1215: File f = null;
1216: while (retry) {
1217: retry = false;
1218:
1219: int value = filterOpenFileChooser
1220: .showSaveDialog(MainFrame.this );
1221:
1222: if (value != JFileChooser.APPROVE_OPTION)
1223: return false;
1224:
1225: f = filterOpenFileChooser.getSelectedFile();
1226:
1227: alreadyExists = f.exists();
1228: if (alreadyExists) {
1229: int response = JOptionPane
1230: .showConfirmDialog(
1231: filterOpenFileChooser,
1232: edu.umd.cs.findbugs.L10N
1233: .getLocalString(
1234: "dlg.file_exists_lbl",
1235: "This file already exists.\nReplace it?"),
1236: edu.umd.cs.findbugs.L10N
1237: .getLocalString(
1238: "dlg.warning_ttl",
1239: "Warning!"),
1240: JOptionPane.OK_CANCEL_OPTION,
1241: JOptionPane.WARNING_MESSAGE);
1242:
1243: if (response == JOptionPane.OK_OPTION)
1244: retry = false;
1245: if (response == JOptionPane.CANCEL_OPTION) {
1246: retry = true;
1247: continue;
1248: }
1249:
1250: }
1251:
1252: Filter suppressionFilter = curProject
1253: .getSuppressionFilter();
1254: try {
1255: suppressionFilter
1256: .writeEnabledMatchersAsXML(new FileOutputStream(
1257: f));
1258: } catch (IOException e) {
1259: JOptionPane.showMessageDialog(MainFrame.this ,
1260: edu.umd.cs.findbugs.L10N.getLocalString(
1261: "dlg.saving_error_lbl",
1262: "An error occurred in saving."));
1263: return false;
1264: }
1265: }
1266:
1267: return true;
1268: }
1269:
1270: private boolean saveAs() {
1271: saveComments(currentSelectedBugLeaf, currentSelectedBugAspects);
1272:
1273: saveOpenFileChooser.setDialogTitle(edu.umd.cs.findbugs.L10N
1274: .getLocalString("dlg.saveas_ttl", "Save as..."));
1275:
1276: if (curProject == null) {
1277: JOptionPane.showMessageDialog(MainFrame.this ,
1278: edu.umd.cs.findbugs.L10N.getLocalString(
1279: "dlg.no_proj_save_lbl",
1280: "There is no project to save"));
1281: return false;
1282: }
1283:
1284: boolean retry = true;
1285: SaveType fileType = SaveType.NOT_KNOWN;
1286: boolean alreadyExists = true;
1287: File f = null;
1288: while (retry) {
1289: retry = false;
1290:
1291: int value = saveOpenFileChooser
1292: .showSaveDialog(MainFrame.this );
1293:
1294: if (value != JFileChooser.APPROVE_OPTION)
1295: return false;
1296:
1297: fileType = convertFilterToType(saveOpenFileChooser
1298: .getFileFilter());
1299: if (fileType == SaveType.NOT_KNOWN) {
1300: Debug.println("Error! fileType == SaveType.NOT_KNOWN");
1301: //This should never happen b/c saveOpenFileChooser can only display filters
1302: //given it.
1303: retry = true;
1304: continue;
1305: }
1306:
1307: f = saveOpenFileChooser.getSelectedFile();
1308:
1309: f = convertFile(f, fileType);
1310:
1311: if (!fileType.isValid(f)) {
1312: JOptionPane
1313: .showMessageDialog(
1314: saveOpenFileChooser,
1315: "That file is not compatible with the chosen file type",
1316: "Invalid File",
1317: JOptionPane.WARNING_MESSAGE);
1318: retry = true;
1319: continue;
1320: }
1321:
1322: alreadyExists = fileAlreadyExists(f, fileType);
1323: if (alreadyExists) {
1324: int response = -1;
1325:
1326: switch (fileType) {
1327: case XML_ANALYSIS:
1328: response = JOptionPane
1329: .showConfirmDialog(
1330: saveOpenFileChooser,
1331: edu.umd.cs.findbugs.L10N
1332: .getLocalString(
1333: "dlg.analysis_exists_lbl",
1334: "This analysis already exists.\nReplace it?"),
1335: edu.umd.cs.findbugs.L10N
1336: .getLocalString(
1337: "dlg.warning_ttl",
1338: "Warning!"),
1339: JOptionPane.OK_CANCEL_OPTION,
1340: JOptionPane.WARNING_MESSAGE);
1341: break;
1342: case PROJECT:
1343: response = JOptionPane
1344: .showConfirmDialog(
1345: saveOpenFileChooser,
1346: edu.umd.cs.findbugs.L10N
1347: .getLocalString(
1348: "dlg.proj_already_exists_lbl",
1349: "This project already exists.\nDo you want to replace it?"),
1350: edu.umd.cs.findbugs.L10N
1351: .getLocalString(
1352: "dlg.warning_ttl",
1353: "Warning!"),
1354: JOptionPane.OK_CANCEL_OPTION,
1355: JOptionPane.WARNING_MESSAGE);
1356: break;
1357: case FBP_FILE:
1358: response = JOptionPane
1359: .showConfirmDialog(
1360: saveOpenFileChooser,
1361: edu.umd.cs.findbugs.L10N
1362: .getLocalString(
1363: "FB Project File already exists",
1364: "This FB project file already exists.\nDo you want to replace it?"),
1365: edu.umd.cs.findbugs.L10N
1366: .getLocalString(
1367: "dlg.warning_ttl",
1368: "Warning!"),
1369: JOptionPane.OK_CANCEL_OPTION,
1370: JOptionPane.WARNING_MESSAGE);
1371: break;
1372: case FBA_FILE:
1373: response = JOptionPane
1374: .showConfirmDialog(
1375: saveOpenFileChooser,
1376: edu.umd.cs.findbugs.L10N
1377: .getLocalString(
1378: "FB Analysis File already exists",
1379: "This FB analysis file already exists.\nDo you want to replace it?"),
1380: edu.umd.cs.findbugs.L10N
1381: .getLocalString(
1382: "dlg.warning_ttl",
1383: "Warning!"),
1384: JOptionPane.OK_CANCEL_OPTION,
1385: JOptionPane.WARNING_MESSAGE);
1386: break;
1387: }
1388:
1389: if (response == JOptionPane.OK_OPTION)
1390: retry = false;
1391: if (response == JOptionPane.CANCEL_OPTION) {
1392: retry = true;
1393: continue;
1394: }
1395:
1396: }
1397:
1398: SaveReturn successful = SaveReturn.SAVE_ERROR;
1399:
1400: switch (fileType) {
1401: case PROJECT:
1402: successful = saveProject(f);
1403: break;
1404: case XML_ANALYSIS:
1405: successful = saveAnalysis(f);
1406: break;
1407: case FBA_FILE:
1408: successful = saveFBAFile(f);
1409: break;
1410: case FBP_FILE:
1411: successful = saveFBPFile(f);
1412: break;
1413: }
1414:
1415: if (successful != SaveReturn.SAVE_SUCCESSFUL) {
1416: JOptionPane.showMessageDialog(MainFrame.this ,
1417: edu.umd.cs.findbugs.L10N.getLocalString(
1418: "dlg.saving_error_lbl",
1419: "An error occurred in saving."));
1420: return false;
1421: }
1422: }
1423:
1424: // saveProjectMenuItem.setEnabled(false);
1425: saveMenuItem.setEnabled(false);
1426: setSaveType(fileType);
1427: saveFile = f;
1428: File xmlFile;
1429: if (getSaveType() == SaveType.PROJECT)
1430: xmlFile = new File(f.getAbsolutePath() + File.separator
1431: + f.getName() + ".xml");
1432: else
1433: xmlFile = f;
1434:
1435: addFileToRecent(xmlFile, getSaveType());
1436:
1437: return true;
1438: }
1439:
1440: /*
1441: * Returns SaveType equivalent depending on what kind of FileFilter passed.
1442: */
1443: private SaveType convertFilterToType(FileFilter f) {
1444: if (f instanceof FindBugsFileFilter)
1445: return ((FindBugsFileFilter) f).getSaveType();
1446:
1447: return SaveType.NOT_KNOWN;
1448: }
1449:
1450: /*
1451: * Depending on File and SaveType determines if f already exists. Returns false if not
1452: * exist, true otherwise. For a project calls
1453: * OriginalGUI2ProjectFile.fileContainingXMLData(f).exists
1454: */
1455: private boolean fileAlreadyExists(File f, SaveType fileType) {
1456: if (fileType == SaveType.PROJECT) {
1457: /*File xmlFile=new File(f.getAbsolutePath() + File.separator + f.getName() + ".xml");
1458: File fasFile=new File(f.getAbsolutePath() + File.separator + f.getName() + ".fas");
1459: return xmlFile.exists() || fasFile.exists();*/
1460: return (OriginalGUI2ProjectFile.fileContainingXMLData(f)
1461: .exists());
1462: }
1463:
1464: return f.exists();
1465: }
1466:
1467: /*
1468: * If f is not of type FileType will convert file so it is.
1469: */
1470: private File convertFile(File f, SaveType fileType) {
1471: //WARNING: This assumes the filefilter for project is working so f can only be a directory.
1472: if (fileType == SaveType.PROJECT)
1473: return f;
1474:
1475: //Checks that it has the correct file extension, makes a new file if it doesn't.
1476: if (!f.getName().endsWith(fileType.getFileExtension()))
1477: f = new File(f.getAbsolutePath()
1478: + fileType.getFileExtension());
1479:
1480: return f;
1481: }
1482:
1483: private void save() {
1484: File sFile = saveFile;
1485: assert sFile != null;
1486: saveComments(currentSelectedBugLeaf, currentSelectedBugAspects);
1487:
1488: SaveReturn result = SaveReturn.SAVE_ERROR;
1489:
1490: switch (getSaveType()) {
1491: case PROJECT:
1492: result = saveProject(sFile);
1493: break;
1494: case XML_ANALYSIS:
1495: result = saveAnalysis(sFile);
1496: break;
1497: case FBA_FILE:
1498: result = saveFBAFile(sFile);
1499: break;
1500: case FBP_FILE:
1501: result = saveFBPFile(sFile);
1502: break;
1503: }
1504:
1505: if (result != SaveReturn.SAVE_SUCCESSFUL) {
1506: JOptionPane.showMessageDialog(MainFrame.this ,
1507: edu.umd.cs.findbugs.L10N.getLocalString(
1508: "dlg.saving_error_lbl",
1509: "An error occurred in saving."));
1510: }
1511: }
1512:
1513: /**
1514: * @param saveFile2
1515: * @return
1516: */
1517: private SaveReturn saveFBAFile(File saveFile2) {
1518: return saveAnalysis(saveFile2);
1519: }
1520:
1521: /**
1522: * @param saveFile2
1523: * @return
1524: */
1525: private SaveReturn saveFBPFile(File saveFile2) {
1526: saveComments(currentSelectedBugLeaf, currentSelectedBugAspects);
1527: try {
1528: curProject.writeXML(saveFile2);
1529: } catch (IOException e) {
1530: AnalysisContext.logError("Couldn't save FBP file to "
1531: + saveFile2, e);
1532: return SaveReturn.SAVE_IO_EXCEPTION;
1533: }
1534:
1535: setProjectChanged(false);
1536:
1537: return SaveReturn.SAVE_SUCCESSFUL;
1538: }
1539:
1540: static final String TREECARD = "Tree";
1541: static final String WAITCARD = "Wait";
1542:
1543: public void showWaitCard() {
1544: showCard(WAITCARD, new Cursor(Cursor.WAIT_CURSOR));
1545: }
1546:
1547: public void showTreeCard() {
1548: showCard(TREECARD, new Cursor(Cursor.DEFAULT_CURSOR));
1549: }
1550:
1551: private void showCard(final String c, final Cursor cursor) {
1552: if (SwingUtilities.isEventDispatchThread()) {
1553: setCursor(cursor);
1554: CardLayout layout = (CardLayout) cardPanel.getLayout();
1555: layout.show(cardPanel, c);
1556: } else
1557: SwingUtilities.invokeLater(new Runnable() {
1558: public void run() {
1559: setCursor(cursor);
1560: CardLayout layout = (CardLayout) cardPanel
1561: .getLayout();
1562: layout.show(cardPanel, c);
1563: }
1564: });
1565: }
1566:
1567: JPanel waitPanel, cardPanel;
1568:
1569: /**
1570: *
1571: * @return
1572: */
1573: JPanel bugListPanel() {
1574: cardPanel = new JPanel(new CardLayout());
1575:
1576: JPanel topPanel = new JPanel();
1577: waitPanel = new JPanel();
1578: waitPanel.add(new JLabel("Please wait..."));
1579: cardPanel.add(topPanel, TREECARD);
1580: cardPanel.add(waitPanel, WAITCARD);
1581:
1582: topPanel.setMinimumSize(new Dimension(200, 200));
1583: tableheader = new JTableHeader();
1584: //Listener put here for when user double clicks on sorting
1585: //column header SorterDialog appears.
1586: tableheader.addMouseListener(new MouseAdapter() {
1587:
1588: @Override
1589: public void mouseClicked(MouseEvent e) {
1590: Debug.println("tableheader.getReorderingAllowed() = "
1591: + tableheader.getReorderingAllowed());
1592: if (!tableheader.getReorderingAllowed())
1593: return;
1594: if (e.getClickCount() == 2)
1595: SorterDialog.getInstance().setVisible(true);
1596: }
1597:
1598: @Override
1599: public void mouseReleased(MouseEvent arg0) {
1600: if (!tableheader.getReorderingAllowed())
1601: return;
1602: BugTreeModel bt = (BugTreeModel) (MainFrame.this
1603: .getTree().getModel());
1604: bt.checkSorter();
1605: }
1606: });
1607: sorter = GUISaveState.getInstance().getStarterTable();
1608: tableheader.setColumnModel(sorter);
1609: tableheader.setToolTipText(edu.umd.cs.findbugs.L10N
1610: .getLocalString("tooltip.reorder_message",
1611: "Drag to reorder tree folder and sort order"));
1612:
1613: tree = new JTree();
1614: treeUI = (BasicTreeUI) tree.getUI();
1615: tree.setLargeModel(true);
1616: tree.getSelectionModel().setSelectionMode(
1617: TreeSelectionModel.SINGLE_TREE_SELECTION);
1618: tree.setCellRenderer(new BugRenderer());
1619: tree.setRowHeight((int) (Driver.getFontSize() + 7));
1620: if (false) {
1621:
1622: System.out.println("Left indent had been "
1623: + treeUI.getLeftChildIndent());
1624: System.out.println("Right indent had been "
1625: + treeUI.getRightChildIndent());
1626: treeUI.setLeftChildIndent(30);
1627: treeUI.setRightChildIndent(30);
1628: }
1629: tree.setModel(new BugTreeModel(tree, sorter, new BugSet(
1630: new ArrayList<BugLeafNode>())));
1631: setupTreeListeners();
1632: curProject = new Project();
1633:
1634: treeScrollPane = new JScrollPane(tree);
1635: topPanel.setLayout(new BorderLayout());
1636:
1637: //New code to fix problem in Windows
1638: JTable t = new JTable(new DefaultTableModel(0, Sortables
1639: .values().length));
1640: t.setTableHeader(tableheader);
1641: JScrollPane sp = new JScrollPane(t);
1642: //This sets the height of the scrollpane so it is dependent on the fontsize.
1643: int num = (int) (Driver.getFontSize() * 1.2);
1644: sp.setPreferredSize(new Dimension(0, 10 + num));
1645: //End of new code.
1646: //Changed code.
1647: topPanel.add(sp, BorderLayout.NORTH);
1648: //topPanel.add(tableheader, BorderLayout.NORTH);
1649: //End of changed code.
1650: topPanel.add(treeScrollPane, BorderLayout.CENTER);
1651:
1652: return cardPanel;
1653: }
1654:
1655: public void newTree(final JTree newTree, final BugTreeModel newModel) {
1656: SwingUtilities.invokeLater(new Runnable() {
1657: public void run() {
1658: tree = newTree;
1659: tree.getSelectionModel().setSelectionMode(
1660: TreeSelectionModel.SINGLE_TREE_SELECTION);
1661: tree.setLargeModel(true);
1662: tree.setCellRenderer(new BugRenderer());
1663: showTreeCard();
1664: Container container = treeScrollPane.getParent();
1665:
1666: container.remove(treeScrollPane);
1667: treeScrollPane = new JScrollPane(newTree);
1668: container.add(treeScrollPane, BorderLayout.CENTER);
1669: setFontSizeHelper(container.getComponents(), Driver
1670: .getFontSize());
1671: tree.setRowHeight((int) (Driver.getFontSize() + 7));
1672: MainFrame.getInstance().getContentPane().validate();
1673: MainFrame.getInstance().getContentPane().repaint();
1674:
1675: setupTreeListeners();
1676: newModel.openPreviouslySelected(((BugTreeModel) (tree
1677: .getModel())).getOldSelectedBugs());
1678: MainFrame.this .getSorter().addColumnModelListener(
1679: newModel);
1680: FilterActivity
1681: .addFilterListener(newModel.bugTreeFilterListener);
1682: MainFrame.this .setSorting(true);
1683:
1684: }
1685: });
1686: }
1687:
1688: private void setupTreeListeners() {
1689: tree.addTreeSelectionListener(new TreeSelectionListener() {
1690: public void valueChanged(TreeSelectionEvent selectionEvent) {
1691:
1692: TreePath path = selectionEvent
1693: .getNewLeadSelectionPath();
1694: if (path != null) {
1695: saveComments(currentSelectedBugLeaf,
1696: currentSelectedBugAspects);
1697:
1698: Object lastPathComponent = path
1699: .getLastPathComponent();
1700: if (lastPathComponent instanceof BugLeafNode) {
1701: boolean beforeProjectChanged = projectChanged;
1702: currentSelectedBugLeaf = (BugLeafNode) lastPathComponent;
1703: currentSelectedBugAspects = null;
1704: syncBugInformation();
1705: setProjectChanged(beforeProjectChanged);
1706: } else {
1707: boolean beforeProjectChanged = projectChanged;
1708: updateDesignationDisplay();
1709: currentSelectedBugLeaf = null;
1710: currentSelectedBugAspects = (BugAspects) lastPathComponent;
1711: syncBugInformation();
1712: setProjectChanged(beforeProjectChanged);
1713: }
1714: }
1715:
1716: // Debug.println("Tree selection count:" + tree.getSelectionCount());
1717: if (tree.getSelectionCount() != 1) {
1718: Debug
1719: .println("Tree selection count not equal to 1, disabling comments tab"
1720: + selectionEvent);
1721:
1722: MainFrame.this .setUserCommentInputEnable(false);
1723: }
1724: }
1725: });
1726:
1727: tree.addMouseListener(new MouseListener() {
1728:
1729: public void mouseClicked(MouseEvent e) {
1730: TreePath path = tree.getPathForLocation(e.getX(), e
1731: .getY());
1732:
1733: if (path == null)
1734: return;
1735:
1736: if ((e.getButton() == MouseEvent.BUTTON3)
1737: || (e.getButton() == MouseEvent.BUTTON1 && e
1738: .isControlDown())) {
1739:
1740: if (tree.getModel().isLeaf(
1741: path.getLastPathComponent())) {
1742: tree.setSelectionPath(path);
1743: bugPopupMenu.show(tree, e.getX(), e.getY());
1744: } else {
1745: tree.setSelectionPath(path);
1746: if (!(path.getParentPath() == null))//If the path's parent path is null, the root was selected, dont allow them to filter out the root.
1747: branchPopupMenu.show(tree, e.getX(), e
1748: .getY());
1749: }
1750: }
1751: }
1752:
1753: public void mousePressed(MouseEvent arg0) {
1754: }
1755:
1756: public void mouseReleased(MouseEvent arg0) {
1757: }
1758:
1759: public void mouseEntered(MouseEvent arg0) {
1760: }
1761:
1762: public void mouseExited(MouseEvent arg0) {
1763: }
1764: });
1765: }
1766:
1767: void syncBugInformation() {
1768: boolean prevProjectChanged = projectChanged;
1769: if (currentSelectedBugLeaf != null) {
1770: BugInstance bug = currentSelectedBugLeaf.getBug();
1771: displayer.displaySource(bug, bug
1772: .getPrimarySourceLineAnnotation());
1773: comments
1774: .updateCommentsFromLeafInformation(currentSelectedBugLeaf);
1775: updateDesignationDisplay();
1776: comments
1777: .updateCommentsFromLeafInformation(currentSelectedBugLeaf);
1778: updateSummaryTab(currentSelectedBugLeaf);
1779: } else if (currentSelectedBugAspects != null) {
1780: updateDesignationDisplay();
1781: comments
1782: .updateCommentsFromNonLeafInformation(currentSelectedBugAspects);
1783: displayer.displaySource(null, null);
1784: clearSummaryTab();
1785: } else {
1786: displayer.displaySource(null, null);
1787: clearSummaryTab();
1788: }
1789: setProjectChanged(prevProjectChanged);
1790: }
1791:
1792: /**
1793: * Clears the source code text pane.
1794: *
1795: */
1796: void clearSourcePane() {
1797: SwingUtilities.invokeLater(new Runnable() {
1798: public void run() {
1799: setSourceTabTitle("Source");
1800: sourceCodeTextPane
1801: .setDocument(SourceCodeDisplay.SOURCE_NOT_RELEVANT);
1802: }
1803: });
1804: }
1805:
1806: /**
1807: * @param b
1808: */
1809: private void setUserCommentInputEnable(boolean b) {
1810: comments.setUserCommentInputEnable(b);
1811:
1812: }
1813:
1814: /**
1815: * Creates the status bar of the GUI.
1816: * @return
1817: */
1818: JPanel statusBar() {
1819: JPanel statusBar = new JPanel();
1820: // statusBar.setBackground(Color.WHITE);
1821:
1822: statusBar.setBorder(new BevelBorder(BevelBorder.LOWERED));
1823: statusBar.setLayout(new BorderLayout());
1824: statusBar.add(statusBarLabel, BorderLayout.WEST);
1825:
1826: JLabel logoLabel = new JLabel();
1827:
1828: ImageIcon logoIcon = new ImageIcon(MainFrame.class
1829: .getResource("logo_umd.png"));
1830: logoLabel.setIcon(logoIcon);
1831: statusBar.add(logoLabel, BorderLayout.EAST);
1832:
1833: return statusBar;
1834: }
1835:
1836: void updateStatusBar() {
1837:
1838: int countFilteredBugs = BugSet.countFilteredBugs();
1839: if (countFilteredBugs == 0)
1840: statusBarLabel
1841: .setText(" http://findbugs.sourceforge.net/");
1842: else if (countFilteredBugs == 1)
1843: statusBarLabel.setText(" 1 "
1844: + edu.umd.cs.findbugs.L10N.getLocalString(
1845: "statusbar.bug_hidden", "bug hidden"));
1846: else
1847: statusBarLabel.setText(" "
1848: + countFilteredBugs
1849: + " "
1850: + edu.umd.cs.findbugs.L10N.getLocalString(
1851: "statusbar.bugs_hidden", "bugs hidden"));
1852: }
1853:
1854: private void updateSummaryTab(BugLeafNode node) {
1855: final BugInstance bug = node.getBug();
1856:
1857: final ArrayList<BugAnnotation> primaryAnnotations = new ArrayList<BugAnnotation>();
1858: boolean classIncluded = false;
1859:
1860: //This ensures the order of the primary annotations of the bug
1861: if (bug.getPrimarySourceLineAnnotation() != null)
1862: primaryAnnotations
1863: .add(bug.getPrimarySourceLineAnnotation());
1864: if (bug.getPrimaryMethod() != null)
1865: primaryAnnotations.add(bug.getPrimaryMethod());
1866: if (bug.getPrimaryField() != null)
1867: primaryAnnotations.add(bug.getPrimaryField());
1868:
1869: /*
1870: * This makes the primary class annotation appear only when
1871: * the visible field and method primary annotations don't have
1872: * the same class.
1873: */
1874: if (bug.getPrimaryClass() != null) {
1875: FieldAnnotation primeField = bug.getPrimaryField();
1876: MethodAnnotation primeMethod = bug.getPrimaryMethod();
1877: ClassAnnotation primeClass = bug.getPrimaryClass();
1878: String fieldClass = "";
1879: String methodClass = "";
1880: if (primeField != null)
1881: fieldClass = primeField.getClassName();
1882: if (primeMethod != null)
1883: methodClass = primeMethod.getClassName();
1884: if ((primaryAnnotations.size() < 2)
1885: || (!(primeClass.getClassName().equals(fieldClass) || primeClass
1886: .getClassName().equals(methodClass)))) {
1887: primaryAnnotations.add(primeClass);
1888: classIncluded = true;
1889: }
1890: }
1891:
1892: final boolean classIncluded2 = classIncluded;
1893:
1894: SwingUtilities.invokeLater(new Runnable() {
1895: public void run() {
1896: summaryTopPanel.removeAll();
1897:
1898: summaryTopPanel.add(bugSummaryComponent(bug
1899: .getMessageWithoutPrefix(), bug));
1900: for (BugAnnotation b : primaryAnnotations)
1901: summaryTopPanel.add(bugSummaryComponent(b, bug));
1902:
1903: if (!classIncluded2 && bug.getPrimaryClass() != null)
1904: primaryAnnotations.add(bug.getPrimaryClass());
1905:
1906: for (Iterator<BugAnnotation> i = bug
1907: .annotationIterator(); i.hasNext();) {
1908: BugAnnotation b = i.next();
1909: boolean cont = true;
1910: for (BugAnnotation p : primaryAnnotations)
1911: if (p == b)
1912: cont = false;
1913:
1914: if (cont)
1915: summaryTopPanel
1916: .add(bugSummaryComponent(b, bug));
1917: }
1918:
1919: summaryHtmlArea.setText(bug.getBugPattern()
1920: .getDetailHTML());
1921:
1922: summaryTopPanel.add(Box.createVerticalGlue());
1923: summaryTopPanel.revalidate();
1924:
1925: SwingUtilities.invokeLater(new Runnable() {
1926: public void run() {
1927: summaryHtmlScrollPane.getVerticalScrollBar()
1928: .setValue(
1929: summaryHtmlScrollPane
1930: .getVerticalScrollBar()
1931: .getMinimum());
1932: }
1933: });
1934: }
1935: });
1936: }
1937:
1938: private void clearSummaryTab() {
1939: summaryHtmlArea.setText("");
1940: summaryTopPanel.removeAll();
1941: summaryTopPanel.revalidate();
1942: }
1943:
1944: /**
1945: * Creates initial summary tab and sets everything up.
1946: * @return
1947: */
1948: JSplitPane summaryTab() {
1949: int fontSize = (int) Driver.getFontSize();
1950: summaryTopPanel = new JPanel();
1951: summaryTopPanel.setLayout(new GridLayout(0, 1));
1952: summaryTopPanel.setBorder(BorderFactory.createEmptyBorder(2, 4,
1953: 2, 4));
1954: summaryTopPanel.setMinimumSize(new Dimension(fontSize * 50,
1955: fontSize * 5));
1956:
1957: JPanel summaryTopOuter = new JPanel(new BorderLayout());
1958: summaryTopOuter.add(summaryTopPanel, BorderLayout.NORTH);
1959:
1960: summaryHtmlArea
1961: .setToolTipText(edu.umd.cs.findbugs.L10N
1962: .getLocalString("tooltip.longer_description",
1963: "This gives a longer description of the detected bug pattern"));
1964: summaryHtmlArea.setContentType("text/html");
1965: summaryHtmlArea.setEditable(false);
1966: summaryHtmlArea
1967: .addHyperlinkListener(new javax.swing.event.HyperlinkListener() {
1968: public void hyperlinkUpdate(
1969: javax.swing.event.HyperlinkEvent evt) {
1970: AboutDialog.editorPaneHyperlinkUpdate(evt);
1971: }
1972: });
1973: setStyleSheets();
1974: //JPanel temp = new JPanel(new BorderLayout());
1975: //temp.add(summaryTopPanel, BorderLayout.CENTER);
1976: JScrollPane summaryScrollPane = new JScrollPane(summaryTopOuter);
1977: summaryScrollPane.getVerticalScrollBar().setUnitIncrement(
1978: (int) Driver.getFontSize());
1979:
1980: JSplitPane splitP = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
1981: false, summaryScrollPane, summaryHtmlScrollPane);
1982: splitP.setDividerLocation(GUISaveState.getInstance()
1983: .getSplitSummary());
1984: splitP.setOneTouchExpandable(true);
1985: return splitP;
1986: }
1987:
1988: /**
1989: * Creates bug summary component. If obj is a string will create a JLabel
1990: * with that string as it's text and return it. If obj is an annotation
1991: * will return a JLabel with the annotation's toString(). If that
1992: * annotation is a SourceLineAnnotation or has a SourceLineAnnotation
1993: * connected to it and the source file is available will attach
1994: * a listener to the label.
1995: * @param obj
1996: * @param bug TODO
1997: * @return
1998: */
1999: private Component bugSummaryComponent(Object obj, BugInstance bug) {
2000: JLabel label = new JLabel();
2001: label.setFont(label.getFont().deriveFont(Driver.getFontSize()));
2002: label.setFont(label.getFont().deriveFont(Font.PLAIN));
2003: label.setForeground(Color.BLACK);
2004:
2005: if (obj instanceof String) {
2006: String str = (String) obj;
2007: label.setText(str);
2008: } else {
2009:
2010: BugAnnotation value = (BugAnnotation) obj;
2011:
2012: if (value == null)
2013: return new JLabel(edu.umd.cs.findbugs.L10N
2014: .getLocalString("summary.null", "null"));
2015:
2016: if (value instanceof SourceLineAnnotation) {
2017: final SourceLineAnnotation note = (SourceLineAnnotation) value;
2018: if (sourceCodeExist(note)) {
2019: String srcStr = "";
2020: int start = note.getStartLine();
2021: int end = note.getEndLine();
2022: if (start < 0 && end < 0)
2023: srcStr = edu.umd.cs.findbugs.L10N
2024: .getLocalString("summary.source_code",
2025: "source code.");
2026: else if (start == end)
2027: srcStr = " ["
2028: + edu.umd.cs.findbugs.L10N
2029: .getLocalString("summary.line",
2030: "Line") + " " + start
2031: + "]";
2032: else if (start < end)
2033: srcStr = " ["
2034: + edu.umd.cs.findbugs.L10N
2035: .getLocalString(
2036: "summary.lines",
2037: "Lines") + " " + start
2038: + " - " + end + "]";
2039:
2040: label.setToolTipText(edu.umd.cs.findbugs.L10N
2041: .getLocalString("tooltip.click_to_go_to",
2042: "Click to go to")
2043: + " " + srcStr);
2044:
2045: label.addMouseListener(new BugSummaryMouseListener(
2046: bug, label, note));
2047: }
2048:
2049: label.setText(note.toString());
2050: } else if (value instanceof PackageMemberAnnotation) {
2051: PackageMemberAnnotation note = (PackageMemberAnnotation) value;
2052: final SourceLineAnnotation noteSrc = note
2053: .getSourceLines();
2054: String srcStr = "";
2055: if (sourceCodeExist(noteSrc) && noteSrc != null) {
2056: int start = noteSrc.getStartLine();
2057: int end = noteSrc.getEndLine();
2058: if (start < 0 && end < 0)
2059: srcStr = edu.umd.cs.findbugs.L10N
2060: .getLocalString("summary.source_code",
2061: "source code.");
2062: else if (start == end)
2063: srcStr = " ["
2064: + edu.umd.cs.findbugs.L10N
2065: .getLocalString("summary.line",
2066: "Line") + " " + start
2067: + "]";
2068: else if (start < end)
2069: srcStr = " ["
2070: + edu.umd.cs.findbugs.L10N
2071: .getLocalString(
2072: "summary.lines",
2073: "Lines") + " " + start
2074: + " - " + end + "]";
2075:
2076: if (!srcStr.equals("")) {
2077: label.setToolTipText(edu.umd.cs.findbugs.L10N
2078: .getLocalString(
2079: "tooltip.click_to_go_to",
2080: "Click to go to")
2081: + " " + srcStr);
2082: label
2083: .addMouseListener(new BugSummaryMouseListener(
2084: bug, label, noteSrc));
2085: }
2086: }
2087: if (!srcStr.equals(edu.umd.cs.findbugs.L10N
2088: .getLocalString("summary.source_code",
2089: "source code.")))
2090: label.setText(note.toString() + srcStr);
2091: else
2092: label.setText(note.toString());
2093: } else {
2094: label.setText(((BugAnnotation) value).toString());
2095: }
2096: }
2097:
2098: return label;
2099: }
2100:
2101: /**
2102: * @author pugh
2103: */
2104: private final class InitializeGUI implements Runnable {
2105: public void run() {
2106: setTitle("FindBugs");
2107:
2108: guiLayout.initialize();
2109: bugPopupMenu = createBugPopupMenu();
2110: branchPopupMenu = createBranchPopUpMenu();
2111: comments.loadPrevCommentsList(GUISaveState.getInstance()
2112: .getPreviousComments().toArray(
2113: new String[GUISaveState.getInstance()
2114: .getPreviousComments().size()]));
2115: updateStatusBar();
2116: setBounds(GUISaveState.getInstance().getFrameBounds());
2117: Toolkit.getDefaultToolkit().setDynamicLayout(true);
2118: setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
2119: setJMenuBar(createMainMenuBar());
2120: setVisible(true);
2121:
2122: //Initializes save and open filechooser. - Kristin
2123:
2124: saveOpenFileChooser = new FBFileChooser();
2125: saveOpenFileChooser
2126: .setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
2127: saveOpenFileChooser.setAcceptAllFileFilterUsed(false);
2128: saveOpenFileChooser
2129: .addChoosableFileFilter(FindBugsProjectFileFilter.INSTANCE);
2130: saveOpenFileChooser
2131: .addChoosableFileFilter(FindBugsAnalysisFileFilter.INSTANCE);
2132: saveOpenFileChooser
2133: .addChoosableFileFilter(FindBugsFBPFileFilter.INSTANCE);
2134: saveOpenFileChooser
2135: .addChoosableFileFilter(FindBugsFBAFileFilter.INSTANCE);
2136: saveOpenFileChooser
2137: .setFileFilter(FindBugsAnalysisFileFilter.INSTANCE);
2138: filterOpenFileChooser = new FBFileChooser();
2139: filterOpenFileChooser
2140: .setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
2141: filterOpenFileChooser
2142: .setFileFilter(FindBugsFilterFileFilter.INSTANCE);
2143:
2144: //Sets the size of the tooltip to match the rest of the GUI. - Kristin
2145: JToolTip tempToolTip = tableheader.createToolTip();
2146: UIManager.put("ToolTip.font", new FontUIResource(
2147: tempToolTip.getFont().deriveFont(
2148: Driver.getFontSize())));
2149:
2150: if (MAC_OS_X) {
2151: try {
2152: osxAdapter = Class
2153: .forName("edu.umd.cs.findbugs.gui2.OSXAdapter");
2154: Class[] defArgs = { MainFrame.class };
2155: Method registerMethod = osxAdapter
2156: .getDeclaredMethod(
2157: "registerMacOSXApplication",
2158: defArgs);
2159: if (registerMethod != null) {
2160: registerMethod.invoke(osxAdapter,
2161: MainFrame.this );
2162: }
2163: defArgs[0] = boolean.class;
2164: osxPrefsEnableMethod = osxAdapter
2165: .getDeclaredMethod("enablePrefs", defArgs);
2166: enablePreferences(true);
2167: } catch (NoClassDefFoundError e) {
2168: // This will be thrown first if the OSXAdapter is loaded on a system without the EAWT
2169: // because OSXAdapter extends ApplicationAdapter in its def
2170: System.err
2171: .println("This version of Mac OS X does not support the Apple EAWT. Application Menu handling has been disabled ("
2172: + e + ")");
2173: } catch (ClassNotFoundException e) {
2174: // This shouldn't be reached; if there's a problem with the OSXAdapter we should get the
2175: // above NoClassDefFoundError first.
2176: System.err
2177: .println("This version of Mac OS X does not support the Apple EAWT. Application Menu handling has been disabled ("
2178: + e + ")");
2179: } catch (Exception e) {
2180: System.err
2181: .println("Exception while loading the OSXAdapter: "
2182: + e);
2183: e.printStackTrace();
2184: if (DEBUG) {
2185: e.printStackTrace();
2186: }
2187: }
2188: }
2189: String loadFromURL = SystemProperties
2190: .getProperty("findbugs.loadBugsFromURL");
2191:
2192: if (loadFromURL != null) {
2193: InputStream in;
2194: try {
2195: URL url = new URL(loadFromURL);
2196: in = url.openConnection().getInputStream();
2197: if (loadFromURL.endsWith(".gz"))
2198: in = new GZIPInputStream(in);
2199: BugTreeModel
2200: .pleaseWait(edu.umd.cs.findbugs.L10N
2201: .getLocalString(
2202: "msg.loading_bugs_over_network_txt",
2203: "Loading bugs over network..."));
2204: loadAnalysisFromInputStream(in, url);
2205: } catch (MalformedURLException e1) {
2206: // TODO Auto-generated catch block
2207: e1.printStackTrace();
2208: JOptionPane.showMessageDialog(MainFrame.this ,
2209: "Error loading " + e1.getMessage());
2210: } catch (IOException e1) {
2211: // TODO Auto-generated catch block
2212: e1.printStackTrace();
2213: JOptionPane.showMessageDialog(MainFrame.this ,
2214: "Error loading " + e1.getMessage());
2215: }
2216: }
2217:
2218: addComponentListener(new ComponentAdapter() {
2219: @Override
2220: public void componentResized(ComponentEvent e) {
2221: comments.resized();
2222: }
2223: });
2224:
2225: addWindowListener(new WindowAdapter() {
2226: @Override
2227: public void windowClosing(WindowEvent e) {
2228: if (comments.hasFocus())
2229: setProjectChanged(true);
2230: callOnClose();
2231: }
2232: });
2233:
2234: Driver.removeSplashScreen();
2235: }
2236: }
2237:
2238: /**
2239: * Listens for when cursor is over the label and when it is clicked.
2240: * When the cursor is over the label will make the label text blue
2241: * and the cursor the hand cursor. When clicked will take the
2242: * user to the source code tab and to the lines of code connected
2243: * to the SourceLineAnnotation.
2244: * @author Kristin Stephens
2245: *
2246: */
2247: private class BugSummaryMouseListener extends MouseAdapter {
2248: private BugInstance bugInstance;
2249: private JLabel label;
2250: private SourceLineAnnotation note;
2251:
2252: BugSummaryMouseListener(@NonNull
2253: BugInstance bugInstance, @NonNull
2254: JLabel label, @NonNull
2255: SourceLineAnnotation note) {
2256: this .bugInstance = bugInstance;
2257: this .label = label;
2258: this .note = note;
2259: }
2260:
2261: @Override
2262: public void mouseClicked(MouseEvent e) {
2263: displayer.displaySource(bugInstance, note);
2264: }
2265:
2266: @Override
2267: public void mouseEntered(MouseEvent e) {
2268: label.setForeground(Color.blue);
2269: setCursor(new Cursor(Cursor.HAND_CURSOR));
2270: }
2271:
2272: @Override
2273: public void mouseExited(MouseEvent e) {
2274: label.setForeground(Color.black);
2275: setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
2276: }
2277: }
2278:
2279: /**
2280: * Checks if source code file exists/is available
2281: * @param note
2282: * @return
2283: */
2284: private boolean sourceCodeExist(SourceLineAnnotation note) {
2285: try {
2286: sourceFinder.findSourceFile(note);
2287: } catch (FileNotFoundException e) {
2288: return false;
2289: } catch (IOException e) {
2290: return false;
2291: }
2292: return true;
2293: }
2294:
2295: private void setStyleSheets() {
2296: StyleSheet styleSheet = new StyleSheet();
2297: styleSheet.addRule("body {font-size: " + Driver.getFontSize()
2298: + "pt}");
2299: styleSheet
2300: .addRule("H1 {color: red; font-size: 120%; font-weight: bold;}");
2301: styleSheet.addRule("code {font-family: courier; font-size: "
2302: + Driver.getFontSize() + "pt}");
2303: htmlEditorKit.setStyleSheet(styleSheet);
2304: summaryHtmlArea.setEditorKit(htmlEditorKit);
2305: }
2306:
2307: JPanel createCommentsInputPanel() {
2308: return comments.createCommentsInputPanel();
2309: }
2310:
2311: /**
2312: * Creates the source code panel, but does not put anything in it.
2313: * @param text
2314: * @return
2315: */
2316: JPanel createSourceCodePanel() {
2317: Font sourceFont = new Font("Monospaced", Font.PLAIN,
2318: (int) Driver.getFontSize());
2319: sourceCodeTextPane.setFont(sourceFont);
2320: sourceCodeTextPane.setEditable(false);
2321: sourceCodeTextPane.getCaret().setSelectionVisible(true);
2322: sourceCodeTextPane
2323: .setDocument(SourceCodeDisplay.SOURCE_NOT_RELEVANT);
2324: sourceCodeScrollPane = new JScrollPane(sourceCodeTextPane);
2325: sourceCodeScrollPane.getVerticalScrollBar()
2326: .setUnitIncrement(20);
2327:
2328: JPanel panel = new JPanel();
2329: panel.setLayout(new BorderLayout());
2330: panel.add(sourceCodeScrollPane, BorderLayout.CENTER);
2331:
2332: panel.revalidate();
2333: if (DEBUG)
2334: System.out.println("Created source code panel");
2335: return panel;
2336: }
2337:
2338: JPanel createSourceSearchPanel() {
2339: GridBagLayout gridbag = new GridBagLayout();
2340: GridBagConstraints c = new GridBagConstraints();
2341: JPanel thePanel = new JPanel();
2342: thePanel.setLayout(gridbag);
2343: c.gridx = 0;
2344: c.gridy = 0;
2345: c.weightx = 1.0;
2346: c.insets = new Insets(0, 5, 0, 5);
2347: c.fill = GridBagConstraints.HORIZONTAL;
2348: gridbag.setConstraints(sourceSearchTextField, c);
2349: thePanel.add(sourceSearchTextField);
2350: //add the buttons
2351: findButton.addActionListener(new ActionListener() {
2352: public void actionPerformed(ActionEvent evt) {
2353: searchSource(0);
2354: }
2355: });
2356: c.gridx = 1;
2357: c.weightx = 0.0;
2358: c.fill = GridBagConstraints.NONE;
2359: gridbag.setConstraints(findButton, c);
2360: thePanel.add(findButton);
2361: findNextButton.addActionListener(new ActionListener() {
2362: public void actionPerformed(ActionEvent evt) {
2363: searchSource(1);
2364: }
2365: });
2366: c.gridx = 2;
2367: c.weightx = 0.0;
2368: c.fill = GridBagConstraints.NONE;
2369: gridbag.setConstraints(findNextButton, c);
2370: thePanel.add(findNextButton);
2371: findPreviousButton.addActionListener(new ActionListener() {
2372: public void actionPerformed(ActionEvent evt) {
2373: searchSource(2);
2374: }
2375: });
2376: c.gridx = 3;
2377: c.weightx = 0.0;
2378: c.fill = GridBagConstraints.NONE;
2379: gridbag.setConstraints(findPreviousButton, c);
2380: thePanel.add(findPreviousButton);
2381: return thePanel;
2382: }
2383:
2384: void searchSource(int type) {
2385: int targetLineNum = -1;
2386: String targetString = sourceSearchTextField.getText();
2387: switch (type) {
2388: case 0:
2389: targetLineNum = displayer.find(targetString);
2390: break;
2391: case 1:
2392: targetLineNum = displayer.findNext(targetString);
2393: break;
2394: case 2:
2395: targetLineNum = displayer.findPrevious(targetString);
2396: break;
2397: }
2398: if (targetLineNum != -1)
2399: displayer.foundItem(targetLineNum);
2400: }
2401:
2402: /**
2403: * Sets the title of the source tabs for either docking or non-docking
2404: * versions.
2405: * @param title
2406: */
2407: void setSourceTabTitle(String title) {
2408: guiLayout.setSourceTitle(title);
2409:
2410: }
2411:
2412: /**
2413: * Returns the SorterTableColumnModel of the MainFrame.
2414: * @return
2415: */
2416: SorterTableColumnModel getSorter() {
2417: return sorter;
2418: }
2419:
2420: /*
2421: * This is overridden for changing the font size
2422: */
2423: @Override
2424: public void addNotify() {
2425: super .addNotify();
2426:
2427: float size = Driver.getFontSize();
2428:
2429: getJMenuBar().setFont(getJMenuBar().getFont().deriveFont(size));
2430: for (int i = 0; i < getJMenuBar().getMenuCount(); i++) {
2431: for (int j = 0; j < getJMenuBar().getMenu(i)
2432: .getMenuComponentCount(); j++) {
2433: Component temp = getJMenuBar().getMenu(i)
2434: .getMenuComponent(j);
2435: temp.setFont(temp.getFont().deriveFont(size));
2436: }
2437: }
2438:
2439: bugPopupMenu.setFont(bugPopupMenu.getFont().deriveFont(size));
2440: setFontSizeHelper(bugPopupMenu.getComponents(), size);
2441:
2442: branchPopupMenu.setFont(branchPopupMenu.getFont().deriveFont(
2443: size));
2444: setFontSizeHelper(branchPopupMenu.getComponents(), size);
2445:
2446: }
2447:
2448: public JTree getTree() {
2449: return tree;
2450: }
2451:
2452: public BugTreeModel getBugTreeModel() {
2453: return (BugTreeModel) getTree().getModel();
2454: }
2455:
2456: static class CutAction extends TextAction {
2457:
2458: public CutAction() {
2459: super (edu.umd.cs.findbugs.L10N.getLocalString("txt.cut",
2460: "Cut"));
2461: }
2462:
2463: public void actionPerformed(ActionEvent evt) {
2464: JTextComponent text = getTextComponent(evt);
2465:
2466: if (text == null)
2467: return;
2468:
2469: text.cut();
2470: }
2471: }
2472:
2473: static class CopyAction extends TextAction {
2474:
2475: public CopyAction() {
2476: super (edu.umd.cs.findbugs.L10N.getLocalString("txt.copy",
2477: "Copy"));
2478: }
2479:
2480: public void actionPerformed(ActionEvent evt) {
2481: JTextComponent text = getTextComponent(evt);
2482:
2483: if (text == null)
2484: return;
2485:
2486: text.copy();
2487: }
2488: }
2489:
2490: static class PasteAction extends TextAction {
2491:
2492: public PasteAction() {
2493: super (edu.umd.cs.findbugs.L10N.getLocalString("txt.paste",
2494: "Paste"));
2495: }
2496:
2497: public void actionPerformed(ActionEvent evt) {
2498: JTextComponent text = getTextComponent(evt);
2499:
2500: if (text == null)
2501: return;
2502:
2503: text.paste();
2504: }
2505: }
2506:
2507: public Project getProject() {
2508: return curProject;
2509: }
2510:
2511: public void setProject(Project p) {
2512: curProject = p;
2513: }
2514:
2515: public SourceFinder getSourceFinder() {
2516: return sourceFinder;
2517: }
2518:
2519: public void setSourceFinder(SourceFinder sf) {
2520: sourceFinder = sf;
2521: }
2522:
2523: @SwingThread
2524: public void setRebuilding(boolean b) {
2525: tableheader.setReorderingAllowed(!b);
2526: enablePreferences(!b);
2527: if (b) {
2528: SorterDialog.getInstance().freeze();
2529: showWaitCard();
2530: } else {
2531: SorterDialog.getInstance().thaw();
2532: showTreeCard();
2533: }
2534: recentMenu.setEnabled(!b);
2535:
2536: }
2537:
2538: public void setSorting(boolean b) {
2539: tableheader.setReorderingAllowed(b);
2540: }
2541:
2542: private void setSaveMenu() {
2543: File s = saveFile;
2544: saveMenuItem.setEnabled(projectChanged && s != null
2545: && getSaveType() != SaveType.FBP_FILE && s.exists());
2546: }
2547:
2548: /**
2549: * Called when something in the project is changed and the change needs to be saved.
2550: * This method should be called instead of using projectChanged = b.
2551: */
2552: public void setProjectChanged(boolean b) {
2553: if (curProject == null)
2554: return;
2555:
2556: if (projectChanged == b)
2557: return;
2558:
2559: projectChanged = b;
2560: setSaveMenu();
2561: // if(projectDirectory != null && projectDirectory.exists())
2562: // saveProjectMenuItem.setEnabled(b);
2563:
2564: getRootPane().putClientProperty(WINDOW_MODIFIED,
2565: Boolean.valueOf(b));
2566:
2567: }
2568:
2569: public boolean getProjectChanged() {
2570: return projectChanged;
2571: }
2572:
2573: /*
2574: * DO NOT use the projectDirectory variable to figure out the current project directory in this function
2575: * use the passed in value, as that variable may or may not have been set to the passed in value at this point.
2576: */
2577: private SaveReturn saveProject(File dir) {
2578: if (curProject == null) {
2579: curProject = new Project();
2580: JOptionPane
2581: .showMessageDialog(
2582: MainFrame.this ,
2583: "Null project; this is unexpected. "
2584: + " Creating a new Project so the bugs can be saved, but please report this error.");
2585:
2586: }
2587:
2588: if (!dir.mkdir()) {
2589: return SaveReturn.SAVE_IO_EXCEPTION;
2590: }
2591:
2592: File f = new File(dir.getAbsolutePath() + File.separator
2593: + dir.getName() + ".xml");
2594: File filtersAndSuppressions = new File(dir.getAbsolutePath()
2595: + File.separator + dir.getName() + ".fas");
2596:
2597: BugSaver.saveBugs(f, bugCollection, curProject);
2598: try {
2599: ProjectSettings.getInstance().save(
2600: new FileOutputStream(filtersAndSuppressions));
2601: } catch (IOException e) {
2602: Debug.println(e);
2603: return SaveReturn.SAVE_IO_EXCEPTION;
2604: }
2605: setProjectChanged(false);
2606:
2607: return SaveReturn.SAVE_SUCCESSFUL;
2608: }
2609:
2610: /**
2611: * @param currentSelectedBugLeaf2
2612: * @param currentSelectedBugAspects2
2613: */
2614: private void saveComments(BugLeafNode theNode, BugAspects theAspects) {
2615: comments.saveComments(theNode, theAspects);
2616:
2617: }
2618:
2619: void saveComments() {
2620: comments.saveComments();
2621:
2622: }
2623:
2624: /**
2625: * Returns the color of the source code pane's background.
2626: * @return the color of the source code pane's background
2627: */
2628: public Color getSourceColor() {
2629: return sourceCodeTextPane.getBackground();
2630: }
2631:
2632: /**
2633: * Show an error dialog.
2634: */
2635: public void error(String message) {
2636: JOptionPane.showMessageDialog(this , message, "Error",
2637: JOptionPane.ERROR_MESSAGE);
2638: }
2639:
2640: /**
2641: * Write a message to the console window.
2642: *
2643: * @param message the message to write
2644: */
2645: public void writeToLog(String message) {
2646: if (DEBUG)
2647: System.out.println(message);
2648: // consoleMessageArea.append(message);
2649: // consoleMessageArea.append("\n");
2650: }
2651:
2652: /**
2653: * Save current analysis as file passed in. Return SAVE_SUCCESSFUL if save successful.
2654: */
2655: /*
2656: * Method doesn't do much. This method is more if need to do other things in the future for
2657: * saving analysis. And to keep saving naming convention.
2658: */
2659: private SaveReturn saveAnalysis(File f) {
2660: BugSaver.saveBugs(f, bugCollection, curProject);
2661:
2662: setProjectChanged(false);
2663:
2664: return SaveReturn.SAVE_SUCCESSFUL;
2665: }
2666:
2667: /**
2668: * Opens the analysis. Also clears the source and summary panes. Makes comments enabled false.
2669: * Sets the saveType and adds the file to the recent menu.
2670: * @param f
2671: * @return
2672: */
2673: private boolean openAnalysis(File f, SaveType saveType) {
2674: if (!f.exists() || !f.canRead()) {
2675: throw new IllegalArgumentException("Can't read " + f);
2676: }
2677:
2678: prepareForFileLoad(f, saveType);
2679: try {
2680: FileInputStream in = new FileInputStream(f);
2681: loadAnalysisFromInputStream(in, f);
2682:
2683: return true;
2684: } catch (IOException e) {
2685: return false;
2686: }
2687: }
2688:
2689: private void prepareForFileLoad(File f, SaveType saveType) {
2690: setRebuilding(true);
2691: //This creates a new filters and suppressions so don't use the previoues one.
2692: ProjectSettings.newInstance();
2693:
2694: clearSourcePane();
2695: clearSummaryTab();
2696: comments.setUserCommentInputEnable(false);
2697: reconfigMenuItem.setEnabled(true);
2698: setProjectChanged(false);
2699: this .setSaveType(saveType);
2700: saveFile = f;
2701:
2702: addFileToRecent(f, saveType);
2703: }
2704:
2705: /**
2706: * @param file
2707: * @return
2708: */
2709: private void loadAnalysisFromInputStream(final InputStream in,
2710: final Object source) {
2711:
2712: Runnable runnable = new Runnable() {
2713: public void run() {
2714:
2715: final Project project = new Project();
2716: if (source instanceof File)
2717: project.setCurrentWorkingDirectory(((File) source)
2718: .getParentFile());
2719: final SortedBugCollection bc = BugLoader.loadBugs(
2720: MainFrame.this , project, in);
2721: SwingUtilities.invokeLater(new Runnable() {
2722: public void run() {
2723: if (bc == null)
2724: setProjectAndBugCollection(new Project(),
2725: new SortedBugCollection());
2726: else
2727: setProjectAndBugCollection(project, bc);
2728: }
2729: });
2730: }
2731: };
2732: if (EventQueue.isDispatchThread())
2733: new Thread(runnable).start();
2734: else
2735: runnable.run();
2736: return;
2737: }
2738:
2739: /**
2740: * @param file
2741: * @return
2742: */
2743: private void loadProjectFromFile(final File f) {
2744:
2745: Runnable runnable = new Runnable() {
2746: public void run() {
2747: final Project project = BugLoader.loadProject(
2748: MainFrame.this , f);
2749: final BugCollection bc = project == null ? null
2750: : BugLoader.doAnalysis(project);
2751: updateProjectAndBugCollection(project, bc, null);
2752: SwingUtilities.invokeLater(new Runnable() {
2753: public void run() {
2754: if (project == null)
2755: setProjectAndBugCollection(new Project(),
2756: new SortedBugCollection());
2757: else
2758: setProjectAndBugCollection(project, bc);
2759: }
2760: });
2761: }
2762: };
2763: if (EventQueue.isDispatchThread())
2764: new Thread(runnable).start();
2765: else
2766: runnable.run();
2767: return;
2768: }
2769:
2770: /**
2771: * Redo the analysis
2772: */
2773: private void redoAnalysis() {
2774: saveComments(currentSelectedBugLeaf, currentSelectedBugAspects);
2775:
2776: showWaitCard();
2777: new Thread() {
2778: @Override
2779: public void run() {
2780: updateDesignationDisplay();
2781: BugCollection bc = BugLoader
2782: .redoAnalysisKeepComments(curProject);
2783: updateProjectAndBugCollection(curProject, bc, null);
2784: }
2785: }.start();
2786: }
2787:
2788: /**
2789: *
2790: */
2791: private void mergeAnalysis() {
2792: saveComments(currentSelectedBugLeaf, currentSelectedBugAspects);
2793:
2794: showWaitCard();
2795: Project p = new Project();
2796: BugCollection bc = BugLoader.combineBugHistories(p);
2797: setProjectAndBugCollection(p, bc);
2798:
2799: }
2800:
2801: /**
2802: * This takes a directory and opens it as a project.
2803: * @param dir
2804: */
2805: private void openProject(final File dir) {
2806: File xmlFile = new File(dir.getAbsolutePath() + File.separator
2807: + dir.getName() + ".xml");
2808: File fasFile = new File(dir.getAbsolutePath() + File.separator
2809: + dir.getName() + ".fas");
2810:
2811: if (!fasFile.exists()) {
2812: JOptionPane
2813: .showMessageDialog(
2814: MainFrame.this ,
2815: edu.umd.cs.findbugs.L10N
2816: .getLocalString(
2817: "dlg.filter_settings_not_found_lbl",
2818: "Filter settings not found, using default settings."));
2819:
2820: ProjectSettings.newInstance();
2821:
2822: } else {
2823: try {
2824: ProjectSettings.loadInstance(new FileInputStream(
2825: fasFile));
2826: } catch (FileNotFoundException e) {
2827: //Impossible.
2828: if (MainFrame.DEBUG)
2829: System.err
2830: .println(".fas file not found, using default settings");
2831: ProjectSettings.newInstance();
2832: }
2833: }
2834: final File extraFinalReferenceToXmlFile = xmlFile;
2835: new Thread(new Runnable() {
2836: public void run() {
2837: BugTreeModel.pleaseWait();
2838: MainFrame.this .setRebuilding(true);
2839: Project project = new Project();
2840: SortedBugCollection bc = BugLoader.loadBugs(
2841: MainFrame.this , project,
2842: extraFinalReferenceToXmlFile);
2843: setProjectAndBugCollection(project, bc);
2844: }
2845: }).start();
2846:
2847: // addFileToRecent(xmlFile, SaveType.PROJECT);
2848: addFileToRecent(dir, SaveType.PROJECT);
2849:
2850: clearSourcePane();
2851: clearSummaryTab();
2852: comments.setUserCommentInputEnable(false);
2853:
2854: setProjectChanged(false);
2855: setSaveType(SaveType.PROJECT);
2856: saveFile = dir;
2857: changeTitle();
2858: }
2859:
2860: /**
2861: * This checks if the xmlFile is in the GUISaveState. If not adds it. Then adds the file
2862: * to the recentMenuCache.
2863: * @param xmlFile
2864: */
2865: /*
2866: * If the file already existed, its already in the preferences, as well as
2867: * the recent projects menu items, only add it if they change the name,
2868: * otherwise everything we're storing is still accurate since all we're
2869: * storing is the location of the file.
2870: */
2871: private void addFileToRecent(File xmlFile, SaveType st) {
2872: ArrayList<File> xmlFiles = GUISaveState.getInstance()
2873: .getRecentFiles();
2874: if (!xmlFiles.contains(xmlFile)) {
2875: GUISaveState.getInstance().addRecentFile(xmlFile);
2876: }
2877: MainFrame.this .recentMenuCache.addRecentFile(xmlFile);
2878: }
2879:
2880: private void newProjectMenu() {
2881: comments.saveComments(currentSelectedBugLeaf,
2882: currentSelectedBugAspects);
2883: new NewProjectWizard();
2884:
2885: newProject = true;
2886: }
2887:
2888: void updateDesignationDisplay() {
2889: comments.updateDesignationComboBox();
2890: }
2891:
2892: void addDesignationItem(JMenu menu, final String menuName,
2893: int keyEvent) {
2894: comments.addDesignationItem(menu, menuName, keyEvent);
2895: }
2896:
2897: void warnUserOfFilters() {
2898: JOptionPane
2899: .showMessageDialog(
2900: MainFrame.this ,
2901: edu.umd.cs.findbugs.L10N
2902: .getLocalString(
2903: "dlg.everything_is_filtered",
2904: "All bugs in this project appear to be filtered out. \nYou may wish to check your filter settings in the preferences menu."),
2905: "Warning", JOptionPane.WARNING_MESSAGE);
2906: }
2907:
2908: /**
2909: * @param saveType The saveType to set.
2910: */
2911: void setSaveType(SaveType saveType) {
2912: if (DEBUG && this .saveType != saveType)
2913: System.out.println("Changing save type from "
2914: + this .saveType + " to " + saveType);
2915: this .saveType = saveType;
2916: }
2917:
2918: /**
2919: * @return Returns the saveType.
2920: */
2921: SaveType getSaveType() {
2922: return saveType;
2923: }
2924: }
|