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;
30 import java.util.Map.Entry;
31 import java.util.NoSuchElementException;
32 import java.util.Set;
33 import java.util.Stack;
34
35 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
36
37 /**
38 * Implements a collection of rowKeys from a TreeModel.
39 * The methods on this class are optimized such that it is possible
40 * to add/remove all the rowkeys in a subtree in constant time.
41 * <P>
42 * The generic type E is the type of a rowKey.
43 */
44 public class RowKeySetTreeImpl extends RowKeySet implements Serializable
45 {
46 /**
47 * Creates a new Set that is initially empty.
48 */
49 public RowKeySetTreeImpl()
50 {
51 this(false);
52 }
53
54 /**
55 * Creates a new Set, that may contain every rowKey by default.
56 * @param addAll if this is true, every rowKey is initially added to this set.
57 */
58 public RowKeySetTreeImpl(boolean addAll)
59 {
60 _root = new Node<Object>(addAll);
61 }
62
63 /**
64 * Tests to see if the given rowKey is included in this Set.
65 * @return true If the rowKey is included in this Set.
66 */
67 @Override
68 public boolean contains(Object rowKey)
69 {
70 return _isContained(rowKey);
71 }
72
73 /**
74 * @deprecated do not use. this will be removed post Tier 1.
75 */
76 @Override
77 @Deprecated
78 public boolean isContainedByDefault()
79 {
80 TreeModel model = getCollectionModel();
81 Object rowkey = model.getRowKey();
82 return new Search().find(rowkey).isDefaultContained;
83 }
84
85 @Override
86 public Iterator<Object> iterator()
87 {
88 if(_root.isDefaultContained)
89 return new PathIterator();
90 else
91 return new NodeIterator();
92 }
93
94 /**
95 * Adds the given rowKey to this Set.
96 * @return false if the given rowKey was already in this Set.
97 * @see #remove(Object)
98 * @see #addAll()
99 */
100 @Override
101 public boolean add(Object rowKey)
102 {
103 return _setContained(rowKey, true);
104 }
105
106 /**
107 * Removes the given rowKey from this Set.
108 * @return false if the given rowKey was already not in this Set.
109 * @see #add
110 * @see #removeAll()
111 */
112 @Override
113 public boolean remove(Object rowKey)
114 {
115 return _setContained(rowKey, false);
116 }
117
118 /**
119 * Adds the current rowKey and all rowKeys beneath the current rowKey to this Set.
120 * This method executes in constant time.
121 * @see #add
122 * @see #removeAll()
123 */
124 @Override
125 public void addAll()
126 {
127 _selectAll(true);
128 }
129
130 /**
131 * Removes the current rowKey and all rowKeys beneath the current rowKey to this Set.
132 * This method executes in constant time.
133 * @see #remove(Object)
134 * @see #clear()
135 * @see #addAll()
136 */
137 @Override
138 public void removeAll()
139 {
140 _selectAll(false);
141 }
142
143 /**
144 * {@inheritDoc}
145 * <P>
146 * If the parameter is another RowKeySetTreeImpl, this method is
147 * optimized to give superior performance and avoid iteration.
148 */
149 @Override
150 public boolean addAll(Collection<? extends Object> other)
151 {
152 if (other instanceof RowKeySetTreeImpl)
153 {
154 RowKeySetTreeImpl otherset = (RowKeySetTreeImpl) other;
155 return _processOperation(this._root, otherset._root, true);
156 }
157 return super.addAll(other);
158 }
159
160 /**
161 * {@inheritDoc}
162 * <P>
163 * If the parameter is another RowKeySetTreeImpl, this method is
164 * optimized to give superior performance and avoid iteration.
165 */
166 @Override
167 public boolean removeAll(Collection<?> other)
168 {
169 if (other instanceof RowKeySetTreeImpl)
170 {
171 RowKeySetTreeImpl otherset = (RowKeySetTreeImpl) other;
172 return _processOperation(this._root, otherset._root, false);
173 }
174 return super.removeAll(other);
175 }
176
177 private boolean _processOperation(Node<Object> set1, Node<Object> set2, boolean add)
178 {
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198 boolean hasChanges = false;
199
200
201
202
203 if (set2.isDefaultContained && set1.keySet().retainAll(set2.keySet()))
204 hasChanges = true;
205
206
207
208
209
210
211
212
213
214
215 boolean addAll = add ^ set1.isDefaultContained;
216
217 for(Entry<Object, Node<Object>> en:set2.entrySet())
218 {
219 Object segment = en.getKey();
220 Node<Object> subset2 = en.getValue();
221 Node<Object> subset1 = set1.get(segment);
222
223 if (subset1 == null)
224 {
225 if (addAll)
226 {
227 subset1 = new Node<Object>(set1, segment);
228 hasChanges = true;
229 }
230 else
231 continue;
232 }
233 if (_processOperation(subset1, subset2, add))
234 hasChanges = true;
235 }
236
237
238
239
240 if (set2.isDefaultContained && (set1.isDefaultContained != add))
241 {
242 set1.isDefaultContained = add;
243
244
245 set1.isDifferent = !set1.isDifferent;
246 hasChanges = true;
247 }
248
249
250
251 if ((set2.isDefaultContained ^ set2.isDifferent) &&
252 ((set1.isDefaultContained ^ set1.isDifferent) != add))
253 {
254 set1.isDifferent = !set1.isDifferent;
255 hasChanges = true;
256 }
257
258 return hasChanges;
259 }
260
261 /**
262 * Removes all rowKeys from this Set.
263 * This method executes in the same time as
264 * {@link HashMap#clear()}
265 */
266 @Override
267 public void clear()
268 {
269 _root.clear();
270 _root.isDefaultContained = _root.isDifferent = false;
271 }
272
273 /**
274 * Gets the number of elements contained by this set.
275 * Does not force the underlying model to compute its size.
276 * @return -1 if the number of elements is unknown.
277 */
278 @Override
279 public int getSize()
280 {
281 return _getSize(null, _root, getCollectionModel(), false);
282 }
283
284 /**
285 * Gets the number of elements in this Set.
286 * This might force the underlying model to compute its size.
287 * @return a non-negative number.
288 */
289 @Override
290 public int size()
291 {
292 return _getSize(null, _root, getCollectionModel(), true);
293 }
294
295 @Override
296 public boolean isEmpty()
297 {
298 return (getSize() == 0);
299 }
300
301 /**
302 * Sets the TreeModel associated with this Set.
303 * @param model This must be of type {@link TreeModel}
304 */
305 @Override
306 public final void setCollectionModel(CollectionModel model)
307 {
308 if (model != null && !(model instanceof TreeModel))
309 throw new IllegalArgumentException();
310
311 _model = (TreeModel) model;
312 }
313
314 /**
315 * Creates a clone of this Set. RowKeys may be added/removed from the
316 * clone without affecting this instance.
317 */
318 @Override
319 public RowKeySetTreeImpl clone()
320 {
321 RowKeySetTreeImpl clone = (RowKeySetTreeImpl) super.clone();
322 clone._root = _root.clone();
323 return clone;
324 }
325
326 /**
327 * @deprecated not implemented.
328 */
329 @Deprecated
330 @Override
331 public void invertAll()
332 {
333
334 throw new UnsupportedOperationException();
335 }
336
337 /**
338 * Gets the TreeModel associated with this set.
339 * This TreeModel will be used to get the current rowKey, and also to
340 * get parent rowKeys, from child rowKeys.
341 * @see TreeModel#getRowKey
342 */
343 @Override
344 protected TreeModel getCollectionModel()
345 {
346 return _model;
347 }
348
349 /**
350 * Gets the total number of nodes in the subtree of the given TreeModel.
351 *
352 * WARNING: this method changes the TreeModel's currency.
353 * The caller is responsible for restoring the model currency.
354 *
355 * @param exclusions any rowKeys present in this Set are excluded from the count.
356 */
357 @SuppressWarnings("unchecked")
358 private int _getTreeSize(TreeModel model, Set<Object> exclusions)
359 {
360 int sz = 0;
361 for(int i=0;true;i++)
362 {
363 model.setRowIndex(i);
364 if (model.isRowAvailable())
365 {
366 Object rowkey = model.getRowKey();
367 if (exclusions.contains(rowkey))
368 continue;
369 sz++;
370 if (model.isContainer())
371 {
372 model.enterContainer();
373 Set<Object> empty = Collections.emptySet();
374 sz += _getTreeSize(model, empty);
375 model.exitContainer();
376 }
377 }
378 else
379 return sz;
380 }
381 }
382
383 private int _getSize(Object rowkey, Node<Object> set, TreeModel model, boolean fetchall)
384 {
385
386 int sz = ((rowkey != null) && (set.isDefaultContained ^ set.isDifferent)) ? 1 : 0;
387 if (set.isDefaultContained)
388 {
389 if (!fetchall)
390 return -1;
391
392 Object old = model.getRowKey();
393 try
394 {
395 model.setRowKey(rowkey);
396
397 if (rowkey == null)
398 {
399 sz += _getTreeSize(model, set.keySet());
400 }
401 else if (model.isContainer())
402 {
403 model.enterContainer();
404 sz += _getTreeSize(model, set.keySet());
405 }
406 }
407 finally
408 {
409 model.setRowKey(old);
410 }
411 }
412
413 for(Entry<Object, Node<Object>> en:set.entrySet())
414 {
415 Object newrowkey = en.getKey();
416 Node<Object> subset = en.getValue();
417 int size = _getSize(newrowkey, subset, model, fetchall);
418 if (size < 0)
419 return -1;
420 sz+= size;
421 }
422 return sz;
423 }
424
425 /**
426 * adds or removes all the paths rooted at the current path
427 * @param isSelectAll if true does an add-all. else does remove-all.
428 */
429 private void _selectAll(final boolean isSelectAll)
430 {
431 Search search = new Search()
432 {
433 @Override
434 protected boolean create(Node<Object> parent, Object rowkey)
435 {
436
437
438
439 return (parent.isDefaultContained != isSelectAll);
440 }
441
442 @Override
443 protected Node<Object> found(Node<Object> child)
444 {
445 child.isDefaultContained = isSelectAll;
446 child.isDifferent = false;
447 child.clear();
448 return null;
449 }
450 };
451
452 TreeModel model = getCollectionModel();
453 Object rowkey = model.getRowKey();
454 search.find(rowkey);
455 }
456
457 private boolean _isContained(Object rowkey)
458 {
459 Search search = new Search()
460 {
461 @Override
462 protected Node<Object> notFound(Node<Object> parent, Object rowkey)
463 {
464 return parent.isDefaultContained ? parent : null;
465 }
466
467 @Override
468 protected Node<Object> found(Node<Object> child)
469 {
470 return (child.isDefaultContained ^ child.isDifferent) ? child : null;
471 }
472 };
473
474 return (search.find(rowkey) != null);
475 }
476
477 /**
478 * Adds or removes the given path from this set.
479 * @param isContained If true, the current path is added. Otherwise,
480 * it is removed.
481 * @return true if this Set changed due to this operation.
482 */
483 private boolean _setContained(Object rowkey, final boolean isContained)
484 {
485 Search search = new Search()
486 {
487 @Override
488 protected boolean create(Node<Object> parent, Object rowkey)
489 {
490
491
492 return parent.isDefaultContained != isContained;
493 }
494
495 @Override
496 protected Node<Object> notFound(Node<Object> parent, Object rowkey)
497 {
498 return null;
499 }
500 };
501
502 Node<Object> current = search.find(rowkey);
503 if ((current != null) &&
504 ((current.isDefaultContained ^ current.isDifferent) != isContained))
505 {
506 current.isDifferent = !current.isDifferent;
507 return true;
508 }
509 return false;
510 }
511
512 /**
513 * Advances the currency of the given TreeModel to the next node in a
514 * depth-first walk of the tree.
515 * @param minDepth the minimum depth of the rowkey. use this to
516 * walk within a subtree. Use 0 to walk entire tree.
517 * @param recurseChildren if true, will walk children.
518 * @return true if the currency of the model was successfully advanced to
519 * the next rowData.
520 */
521 private static boolean _advanceToNextItem(
522 TreeModel model, int minDepth, boolean recurseChildren)
523 {
524 assert minDepth >= 0;
525
526 if (recurseChildren && model.isRowAvailable() && model.isContainer())
527 {
528 model.enterContainer();
529 model.setRowIndex(-1);
530 }
531 while(true)
532 {
533 int ri = model.getRowIndex();
534 model.setRowIndex(ri+1);
535 if (model.isRowAvailable())
536 return true;
537
538 int depth = model.getDepth();
539 if (depth <= minDepth)
540 return false;
541
542 model.exitContainer();
543 }
544 }
545
546 /**
547 * Check for "default contained" nodes in the set
548 * @return true if there are "default contained" nodes
549 */
550 private boolean _containsDefaultNodes()
551 {
552 if(_root.isDefaultContained)
553 return true;
554
555 SetLoop loop = new SetLoop()
556 {
557 protected boolean next(Object rowKey, Node<Object> value)
558 {
559 return value.isDefaultContained;
560 }
561 };
562 return loop.run(_root);
563 }
564
565 /**
566 * Utility to dump Node attributes
567 */
568 private void _dumpFlags()
569 {
570 System.out.println("root " + _root.isDefaultContained + " " + _root.isDifferent);
571 SetLoop loop = new SetLoop()
572 {
573 protected boolean next(Object rowKey, Node<Object> value)
574 {
575 System.out.println(rowKey + " " + value.isDefaultContained + " " + value.isDifferent);
576 return false;
577 }
578 };
579 loop.run(_root);
580 }
581
582
583 private static final class Node<K> extends HashMap<K, Node<K>>
584
585 {
586 public boolean isDifferent = false;
587 public boolean isDefaultContained = false;
588
589 public Node(boolean isDefaultContained)
590 {
591 this.isDefaultContained = isDefaultContained;
592 }
593
594 public Node(Node<K> parent, K segment)
595 {
596 this(parent.isDefaultContained);
597 parent.put(segment, this);
598 }
599
600
601 private void _deepClone(Node<K> root)
602 {
603 for(Entry<K, Node<K>> en:root.entrySet())
604 {
605 Node<K> original = en.getValue();
606 Node<K> clone = original.clone();
607 en.setValue(clone);
608 }
609 }
610
611 @SuppressWarnings("unchecked")
612 @Override
613 public Node<K> clone()
614 {
615 Node<K> clone = (Node<K>) super.clone();
616 _deepClone(clone);
617 return clone;
618 }
619
620 private static final long serialVersionUID = 1L;
621 }
622
623 private class Search
624 {
625 public Search()
626 {
627 }
628
629 protected boolean create(Node<Object> parent, Object rowkey)
630 {
631 return false;
632 }
633
634 protected Node<Object> notFound(Node<Object> parent, Object rowkey)
635 {
636 return parent;
637 }
638
639 protected Node<Object> found(Node<Object> result)
640 {
641 return result;
642 }
643
644 public Node<Object> find(Object rowkey)
645 {
646 Node<Object> current = _root;
647 if (rowkey != null)
648 {
649 TreeModel model = getCollectionModel();
650 List<Object> parentkeys = model.getAllAncestorContainerRowKeys(rowkey);
651 List<Object> allkeys = new ArrayList<Object>(parentkeys.size() + 1);
652 allkeys.addAll(parentkeys);
653 allkeys.add(rowkey);
654 for(Object key:allkeys)
655 {
656 Node<Object> next = current.get(key);
657 if (next == null)
658 {
659 if (create(current, key))
660 next = new Node<Object>(current, key);
661 else
662 return notFound(current, key);
663 }
664 current = next;
665 }
666 }
667 return found(current);
668 }
669 }
670
671 /**
672 * Loop (depth first) over the set or a subset and call a callback function
673 * for each node
674 */
675 private static abstract class SetLoop
676 {
677 public SetLoop()
678 {
679 }
680
681 public boolean run (Node<Object> set)
682 {
683 for(Entry<Object, Node<Object>> en : set.entrySet())
684 {
685 Object keyEnt = en.getKey();
686 Node<Object> subset = en.getValue();
687 if(next(keyEnt, subset))
688 return true;
689 if(run(subset))
690 return true;
691 }
692 return false;
693 }
694
695 protected abstract boolean next(Object rowKey, Node<Object> value );
696 }
697
698 private class PathIterator implements Iterator<Object>
699 {
700 PathIterator()
701 {
702 _value = isEmpty() ? null : nextItem();
703 }
704
705 PathIterator(Object noop)
706 {
707 }
708
709 public Object next()
710 {
711 if (!hasNext())
712 throw new NoSuchElementException();
713 Object value = _value;
714 _value = nextItem();
715 return value;
716 }
717
718 public boolean hasNext()
719 {
720 return (_value != null);
721 }
722
723 public void remove()
724 {
725 throw new UnsupportedOperationException();
726 }
727
728 protected Object nextItem()
729 {
730 return nextModelKey(0);
731 }
732
733 protected Object nextModelKey(int minDepth)
734 {
735 TreeModel model = getCollectionModel();
736 if (model == null)
737 return null;
738
739 Object oldPath = model.getRowKey();
740 try
741 {
742 model.setRowKey(_currPath);
743 while(true)
744 {
745 boolean searchChildren = _containsSubtree(_currPath);
746 boolean hasMore = _advanceToNextItem(model, minDepth, searchChildren);
747 if (!hasMore)
748 return null;
749
750 _currPath = model.getRowKey();
751 if (contains(_currPath))
752 return _currPath;
753 }
754 }
755 finally
756 {
757 model.setRowKey(oldPath);
758 }
759 }
760
761 private boolean _containsSubtree(Object rowkey)
762 {
763 Search search = new Search()
764 {
765 @Override
766 protected Node<Object> notFound(Node<Object> parent, Object rowkey)
767 {
768 return parent.isDefaultContained ? parent : null;
769 }
770 };
771 Node<Object> current = search.find(rowkey);
772 return (current != null) &&
773 ((!current.isEmpty()) || current.isDefaultContained);
774 }
775
776 protected Object _value;
777 protected Object _currPath = null;
778 }
779
780 /**
781 * An iterator which avoids looping over the model by default (like the
782 * PathIterator does). Instead NodeIterator loops over the model only for nodes that are "default contained".
783 * Otherwise, it just does a depth first walk of the set and returns "isDifferent" nodes.
784 */
785 private class NodeIterator extends PathIterator
786 {
787 public NodeIterator()
788 {
789 super(null);
790 _currIterator = _root.entrySet().iterator();
791 _value = isEmpty() ? null : nextItem();
792 }
793
794 protected Object nextItem()
795 {
796 Object nextKey = null;
797
798 while(((nextKey = _nextEntry()) == null) && _iteratorStack.size() > 0)
799 if(_currPath == null)
800 _currIterator = _iteratorStack.pop();
801 return nextKey;
802 }
803
804 private Object _nextEntry()
805 {
806 Object nextKey = null;
807 if(_currPath != null)
808 {
809 nextKey = nextModelKey(_minDepth);
810 if(nextKey == null)
811 {
812 _currPath = null;
813 _nextEntry();
814 }
815 }
816 else
817 {
818 Map.Entry<Object, Node<Object>> nextNode;
819 while(nextKey == null && _currIterator.hasNext())
820 {
821 nextNode = _currIterator.next();
822 if(_isContained(nextNode.getKey()))
823 nextKey = nextNode.getKey();
824 _iteratorStack.push(_currIterator);
825 _currIterator = nextNode.getValue().entrySet().iterator();
826 if(nextNode.getValue().isDefaultContained)
827 {
828 _currPath = nextNode.getKey();
829 TreeModel model = getCollectionModel();
830 Object oldPath = model.getRowKey();
831 model.setRowKey(_currPath);
832 _minDepth = model.getDepth()+1;
833 model.setRowKey(oldPath);
834 return nextKey;
835 }
836 }
837 }
838 return nextKey;
839 }
840
841 private Stack<Iterator <Map.Entry<Object, Node<Object>>>> _iteratorStack =
842 new Stack<Iterator <Map.Entry<Object, Node<Object>>>>();
843 private Iterator <Map.Entry<Object, Node<Object>>> _currIterator;
844 private int _minDepth;
845 }
846
847 private Node<Object> _root;
848 private transient TreeModel _model = null;
849 private static final long serialVersionUID = 1L;
850 private static final TrinidadLogger _LOG =
851 TrinidadLogger.createTrinidadLogger(RowKeySetTreeImpl.class);
852 }