-
Bug
-
Resolution: Duplicate
-
P2
-
None
-
1.2.2
-
x86
-
windows_98
Name: jk109818 Date: 07/07/2000
java version "1.2.2"
Classic VM (build JDK-1.2.2-W, native threads, symcjit)
/*
This class demonstrates a bug in Sun's JDK 1.2 Swing JTree UI when the tree
model handles the case of deleting or adding more than one node by firing a
tree structure change event. It seems to manifest only with the Windows Look &
Feel.
Run using "java JTreeBug" on windows to run with windows Look & Feel.
Run using "java JTreeBug metal" to run without apparent problems.
Expand child2 (the node whose children are deleted and restored.
Push the "delete bad" button to see the result. I believe that what
happens is the UI incorrectly computes the jtree size after the newStruct
event, and the scrollpane gets confused.
Push the "updateUI" button to get the tree back in a usable state.
After that, push the "restore children" button to see the failure after
children are restored. Another way: start in a "good" state, push the
"delete safe" button, then push restore children to see the failure.
Failure symptoms: the scroll bar disappears when it should not, expand/close
clicks fail or are misdirected, arrow down key fails at the bottom,
the tree disappears entirely!!
Failures occur on both Win 98 and Win NT. This example was tested on
Windows 98, usng
Classic VM (build JDK-1.2.2-W, native threads, symcjit)
Author: Arra Avakian ###@###.###
This example program was constructed to duplicate the problem as encountered
in a substantial Swing-based program called "eTective". For more info, see
http://www.averstar.com/products/etective.html
*/
import java.util.Hashtable;
import java.util.Vector;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
class JTreeBug implements TreeModel, ActionListener {
// The test driver
public static void main(String[] args) {
System.setErr(System.out);
String LAFName = args.length >= 1 ? args[0] : null;
String lookAndFeel = LAFName == null ?
UIManager.getSystemLookAndFeelClassName() :
findLAF(LAFName);
try {
UIManager.setLookAndFeel(lookAndFeel);
} catch (Exception e) {
}
JTreeBug model = new JTreeBug();
JTree tree = new JTree(model);
// Model acts as action listener for the reset UI button,
// so it needs the JTree object.
model.fixTree = tree;
tree.setLargeModel(true);
JScrollPane scroll = new JScrollPane(tree);
JFrame frame = new JFrame("JTreeBug");
Container pane = frame.getContentPane();
pane.add(scroll);
pane.add(model.createToolBar(), "North");
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) { System.exit(0); }
});
frame.pack();
frame.setVisible(true);
}
static String findLAF(String LAFName) {
String result;
UIManager.LookAndFeelInfo[] lafs =
UIManager.getInstalledLookAndFeels();
for (int i = 0; i < lafs.length; i++) {
if (lafs[i].getName().equalsIgnoreCase(LAFName)) {
return lafs[i].getClassName();
}
}
return UIManager.getCrossPlatformLookAndFeelClassName();
} // findLAF
static final int
KIND_CHANGED = 0,
KIND_INSERTED = 1,
KIND_REMOVED = 2,
KIND_STRUCTURE = 3;
final static Node empty[] = {};
final EventListenerList ll = new EventListenerList();
final Hashtable nodes = new Hashtable();
Node root;
Node deleteParent;
Node[] restoreKids;
JButton deleteSafe, deleteBad, restore, update;
JTree fixTree;
JTreeBug() {
// Build the tree.
createRoot();
}
public Object getRoot() { return root; }
public Object getChild(Object parent, int index) {
Node node = (Node)parent;
Node[] kids = (Node[])nodes.get(parent);
if (kids == null || index >= kids.length) {
throw new RuntimeException("getChild(" + node.name + ", " +
index + ')');
}
return kids[index];
}
public int getChildCount(Object parent) {
Node[] kids = (Node[])nodes.get(parent);
if (kids == null) return 0;
return kids.length;
}
public boolean isLeaf(Object node) {
return getChildCount(node) == 0;
}
public void valueForPathChanged(TreePath path, Object newValue) {}
public int getIndexOfChild(Object parent, Object child) {
Node[] kids = (Node[])nodes.get(parent);
int i = kids.length;
while (--i >= 0) if (kids[i] == child) return i;
return -1;
}
public void addTreeModelListener(TreeModelListener l) {
ll.add(TreeModelListener.class, l);
}
public void removeTreeModelListener(TreeModelListener l) {
ll.remove(TreeModelListener.class, l);
}
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == deleteSafe) delete(false);
else if (source == deleteBad) delete(true);
else if (source == restore) restore();
else if (source == update) fixTree.updateUI();
}
void restore() {
nodes.put(deleteParent, restoreKids);
fireNewStructure(deleteParent);
}
// Delete two children. Report as two events (false) or as one event (true)
void delete(boolean bad) {
if (getChildCount(deleteParent) < 2) return;
int gone;
if (bad) {
// Delete two at once, fire a single structure event
gone = delete(deleteParent, 2);
fireNewStructure(deleteParent);
} else {
// Delete two separately, each with a remove event
Object child;
int index;
index = getChildCount(deleteParent) - 1;
child = getChild(deleteParent, index);
gone = delete(deleteParent, 1);
if (gone != index) throw new RuntimeException("??");
fireRemoved(deleteParent, child, gone);
index = getChildCount(deleteParent) - 1;
child = getChild(deleteParent, index);
gone = delete(deleteParent, 1);
if (gone != index) throw new RuntimeException("??");
fireRemoved(deleteParent, child, gone);
}
}
// Delete count last children of parent.
// Returns index of first child deleted.
// No event is fired here.
int delete(Node parent, int count) {
Node[] kids = (Node[])nodes.get(parent);
if (kids == null || kids.length < count) {
throw new RuntimeException("Can't delete");
}
int len = kids.length;
int newlen = len - count;
Node[] newkids = new Node[newlen];
System.arraycopy(kids, 0, newkids, 0, newlen);
nodes.put(parent, newkids);
return newlen;
}
TreePath path2Root(Node node) {
if (node == root) return new TreePath(node);
TreePath path = new TreePath(node.parent);
return path.pathByAddingChild(node);
}
void fireNewStructure(Node parent) {
TreeModelEvent event = new TreeModelEvent(this, path2Root(parent));
fireEvent(event, KIND_STRUCTURE);
}
void fireRemoved(Node parent, Object child, int index) {
Object[] children = { child };
int[] indices = { index };
TreeModelEvent event =
new TreeModelEvent(this, path2Root(parent), indices, children);
fireEvent(event, KIND_REMOVED);
}
void fireEvent(TreeModelEvent event, int kind) {
// getListenerList is guaranteed to return a non-null array.
Object[] listeners = ll.getListenerList();
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==TreeModelListener.class) {
TreeModelListener target =
((TreeModelListener)listeners[i+1]);
try {
switch (kind) {
case KIND_CHANGED:
target.treeNodesChanged(event);
break;
case KIND_INSERTED:
target.treeNodesInserted(event);
break;
case KIND_REMOVED:
target.treeNodesRemoved(event);
break;
case KIND_STRUCTURE:
target.treeStructureChanged(event);
break;
}
} catch (Exception e) {
System.err.println(
"Unexpected exception while firing TreeModelEvent");
e.printStackTrace(System.err);
System.err.println("target=" +
target.getClass().getName());
System.err.println("event=" + event);
System.err.println("kind=" + kind);
Object path[] = event.getPath();
if (path != null) {
Object last = path[path.length-1];
System.err.println("node=" +
((Node)last).name);
}
}
}
}
} // fireEvent
JToolBar createToolBar() {
JToolBar bar = new JToolBar();
bar.add(deleteSafe = newButton("delete safe"));
bar.add(deleteBad = newButton("delete bad"));
bar.add(restore = newButton("restore children"));
bar.add(update = newButton("updateUI"));
return bar;
}
JButton newButton(String name) {
JButton result = new JButton(name);
result.addActionListener(this);
return result;
}
Node createParent(String name, Node[] children) {
Node parent = new Node(name);
int i = children.length;
while (--i >= 0) children[i].parent = parent;
nodes.put(parent, children);
return parent;
}
Node createParent(String name, Node child) {
Node[] kids = {child};
return createParent(name, kids);
}
Node createParent(String name, Node child0, Node child1) {
Node[] kids = {child0, child1};
return createParent(name, kids);
}
Node createParent(String name, Node child0, Node child1, Node child2) {
Node[] kids = {child0, child1, child2};
return createParent(name, kids);
}
Node createParent(String name, int kids) {
Vector v = new Vector();
while (--kids >= 0) v.add(new Node(name + kids));
return createParent(name, (Node[])v.toArray(empty));
}
Node createTree(String name, String name2, int a, int b, int c) {
Node child0 = createParent(name2 + '0', a);
Node child1 = createParent(name2 + '1', b);
Node child2 = createParent(name2 + '2', c);
return createParent(name, child0, child1, child2);
}
void createRoot() {
root = createTree("root", "child", 3, 9, 22);
deleteParent = (Node)getChild(root, 2);
restoreKids = (Node[])nodes.get(deleteParent);
}
// A tree node
class Node {
Node parent;
String name;
Node(String name) {this.name = name;}
public String toString() { return name; }
}
} // class JTreeBug
(Review ID: 106882)
======================================================================
- duplicates
-
JDK-4346229 JTree causes new Nodes to be unselectable when rowHeight and largeModel are set
-
- Resolved
-