1 | /**
|
---|
2 | *#########################################################################
|
---|
3 | *
|
---|
4 | * A component of the Gatherer application, part of the Greenstone digital
|
---|
5 | * library suite from the New Zealand Digital Library Project at the
|
---|
6 | * University of Waikato, New Zealand.
|
---|
7 | *
|
---|
8 | * Author: John Thompson, Greenstone Digital Library, University of Waikato
|
---|
9 | *
|
---|
10 | * Copyright (C) 1999 New Zealand Digital Library Project
|
---|
11 | *
|
---|
12 | * This program is free software; you can redistribute it and/or modify
|
---|
13 | * it under the terms of the GNU General Public License as published by
|
---|
14 | * the Free Software Foundation; either version 2 of the License, or
|
---|
15 | * (at your option) any later version.
|
---|
16 | *
|
---|
17 | * This program is distributed in the hope that it will be useful,
|
---|
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
20 | * GNU General Public License for more details.
|
---|
21 | *
|
---|
22 | * You should have received a copy of the GNU General Public License
|
---|
23 | * along with this program; if not, write to the Free Software
|
---|
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
---|
25 | *########################################################################
|
---|
26 | */
|
---|
27 | package org.greenstone.gatherer.util;
|
---|
28 |
|
---|
29 | import java.lang.Runnable;
|
---|
30 | import javax.swing.SwingUtilities;
|
---|
31 | import javax.swing.tree.*;
|
---|
32 | import org.greenstone.gatherer.DebugStream;
|
---|
33 |
|
---|
34 | /** Due to the TreeModel objects not having any synchronization, certain assumptions, such as the model state remaining constant during a repaint, don't always hold - especially given that I'm changing the tree model on a different thread. In order to get around this I will use the latest swing paradigm wherein you flag a section of code to be executed by the AWT GUI Event queue, as soon as other gui tasks have finished. This way I shouldn't have tree redraws throwing NPEs because the array size of the children of a certain node has changed -while- the repaint call was made, i.e. repaint() calls getChildCount() = 13, removeNodeFromParent() called, repaint calls getChildAt(12) = ArrayIndexOutOfBoundsException.
|
---|
35 | * @author John Thompson, Greenstone Digital Library, University of Waikato
|
---|
36 | * @version 2.3c
|
---|
37 | */
|
---|
38 | public class SynchronizedTreeModelTools {
|
---|
39 | /** Adds an insertNodeInto model update onto the AWT Event queue. This gets around the lack of synchronization illustrated above. */
|
---|
40 | static final public Runnable insertNodeInto(DefaultTreeModel model, MutableTreeNode parent, MutableTreeNode target_node) {
|
---|
41 | return insertNodeInto(model, parent, target_node, true);
|
---|
42 | }
|
---|
43 |
|
---|
44 | static final public Runnable insertNodeInto(final DefaultTreeModel model, final MutableTreeNode parent, final MutableTreeNode target_node, final boolean wait_allowed) {
|
---|
45 | final Runnable doInsertNodeInto = new Runnable() {
|
---|
46 | public void run() {
|
---|
47 | ///ystem.err.print("Running task... ");
|
---|
48 | DebugStream.println("insertNodeInto(" + model + ", " + parent + ", " + target_node + ", " + wait_allowed);
|
---|
49 | int index = -1;
|
---|
50 | int pos = 0;
|
---|
51 | while(index == -1 && pos < parent.getChildCount()) {
|
---|
52 | TreeNode node = parent.getChildAt(pos);
|
---|
53 | int result = 0;
|
---|
54 | ///ystem.err.println("Compare " + target_node + " to " + node);
|
---|
55 | if((target_node.isLeaf() && node.isLeaf()) || (!target_node.isLeaf() && !node.isLeaf())) {
|
---|
56 | result = target_node.toString().toLowerCase().compareTo(node.toString().toLowerCase());
|
---|
57 | }
|
---|
58 | else if(target_node.isLeaf()) {
|
---|
59 | result = 1;
|
---|
60 | }
|
---|
61 | else {
|
---|
62 | result = -1;
|
---|
63 | }
|
---|
64 | if(result > 0) {
|
---|
65 | ///ystem.err.println("Keep searching...");
|
---|
66 | pos++;
|
---|
67 | }
|
---|
68 | else {
|
---|
69 | ///ystem.err.println("Found!");
|
---|
70 | index = pos;
|
---|
71 | }
|
---|
72 | }
|
---|
73 | if(index == -1) {
|
---|
74 | index = parent.getChildCount();
|
---|
75 | }
|
---|
76 | model.insertNodeInto(target_node, parent, index);
|
---|
77 | }
|
---|
78 | };
|
---|
79 | ///ystem.err.print("Queuing Task... ");
|
---|
80 | try {
|
---|
81 | if(wait_allowed && !SwingUtilities.isEventDispatchThread()) {
|
---|
82 | ///ystem.err.print("In another thread - invoke and wait... ");
|
---|
83 | SwingUtilities.invokeAndWait(doInsertNodeInto);
|
---|
84 | }
|
---|
85 | else {
|
---|
86 | ///ystem.err.print("In Event Thread or wait not allowed - invoke later... ");
|
---|
87 | SwingUtilities.invokeLater(doInsertNodeInto);
|
---|
88 | }
|
---|
89 | }
|
---|
90 | catch (Exception exception) {
|
---|
91 | DebugStream.printStackTrace(exception);
|
---|
92 | }
|
---|
93 | ///ystem.err.print("Added Task... ");
|
---|
94 | return doInsertNodeInto;
|
---|
95 | }
|
---|
96 |
|
---|
97 |
|
---|
98 | /** Adds a removeNodeFromParent model update onto the AWT Event queue. This gets around the lack of synchronization illustrated above.
|
---|
99 | * @param model The <strong>GTreeModel</strong> we want to remove the node from.
|
---|
100 | * @param target_node The <strong>GTreeNode</strong> to remove.
|
---|
101 | */
|
---|
102 | static final public void removeNodeFromParent(final DefaultTreeModel model, final MutableTreeNode target_node) {
|
---|
103 | ///ystem.err.println("Remove " + target_node + " from parent in model " + model);
|
---|
104 | final Runnable doRemoveNodeFromParent = new Runnable() {
|
---|
105 | public void run() {
|
---|
106 | model.removeNodeFromParent(target_node);
|
---|
107 | }
|
---|
108 | };
|
---|
109 | try {
|
---|
110 | //SwingUtilities.invokeLater(doRemoveNodeFromParent);
|
---|
111 | SwingUtilities.invokeAndWait(doRemoveNodeFromParent);
|
---|
112 | }
|
---|
113 | catch (Exception exception) {
|
---|
114 | DebugStream.printStackTrace(exception);
|
---|
115 | }
|
---|
116 | // If we've thrown an error because we tried to invoke the runnable task and wait, when we are in the AWTEvent thread already, then try agin but with an invoke later.
|
---|
117 | catch (java.lang.Error error) {
|
---|
118 | if(error.toString().equals("java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread")) {
|
---|
119 | SwingUtilities.invokeLater(doRemoveNodeFromParent);
|
---|
120 | }
|
---|
121 | }
|
---|
122 | }
|
---|
123 | }
|
---|