|
1 /* |
|
2 * CDDL HEADER START |
|
3 * |
|
4 * The contents of this file are subject to the terms of the |
|
5 * Common Development and Distribution License (the "License"). |
|
6 * You may not use this file except in compliance with the License. |
|
7 * |
|
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
9 * or http://www.opensolaris.org/os/licensing. |
|
10 * See the License for the specific language governing permissions |
|
11 * and limitations under the License. |
|
12 * |
|
13 * When distributing Covered Code, include this CDDL HEADER in each |
|
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
15 * If applicable, add the following below this CDDL HEADER, with the |
|
16 * fields enclosed by brackets "[]" replaced with your own identifying |
|
17 * information: Portions Copyright [yyyy] [name of copyright owner] |
|
18 * |
|
19 * CDDL HEADER END |
|
20 */ |
|
21 |
|
22 /* |
|
23 * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. |
|
24 */ |
|
25 |
|
26 package com.oracle.solaris.vp.util.swing.tree; |
|
27 |
|
28 import java.util.*; |
|
29 import javax.swing.ListModel; |
|
30 import javax.swing.event.*; |
|
31 import javax.swing.table.TableModel; |
|
32 import javax.swing.tree.TreeNode; |
|
33 import com.oracle.solaris.vp.util.swing.ListDataListenerAdapter; |
|
34 import com.oracle.solaris.vp.util.swing.event.TableModelListeners; |
|
35 |
|
36 /** |
|
37 * The {@code TreeTableModelAdapter} class maps a {@link TreeTableModel} onto a |
|
38 * {@code TableModel} and a {@code ListModel}. |
|
39 */ |
|
40 @SuppressWarnings({"serial"}) |
|
41 public class TreeTableModelAdapter implements TableModel, ListModel, |
|
42 TreeModelListener { |
|
43 |
|
44 // |
|
45 // Instance data |
|
46 // |
|
47 |
|
48 private List<ModelRowData> rows = new LinkedList<ModelRowData>(); |
|
49 private TreeTableModel model; |
|
50 private TableModelListeners listeners = new TableModelListeners(); |
|
51 |
|
52 private Map<ListDataListener, ListDataListenerAdapter> listenerMap = |
|
53 new HashMap<ListDataListener, ListDataListenerAdapter>(); |
|
54 |
|
55 private int listColumn; |
|
56 |
|
57 // |
|
58 // Constructors |
|
59 // |
|
60 |
|
61 public TreeTableModelAdapter(TreeTableModel model) { |
|
62 this.model = model; |
|
63 |
|
64 addRows(0, (TreeNode)model.getRoot(), 0); |
|
65 model.addTreeModelListener(this); |
|
66 } |
|
67 |
|
68 // |
|
69 // TableModel methods |
|
70 // |
|
71 |
|
72 @Override |
|
73 public void addTableModelListener(TableModelListener l) { |
|
74 listeners.add(l); |
|
75 } |
|
76 |
|
77 @Override |
|
78 public Class<?> getColumnClass(int column) { |
|
79 return model.getColumnClass(column); |
|
80 } |
|
81 |
|
82 @Override |
|
83 public int getColumnCount() { |
|
84 return model.getColumnCount(); |
|
85 } |
|
86 |
|
87 @Override |
|
88 public String getColumnName(int column) { |
|
89 return model.getColumnName(column); |
|
90 } |
|
91 |
|
92 @Override |
|
93 public int getRowCount() { |
|
94 return rows.size(); |
|
95 } |
|
96 |
|
97 @Override |
|
98 public Object getValueAt(int row, int column) { |
|
99 return model.getValueAt(getTreeNode(row), column); |
|
100 } |
|
101 |
|
102 @Override |
|
103 public boolean isCellEditable(int row, int column) { |
|
104 return model.isCellEditable(getTreeNode(row), column); |
|
105 } |
|
106 |
|
107 @Override |
|
108 public void removeTableModelListener(TableModelListener l) { |
|
109 listeners.remove(l); |
|
110 } |
|
111 |
|
112 @Override |
|
113 public void setValueAt(Object value, int row, int column) { |
|
114 model.setValueAt(value, getTreeNode(row), column); |
|
115 } |
|
116 |
|
117 // |
|
118 // ListModel methods |
|
119 // |
|
120 |
|
121 @Override |
|
122 public void addListDataListener(ListDataListener l) { |
|
123 synchronized (listenerMap) { |
|
124 if (!listenerMap.containsKey(l)) { |
|
125 ListDataListenerAdapter adapter = |
|
126 new ListDataListenerAdapter(l); |
|
127 |
|
128 addTableModelListener(adapter); |
|
129 listenerMap.put(l, adapter); |
|
130 } |
|
131 } |
|
132 } |
|
133 |
|
134 /** |
|
135 * Returns the value of the cell in the {@link #getListColumn list column} |
|
136 * of the given row. |
|
137 */ |
|
138 @Override |
|
139 public Object getElementAt(int row) { |
|
140 return getValueAt(row, getListColumn()); |
|
141 } |
|
142 |
|
143 @Override |
|
144 public int getSize() { |
|
145 return getRowCount(); |
|
146 } |
|
147 |
|
148 @Override |
|
149 public void removeListDataListener(ListDataListener l) { |
|
150 synchronized (listenerMap) { |
|
151 ListDataListenerAdapter adapter = listenerMap.get(l); |
|
152 if (adapter != null) { |
|
153 removeTableModelListener(adapter); |
|
154 listenerMap.remove(l); |
|
155 } |
|
156 } |
|
157 } |
|
158 |
|
159 // |
|
160 // TreeModelListener methods |
|
161 // |
|
162 |
|
163 @Override |
|
164 public void treeNodesChanged(TreeModelEvent e) { |
|
165 synchronized (rows) { |
|
166 for (Object child : e.getChildren()) { |
|
167 TreeNode node = (TreeNode)child; |
|
168 int index = indexOf(node); |
|
169 |
|
170 TableModelEvent event = new TableModelEvent( |
|
171 this, index, index, TableModelEvent.ALL_COLUMNS, |
|
172 TableModelEvent.UPDATE); |
|
173 |
|
174 fireTableChanged(event); |
|
175 } |
|
176 } |
|
177 } |
|
178 |
|
179 @Override |
|
180 public void treeNodesInserted(TreeModelEvent e) { |
|
181 TreeNode node = (TreeNode)e.getTreePath().getLastPathComponent(); |
|
182 ModelRowData row = getRow(indexOf(node)); |
|
183 int level = row.getLevel() + 1; |
|
184 |
|
185 int[] indexes = e.getChildIndices(); |
|
186 Object[] children = e.getChildren(); |
|
187 |
|
188 synchronized (rows) { |
|
189 for (int i = 0; i < indexes.length; i++) { |
|
190 int first = indexOf(node, indexes[i]); |
|
191 TreeNode child = (TreeNode)children[i]; |
|
192 int last = addRows(first, child, level) - 1; |
|
193 |
|
194 TableModelEvent event = new TableModelEvent( |
|
195 this, first, last, TableModelEvent.ALL_COLUMNS, |
|
196 TableModelEvent.INSERT); |
|
197 |
|
198 fireTableChanged(event); |
|
199 } |
|
200 } |
|
201 } |
|
202 |
|
203 @Override |
|
204 public void treeNodesRemoved(TreeModelEvent e) { |
|
205 TreeNode node = (TreeNode)e.getTreePath().getLastPathComponent(); |
|
206 Object[] children = e.getChildren(); |
|
207 |
|
208 synchronized (rows) { |
|
209 for (int i = children.length - 1; i >= 0; i--) { |
|
210 TreeNode child = (TreeNode)children[i]; |
|
211 int first = indexOf(child); |
|
212 int last = indexOf(child, -1) - 1; |
|
213 |
|
214 for (int j = last; j >= first; j--) { |
|
215 rows.remove(j); |
|
216 } |
|
217 |
|
218 TableModelEvent event = new TableModelEvent( |
|
219 this, first, last, TableModelEvent.ALL_COLUMNS, |
|
220 TableModelEvent.DELETE); |
|
221 |
|
222 fireTableChanged(event); |
|
223 } |
|
224 } |
|
225 } |
|
226 |
|
227 @Override |
|
228 public void treeStructureChanged(TreeModelEvent e) { |
|
229 TableModelEvent event = new TableModelEvent( |
|
230 this, TableModelEvent.HEADER_ROW); |
|
231 |
|
232 fireTableChanged(event); |
|
233 } |
|
234 |
|
235 // |
|
236 // TreeTableModelAdapter methods |
|
237 // |
|
238 |
|
239 public void fireTableChanged(TableModelEvent e) { |
|
240 listeners.tableChanged(e); |
|
241 } |
|
242 |
|
243 /** |
|
244 * Gets the column to returned by the {@code ListModel} method {@link |
|
245 * #getElementAt}. |
|
246 * |
|
247 * @see #setListColumn |
|
248 */ |
|
249 public int getListColumn() { |
|
250 return listColumn; |
|
251 } |
|
252 |
|
253 protected ModelRowData getParent(ModelRowData row) { |
|
254 synchronized (rows) { |
|
255 TreeNode parent = row.getTreeNode().getParent(); |
|
256 if (parent != null) { |
|
257 return getRow(parent); |
|
258 } |
|
259 return null; |
|
260 } |
|
261 } |
|
262 |
|
263 protected ModelRowData getRow(int row) { |
|
264 synchronized (rows) { |
|
265 return rows.get(row); |
|
266 } |
|
267 } |
|
268 |
|
269 protected ModelRowData getRow(TreeNode node) { |
|
270 synchronized (rows) { |
|
271 for (ModelRowData row : rows) { |
|
272 if (row.getTreeNode() == node) { |
|
273 return row; |
|
274 } |
|
275 } |
|
276 } |
|
277 return null; |
|
278 } |
|
279 |
|
280 public TreeNode getTreeNode(int row) { |
|
281 return getRow(row).getTreeNode(); |
|
282 } |
|
283 |
|
284 public TreeTableModel getTreeTableModel() { |
|
285 return model; |
|
286 } |
|
287 |
|
288 public int indexOf(TreeNode node) { |
|
289 for (int i = 0, n = rows.size(); i < n; i++) { |
|
290 ModelRowData row = getRow(i); |
|
291 if (row.getTreeNode() == node) { |
|
292 return i; |
|
293 } |
|
294 } |
|
295 return -1; |
|
296 } |
|
297 |
|
298 /** |
|
299 * Sets the column to returned by the {@code ListModel} method {@link |
|
300 * #getElementAt}. The default value is 0. |
|
301 * |
|
302 * @see #getListColumn |
|
303 */ |
|
304 public void setListColumn(int listColumn) { |
|
305 this.listColumn = listColumn; |
|
306 } |
|
307 |
|
308 // |
|
309 // Private methods |
|
310 // |
|
311 |
|
312 /** |
|
313 * Adds the given {@code TreeNode} and its children to the list of rows; |
|
314 * returns the current insertion point. |
|
315 */ |
|
316 private int addRows(int index, TreeNode node, int level) { |
|
317 synchronized (rows) { |
|
318 ModelRowData row = new ModelRowData(node, level); |
|
319 rows.add(index, row); |
|
320 index++; |
|
321 level++; |
|
322 for (Enumeration i = node.children(); i.hasMoreElements();) { |
|
323 TreeNode child = (TreeNode)i.nextElement(); |
|
324 index = addRows(index, child, level); |
|
325 } |
|
326 return index; |
|
327 } |
|
328 } |
|
329 |
|
330 /** |
|
331 * Gets the index of the {@code (childIndex + 1)}th child of the given node. |
|
332 * |
|
333 * @return an index, or -1 if the given {@code TreeNode} is not found |
|
334 */ |
|
335 private int indexOf(TreeNode node, int childIndex) { |
|
336 synchronized (rows) { |
|
337 int i = indexOf(node); |
|
338 if (i != -1) { |
|
339 ModelRowData row = getRow(i++); |
|
340 if (row.getTreeNode() == node) { |
|
341 |
|
342 for (int n = rows.size(); i < n; i++) { |
|
343 int level = getRow(i).getLevel(); |
|
344 if (level <= row.getLevel()) { |
|
345 break; |
|
346 } |
|
347 |
|
348 if (level == row.getLevel() + 1) { |
|
349 childIndex--; |
|
350 if (childIndex == -1) { |
|
351 break; |
|
352 } |
|
353 } |
|
354 } |
|
355 } |
|
356 } |
|
357 |
|
358 return i; |
|
359 } |
|
360 } |
|
361 } |