KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > swing > outline > DefaultOutlineModel


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.swing.outline;
21
22 import java.util.ArrayList JavaDoc;
23 import java.util.Arrays JavaDoc;
24 import java.util.Enumeration JavaDoc;
25 import java.util.List JavaDoc;
26 import javax.swing.event.TableModelEvent JavaDoc;
27 import javax.swing.event.TableModelListener JavaDoc;
28 import javax.swing.event.TreeExpansionEvent JavaDoc;
29 import javax.swing.event.TreeExpansionListener JavaDoc;
30 import javax.swing.event.TreeModelEvent JavaDoc;
31 import javax.swing.event.TreeModelListener JavaDoc;
32 import javax.swing.event.TreeWillExpandListener JavaDoc;
33 import javax.swing.table.TableModel JavaDoc;
34 import javax.swing.tree.AbstractLayoutCache JavaDoc;
35 import javax.swing.tree.ExpandVetoException JavaDoc;
36 import javax.swing.tree.FixedHeightLayoutCache JavaDoc;
37 import javax.swing.tree.TreeModel JavaDoc;
38 import javax.swing.tree.TreePath JavaDoc;
39 import javax.swing.tree.VariableHeightLayoutCache JavaDoc;
40
41 /** Proxies a standard TreeModel and TableModel, translating events between
42  * the two. Note that the constructor is not public; the TableModel that is
43  * proxied is the OutlineModel's own. To make use of this class, implement
44  * RowModel - that is a mini-table model in which the TreeModel is responsible
45  * for defining the set of rows; it is passed an object from the tree, which
46  * it may use to generate values for the other columns. Pass that and the
47  * TreeModel you want to use to <code>createOutlineModel</code>.
48  * <p>
49  * A note on TableModelEvents produced by this model: There is a slight
50  * impedance mismatch between TableModelEvent and TreeModelEvent. When the
51  * tree changes, it is necessary to fire TableModelEvents to update the display.
52  * However, TreeModelEvents support changes to discontiguous segments of the
53  * model (i.e. &quot;child nodes 3, 4 and 9 were deleted&quot;). TableModelEvents
54  * have no such concept - they operate on contiguous ranges of rows. Therefore,
55  * one incoming TreeModelEvent may result in more than one TableModelEvent being
56  * fired. Discontiguous TreeModelEvents will be broken into their contiguous
57  * segments, which will be fired sequentially (in the case of removals, in
58  * reverse order). So, the example above would generate two TableModelEvents,
59  * the first indicating that row 9 was removed, and the second indicating that
60  * rows 3 and 4 were removed.
61  * <p>
62  * Clients which need to know whether the TableModelEvent they have just
63  * received is one of a group (perhaps they update some data structure, and
64  * should not do so until the table's state is fully synchronized with that
65  * of the tree model) may call <code>areMoreEventsPending()</code>.
66  * <p>
67  * In the case of TreeModelEvents which add items to an unexpanded tree node,
68  * a simple value change TableModelEvent will be fired for the row in question
69  * on the tree column index.
70  * <p>
71  * Note also that if the model is large-model, removal events may only indicate
72  * those indices which were visible at the time of removal, because less data
73  * is retained about the position of nodes which are not displayed. In this
74  * case, the only issue is the accuracy of the scrollbar in the model; in
75  * practice this is a non-issue, since it is based on the Outline's row count,
76  * which will be accurate.
77  * <p>
78  * A note to subclassers, if we even leave this class non-final: If you do
79  * not use ProxyTableModel and RowMapper (which probably means you are doing
80  * something wrong), <strong>do not fire structural changes from the TableModel</strong>.
81  * This class is designed such that the TreeModel is entirely in control of the
82  * count and contents of the rows of the table. It and only it may fire
83  * structural changes.
84  * <p>
85  * Note that this class enforces access only on the event dispatch thread
86  * with assertions. All events fired by the underlying table and tree model
87  * must be fired on the event dispatch thread.
88  *
89  * @author Tim Boudreau
90  */

