📄 bugtreemodel.java
字号:
/* * FindBugs - Find Bugs in Java programs * Copyright (C) 2006, University of Maryland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307, USA */package edu.umd.cs.findbugs.gui2;import java.lang.management.ManagementFactory;import java.lang.management.ThreadMXBean;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.List;import java.util.Vector;import javax.swing.JTree;import javax.swing.SwingUtilities;import javax.swing.event.ChangeEvent;import javax.swing.event.ListSelectionEvent;import javax.swing.event.TableColumnModelEvent;import javax.swing.event.TableColumnModelListener;import javax.swing.event.TreeExpansionEvent;import javax.swing.event.TreeExpansionListener;import javax.swing.event.TreeModelEvent;import javax.swing.event.TreeModelListener;import javax.swing.tree.DefaultTreeModel;import javax.swing.tree.TreeModel;import javax.swing.tree.TreeNode;import javax.swing.tree.TreePath;import edu.umd.cs.findbugs.BugInstance;import edu.umd.cs.findbugs.annotations.NonNull;import edu.umd.cs.findbugs.gui2.BugAspects.SortableValue;/* * Our TreeModel. Once upon a time it was a simple model, that queried data, its BugSet, for what to show under each branch * Then it got more and more complicated, no one knows why it still seems to work... or why it doesn't if it in fact doesn't. * * Here's a tip, Dont even attempt to deal with suppressions or filtering and their related tree model events without the API * for TreeModelEvents open. And read it three times first. Ignore the fact that its inconsistent for sending events about the root, just pick one of the things it says and go with it * * Heres the order things MUST be done when dealing with suppressions, filtering, unsuppressions... unfiltering... all that fun stuff * * Inserts: * Update model * Get Path * Make Event * Send Event * ResetData * * Removes: * Get Path * Make Event * Update Model * Send Event * ResetData * * Restructure: * Update Model * Get Path * Make Event * Send Event * resetData? hmmm * * These may or may not be the orders of events used in suppressBug, unsuppressBug, branchOperations and so forth * if they seem to work anyway, I wouldn't touch them. * * changeSet() is what to do when the data set is completely different (loaded a new collection, reran the analysis what have you) * changeSet calls rebuild(), which does a very tricky thing, where it makes a new model, and a new JTree, and swaps them in in place of this one, as well as * turning off user input in hopefully every place it needs to be turned off * *//** * The treeModel for our JTree */ public class BugTreeModel implements TreeModel, TableColumnModelListener, TreeExpansionListener { private BugAspects root = new BugAspects(); private SorterTableColumnModel st; private BugSet data; private ArrayList<TreeModelListener> listeners = new ArrayList<TreeModelListener>(); private JTree tree; static Vector<BugLeafNode> selectedBugLeafNodes = new Vector<BugLeafNode>(); private static final boolean DEBUG = false; private volatile Thread rebuildingThread; private boolean sortOrderChanged; private boolean sortsAddedOrRemoved; public BugTreeModel(JTree tree, SorterTableColumnModel st, BugSet data) { st.addColumnModelListener(this); this.tree = tree; this.st = st; this.data = data; BugSet.setAsRootAndCache(this.data); root.setCount(data.size()); FilterActivity.addFilterListener(bugTreeFilterListener); if (DEBUG) this.addTreeModelListener(new TreeModelListener() { public void treeNodesChanged(TreeModelEvent arg0) { System.out.println("Tree nodes changed"); System.out.println(" " + arg0.getTreePath()); } public void treeNodesInserted(TreeModelEvent arg0) { System.out.println("Tree nodes inserted"); System.out.println(" " + arg0.getTreePath()); } public void treeNodesRemoved(TreeModelEvent arg0) { System.out.println("Tree nodes removed"); System.out.println(" " + arg0.getTreePath()); } public void treeStructureChanged(TreeModelEvent arg0) { System.out.println("Tree structure changed"); System.out.println(" " + arg0.getTreePath()); }}); } public BugTreeModel(BugTreeModel other) { this.root = new BugAspects(other.root); this.st = other.st; this.data = new BugSet(other.data); //this.listeners = other.listeners; this.tree = other.tree; } public void getOffListenerList() { FilterActivity.removeFilterListener(bugTreeFilterListener); st.removeColumnModelListener(this); tree.removeTreeExpansionListener(this); } public Object getRoot() { return root; } public Object getChild(Object o, int index) { Object result = getChild0(o,index); assert o != null : "child " + index + " of " + this + " is null"; return result; } public Object getChild0(Object o, int index) { BugAspects a = (BugAspects) o; if (st.getOrderBeforeDivider().size()==0 && a.size()==0)//Root without any sortables return data.get(index); try { if ((a.size() == 0) || (a.last().key != st.getOrderBeforeDivider().get(st.getOrderBeforeDivider().size() - 1))) { BugAspects child=a.addToNew(enumsThatExist(a).get(index)); child.setCount(data.query(child).size()); return child; } else return data.query(a).get(index); } catch (IndexOutOfBoundsException e) { Debug.println("IndexOutOfBounds caught: I am treemodel #" + this + "I am no longer the current treemodel," + " my data is cached and I return bad values for getChild. Something is wrong with rebuild," + " since the tree is asking both of us for children"); return null; } } public int getChildCount(Object o) {// long start = bean.getCurrentThreadCpuTime();// try// {// System.out.println("# getChildCount [o = " + o + "]"); // System.out.println("getChildCount: " + Thread.currentThread().toString()); if(!(o instanceof BugAspects)) return 0; BugAspects a = (BugAspects) o; if (st.getOrderBeforeDivider().size()==0 && a.size() == 0)//If its the root and we aren't sorting by anything return data.size(); if ((a.size() == 0) || (a.last().key != st.getOrderBeforeDivider().get(st.getOrderBeforeDivider().size() - 1)))// {// System.out.println("# before enumsThatExist: " + (bean.getCurrentThreadCpuTime() - start)); return enumsThatExist(a).size();// } else// {// System.out.println("# before query: " + (bean.getCurrentThreadCpuTime() - start)); return data.query(a).size();// }// }// finally// {// System.out.println("# finished: " + (bean.getCurrentThreadCpuTime() - start));// } } /*This contract has been changed to return a HashList of Stringpair, our own data structure in which finding the index of an object in the list is very fast*/ private HashList<SortableValue> enumsThatExist(BugAspects a) { // long start = bean.getCurrentThreadCpuTime();// System.out.println(" ## enumsThatExist [a = " + a + "]");// try// { //StringPair[] toCheck = null; if (st.getOrderBeforeDivider().size()==0) return null; // if (a.size() == 0) // root// toCheck = getValues(st.getOrder().get(0));// else if (st.getOrder().indexOf(a.get(a.size() - 1).key) == st.getOrder().size() - 1) // last branch// return null;// else // somewhere in between// toCheck = getValues(st.getOrder().get(st.getOrder().indexOf(a.get(a.size() - 1).key) + 1));// BugSet set = data.query(a);// System.out.println(" ## after query: " + (bean.getCurrentThreadCpuTime() - start));// for (StringPair sp : toCheck)// if (set.contains(sp))// result.add(sp);// System.out.println(" ## after loop (" + toCheck.length + " elements): " + (bean.getCurrentThreadCpuTime() - start));// return result; Sortables key = (a.size() == 0 ? st.getOrderBeforeDivider().get(0) : st.getOrderBeforeDivider().get(st.getOrderBeforeDivider().indexOf(a.last().key) + 1)); String[] all = key.getAll(data.query(a)); ArrayList<SortableValue> result = new ArrayList<SortableValue>(); for (String i : all) result.add(new SortableValue(key, i));// System.out.println(" ## before sort: " + (bean.getCurrentThreadCpuTime() - start));// Collections.sort(result, key);// System.out.println(" ## after sort: " + (bean.getCurrentThreadCpuTime() - start)); return new HashList<SortableValue>(result);// }// finally// {// System.out.println(" ## finished: " + (bean.getCurrentThreadCpuTime() - start));// } } public boolean isLeaf(Object o) { return (o instanceof BugLeafNode); } public void valueForPathChanged(TreePath arg0, Object arg1) {} public int getIndexOfChild(Object parent, Object child) { if (parent == null || child == null || isLeaf(parent)) return -1; if (isLeaf(child)) { return data.query((BugAspects) parent).indexOf((BugLeafNode) child); } else { HashList<SortableValue> stringPairs = enumsThatExist((BugAspects) parent); if (stringPairs==null) { //XXX-Threading difficulties-stringpairs is null somehow Debug.println("Stringpairs is null on getIndexOfChild! Error!"); assert(false); return -1; } return stringPairs.indexOf(((BugAspects)child).last());// for (int i = 0; i < stringPairs.size(); i++)// if (stringPairs.get(i).equals(((BugAspects)child).get(((BugAspects)child).size() -1)))// return i;// // if (stringPairArray[i] == ((BugAspects)child).get(((BugAspects)child).size() - 1))// return i;// return -1; } } public void addTreeModelListener(TreeModelListener listener) { listeners.add(listener); } public void removeTreeModelListener(TreeModelListener listener) { listeners.remove(listener); } private static SortableValue[] getValues(Sortables key) { String[] values= key.getAllSorted(); SortableValue[] result = new SortableValue[values.length]; for (int i = 0; i < values.length; i++) { result[i] = new SortableValue(key, values[i]); } return result; } public void columnAdded(TableColumnModelEvent e) { sortsAddedOrRemoved=true; //rebuild(); } public void columnRemoved(TableColumnModelEvent e) { sortsAddedOrRemoved=true; //rebuild(); } public void columnMoved(final TableColumnModelEvent evt) { if (evt.getFromIndex() == evt.getToIndex()) return; sortOrderChanged=true; //rebuild(); } void changeSet(BugSet set) { BugSet.setAsRootAndCache(set); data=new BugSet(set); root.setCount(data.size()); rebuild(); } /** * Swaps in a new BugTreeModel and a new JTree * */ private void rebuild() { if (TRACE) System.out.println("rebuilding bug tree model"); PreferencesFrame.getInstance().freeze(); st.freezeOrder(); MainFrame.getInstance().setRebuilding(true); NewFilterFromBug.closeAll(); //If this thread is not interrupting a previous thread, set the paths to be opened when the new tree is complete //If the thread is interrupting another thread, dont do this, because you dont have the tree with the correct paths selected //As of now, it should be impossible to interrupt a rebuilding thread, in another version this may change, so this if statement check is left in, even though it should always be true. if (rebuildingThread==null) setOldSelectedBugs(); Debug.println("Please Wait called right before starting rebuild thread"); pleaseWait(); rebuildingThread = new Thread() { BugTreeModel newModel; @Override public void run() { try { newModel = new BugTreeModel(BugTreeModel.this); newModel.listeners = listeners; newModel.resetData(); newModel.data.sortList(); } finally { rebuildingThread = null; SwingUtilities.invokeLater(new Runnable() { public void run() { if (newModel != null) { JTree newTree = new JTree(newModel); newModel.tree = newTree; MainFrame.getInstance().newTree(newTree,newModel); } getOffListenerList(); MainFrame.getInstance().setRebuilding(false); PreferencesFrame.getInstance().thaw(); //st.thawOrder should be the last thing that happens, otherwise a very determined user could slip a new order in before we allow him to rebuild the tree, things get out of sync, nothing bad happens, it just looks wrong until he resorts. st.thawOrder(); }}); } } }; rebuildingThread.start(); } public void crawl(final ArrayList<BugAspects> path, final int depth) { for (int i = 0; i < getChildCount(path.get(path.size() - 1)); i++) if (depth > 0) { ArrayList<BugAspects> newPath = new ArrayList<BugAspects>(path); newPath.add((BugAspects) getChild(path.get(path.size() - 1), i)); crawl(newPath, depth - 1); } else { for (TreeModelListener l : listeners) l.treeStructureChanged(new TreeModelEvent(this, path.toArray())); } } void openPreviouslySelected(List<BugLeafNode> selected) { BugInstance bug=null; TreePath path=null; Debug.println("Starting Open Previously Selected"); for (BugLeafNode b: selected) { try { bug=b.getBug(); path=getPathToBug(bug); MainFrame.getInstance().tree.expandPath(path.getParentPath()); MainFrame.getInstance().tree.addSelectionPath(path); } catch(NullPointerException e) { //Try to recover! if (MainFrame.DEBUG) System.err.println("Failure opening a selected node, node will not be opened in new tree");// System.err.println(b); This will be accurate// System.err.println(bug); This will be accurate //System.err.println(path); // e.printStackTrace(); continue; } catch(ArrayIndexOutOfBoundsException e) { //System.err.println("Failure opening a selected node"); //System.err.println(b); //System.err.println(bug); //System.err.println(path); if (MainFrame.DEBUG) System.err.println("Failure opening a selected node, node will not be opened in new tree");// e.printStackTrace(); continue; } } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -