1
2
3
4
5
6
7
8
9
10
11 package de.jaret.util.ui.table.model;
12
13 import java.beans.PropertyChangeEvent;
14 import java.beans.PropertyChangeListener;
15 import java.util.ArrayList;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Map;
19
20 import de.jaret.util.misc.PropertyObservable;
21
22 /***
23 * Implementation of a "normal" jaret table model based on a hierarchical jaret table model. The StdHierarchicalmodel
24 * will listen on propchanges if the nodes are PropertyObservables.
25 *
26 * @author Peter Kliem
27 * @version $Id: StdHierarchicalTableModel.java 1076 2010-12-05 13:34:42Z kliem $
28 */
29 public class StdHierarchicalTableModel extends AbstractJaretTableModel implements IHierarchicalTableViewStateListener,
30 ITableNodeListener, PropertyChangeListener {
31 /***
32 * Current row list = list of visible nodes.
33 */
34 protected List<ITableNode> _rows;
35
36 /*** the hierarchical model this "normal" model maps. */
37 protected IHierarchicalJaretTableModel _hModel;
38
39 /*** the hierarchical viewstate responsible for node visibility. */
40 protected IHierarchicalTableViewState _hvs;
41
42 /*** the list of columns. */
43 protected List<IColumn> _cols = new ArrayList<IColumn>();
44
45 protected boolean _excludeRootNode = false;
46
47 /***
48 * Construct a new stdhierarchical table model for a viewstate and a hierarchical model.
49 *
50 * @param hModel hierarchical table model
51 * @param hvs hierarchical viewstate
52 */
53 public StdHierarchicalTableModel(IHierarchicalJaretTableModel hModel, IHierarchicalTableViewState hvs) {
54 _hModel = hModel;
55 _hvs = hvs;
56 _hvs.addHierarchicalViewstateListener(this);
57 updateRowList();
58 }
59
60 /***
61 * Register as propchange listener if node is observable.
62 *
63 * @param node node
64 */
65 private void registerPropChange(ITableNode node) {
66
67 if (node instanceof PropertyObservable) {
68 ((PropertyObservable) node).addPropertyChangeListener(this);
69 }
70 }
71
72 /***
73 * Deregister as propchange listener if node is observable.
74 *
75 * @param node node
76 */
77 private void deRegisterPropChange(ITableNode node) {
78 if (node instanceof PropertyObservable) {
79 ((PropertyObservable) node).removePropertyChangeListener(this);
80 }
81 }
82
83 /***
84 * Update the internal rowlist by traversing the hierarchy.
85 */
86 private void updateRowList() {
87 _rows = new ArrayList<ITableNode>();
88 updateRowList(_rows, 0, _hModel.getRootNode(), true);
89 }
90
91 /***
92 * Recursive creation of the list of rows.
93 *
94 * @param rows list to be filled
95 * @param level current level
96 * @param node current node
97 * @param visible true if visible
98 */
99 private void updateRowList(List<ITableNode> rows, int level, ITableNode node, boolean visible) {
100 if (visible) {
101 if (!(_excludeRootNode && node.equals(_hModel.getRootNode()))) {
102 rows.add(node);
103 registerPropChange(node);
104 }
105 }
106
107 node.addTableNodeListener(this);
108
109
110 node.setLevel(level);
111
112 for (ITableNode n : node.getChildren()) {
113 updateRowList(rows, level + 1, n, _hvs.isExpanded(node) && visible);
114 }
115 }
116
117 /***
118 * Check whether more siblings exist for a given node on a given level.
119 *
120 * @param node node
121 * @param level level
122 * @return true if more siblings exist (even with other parent)
123 */
124 public boolean moreSiblings(ITableNode node, int level) {
125 int idx = _rows.indexOf(node);
126 if (idx == -1) {
127 throw new RuntimeException();
128 }
129 if (node.getLevel() == level) {
130 return getNextSibling(node) != null;
131 } else {
132 ITableNode n = node;
133 for (int l = node.getLevel(); l > level + 1; l--) {
134 n = getParent(n);
135 }
136 return getNextSibling(n) != null;
137 }
138 }
139
140 /***
141 * Return the next sibling of a node.
142 *
143 * @param node node to get the next sibling for
144 * @return the sibling or <code>null</code> if there is none
145 */
146 public ITableNode getNextSibling(ITableNode node) {
147 ITableNode parent = getParent(node);
148 if (parent == null) {
149 return null;
150 }
151 int idx = parent.getChildren().indexOf(node);
152 if (parent.getChildren().size() > idx + 1) {
153 return parent.getChildren().get(idx + 1);
154 } else {
155 return null;
156 }
157 }
158
159 /***
160 * Retrieve the parent of a particular node.
161 *
162 * @param node node
163 * @return parent of the node or <code>null</code> if it is the root node or is not in the list of nodes (not
164 * visible)
165 */
166 private ITableNode getParent(ITableNode node) {
167 int idx = _rows.indexOf(node);
168 if (idx == -1) {
169 return null;
170 }
171 for (int i = idx - 1; i >= 0; i--) {
172 ITableNode n = _rows.get(i);
173 if (n.getChildren().contains(node)) {
174 return n;
175 }
176 }
177 return null;
178 }
179
180 /***
181 * Check whether a node is visible.
182 *
183 * @param node node to check
184 * @return true if the node is visible
185 */
186 public boolean isVisible(ITableNode node) {
187 return getIdxForNode(node) != -1;
188 }
189
190 /***
191 * Get the index of a node in the list of visible nodes.
192 *
193 * @param node node to check
194 * @return index or -1 if not found
195 */
196 private int getIdxForNode(ITableNode node) {
197 return _rows.indexOf(node);
198 }
199
200 /***
201 * {@inheritDoc}
202 */
203 public IRow getRow(int rowIdx) {
204 return _rows.get(rowIdx);
205 }
206
207 /***
208 * {@inheritDoc}
209 */
210 public int getRowCount() {
211 return _rows.size();
212 }
213
214 /***
215 * {@inheritDoc}
216 */
217 public void nodeAdded(ITableNode parent, ITableNode newChild) {
218 newChild.addTableNodeListener(this);
219 if (_hvs.isExpanded(parent)) {
220
221 Map<ITableNode, Integer> map = new HashMap<ITableNode, Integer>();
222 posForNode(parent, map);
223 int pos = map.get(newChild);
224 _rows.add(pos, newChild);
225 fireRowAdded(pos, newChild);
226
227 List<ITableNode> toAdd = new ArrayList<ITableNode>();
228 enumerateChildren(newChild, toAdd);
229 pos++;
230 for (ITableNode tableNode : toAdd) {
231 _rows.add(pos, tableNode);
232 fireRowAdded(pos, tableNode);
233 pos++;
234 }
235
236 }
237
238 }
239
240 /***
241 * Fill a list with all children of the given node that are visible.
242 *
243 * @param node starting ndoe
244 * @param children list to fill
245 */
246 private void enumerateChildren(ITableNode node, List<ITableNode> children) {
247 if (node.getChildren() != null && _hvs.isExpanded(node)) {
248 for (ITableNode tableNode : node.getChildren()) {
249 children.add(tableNode);
250 enumerateChildren(tableNode, children);
251 }
252 }
253 }
254
255 /***
256 * Fill a map with the index positions for the underlying nodes.
257 *
258 * @param node starting node
259 * @param map map to fill with the indizes
260 * @return "inserted" count for recursive call
261 */
262 private int posForNode(ITableNode node, Map<ITableNode, Integer> map) {
263 int idx = getIdxForNode(node);
264 int count = node.getChildren().size();
265 int inserted = 0;
266 for (int i = 0; i < count; i++) {
267 ITableNode n = node.getChildren().get(i);
268 map.put(n, idx + 1 + inserted);
269 inserted++;
270 if (_hvs.isExpanded(n) && n.getChildren().size() > 0) {
271 inserted += posForNode(n, map);
272 }
273 }
274 return inserted;
275 }
276
277 /***
278 * {@inheritDoc}
279 */
280 public void nodeRemoved(ITableNode parent, ITableNode removedChild) {
281 removedChild.removeTableNodeListener(this);
282 if (_hvs.isExpanded(parent)) {
283
284 _rows.remove(removedChild);
285 fireRowRemoved(removedChild);
286
287 List<ITableNode> toRemove = new ArrayList<ITableNode>();
288 enumerateChildren(removedChild, toRemove);
289 for (ITableNode tableNode : toRemove) {
290 _rows.remove(tableNode);
291 fireRowRemoved(tableNode);
292 }
293 }
294 }
295
296 /***
297 * {@inheritDoc }Handle expansion of a node. This means adding all rows that become visible when expanding.
298 */
299 public void nodeExpanded(ITableNode node) {
300 nodeExpanded2(node);
301 }
302
303 /***
304 * Handle expansion of a node by traversing all children of the node to check whether they are visible.
305 *
306 * @param node node expanded
307 * @return count of nodes that became visible ()= number of insertedlines)
308 */
309 private int nodeExpanded2(ITableNode node) {
310 int idx = getIdxForNode(node);
311 int count = node.getChildren().size();
312 int inserted = 0;
313 for (int i = 0; i < count; i++) {
314 ITableNode n = node.getChildren().get(i);
315 int newIdx = idx + 1 + inserted;
316 _rows.add(newIdx, n);
317 inserted++;
318 fireRowAdded(newIdx, n);
319 registerPropChange(n);
320 if (_hvs.isExpanded(n) && n.getChildren().size() > 0) {
321 inserted += nodeExpanded2(n);
322 }
323 }
324 return inserted;
325 }
326
327 /***
328 * {@inheritDoc} Handle folding of a node. This means removing all rows that "disappear" with folding.
329 */
330 public void nodeFolded(ITableNode node) {
331 int count = node.getChildren().size();
332 for (int i = 0; i < count; i++) {
333 ITableNode n = node.getChildren().get(i);
334 if (_hvs.isExpanded(n) && n.getChildren().size() > 0) {
335 nodeFolded(n);
336 }
337 int idx2 = getIdxForNode(n);
338
339 if (idx2 != -1) {
340 _rows.remove(idx2);
341 }
342 fireRowRemoved(n);
343 deRegisterPropChange(n);
344 }
345 }
346
347 /***
348 * {@inheritDoc}
349 */
350 public int getColumnCount() {
351 return _cols.size();
352 }
353
354 /***
355 * {@inheritDoc}
356 */
357 public IColumn getColumn(int idx) {
358 return _cols.get(idx);
359 }
360
361 /***
362 * Add a column to the list of columns.
363 *
364 * @param column column to add
365 */
366 public void addColumn(IColumn column) {
367 _cols.add(column);
368 fireColumnAdded(_cols.size() - 1, column);
369 }
370
371 /***
372 * Check whether the root node should be excluded.
373 *
374 * @return true if the root node should be excluded
375 */
376 public boolean getExcludeRootNode() {
377 return _excludeRootNode;
378 }
379
380 /***
381 * Set whether the rot node should be excluded in the display.
382 *
383 * @param excludeRootNode true to hide the root node
384 */
385 public void setExcludeRootNode(boolean excludeRootNode) {
386 boolean old = _excludeRootNode;
387 _excludeRootNode = excludeRootNode;
388 if (old != _excludeRootNode) {
389 updateRowList();
390 fireTableDataChanged();
391 }
392 }
393
394 /***
395 * {@inheritDoc}
396 */
397 public void propertyChange(PropertyChangeEvent evt) {
398 if (evt.getSource() instanceof IRow) {
399 fireRowChanged((IRow) evt.getSource());
400 }
401 }
402 }