1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.wotonomy.ui.swing;
20
21 import javax.swing.JTree;
22 import javax.swing.table.TableColumn;
23
24 import net.wotonomy.foundation.NSArray;
25 import net.wotonomy.ui.EODisplayGroup;
26 import net.wotonomy.ui.swing.components.TreeTableCellRenderer;
27
28 /***
29 * TreeColumnAssociation is a TableColumnAssocation
30 * that works like a TreeAssociation, allowing any
31 * table to display hierarchical data in a tabular format.
32 * This class is mainly a convenience for connecting a
33 * TreeAssociation to a JTree to a TreeTableCellRenderer
34 * to a TableColumn.<br><br>
35 *
36 * Like TableColumnAssociation, you must call setTable()
37 * to specify the JTable to be used. (The corresponding
38 * table association will direct all column header sorting
39 * to the root node of the tree association.)
40 *
41 * You may also optionally call setTree() to specify a
42 * customized JTree to be used. If not specified, a
43 * slightly customized JTree will be used (see createTree()
44 * and configureColumn()).
45 *
46 * TreeColumnAssociation supports the following bindings,
47 * just as TableColumnAssociation does:
48 * <ul>
49 * <li>value: a property convertable to a string for
50 * display in the cells of the table column. This
51 * binding is equivalent to the titles binding of
52 * TreeAssociation.</li>
53 *
54 * <li>editable: a property convertable to a boolean
55 * that determines the editability of the corresponding
56 * cells in the column.</li>
57 * </ul>
58 *
59 * TreeColumnAssociation additionally supports the following
60 * bindings, just as TreeAssociation does, except that the
61 * value binding is used instead of the titles binding.
62 * <ul>
63 * <li>children: a property of a node value that returns
64 * zero, one or many objects, each of which will correspond
65 * to a child node for the corresponding node in the tree.
66 * If this aspect is not bound, the tree behaves like a list.</li>
67 *
68 * <li>isLeaf: a property of a node value that returns
69 * a value convertable to a boolean value.
70 * If the isLeaf aspect is not bound, the tree will force
71 * nodes to load their children to determine whether they
72 * are leaf nodes (in effect loading the grandchildren for
73 * any expanded node).
74 * </li>
75 * </ul>
76 *
77 * @author michael@mpowers.net
78 * @author $Author: cgruber $
79 * @version $Revision: 904 $
80 */
81 public class TreeColumnAssociation extends TableColumnAssociation
82 {
83 static final NSArray aspects =
84 new NSArray( new Object[] {
85 ValueAspect, EditableAspect, ChildrenAspect, IsLeafAspect
86 } );
87 static final NSArray aspectSignatures =
88 new NSArray( new Object[] {
89 AttributeToOneAspectSignature
90 } );
91 static final NSArray objectKeysTaken =
92 new NSArray( new Object[] {
93 "table"
94 } );
95
96 EODisplayGroup childrenDisplayGroup, leafDisplayGroup;
97 String childrenKey, leafKey;
98
99 TreeModelAssociation treeAssociation;
100 JTree tree;
101
102 /***
103 * Constructor specifying the object to be controlled by this
104 * association. Throws an exception if the object is not
105 * a TableColumn.
106 */
107 public TreeColumnAssociation ( Object anObject )
108 {
109 super( anObject );
110 }
111
112 /***
113 * Returns a List of aspect signatures whose contents
114 * correspond with the aspects list. Each element is
115 * a string whose characters represent a capability of
116 * the corresponding aspect. <ul>
117 * <li>"A" attribute: the aspect can be bound to
118 * an attribute.</li>
119 * <li>"1" to-one: the aspect can be bound to a
120 * property that returns a single object.</li>
121 * <li>"M" to-one: the aspect can be bound to a
122 * property that returns multiple objects.</li>
123 * </ul>
124 * An empty signature "" means that the aspect can
125 * bind without needing a key.
126 * This implementation returns "A1M" for each
127 * element in the aspects array.
128 */
129 public static NSArray aspectSignatures ()
130 {
131 return aspectSignatures;
132 }
133
134 /***
135 * Returns a List that describes the aspects supported
136 * by this class. Each element in the list is the string
137 * name of the aspect. This implementation returns an
138 * empty list.
139 */
140 public static NSArray aspects ()
141 {
142 return aspects;
143 }
144
145 /***
146 * Binds the specified aspect of this association to the
147 * specified key on the specified display group.
148 */
149 public void bindAspect (
150 String anAspect, EODisplayGroup aDisplayGroup, String aKey )
151 {
152 if ( ChildrenAspect.equals( anAspect ) )
153 {
154 childrenDisplayGroup = aDisplayGroup;
155 childrenKey = aKey;
156 }
157 if ( IsLeafAspect.equals( anAspect ) )
158 {
159 leafDisplayGroup = aDisplayGroup;
160 leafKey = aKey;
161 }
162 super.bindAspect( anAspect, aDisplayGroup, aKey );
163 }
164
165 /***
166 * Overridden to call createTree if necessary,
167 * call configureColumn, call createTreeAssociation
168 * if necessary, and then call to super.
169 */
170 public void establishConnection ()
171 {
172 if ( tree == null )
173 {
174 tree = createTree();
175 }
176
177 configureColumn( (TableColumn) object() );
178
179 if ( treeAssociation == null )
180 {
181 treeAssociation = createTreeAssociation( tree );
182 }
183
184 treeAssociation.bindAspect( TitlesAspect, valueDisplayGroup, valueKey );
185 if ( childrenKey != null )
186 {
187 treeAssociation.bindAspect( ChildrenAspect, childrenDisplayGroup, childrenKey );
188 }
189 if ( leafKey != null )
190 {
191 treeAssociation.bindAspect( IsLeafAspect, leafDisplayGroup, leafKey );
192 }
193
194
195 getTableAssociation().bindAspect(
196 SourceAspect, treeAssociation.childrenDisplayGroup, "" );
197
198 treeAssociation.establishConnection();
199
200 table.setRowHeight( tree.getRowHeight() );
201
202 super.establishConnection();
203
204
205 if ( childrenKey != null )
206 {
207 getTableAssociation().sortTarget =
208 (EODisplayGroup) treeAssociation.getRoot();
209 }
210 }
211
212 /***
213 * Breaks the connection between this association and
214 * its object. Override to stop listening for events
215 * from the object.
216 */
217 public void breakConnection ()
218 {
219 super.breakConnection();
220 treeAssociation.breakConnection();
221
222
223 getTableAssociation().sortTarget = null;
224 }
225
226 /***
227 * Called by establishConnection if setTree was not called.
228 * This implementation returns a stock JTree. Override
229 * to provide your own customized JTree. Note that
230 * TreeTableCellRenderer will further customize this tree
231 * when configureColumn is called.
232 */
233 protected JTree createTree()
234 {
235 return new JTree();
236 }
237
238 /***
239 * Called by establishConnection to create a tree association
240 * only if no tree association has already been created.
241 * This implementation returns a stock TreeAssociation.
242 * Override to return your own customized TreeAssociation.
243 */
244 protected TreeModelAssociation createTreeAssociation( JTree aTree )
245 {
246 return new TreeAssociation( aTree );
247 }
248
249 /***
250 * Called by establishConnection to configure the column
251 * with a TreeTableCellRenderer using the current JTree.
252 * Override to further customize the column, or customize
253 * your column yourself after the call to establishConnection.
254 */
255 protected void configureColumn( TableColumn aColumn )
256 {
257 aColumn.setCellRenderer( new TreeTableCellRenderer( tree ) );
258 }
259
260 /***
261 * Gets the JTree currently used for the column renderer.
262 * If not specified, returns null.
263 */
264 public JTree getTree()
265 {
266 return tree;
267 }
268
269 /***
270 * Gets the TreeModelAssociation currently used for the tree.
271 * If not tree is not specified, returns null.
272 */
273 public TreeModelAssociation getTreeModelAssociation()
274 {
275 if ( tree == null ) return null;
276 if (!( tree.getModel() instanceof TreeModelAssociation )) return null;
277 return (TreeModelAssociation) tree.getModel();
278 }
279
280 /***
281 * Sets the JTree to be used for the column renderer.
282 * If not specified, createTree() will be called to create a JTree.
283 */
284 public void setTree( JTree aTree )
285 {
286 tree = aTree;
287 }
288
289 }
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331