1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.trinidad.model;
20
21 import java.io.Serializable;
22
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map.Entry;
30 import java.util.NoSuchElementException;
31 import java.util.Set;
32 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
33
34
35
36
37
38
39
40
41 public class RowKeySetTreeImpl extends RowKeySet implements Serializable
42 {
43
44
45
46 public RowKeySetTreeImpl()
47 {
48 this(false);
49 }
50
51
52
53
54
55 public RowKeySetTreeImpl(boolean addAll)
56 {
57 _root = new Node<Object>(addAll);
58 }
59
60
61
62
63
64 @Override
65 public boolean contains(Object rowKey)
66 {
67 return _isContained(rowKey);
68 }
69
70
71
72
73 @Override
74 @Deprecated
75 public boolean isContainedByDefault()
76 {
77 TreeModel model = getCollectionModel();
78 Object rowkey = model.getRowKey();
79 return new Search().find(rowkey).isDefaultContained;
80 }
81
82 @Override
83 public Iterator<Object> iterator()
84 {
85 return new PathIterator();
86 }
87
88
89
90
91
92
93
94 @Override
95 public boolean add(Object rowKey)
96 {
97 return _setContained(rowKey, true);
98 }
99
100
101
102
103
104
105
106 @Override
107 public boolean remove(Object rowKey)
108 {
109 return _setContained(rowKey, false);
110 }
111
112
113
114
115
116
117
118 @Override
119 public void addAll()
120 {
121 _selectAll(true);
122 }
123
124
125
126
127
128
129
130
131 @Override
132 public void removeAll()
133 {
134 _selectAll(false);
135 }
136
137
138
139
140
141
142
143 @Override
144 public boolean addAll(Collection<? extends Object> other)
145 {
146 if (other instanceof RowKeySetTreeImpl)
147 {
148 RowKeySetTreeImpl otherset = (RowKeySetTreeImpl) other;
149 return _processOperation(this._root, otherset._root, true);
150 }
151 return super.addAll(other);
152 }
153
154
155
156
157
158
159
160 @Override
161 public boolean removeAll(Collection<?> other)
162 {
163 if (other instanceof RowKeySetTreeImpl)
164 {
165 RowKeySetTreeImpl otherset = (RowKeySetTreeImpl) other;
166 return _processOperation(this._root, otherset._root, false);
167 }
168 return super.removeAll(other);
169 }
170
171 private boolean _processOperation(Node<Object> set1, Node<Object> set2, boolean add)
172 {
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192 boolean hasChanges = false;
193
194
195
196
197 if (set2.isDefaultContained && set1.keySet().retainAll(set2.keySet()))
198 hasChanges = true;
199
200
201
202
203
204
205
206
207
208
209 boolean addAll = add ^ set1.isDefaultContained;
210
211 for(Entry<Object, Node<Object>> en:set2.entrySet())
212 {
213 Object segment = en.getKey();
214 Node<Object> subset2 = en.getValue();
215 Node<Object> subset1 = set1.get(segment);
216
217 if (subset1 == null)
218 {
219 if (addAll)
220 {
221 subset1 = new Node<Object>(set1, segment);
222 hasChanges = true;
223 }
224 else
225 continue;
226 }
227 if (_processOperation(subset1, subset2, add))
228 hasChanges = true;
229 }
230
231
232
233
234 if (set2.isDefaultContained && (set1.isDefaultContained != add))
235 {
236 set1.isDefaultContained = add;
237
238
239 set1.isDifferent = !set1.isDifferent;
240 hasChanges = true;
241 }
242
243
244
245 if ((set2.isDefaultContained ^ set2.isDifferent) &&
246 ((set1.isDefaultContained ^ set1.isDifferent) != add))
247 {
248 set1.isDifferent = !set1.isDifferent;
249 hasChanges = true;
250 }
251
252 return hasChanges;
253 }
254
255
256
257
258
259
260 @Override
261 public void clear()
262 {
263 _root.clear();
264 _root.isDefaultContained = _root.isDifferent = false;
265 }
266
267
268
269
270
271
272 @Override
273 public int getSize()
274 {
275 return _getSize(null, _root, getCollectionModel(), false);
276 }
277
278
279
280
281
282
283 @Override
284 public int size()
285 {
286 return _getSize(null, _root, getCollectionModel(), true);
287 }
288
289 @Override
290 public boolean isEmpty()
291 {
292 return (getSize() == 0);
293 }
294
295
296
297
298
299 @Override
300 public final void setCollectionModel(CollectionModel model)
301 {
302 if (!(model instanceof TreeModel))
303 throw new IllegalArgumentException();
304
305 _model = (TreeModel) model;
306 }
307
308
309
310
311
312 @Override
313 public RowKeySetTreeImpl clone()
314 {
315 RowKeySetTreeImpl clone = (RowKeySetTreeImpl) super.clone();
316 clone._root = _root.clone();
317 return clone;
318 }
319
320
321
322
323 @Deprecated
324 @Override
325 public void invertAll()
326 {
327
328 throw new UnsupportedOperationException();
329 }
330
331
332
333
334
335
336
337 @Override
338 protected TreeModel getCollectionModel()
339 {
340 return _model;
341 }
342
343
344
345
346
347
348
349
350
351 @SuppressWarnings("unchecked")
352 private int _getTreeSize(TreeModel model, Set<Object> exclusions)
353 {
354 int sz = 0;
355 for(int i=0;true;i++)
356 {
357 model.setRowIndex(i);
358 if (model.isRowAvailable())
359 {
360 Object rowkey = model.getRowKey();
361 if (exclusions.contains(rowkey))
362 continue;
363 sz++;
364 if (model.isContainer())
365 {
366 model.enterContainer();
367 Set<Object> empty = Collections.emptySet();
368 sz += _getTreeSize(model, empty);
369 model.exitContainer();
370 }
371 }
372 else
373 return sz;
374 }
375 }
376
377 private int _getSize(Object rowkey, Node<Object> set, TreeModel model, boolean fetchall)
378 {
379
380 int sz = ((rowkey != null) && (set.isDefaultContained ^ set.isDifferent)) ? 1 : 0;
381 if (set.isDefaultContained)
382 {
383 if (!fetchall)
384 return -1;
385
386 Object old = model.getRowKey();
387 try
388 {
389 model.setRowKey(rowkey);
390
391 if (rowkey == null)
392 {
393 sz += _getTreeSize(model, set.keySet());
394 }
395 else if (model.isContainer())
396 {
397 model.enterContainer();
398 sz += _getTreeSize(model, set.keySet());
399 }
400 }
401 finally
402 {
403 model.setRowKey(old);
404 }
405 }
406
407 for(Entry<Object, Node<Object>> en:set.entrySet())
408 {
409 Object newrowkey = en.getKey();
410 Node<Object> subset = en.getValue();
411 int size = _getSize(newrowkey, subset, model, fetchall);
412 if (size < 0)
413 return -1;
414 sz+= size;
415 }
416 return sz;
417 }
418
419
420
421
422
423 private void _selectAll(final boolean isSelectAll)
424 {
425 Search search = new Search()
426 {
427 @Override
428 protected boolean create(Node<Object> parent, Object rowkey)
429 {
430
431
432
433 return (parent.isDefaultContained != isSelectAll);
434 }
435
436 @Override
437 protected Node<Object> found(Node<Object> child)
438 {
439 child.isDefaultContained = isSelectAll;
440 child.isDifferent = false;
441 child.clear();
442 return null;
443 }
444 };
445
446 TreeModel model = getCollectionModel();
447 Object rowkey = model.getRowKey();
448 search.find(rowkey);
449 }
450
451 private boolean _isContained(Object rowkey)
452 {
453 Search search = new Search()
454 {
455 @Override
456 protected Node<Object> notFound(Node<Object> parent, Object rowkey)
457 {
458 return parent.isDefaultContained ? parent : null;
459 }
460
461 @Override
462 protected Node<Object> found(Node<Object> child)
463 {
464 return (child.isDefaultContained ^ child.isDifferent) ? child : null;
465 }
466 };
467
468 return (search.find(rowkey) != null);
469 }
470
471
472
473
474
475
476
477 private boolean _setContained(Object rowkey, final boolean isContained)
478 {
479 Search search = new Search()
480 {
481 @Override
482 protected boolean create(Node<Object> parent, Object rowkey)
483 {
484
485
486 return parent.isDefaultContained != isContained;
487 }
488
489 @Override
490 protected Node<Object> notFound(Node<Object> parent, Object rowkey)
491 {
492 return null;
493 }
494 };
495
496 Node<Object> current = search.find(rowkey);
497 if ((current != null) &&
498 ((current.isDefaultContained ^ current.isDifferent) != isContained))
499 {
500 current.isDifferent = !current.isDifferent;
501 return true;
502 }
503 return false;
504 }
505
506
507
508
509
510
511
512
513
514
515 private static boolean _advanceToNextItem(
516 TreeModel model, int minDepth, boolean recurseChildren)
517 {
518 assert minDepth >= 0;
519
520 if (recurseChildren && model.isRowAvailable() && model.isContainer())
521 {
522 model.enterContainer();
523 model.setRowIndex(-1);
524 }
525 while(true)
526 {
527 int ri = model.getRowIndex();
528 model.setRowIndex(ri+1);
529 if (model.isRowAvailable())
530 return true;
531
532 int depth = model.getDepth();
533 if (depth <= minDepth)
534 return false;
535
536 model.exitContainer();
537 }
538 }
539
540
541 private static final class Node<K> extends HashMap<K, Node<K>>
542
543 {
544 public boolean isDifferent = false;
545 public boolean isDefaultContained = false;
546
547 public Node(boolean isDefaultContained)
548 {
549 this.isDefaultContained = isDefaultContained;
550 }
551
552 public Node(Node<K> parent, K segment)
553 {
554 this(parent.isDefaultContained);
555 parent.put(segment, this);
556 }
557
558
559 private void _deepClone(Node<K> root)
560 {
561 for(Entry<K, Node<K>> en:root.entrySet())
562 {
563 Node<K> original = en.getValue();
564 Node<K> clone = original.clone();
565 en.setValue(clone);
566 }
567 }
568
569 @SuppressWarnings("unchecked")
570 @Override
571 public Node<K> clone()
572 {
573 Node<K> clone = (Node<K>) super.clone();
574 _deepClone(clone);
575 return clone;
576 }
577
578 private static final long serialVersionUID = 1L;
579 }
580
581 private class Search
582 {
583 public Search()
584 {
585 }
586
587 protected boolean create(Node<Object> parent, Object rowkey)
588 {
589 return false;
590 }
591
592 protected Node<Object> notFound(Node<Object> parent, Object rowkey)
593 {
594 return parent;
595 }
596
597 protected Node<Object> found(Node<Object> result)
598 {
599 return result;
600 }
601
602 public Node<Object> find(Object rowkey)
603 {
604 Node<Object> current = _root;
605 if (rowkey != null)
606 {
607 TreeModel model = getCollectionModel();
608 List<Object> parentkeys = model.getAllAncestorContainerRowKeys(rowkey);
609 List<Object> allkeys = new ArrayList<Object>(parentkeys.size() + 1);
610 allkeys.addAll(parentkeys);
611 allkeys.add(rowkey);
612 for(Object key:allkeys)
613 {
614 Node<Object> next = current.get(key);
615 if (next == null)
616 {
617 if (create(current, key))
618 next = new Node<Object>(current, key);
619 else
620 return notFound(current, key);
621 }
622 current = next;
623 }
624 }
625 return found(current);
626 }
627 }
628
629 private final class PathIterator implements Iterator<Object>
630 {
631 PathIterator()
632 {
633 _value = _next();
634 }
635
636 public Object next()
637 {
638 if (!hasNext())
639 throw new NoSuchElementException();
640 Object value = _value;
641 _value = _next();
642 return value;
643 }
644
645 public boolean hasNext()
646 {
647 return (_value != null);
648 }
649
650 public void remove()
651 {
652 throw new UnsupportedOperationException();
653 }
654
655 private boolean _containsSubtree(Object rowkey)
656 {
657 Search search = new Search()
658 {
659 @Override
660 protected Node<Object> notFound(Node<Object> parent, Object rowkey)
661 {
662 return parent.isDefaultContained ? parent : null;
663 }
664 };
665 Node<Object> current = search.find(rowkey);
666 return (current != null) &&
667 ((!current.isEmpty()) || current.isDefaultContained);
668 }
669
670 private Object _next()
671 {
672 TreeModel model = getCollectionModel();
673 if (model == null)
674 return null;
675
676 Object oldPath = model.getRowKey();
677 try
678 {
679 model.setRowKey(_currPath);
680 while(true)
681 {
682 boolean searchChildren = _containsSubtree(_currPath);
683 boolean hasMore = _advanceToNextItem(model, 0, searchChildren);
684 if (!hasMore)
685 return null;
686
687 _currPath = model.getRowKey();
688 if (contains(_currPath))
689 return _currPath;
690 }
691 } finally
692 {
693 model.setRowKey(oldPath);
694 }
695 }
696
697 private Object _value;
698 private Object _currPath = null;
699 }
700
701 private Node<Object> _root;
702 private transient TreeModel _model = null;
703 private static final long serialVersionUID = 1L;
704 private static final TrinidadLogger _LOG =
705 TrinidadLogger.createTrinidadLogger(RowKeySetTreeImpl.class);
706 }