001: /*=============================================================================
002: * Copyright Texas Instruments 2001, 2002. All Rights Reserved.
003: *
004: * This program is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with this program; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018:
019: package ti.chimera.registry;
020:
021: import java.util.*;
022:
023: import ti.swing.treetable.*;
024: import ti.chimera.*;
025: import ti.exceptions.ProgrammingErrorException;
026:
027: /**
028: * A tree model for a subtree of the registry. The model tracks changes to
029: * the form of the registry (ie. link/unlink nodes), and fires the appropriate
030: * tree structure change events.
031: * <p>
032: * XXX add a RegistryTreeFilter, so different registry trees can filter out
033: * nodes...
034: * <p>
035: * XXX add a TreeCellRender plus interface to set icons, etc., for different
036: * node contract types... this should be done here, rather than in the user of
037: * the RegistryTreeModel in order to force a more consistent user interface,
038: * because the icon will be conveying type information, it would be confusing
039: * to the user if the same type results in different icons in different places
040: * <p>
041: * XXX need some way to unsubscribe all DirectorySubscriber-s when the tree
042: * model is no longer in use...
043: *
044: * @author Rob Clark
045: * @version 0.1
046: */
047: public class RegistryTreeModel extends AbstractTreeModel {
048: /**
049: * The root-path of this model
050: */
051: private String path;
052:
053: private Main main;
054: private Registry registry;
055:
056: private WeakHashMap nodeTable = new WeakHashMap();
057:
058: /**
059: * Dispose of the tree model
060: */
061: public void dispose() {
062: for (Iterator itr = nodeTable.keySet().iterator(); itr
063: .hasNext();) {
064: Node node = (Node) (itr.next());
065: node.unsubscribe((NodeSubscriber) (nodeTable.get(node)));
066: }
067: }
068:
069: /**
070: * Class Constructor.
071: *
072: * @param path the path to the root node of the tree
073: */
074: public RegistryTreeModel(Main main, String path)
075: throws RegistryException {
076: super (new NodeWrapper(path, main.getRegistry().resolve(path)));
077:
078: this .path = path;
079: this .main = main;
080:
081: registry = main.getRegistry();
082:
083: watch(registry.resolve(path));
084: }
085:
086: private void watch(Node node) {
087: NodeSubscriber s = new DirectorySubscriber();
088: nodeTable.put(node, s);
089: node.subscribe(s);
090: }
091:
092: private class DirectorySubscriber implements NodeSubscriber {
093: private DirectoryTable oldDt;
094:
095: public void publish(Node node, Object value) {
096: DirectoryTable newDt = (DirectoryTable) value;
097:
098: for (Iterator itr = newDt.notIn(oldDt); itr.hasNext();) {
099: Node child = newDt.get((String) (itr.next()));
100: if (child.getValue() instanceof DirectoryTable)
101: watch(child);
102: }
103:
104: oldDt = newDt;
105:
106: final LinkedList pathList = new LinkedList();
107:
108: // XXX probably some synchronization needed here:
109: while (true) {
110: pathList.addFirst(new NodeWrapper("foo", node));
111:
112: String path = node.getPrimaryPath();
113:
114: if (path == null)
115: return;
116:
117: if (path.equals(RegistryTreeModel.this .path))
118: break;
119:
120: try {
121: path = node.getPrimaryPath();
122: if (path == null)
123: return;
124: path = registry.dirname(node.getPrimaryPath());
125: if (!registry.exists(path)) // can happen in cases of nodes being unlinked
126: return;
127: node = registry.resolve(path);
128: } catch (RegistryException e) {
129: throw new ProgrammingErrorException(e);
130: }
131: }
132:
133: javax.swing.SwingUtilities.invokeLater(new Runnable() {
134:
135: public void run() {
136: // this doesn't entirely prevent mutations to the tree structure
137: // while the JTree updates itself, but this should be good enough:
138: synchronized (registry) {
139: fireTreeStructureChanged(
140: DirectorySubscriber.this , pathList
141: .toArray(), null, null);
142: }
143: }
144:
145: });
146: }
147: }
148:
149: public boolean isLeaf(Object node) {
150: return !(((NodeWrapper) node).getNode().getValue() instanceof DirectoryTable);
151: }
152:
153: public Object getChild(Object parent, int idx) {
154: DirectoryTable dt = (DirectoryTable) ((NodeWrapper) parent)
155: .getNode().getValue();
156: return new NodeWrapper(dt.getChildName(idx), dt
157: .getChildNode(idx));
158: }
159:
160: public int getChildCount(Object parent) {
161: return ((DirectoryTable) ((NodeWrapper) parent).getNode()
162: .getValue()).getChildCount();
163: }
164:
165: public static class NodeWrapper {
166: private String name;
167: private Node node;
168:
169: NodeWrapper(String name, Node node) {
170: this .name = name;
171: this .node = node;
172: }
173:
174: public String toString() {
175: return name;
176: }
177:
178: public Node getNode() {
179: return node;
180: }
181:
182: public int hashCode() {
183: return node.hashCode();
184: }
185:
186: public boolean equals(Object obj) {
187: return (obj instanceof NodeWrapper)
188: && ((NodeWrapper) obj).node.equals(node);
189: }
190:
191: }
192:
193: // /**
194: // * Filter which controls what nodes are visible in the model.
195: // */
196: // public interface RegistryTreeFilter
197: // {
198: // /**
199: // * Test whether the node is accepted by this filter.
200: // */
201: // public boolean accept( Node node );
202: // }
203:
204: /**
205: * A {@link javax.swing.tree.TreeCellRenderer} that uses the icons set
206: * with {@link #setFileIcon}. To make the correct icons be displayed,
207: * when you create a new tree, you need to set it's renderer to be an
208: * instance of this class (or a subclass).
209: * <pre>
210: * JTree tree = new JTree( new RegistryTreeModel(...) )
211: * tree.setCellRenderer( new RegistryTreeModel.RegistryTreeCellRenderer() );
212: * </pre>
213: */
214: public static class RegistryTreeCellRenderer extends
215: javax.swing.tree.DefaultTreeCellRenderer {
216: public java.awt.Component getTreeCellRendererComponent(
217: javax.swing.JTree tree, Object value, boolean selected,
218: boolean expanded, boolean leaf, int row,
219: boolean hasFocus) {
220: super .getTreeCellRendererComponent(tree, value, selected,
221: expanded, leaf, row, hasFocus);
222:
223: return this ;
224: }
225: }
226:
227: }
228:
229: /*
230: * Local Variables:
231: * tab-width: 2
232: * indent-tabs-mode: nil
233: * mode: java
234: * c-indentation-style: java
235: * c-basic-offset: 2
236: * eval: (c-set-offset 'substatement-open '0)
237: * eval: (c-set-offset 'case-label '+)
238: * eval: (c-set-offset 'inclass '+)
239: * eval: (c-set-offset 'inline-open '0)
240: * End:
241: */
|