KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > viewers > AbstractTreeViewer


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Tom Schindl <tom.schindl@bestsolution.at> - bug 153993, bug 167323, bug 175192
11  *******************************************************************************/

12
13 package org.eclipse.jface.viewers;
14
15 import java.util.ArrayList JavaDoc;
16 import java.util.Arrays JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.LinkedList JavaDoc;
19 import java.util.List JavaDoc;
20
21 import org.eclipse.core.runtime.Assert;
22 import org.eclipse.core.runtime.ListenerList;
23 import org.eclipse.jface.util.SafeRunnable;
24 import org.eclipse.swt.SWT;
25 import org.eclipse.swt.custom.BusyIndicator;
26 import org.eclipse.swt.events.SelectionEvent;
27 import org.eclipse.swt.events.SelectionListener;
28 import org.eclipse.swt.events.TreeEvent;
29 import org.eclipse.swt.events.TreeListener;
30 import org.eclipse.swt.graphics.Point;
31 import org.eclipse.swt.widgets.Control;
32 import org.eclipse.swt.widgets.Item;
33 import org.eclipse.swt.widgets.Widget;
34
35 /**
36  * Abstract base implementation for tree-structure-oriented viewers (trees and
37  * table trees).
38  * <p>
39  * Nodes in the tree can be in either an expanded or a collapsed state,
40  * depending on whether the children on a node are visible. This class
41  * introduces public methods for controlling the expanding and collapsing of
42  * nodes.
43  * </p>
44  * <p>
45  * As of 3.2, AbstractTreeViewer supports multiple equal elements (each with a
46  * different parent chain) in the tree. This support requires that clients
47  * enable the element map by calling <code>setUseHashLookup(true)</code>.
48  * </p>
49  * <p>
50  * Content providers for abstract tree viewers must implement one of the
51  * interfaces <code>ITreeContentProvider</code> or (as of 3.2, to support
52  * multiple equal elements) <code>ITreePathContentProvider</code>.
53  * </p>
54  *
55  * @see TreeViewer
56  */

