Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/greenstone/gatherer/util/SynchronizedTreeModel.java


1   package org.greenstone.gatherer.util;
2   
3   import javax.swing.*;
4   import javax.swing.tree.*;
5   import org.greenstone.gatherer.util.DefaultSynchronizedTreeNode;
6   import org.greenstone.gatherer.util.TreeModelTest;
7   import org.greenstone.gatherer.util.SynchronizedTreeNode;
8   
9   /** This synchronized TreeModel is comprised of two seperate models. The extended model is used to paint the screen, and can only be updated on the AWTEvent Thread. The second, private, model contains the current actual state of the model with changes made immediately. If such changes occur then a task is queued in the AWTEvent Thread to update the 'painted' model. This model depends on the TreeNodes used having these properties:
10  * TreeNode x_node = new TreeNode("x");
11  * TreeNode y_node = x_node.cloneNode();
12  * for(int i = 0; i < x_node.getChildCount(); i++) {
13  *  x_node.getChildAt(i) != y_node.getChildAt(i);
14  *  x_node.getChildAt(i).equals(y_node.getChildAt(i));
15  * }
16  * x_node != y_node;
17  * (new TreeNode("x")).equals(new TreeNode("x"));
18  * In other words, any two different instances of the tree node must be equal according to the equals method.
19  * Methods which need data from the tree should also use the model methods (ie model.getChildCount(node) rather than node.getChildCount()) and should not be in the AWTEvent Thread (as such calls will see the possibly out of date 'visual' model, not the actual underlying model).
20  */
21  public class SynchronizedTreeModel
22      extends DefaultTreeModel
23      implements Runnable {
24  
25      private boolean changed = false;
26      private DefaultTreeModel offscreen;
27      private TreeModelTest test = null;
28  
29      SynchronizedTreeModel(SynchronizedTreeNode root) {
30    super(root);
31    offscreen = new DefaultTreeModel(root.cloneNode());
32      }
33  
34      SynchronizedTreeModel(SynchronizedTreeNode root, TreeModelTest test) {
35    super(root);
36    offscreen = new DefaultTreeModel(root.cloneNode());
37    this.test = test;
38      }
39  
40      /** Returns the child of parent at index index in the parent's child array. */
41      public Object getChild(Object parent, int index) {
42    if(isEventThread()) {
43        return super.getChild(parent, index);
44    }
45    else {
46        return offscreen.getChild(parent, index);
47    }
48      }
49  
50      /** Returns the number of children of parent. */
51      public int getChildCount(Object parent) {
52    if(isEventThread()) {
53        return super.getChildCount(parent);
54    }
55    else {
56        return offscreen.getChildCount(parent);
57    }
58      }
59  
60      /** Builds the parents of node up to and including the root node, where the original node is the last element in the returned array. This is probably the most 'expensive' method we make synchronized, but it isn't called often so thats ok. */
61      public TreeNode[] getPathToRoot(TreeNode aNode) {
62    if(isEventThread()) {
63        return super.getPathToRoot(aNode);
64    }
65    else {
66        return offscreen.getPathToRoot(aNode);
67    }
68      }
69  
70      public Object getRoot() {
71    if(isEventThread()) {
72        return super.getRoot();
73    }
74    else {
75        return offscreen.getRoot();
76    }
77      }
78  
79      /** Invoked this to insert newChild at location index in parents children. */
80      public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index) {
81    if(isEventThread()) {
82        if(test != null) test.debug("insertNodeInto(" + newChild + ", " + parent + ", " + index + ")");
83        super.insertNodeInto(newChild, parent, index);
84    }
85    else {
86        if(test != null) test.debug("offscreen.insertNodeInto(" + newChild + ", " + parent + ", " + index + ")");
87        offscreen.insertNodeInto(newChild, parent, index);
88        queueUpdate();
89    }
90      }
91  
92      /** Message this to remove node from its parent. */
93      public void removeNodeFromParent(MutableTreeNode node) {
94    if(isEventThread()) {
95        if(test != null) test.debug("removeNodeFromParent(" + node + ")");
96        super.removeNodeFromParent(node);
97    }
98    else {
99        if(test != null) test.debug("offscreen.removeNodeFromParent(" + node + ")");
100       offscreen.removeNodeFromParent(node);
101       queueUpdate();
102   }
103     }
104 
105     public void run() {
106   synchronized(this) {
107       if(changed) {
108     setRoot(((SynchronizedTreeNode)(offscreen.getRoot())).  cloneNode());
109     nodeChanged(root);
110     changed = false;
111     if(test != null) test.debug("Painted model refreshed.");
112       }
113       else {
114     if(test != null) test.debug("Painted model is current.");
115       }
116   }
117     }
118 
119     /** Determine if this call is happening on the AWTEvent Dispatch Thread. */
120     private boolean isEventThread() {
121   return SwingUtilities.isEventDispatchThread();
122     }
123 
124     private synchronized void queueUpdate() {
125   changed = true;
126   SwingUtilities.invokeLater(this);
127   if(test != null) test.debug("Painted model is obsolete.");
128     }
129 }