[6096] | 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.*;
|
---|
[8236] | 32 | import org.greenstone.gatherer.DebugStream;
|
---|
| 33 |
|
---|
[6096] | 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 |
|
---|
[6622] | 44 | static final public Runnable insertNodeInto(final DefaultTreeModel model, final MutableTreeNode parent, final MutableTreeNode target_node, final boolean wait_allowed) {
|
---|
[6096] | 45 | final Runnable doInsertNodeInto = new Runnable() {
|
---|
| 46 | public void run() {
|
---|
| 47 | ///ystem.err.print("Running task... ");
|
---|
[8236] | 48 | DebugStream.println("insertNodeInto(" + model + ", " + parent + ", " + target_node + ", " + wait_allowed);
|
---|
[6096] | 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 | }
|
---|
[6212] | 90 | catch (Exception exception) {
|
---|
[8236] | 91 | DebugStream.printStackTrace(exception);
|
---|
[6096] | 92 | }
|
---|
| 93 | ///ystem.err.print("Added Task... ");
|
---|
| 94 | return doInsertNodeInto;
|
---|
| 95 | }
|
---|
[8577] | 96 |
|
---|
| 97 |
|
---|
[6096] | 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 {
|
---|
[6212] | 110 | //SwingUtilities.invokeLater(doRemoveNodeFromParent);
|
---|
[6096] | 111 | SwingUtilities.invokeAndWait(doRemoveNodeFromParent);
|
---|
| 112 | }
|
---|
[6212] | 113 | catch (Exception exception) {
|
---|
[8236] | 114 | DebugStream.printStackTrace(exception);
|
---|
[6096] | 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 | }
|
---|