57 public abstract class AbstractTreeViewer extends ColumnViewer {
58
59     /**
60      * Constant indicating that all levels of the tree should be expanded or
61      * collapsed.
62      *
63      * @see #expandToLevel(int)
64      * @see #collapseToLevel(Object, int)
65      */

66     public static final int ALL_LEVELS = -1;
67
68     /**
69      * List of registered tree listeners (element type:
70      * <code>TreeListener</code>).
71      */

72     private ListenerList treeListeners = new ListenerList();
73
74     /**
75      * The level to which the tree is automatically expanded each time the
76      * viewer's input is changed (that is, by <code>setInput</code>). A value
77      * of 0 means that auto-expand is off.
78      *
79      * @see #setAutoExpandLevel
80      */

81     private int expandToLevel = 0;
82
83     /**
84      * Safe runnable used to update an item.
85      */

86     class UpdateItemSafeRunnable extends SafeRunnable {
87         private Object JavaDoc element;
88
89         private Item item;
90
91         UpdateItemSafeRunnable(Item item, Object JavaDoc element) {
92             this.item = item;
93             this.element = element;
94         }
95
96         public void run() {
97             doUpdateItem(item, element);
98         }
99
100     }
101
102     /**
103      * Creates an abstract tree viewer. The viewer has no input, no content
104      * provider, a default label provider, no sorter, no filters, and has
105      * auto-expand turned off.
106      */

107     protected AbstractTreeViewer() {
108         // do nothing
109
}
110
111     /**
112      * Adds the given child elements to this viewer as children of the given
113      * parent element. If this viewer does not have a sorter, the elements are
114      * added at the end of the parent's list of children in the order given;
115      * otherwise, the elements are inserted at the appropriate positions.
116      * <p>
117      * This method should be called (by the content provider) when elements have
118      * been added to the model, in order to cause the viewer to accurately
119      * reflect the model. This method only affects the viewer, not the model.
120      * </p>
121      *
122      * @param parentElementOrTreePath
123      * the parent element
124      * @param childElements
125      * the child elements to add
126      */

127     public void add(Object JavaDoc parentElementOrTreePath, Object JavaDoc[] childElements) {
128         Assert.isNotNull(parentElementOrTreePath);
129         assertElementsNotNull(childElements);
130         if (isBusy())
131             return;
132         Widget[] widgets = internalFindItems(parentElementOrTreePath);
133         // If parent hasn't been realized yet, just ignore the add.
134
if (widgets.length == 0) {
135             return;
136         }
137
138         for (int i = 0; i < widgets.length; i++) {
139             internalAdd(widgets[i], parentElementOrTreePath, childElements);
140         }
141     }
142
143     /**
144      * Find the items for the given element of tree path
145      *
146      * @param parentElementOrTreePath
147      * the element or tree path
148      * @return the items for that element
149      *
150      * @since 3.3
151      */

152     final protected Widget[] internalFindItems(Object JavaDoc parentElementOrTreePath) {
153         Widget[] widgets;
154         if (parentElementOrTreePath instanceof TreePath) {
155             TreePath path = (TreePath) parentElementOrTreePath;
156             Widget w = internalFindItem(path);
157             if (w == null) {
158                 widgets = new Widget[] {};
159             } else {
160                 widgets = new Widget[] { w };
161             }
162         } else {
163             widgets = findItems(parentElementOrTreePath);
164         }
165         return widgets;
166     }
167
168     /**
169      * Return the item at the given path or <code>null</code>
170      *
171      * @param path
172      * the path
173      * @return {@link Widget} the item at that path
174      */

175     private Widget internalFindItem(TreePath path) {
176         Widget[] widgets = findItems(path.getLastSegment());
177         for (int i = 0; i < widgets.length; i++) {
178             Widget widget = widgets[i];
179             if (widget instanceof Item) {
180                 Item item = (Item) widget;
181                 TreePath p = getTreePathFromItem(item);
182                 if (p.equals(path)) {
183                     return widget;
184                 }
185             }
186         }
187         return null;
188     }
189
190     /**
191      * Adds the given child elements to this viewer as children of the given
192      * parent element.
193      * <p>
194      * EXPERIMENTAL. Not to be used except by JDT. This method was added to
195      * support JDT's explorations into grouping by working sets, which requires
196      * viewers to support multiple equal elements. See bug 76482 for more
197      * details. This support will likely be removed in Eclipse 3.2 in favor of
198      * proper support for multiple equal elements.
199      * </p>
200      *
201      * @param widget
202      * the widget for the parent element
203      * @param parentElementOrTreePath
204      * the parent element
205      * @param childElements
206      * the child elements to add
207      * @since 3.1
208      */

209     protected void internalAdd(Widget widget, Object JavaDoc parentElementOrTreePath,
210             Object JavaDoc[] childElements) {
211         Object JavaDoc parent;
212         TreePath path;
213         if (parentElementOrTreePath instanceof TreePath) {
214             path = (TreePath) parentElementOrTreePath;
215             parent = path.getLastSegment();
216         } else {
217             parent = parentElementOrTreePath;
218             path = null;
219         }
220
221         // optimization!
222
// if the widget is not expanded we just invalidate the subtree
223
if (widget instanceof Item) {
224             Item ti = (Item) widget;
225             if (!getExpanded(ti)) {
226                 boolean needDummy = isExpandable(ti, path, parent);
227                 boolean haveDummy = false;
228                 // remove all children
229
Item[] items = getItems(ti);
230                 for (int i = 0; i < items.length; i++) {
231                     if (items[i].getData() != null) {
232                         disassociate(items[i]);
233                         items[i].dispose();
234                     } else {
235                         if (needDummy && !haveDummy) {
236                             haveDummy = true;
237                         } else {
238                             items[i].dispose();
239                         }
240                     }
241                 }
242                 // append a dummy if necessary
243
if (needDummy && !haveDummy) {
244                     newItem(ti, SWT.NULL, -1);
245                 }
246                 return;
247             }
248         }
249
250         if (childElements.length > 0) {
251             // TODO: Add filtering back?
252
Object JavaDoc[] filtered = filter(parentElementOrTreePath, childElements);
253             ViewerComparator comparator = getComparator();
254             if (comparator != null) {
255                 if (comparator instanceof TreePathViewerSorter) {
256                     TreePathViewerSorter tpvs = (TreePathViewerSorter) comparator;
257                     if (path == null) {
258                         path = internalGetSorterParentPath(widget, comparator);
259                     }
260                     tpvs.sort(this, path, filtered);
261                 } else {
262                     comparator.sort(this, filtered);
263                 }
264             }
265             createAddedElements(widget, filtered);
266         }
267     }
268
269     /**
270      * Filter the children elements.
271      *
272      * @param parentElementOrTreePath
273      * the parent element or path
274      * @param elements
275      * the child elements
276      * @return the filter list of children
277      */

278     private Object JavaDoc[] filter(Object JavaDoc parentElementOrTreePath, Object JavaDoc[] elements) {
279         ViewerFilter[] filters = getFilters();
280         if (filters != null) {
281             ArrayList JavaDoc filtered = new ArrayList JavaDoc(elements.length);
282             for (int i = 0; i < elements.length; i++) {
283                 boolean add = true;
284                 for (int j = 0; j < filters.length; j++) {
285                     add = filters[j].select(this, parentElementOrTreePath,
286                             elements[i]);
287                     if (!add) {
288                         break;
289                     }
290                 }
291                 if (add) {
292                     filtered.add(elements[i]);
293                 }
294             }
295             return filtered.toArray();
296         }
297         return elements;
298     }
299
300     /**
301      * Create the new elements in the parent widget. If the child already exists
302      * do nothing.
303      *
304      * @param widget
305      * @param elements
306      * Sorted list of elements to add.
307      */

308     private void createAddedElements(Widget widget, Object JavaDoc[] elements) {
309
310         if (elements.length == 1) {
311             if (equals(elements[0], widget.getData())) {
312                 return;
313             }
314         }
315
316         ViewerComparator comparator = getComparator();
317         TreePath parentPath = internalGetSorterParentPath(widget, comparator);
318         Item[] items = getChildren(widget);
319
320         // As the items are sorted already we optimize for a
321
// start position
322
int lastInsertion = 0;
323
324         // Optimize for the empty case
325
if (items.length == 0) {
326             for (int i = 0; i < elements.length; i++) {
327                 createTreeItem(widget, elements[i], -1);
328             }
329             return;
330         }
331
332         for (int i = 0; i < elements.length; i++) {
333             boolean newItem = true;
334             Object JavaDoc element = elements[i];
335             int index;
336             if (comparator == null) {
337                 if (itemExists(items, element)) {
338                     internalRefresh(element);
339                     newItem = false;
340                 }
341                 index = -1;
342             } else {
343                 lastInsertion = insertionPosition(items, comparator,
344                         lastInsertion, element, parentPath);
345                 // As we are only searching the original array we keep track of
346
// those positions only
347
if (lastInsertion == items.length) {
348                     index = -1;
349                 } else {// See if we should just refresh
350
while (lastInsertion < items.length
351                             && internalCompare(comparator, parentPath, element,
352                                     items[lastInsertion].getData()) == 0) {
353                         // As we cannot assume the sorter is consistent with
354
// equals() - therefore we can
355
// just check against the item prior to this index (if
356
// any)
357
if (items[lastInsertion].getData().equals(element)) {
358                             // refresh the element in case it has new children
359
internalRefresh(element);
360                             newItem = false;
361                         }
362                         lastInsertion++;// We had an insertion so increment
363
}
364                     // Did we get to the end?
365
if (lastInsertion == items.length) {
366                         index = -1;
367                     } else {
368                         index = lastInsertion + i; // Add the index as the
369
// array is growing
370
}
371                 }
372             }
373             if (newItem) {
374                 createTreeItem(widget, element, index);
375             }
376         }
377     }
378
379     /**
380      * See if element is the data of one of the elements in items.
381      *
382      * @param items
383      * @param element
384      * @return <code>true</code> if the element matches.
385      */

386     private boolean itemExists(Item[] items, Object JavaDoc element) {
387         if (usingElementMap()) {
388             Widget[] existingItems = findItems(element);
389             // optimization for two common cases
390
if (existingItems.length == 0) {
391                 return false;
392             } else if (existingItems.length == 1) {
393                 if (items.length > 0 && existingItems[0] instanceof Item) {
394                     Item existingItem = (Item) existingItems[0];
395                     return getParentItem(existingItem) == getParentItem(items[0]);
396                 }
397             }
398         }
399         for (int i = 0; i < items.length; i++) {
400             if (items[i].getData().equals(element)) {
401                 return true;
402             }
403         }
404         return false;
405     }
406
407     /**
408      * Returns the index where the item should be inserted. It uses sorter to
409      * determine the correct position, if sorter is not assigned, returns the
410      * index of the element after the last.
411      *
412      * @param items
413      * the items to search
414      * @param comparator
415      * The comparator to use.
416      * @param lastInsertion
417      * the start index to start search for position from this allows
418      * optimizing search for multiple elements that are sorted
419      * themselves.
420      * @param element
421      * element to find position for.
422      * @param parentPath
423      * the tree path for the element's parent or <code>null</code>
424      * if the element is a root element or the sorter is not a
425      * {@link TreePathViewerSorter}
426      * @return the index to use when inserting the element.
427      *
428      */

429
430     private int insertionPosition(Item[] items, ViewerComparator comparator,
431             int lastInsertion, Object JavaDoc element, TreePath parentPath) {
432
433         int size = items.length;
434         if (comparator == null) {
435             return size;
436         }
437         int min = lastInsertion, max = size - 1;
438
439         while (min <= max) {
440             int mid = (min + max) / 2;
441             Object JavaDoc data = items[mid].getData();
442             int compare = internalCompare(comparator, parentPath, data, element);
443             if (compare == 0) {
444                 return mid;// Return if we already match
445
}
446             if (compare < 0) {
447                 min = mid + 1;
448             } else {
449                 max = mid - 1;
450             }
451         }
452         return min;
453
454     }
455
456     /**
457      * Returns the index where the item should be inserted. It uses sorter to
458      * determine the correct position, if sorter is not assigned, returns the
459      * index of the element after the last.
460      *
461      * @param parent
462      * The parent widget
463      * @param sorter
464      * The sorter to use.
465      * @param startIndex
466      * the start index to start search for position from this allows
467      * optimizing search for multiple elements that are sorted
468      * themselves.
469      * @param element
470      * element to find position for.
471      * @param currentSize
472      * the current size of the collection
473      * @return the index to use when inserting the element.
474      *
475      */

476
477     /**
478      * Returns the index where the item should be inserted.
479      *
480      * @param parent
481      * The parent widget the element will be inserted into.
482      * @param element
483      * The element to insert.
484      * @return the index of the element
485      */

486     protected int indexForElement(Widget parent, Object JavaDoc element) {
487         ViewerComparator comparator = getComparator();
488         TreePath parentPath = internalGetSorterParentPath(parent, comparator);
489
490         Item[] items = getChildren(parent);
491         int count = items.length;
492
493         if (comparator == null) {
494             return count;
495         }
496         int min = 0, max = count - 1;
497
498         while (min <= max) {
499             int mid = (min + max) / 2;
500             Object JavaDoc data = items[mid].getData();
501             int compare = internalCompare(comparator, parentPath, data, element);
502             if (compare == 0) {
503                 // find first item > element
504
while (compare == 0) {
505                     ++mid;
506                     if (mid >= count) {
507                         break;
508                     }
509                     data = items[mid].getData();
510                     compare = internalCompare(comparator, parentPath, data,
511                             element);
512                 }
513                 return mid;
514             }
515             if (compare < 0) {
516                 min = mid + 1;
517             } else {
518                 max = mid - 1;
519             }
520         }
521         return min;
522     }
523
524     /**
525      * Return the tree path that should be used as the parent path for the given
526      * widget and sorter. A <code>null</code> is returned if either the sorter
527      * is not a {@link TreePathViewerSorter} or if the parent widget is not an
528      * {@link Item} (i.e. is the root of the tree).
529      *
530      * @param parent
531      * the parent widget
532      * @param comparator
533      * the sorter
534      * @return the tree path that should be used as the parent path for the
535      * given widget and sorter
536      */

537     private TreePath internalGetSorterParentPath(Widget parent,
538             ViewerComparator comparator) {
539         TreePath path;
540         if (comparator instanceof TreePathViewerSorter
541                 && parent instanceof Item) {
542             Item item = (Item) parent;
543             path = getTreePathFromItem(item);
544         } else {
545             path = null;
546         }
547         return path;
548     }
549
550     /**
551      * Compare the two elements using the given sorter. If the sorter is a
552      * {@link TreePathViewerSorter}, the provided tree path will be used. If
553      * the tree path is null and the sorter is a tree path sorter, then the
554      * elements are root elements
555      *
556      * @param comparator
557      * the sorter
558      * @param parentPath
559      * the path of the elements' parent
560      * @param e1
561      * the first element
562      * @param e2
563      * the second element
564      * @return the result of comparing the two elements
565      */

566     private int internalCompare(ViewerComparator comparator,
567             TreePath parentPath, Object JavaDoc e1, Object JavaDoc e2) {
568         if (comparator instanceof TreePathViewerSorter) {
569             TreePathViewerSorter tpvs = (TreePathViewerSorter) comparator;
570             return tpvs.compare(this, parentPath, e1, e2);
571         }
572         return comparator.compare(this, e1, e2);
573     }
574
575     /*
576      * (non-Javadoc)
577      *
578      * @see org.eclipse.jface.viewers.StructuredViewer#getSortedChildren(java.lang.Object)
579      */

580     protected Object JavaDoc[] getSortedChildren(Object JavaDoc parentElementOrTreePath) {
581         Object JavaDoc[] result = getFilteredChildren(parentElementOrTreePath);
582         ViewerComparator comparator = getComparator();
583         if (parentElementOrTreePath != null
584                 && comparator instanceof TreePathViewerSorter) {
585             TreePathViewerSorter tpvs = (TreePathViewerSorter) comparator;
586
587             // be sure we're not modifying the original array from the model
588
result = (Object JavaDoc[]) result.clone();
589
590             TreePath path = null;
591             if (parentElementOrTreePath instanceof TreePath) {
592                 path = (TreePath) parentElementOrTreePath;
593             } else {
594                 Object JavaDoc parent = parentElementOrTreePath;
595                 Widget w = internalGetWidgetToSelect(parent);
596                 if (w != null) {
597                     path = internalGetSorterParentPath(w, comparator);
598                 }
599             }
600             tpvs.sort(this, path, result);
601         } else if (comparator != null) {
602             // be sure we're not modifying the original array from the model
603
result = (Object JavaDoc[]) result.clone();
604             comparator.sort(this, result);
605         }
606         return result;
607     }
608
609     /*
610      * (non-Javadoc)
611      *
612      * @see org.eclipse.jface.viewers.StructuredViewer#getFilteredChildren(java.lang.Object)
613      */

614     protected Object JavaDoc[] getFilteredChildren(Object JavaDoc parentElementOrTreePath) {
615         Object JavaDoc[] result = getRawChildren(parentElementOrTreePath);
616         ViewerFilter[] filters = getFilters();
617         for (int i = 0; i < filters.length; i++) {
618             ViewerFilter filter = filters[i];
619             result = filter.filter(this, parentElementOrTreePath, result);
620         }
621         return result;
622     }
623
624     /**
625      * Adds the given child element to this viewer as a child of the given
626      * parent element. If this viewer does not have a sorter, the element is
627      * added at the end of the parent's list of children; otherwise, the element
628      * is inserted at the appropriate position.
629      * <p>
630      * This method should be called (by the content provider) when a single
631      * element has been added to the model, in order to cause the viewer to
632      * accurately reflect the model. This method only affects the viewer, not
633      * the model. Note that there is another method for efficiently processing
634      * the simultaneous addition of multiple elements.
635      * </p>
636      *
637      * @param parentElementOrTreePath
638      * the parent element or path
639      * @param childElement
640      * the child element
641      */

642     public void add(Object JavaDoc parentElementOrTreePath, Object JavaDoc childElement) {
643         add(parentElementOrTreePath, new Object JavaDoc[] { childElement });
644     }
645
646     /**
647      * Adds the given SWT selection listener to the given SWT control.
648      *
649      * @param control
650      * the SWT control
651      * @param listener
652      * the SWT selection listener
653      * @deprecated
654      */

655     protected void addSelectionListener(Control control,
656             SelectionListener listener) {
657         // do nothing
658
}
659
660     /**
661      * Adds a listener for expand and collapse events in this viewer. Has no
662      * effect if an identical listener is already registered.
663      *
664      * @param listener
665      * a tree viewer listener
666      */

667     public void addTreeListener(ITreeViewerListener listener) {
668         treeListeners.add(listener);
669     }
670
671     /**
672      * Adds the given SWT tree listener to the given SWT control.
673      *
674      * @param control
675      * the SWT control
676      * @param listener
677      * the SWT tree listener
678      */

679     protected abstract void addTreeListener(Control control,
680             TreeListener listener);
681
682     /*
683      * (non-Javadoc)
684      *
685      * @see StructuredViewer#associate(Object, Item)
686      */

687     protected void associate(Object JavaDoc element, Item item) {
688         Object JavaDoc data = item.getData();
689         if (data != null && data != element && equals(data, element)) {
690             // workaround for PR 1FV62BT
691
// assumption: elements are equal but not identical
692
// -> remove from map but don't touch children
693
unmapElement(data, item);
694             item.setData(element);
695             mapElement(element, item);
696         } else {
697             // recursively disassociate all
698
super.associate(element, item);
699         }
700     }
701
702     /**
703      * Collapses all nodes of the viewer's tree, starting with the root. This
704      * method is equivalent to <code>collapseToLevel(ALL_LEVELS)</code>.
705      */

706     public void collapseAll() {
707         Object JavaDoc root = getRoot();
708         if (root != null) {
709             collapseToLevel(root, ALL_LEVELS);
710         }
711     }
712
713     /**
714      * Collapses the subtree rooted at the given element or tree path to the
715      * given level.
716      *
717      * @param elementOrTreePath
718      * the element or tree path
719      * @param level
720      * non-negative level, or <code>ALL_LEVELS</code> to collapse
721      * all levels of the tree
722      */

723     public void collapseToLevel(Object JavaDoc elementOrTreePath, int level) {
724         Assert.isNotNull(elementOrTreePath);
725         Widget w = internalGetWidgetToSelect(elementOrTreePath);
726         if (w != null) {
727             internalCollapseToLevel(w, level);
728         }
729     }
730
731     /**
732      * Creates all children for the given widget.
733      * <p>
734      * The default implementation of this framework method assumes that
735      * <code>widget.getData()</code> returns the element corresponding to the
736      * node. Note: the node is not visually expanded! You may have to call
737      * <code>parent.setExpanded(true)</code>.
738      * </p>
739      *
740      * @param widget
741      * the widget
742      */

743     protected void createChildren(final Widget widget) {
744         boolean oldBusy = busy;
745         busy = true;
746         try {
747             final Item[] tis = getChildren(widget);
748             if (tis != null && tis.length > 0) {
749                 Object JavaDoc data = tis[0].getData();
750                 if (data != null) {
751                     return; // children already there!
752
}
753             }
754     
755             BusyIndicator.showWhile(widget.getDisplay(), new Runnable JavaDoc() {
756                 public void run() {
757                     // fix for PR 1FW89L7:
758
// don't complain and remove all "dummies" ...
759
if (tis != null) {
760                         for (int i = 0; i < tis.length; i++) {
761                             if (tis[i].getData() != null) {
762                                 disassociate(tis[i]);
763                                 Assert.isTrue(tis[i].getData() == null,
764                                         "Second or later child is non -null");//$NON-NLS-1$
765

766                             }
767                             tis[i].dispose();
768                         }
769                     }
770                     Object JavaDoc d = widget.getData();
771                     if (d != null) {
772                         Object JavaDoc parentElement = d;
773                         Object JavaDoc[] children;
774                         if (isTreePathContentProvider() && widget instanceof Item) {
775                             TreePath path = getTreePathFromItem((Item) widget);
776                             children = getSortedChildren(path);
777                         } else {
778                             children = getSortedChildren(parentElement);
779                         }
780                         for (int i = 0; i < children.length; i++) {
781                             createTreeItem(widget, children[i], -1);
782                         }
783                     }
784                 }
785     
786             });
787         } finally {
788             busy = oldBusy;
789         }
790     }
791
792     /**
793      * Creates a single item for the given parent and synchronizes it with the
794      * given element.
795      *
796      * @param parent
797      * the parent widget
798      * @param element
799      * the element
800      * @param index
801      * if non-negative, indicates the position to insert the item
802      * into its parent
803      */

804     protected void createTreeItem(Widget parent, Object JavaDoc element, int index) {
805         Item item = newItem(parent, SWT.NULL, index);
806         updateItem(item, element);
807         updatePlus(item, element);
808     }
809
810     /**
811      * The <code>AbstractTreeViewer</code> implementation of this method also
812      * recurses over children of the corresponding element.
813      */

814     protected void disassociate(Item item) {
815         super.disassociate(item);
816         // recursively unmapping the items is only required when
817
// the hash map is used. In the other case disposing
818
// an item will recursively dispose its children.
819
if (usingElementMap()) {
820             disassociateChildren(item);
821         }
822     }
823
824     /**
825      * Disassociates the children of the given SWT item from their corresponding
826      * elements.
827      *
828      * @param item
829      * the widget
830      */

831     private void disassociateChildren(Item item) {
832         Item[] items = getChildren(item);
833         for (int i = 0; i < items.length; i++) {
834             if (items[i].getData() != null) {
835                 disassociat