91 public class DefaultOutlineModel implements OutlineModel {
92     private TreeModel JavaDoc treeModel;
93     private TableModel JavaDoc tableModel;
94     private AbstractLayoutCache JavaDoc layout;
95     private TreePathSupport treePathSupport;
96     private EventBroadcaster broadcaster;
97     //Some constants we use to have a single method handle all translated
98
//event firing
99
private static final int NODES_CHANGED = 0;
100     private static final int NODES_INSERTED = 1;
101     private static final int NODES_REMOVED = 2;
102     private static final int STRUCTURE_CHANGED = 3;
103     
104     /**
105      * 4/19/2004 - Added ability to set the node column name.
106      * David Botterill
107      */

108     
109     private String JavaDoc nodeColumnName;
110     
111     //XXX deleteme - string version of the avoid constants debug output:
112
private static final String JavaDoc[] types = new String JavaDoc[] {
113         "nodesChanged", "nodesInserted", "nodesRemoved", "structureChanged"
114     };
115     
116     /** Create a small model OutlineModel using the supplied tree model and row model
117      * @param treeModel The tree model that is the data model for the expandable
118      * tree column of an Outline
119      * @param rowModel The row model which will supply values for each row based
120      * on the tree node in that row in the tree model
121      */

122     public static OutlineModel createOutlineModel(TreeModel JavaDoc treeModel, RowModel rowModel) {
123         return createOutlineModel (treeModel, rowModel, false);
124     }
125
126     /** Create an OutlineModel using the supplied tree model and row model,
127      * specifying if it is a large-model tree */

128     public static OutlineModel createOutlineModel(TreeModel JavaDoc treeModel, RowModel rowModel, boolean isLargeModel) {
129         TableModel JavaDoc tableModel = new ProxyTableModel(rowModel);
130         return new DefaultOutlineModel (treeModel, tableModel, isLargeModel);
131     }
132     
133     /** Creates a new instance of DefaultOutlineModel. <strong><b>Note</b>
134      * Do not fire table structure changes from the wrapped TableModel (value
135      * changes are okay). Changes that affect the number of rows must come
136      * from the TreeModel. */

137     protected DefaultOutlineModel(TreeModel JavaDoc treeModel, TableModel JavaDoc tableModel, boolean largeModel) {
138         this.treeModel = treeModel;
139         this.tableModel = tableModel;
140         
141         layout = largeModel ? (AbstractLayoutCache JavaDoc) new FixedHeightLayoutCache JavaDoc()
142             : (AbstractLayoutCache JavaDoc) new VariableHeightLayoutCache JavaDoc();
143             
144         broadcaster = new EventBroadcaster (this);
145         
146         layout.setRootVisible(true);
147         layout.setModel(this);
148         treePathSupport = new TreePathSupport(this, layout);
149         treePathSupport.addTreeExpansionListener(broadcaster);
150         treePathSupport.addTreeWillExpandListener(broadcaster);
151         treeModel.addTreeModelListener(broadcaster);
152         tableModel.addTableModelListener(broadcaster);
153         if (tableModel instanceof ProxyTableModel) {
154             ((ProxyTableModel) tableModel).setOutlineModel(this);
155         }
156     }
157     
158     public final TreePathSupport getTreePathSupport() {
159         return treePathSupport;
160     }
161     
162     public final AbstractLayoutCache JavaDoc getLayout() {
163         return layout;
164     }
165     
166     public boolean areMoreEventsPending() {
167         return broadcaster.areMoreEventsPending();
168     }
169     
170     /** Accessor for EventBroadcaster */
171     TreeModel JavaDoc getTreeModel() {
172         return treeModel;
173     }
174     
175     /** Accessor for EventBroadcaster */
176     TableModel JavaDoc getTableModel() {
177         return tableModel;
178     }
179     
180     public final Object JavaDoc getChild(Object JavaDoc parent, int index) {
181         return treeModel.getChild (parent, index);
182     }
183     
184     public final int getChildCount(Object JavaDoc parent) {
185         return treeModel.getChildCount (parent);
186     }
187     
188     /** Delegates to the RowMapper for > 0 columns; column 0 always
189      * returns Object.class */

190     public final Class JavaDoc getColumnClass(int columnIndex) {
191         if (columnIndex == 0) {
192             return Object JavaDoc.class;
193         } else {
194             return tableModel.getColumnClass(columnIndex-1);
195         }
196     }
197     
198     public final int getColumnCount() {
199         return tableModel.getColumnCount()+1;
200     }
201     /**
202      * Added 4/19/2004 David Botterill
203      */

204     public void setNodeColumnName(String JavaDoc inName) {
205         nodeColumnName = inName;
206     }
207     public String JavaDoc getColumnName(int columnIndex) {
208         /**
209          * Changed 4/19/2004 to allow the node column to be named.
210          * - David Botterill
211          */

212         if (columnIndex == 0) {
213             return null == nodeColumnName ? "Nodes": nodeColumnName; //XXX
214
} else {
215             return tableModel.getColumnName(columnIndex-1);
216         }
217     }
218     
219     public final int getIndexOfChild(Object JavaDoc parent, Object JavaDoc child) {
220         return treeModel.getIndexOfChild(parent, child);
221     }
222     
223     public final Object JavaDoc getRoot() {
224         return treeModel.getRoot();
225     }
226     
227     public final int getRowCount() {
228         return layout.getRowCount();
229     }
230     
231     public final Object JavaDoc getValueAt(int rowIndex, int columnIndex) {
232         Object JavaDoc result;
233         if (columnIndex == 0) { //XXX need a column ID - columnIndex = 0 depends on the column model
234
TreePath JavaDoc path = getLayout().getPathForRow(rowIndex);
235             if (path != null) {
236                 result = path.getLastPathComponent();
237             } else {
238                 result = null;
239             }
240         } else {
241             result = (tableModel.getValueAt(rowIndex, columnIndex -1));
242         }
243         return result;
244     }
245     
246     public boolean isCellEditable(int rowIndex, int columnIndex) {
247         if (columnIndex == 0) {
248             return false; //XXX support editing of node names
249
} else {
250             return tableModel.isCellEditable(rowIndex, columnIndex-1);
251         }
252     }
253     
254     public final boolean isLeaf(Object JavaDoc node) {
255         return treeModel.isLeaf(node);
256     }
257
258     /** Delegates to the EventBroadcaster for this model */
259     public final synchronized void addTableModelListener(TableModelListener JavaDoc l) {
260         broadcaster.addTableModelListener (l);
261     }
262     
263     /** Delegates to the EventBroadcaster for this model */
264     public final synchronized void addTreeModelListener(TreeModelListener JavaDoc l) {
265         broadcaster.addTreeModelListener (l);
266     }
267     
268     /** Delegates to the EventBroadcaster for this model */
269     public final synchronized void removeTableModelListener(TableModelListener JavaDoc l) {
270         broadcaster.removeTableModelListener(l);
271     }
272     
273     /** Delegates to the EventBroadcaster for this model */
274     public final synchronized void removeTreeModelListener(TreeModelListener JavaDoc l) {
275         broadcaster.removeTreeModelListener(l);
276     }
277     
278     /** Delegates to the RowModel (or TableModel) for non-0 columns */
279     public final void setValueAt(Object JavaDoc aValue, int rowIndex, int columnIndex) {
280         if (columnIndex != 0) {
281             tableModel.setValueAt (aValue, rowIndex, columnIndex-1);
282         } else {
283             //XXX do something
284
}
285     }
286     
287     public final void valueForPathChanged(javax.swing.tree.TreePath JavaDoc path, Object JavaDoc newValue) {
288         //if the model is correctly implemented, this will trigger a change
289
//event
290
treeModel.valueForPathChanged(path, newValue);
291     }
292
293     public boolean isLargeModel() {
294         return layout instanceof FixedHeightLayoutCache JavaDoc;
295     }
296     
297     public NodeRowModel getRowNodeModel() {
298         return (ProxyTableModel)tableModel;
299     }
300
301     
302 }
303
Popular Tags