1 /*******************************************************************************
2  * Copyright (c) 2000, 2019 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Tom Schindl <tom.schindl@bestsolution.at> - bug 153993, bug 167323, bug 175192
14  *     Lasse Knudsen, bug 205700
15  *     Micah Hainline, bug 210448
16  *     Michael Schneider, bug 210747
17  *     Bruce Sutton, bug 221768
18  *     Matthew Hall, bug 221988
19  *     Julien Desgats, bug 203950
20  *     Alexander Fedorov <alexander.fedorov@arsysop.ru> - Bug 548314
21  *******************************************************************************/
22 
23 package org.eclipse.jface.viewers;
24 
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.LinkedList;
28 import java.util.List;
29 
30 import org.eclipse.core.runtime.Assert;
31 import org.eclipse.core.runtime.IStatus;
32 import org.eclipse.core.runtime.ListenerList;
33 import org.eclipse.core.runtime.Status;
34 import org.eclipse.jface.internal.InternalPolicy;
35 import org.eclipse.jface.util.Policy;
36 import org.eclipse.jface.util.SafeRunnable;
37 import org.eclipse.swt.SWT;
38 import org.eclipse.swt.custom.BusyIndicator;
39 import org.eclipse.swt.events.SelectionEvent;
40 import org.eclipse.swt.events.SelectionListener;
41 import org.eclipse.swt.events.TreeEvent;
42 import org.eclipse.swt.events.TreeListener;
43 import org.eclipse.swt.graphics.Point;
44 import org.eclipse.swt.widgets.Control;
45 import org.eclipse.swt.widgets.Item;
46 import org.eclipse.swt.widgets.Widget;
47 
48 /**
49  * Abstract base implementation for tree-structure-oriented viewers (trees and
50  * table trees).
51  * <p>
52  * Nodes in the tree can be in either an expanded or a collapsed state,
53  * depending on whether the children on a node are visible. This class
54  * introduces public methods for controlling the expanding and collapsing of
55  * nodes.
56  * </p>
57  * <p>
58  * As of 3.2, AbstractTreeViewer supports multiple equal elements (each with a
59  * different parent chain) in the tree. This support requires that clients
60  * enable the element map by calling <code>setUseHashlookup(true)</code>.
61  * </p>
62  * <p>
63  * Content providers for abstract tree viewers must implement one of the
64  * interfaces <code>ITreeContentProvider</code> or (as of 3.2, to support
65  * multiple equal elements) <code>ITreePathContentProvider</code>.
66  * </p>
67  * <p>
68  * <strong> This class is not intended to be subclassed outside of the JFace
69  * viewers framework.</strong>
70  * </p>
71  *
72  * @see TreeViewer
73  */
74 public abstract class AbstractTreeViewer extends ColumnViewer {
75 
76 	/**
77 	 * Constant indicating that all levels of the tree should be expanded or
78 	 * collapsed.
79 	 *
80 	 * @see #expandToLevel(int)
81 	 * @see #collapseToLevel(Object, int)
82 	 */
83 	public static final int ALL_LEVELS = -1;
84 
85 	/**
86 	 * List of registered tree listeners (element type:
87 	 * <code>TreeListener</code>).
88 	 */
89 	private ListenerList<ITreeViewerListener> treeListeners = new ListenerList<>();
90 
91 	/**
92 	 * The level to which the tree is automatically expanded each time the
93 	 * viewer's input is changed (that is, by <code>setInput</code>). A value
94 	 * of 0 means that auto-expand is off.
95 	 *
96 	 * @see #setAutoExpandLevel
97 	 */
98 	private int expandToLevel = 0;
99 
100 	/**
101 	 * Indicates if filters should be checked to determine expandability of
102 	 * a tree node.
103 	 */
104 	private boolean isExpandableCheckFilters = false;
105 
106 	/**
107 	 * Safe runnable used to update an item.
108 	 */
109 	class UpdateItemSafeRunnable extends SafeRunnable {
110 		private Object element;
111 
112 		private Item item;
113 
UpdateItemSafeRunnable(Item item, Object element)114 		UpdateItemSafeRunnable(Item item, Object element) {
115 			this.item = item;
116 			this.element = element;
117 		}
118 
119 		@Override
run()120 		public void run() {
121 			doUpdateItem(item, element);
122 		}
123 
124 	}
125 
126 	/**
127 	 * Creates an abstract tree viewer. The viewer has no input, no content
128 	 * provider, a default label provider, no sorter, no filters, and has
129 	 * auto-expand turned off.
130 	 */
AbstractTreeViewer()131 	protected AbstractTreeViewer() {
132 		// do nothing
133 	}
134 
135 	/**
136 	 * Adds the given child elements to this viewer as children of the given
137 	 * parent element. If this viewer does not have a sorter, the elements are
138 	 * added at the end of the parent's list of children in the order given;
139 	 * otherwise, the elements are inserted at the appropriate positions.
140 	 * <p>
141 	 * This method should be called (by the content provider) when elements have
142 	 * been added to the model, in order to cause the viewer to accurately
143 	 * reflect the model. This method only affects the viewer, not the model.
144 	 * </p>
145 	 *
146 	 * @param parentElementOrTreePath
147 	 *            the parent element
148 	 * @param childElements
149 	 *            the child elements to add
150 	 */
add(Object parentElementOrTreePath, Object... childElements)151 	public void add(Object parentElementOrTreePath, Object... childElements) {
152 		Assert.isNotNull(parentElementOrTreePath);
153 		assertElementsNotNull(childElements);
154 		if (checkBusy())
155 			return;
156 		Widget[] widgets = internalFindItems(parentElementOrTreePath);
157 		// If parent hasn't been realized yet, just ignore the add.
158 		if (widgets.length == 0) {
159 			return;
160 		}
161 
162 		for (Widget widget : widgets) {
163 			internalAdd(widget, parentElementOrTreePath, childElements);
164 		}
165 	}
166 
167 	/**
168 	 * Find the items for the given element of tree path
169 	 *
170 	 * @param parentElementOrTreePath
171 	 *            the element or tree path
172 	 * @return the items for that element
173 	 *
174 	 * @since 3.3
175 	 */
internalFindItems(Object parentElementOrTreePath)176 	final protected Widget[] internalFindItems(Object parentElementOrTreePath) {
177 		Widget[] widgets;
178 		if (parentElementOrTreePath instanceof TreePath) {
179 			TreePath path = (TreePath) parentElementOrTreePath;
180 			Widget w = internalFindItem(path);
181 			if (w == null) {
182 				widgets = new Widget[] {};
183 			} else {
184 				widgets = new Widget[] { w };
185 			}
186 		} else {
187 			widgets = findItems(parentElementOrTreePath);
188 		}
189 		return widgets;
190 	}
191 
192 	/**
193 	 * Return the item at the given path or <code>null</code>
194 	 *
195 	 * @param path
196 	 *            the path
197 	 * @return {@link Widget} the item at that path
198 	 */
internalFindItem(TreePath path)199 	private Widget internalFindItem(TreePath path) {
200 		Widget[] widgets = findItems(path.getLastSegment());
201 		for (Widget widget : widgets) {
202 			if (widget instanceof Item) {
203 				Item item = (Item) widget;
204 				TreePath p = getTreePathFromItem(item);
205 				if (p.equals(path)) {
206 					return widget;
207 				}
208 			}
209 		}
210 		return null;
211 	}
212 
213 	/**
214 	 * Adds the given child elements to this viewer as children of the given
215 	 * parent element.
216 	 * <p>
217 	 * EXPERIMENTAL. Not to be used except by JDT. This method was added to
218 	 * support JDT's explorations into grouping by working sets. This method
219 	 * cannot be removed without breaking binary backwards compatibility, but
220 	 * should not be called by clients.
221 	 * </p>
222 	 *
223 	 * @param widget
224 	 *            the widget for the parent element
225 	 * @param parentElementOrTreePath
226 	 *            the parent element
227 	 * @param childElements
228 	 *            the child elements to add
229 	 * @since 3.1
230 	 */
internalAdd(Widget widget, Object parentElementOrTreePath, Object[] childElements)231 	protected void internalAdd(Widget widget, Object parentElementOrTreePath,
232 			Object[] childElements) {
233 		Object parent;
234 		TreePath path;
235 		if (parentElementOrTreePath instanceof TreePath) {
236 			path = (TreePath) parentElementOrTreePath;
237 			parent = path.getLastSegment();
238 		} else {
239 			parent = parentElementOrTreePath;
240 			path = null;
241 		}
242 
243 		// optimization!
244 		// if the widget is not expanded we just invalidate the subtree
245 		if (widget instanceof Item) {
246 			Item ti = (Item) widget;
247 			if (!getExpanded(ti)) {
248 				boolean needDummy = isExpandable(ti, path, parent);
249 				boolean haveDummy = false;
250 				// remove all children
251 				Item[] items = getItems(ti);
252 				for (Item item : items) {
253 					if (item.getData() != null) {
254 						disassociate(item);
255 						item.dispose();
256 					} else {
257 						if (needDummy && !haveDummy) {
258 							haveDummy = true;
259 						} else {
260 							item.dispose();
261 						}
262 					}
263 				}
264 				// append a dummy if necessary
265 				if (needDummy && !haveDummy) {
266 					newItem(ti, SWT.NULL, -1);
267 				}
268 				return;
269 			}
270 		}
271 
272 		if (childElements.length > 0) {
273 			// TODO: Add filtering back?
274 			Object[] filtered = filter(parentElementOrTreePath, childElements);
275 			ViewerComparator comparator = getComparator();
276 			if (comparator != null) {
277 				if (comparator instanceof TreePathViewerSorter) {
278 					TreePathViewerSorter tpvs = (TreePathViewerSorter) comparator;
279 					if (path == null) {
280 						path = internalGetSorterParentPath(widget, comparator);
281 					}
282 					tpvs.sort(this, path, filtered);
283 				} else {
284 					comparator.sort(this, filtered);
285 				}
286 			}
287 			createAddedElements(widget, filtered);
288 			if (InternalPolicy.DEBUG_LOG_EQUAL_VIEWER_ELEMENTS) {
289 				Item[] children = getChildren(widget);
290 				Object[] elements = new Object[children.length];
291 				for (int i = 0; i < children.length; i++) {
292 					elements[i] = children[i].getData();
293 				}
294 				assertElementsNotNull(parent, elements);
295 			}
296 		}
297 	}
298 
299 	/**
300 	 * Filter the children elements.
301 	 *
302 	 * @param parentElementOrTreePath
303 	 *            the parent element or path
304 	 * @param elements
305 	 *            the child elements
306 	 * @return the filter list of children
307 	 */
filter(Object parentElementOrTreePath, Object[] elements)308 	private Object[] filter(Object parentElementOrTreePath, Object[] elements) {
309 		ViewerFilter[] filters = getFilters();
310 		if (filters != null) {
311 			List<Object> filtered = new ArrayList<>(elements.length);
312 			for (Object element : elements) {
313 				boolean add = true;
314 				for (ViewerFilter filter : filters) {
315 					add = filter.select(this, parentElementOrTreePath,
316 							element);
317 					if (!add) {
318 						break;
319 					}
320 				}
321 				if (add) {
322 					filtered.add(element);
323 				}
324 			}
325 			return filtered.toArray();
326 		}
327 		return elements;
328 	}
329 
330 	/**
331 	 * Create the new elements in the parent widget. If the child already exists
332 	 * do nothing.
333 	 *
334 	 * @param widget
335 	 * @param elements
336 	 *            Sorted list of elements to add.
337 	 */
createAddedElements(Widget widget, Object[] elements)338 	private void createAddedElements(Widget widget, Object[] elements) {
339 
340 		if (elements.length == 1) {
341 			if (equals(elements[0], widget.getData())) {
342 				return;
343 			}
344 		}
345 
346 		ViewerComparator comparator = getComparator();
347 		TreePath parentPath = internalGetSorterParentPath(widget, comparator);
348 		Item[] items = getChildren(widget);
349 
350 		// Optimize for the empty case
351 		if (items.length == 0) {
352 			for (Object element : elements) {
353 				createTreeItem(widget, element, -1);
354 			}
355 			return;
356 		}
357 
358 		// Optimize for no comparator
359 		if (comparator == null) {
360 			for (Object element : elements) {
361 				if (itemExists(items, element)) {
362 					internalRefresh(element);
363 				} else {
364 					createTreeItem(widget, element, -1);
365 				}
366 			}
367 			return;
368 		}
369 		// As the items are sorted already we optimize for a
370 		// start position. This is the insertion position relative to the
371 		// original item array.
372 		int indexInItems = 0;
373 
374 		// Count of elements we have added. See bug 205700 for why this is needed.
375 		int newItems = 0;
376 
377 		elementloop: for (Object element : elements) {
378 			// update the index relative to the original item array
379 			indexInItems = insertionPosition(items, comparator,
380 					indexInItems, element, parentPath);
381 			if (indexInItems == items.length) {
382 				createTreeItem(widget, element, -1);
383 				newItems++;
384 			} else {
385 				// Search for an item for the element. The comparator might
386 				// regard elements as equal when they are not.
387 
388 				// Use a separate index variable to search within the existing
389 				// elements that compare equally, see
390 				// TreeViewerTestBug205700.testAddEquallySortedElements.
391 				int insertionIndexInItems = indexInItems;
392 				while( insertionIndexInItems < items.length
393 						&& internalCompare(comparator, parentPath, element,
394 								items[insertionIndexInItems].getData()) == 0) {
395 					// As we cannot assume the sorter is consistent with
396 					// equals() - therefore we can
397 					// just check against the item prior to this index (if
398 					// any)
399 					if (items[insertionIndexInItems].getData().equals(element)) {
400 						// Found the item for the element.
401 						// Refresh the element in case it has new children.
402 						internalRefresh(element);
403 						// Do not create a new item - continue with the next element.
404 						continue elementloop;
405 					}
406 					insertionIndexInItems++;
407 				}
408 				// Did we get to the end?
409 				if (insertionIndexInItems == items.length) {
410 					createTreeItem(widget, element, -1);
411 					newItems++;
412 				} else {
413 					// InsertionIndexInItems is the index in the original array. We
414 					// need to correct by the number of new items we have
415 					// created. See bug 205700.
416 					createTreeItem(widget, element, insertionIndexInItems + newItems);
417 					newItems++;
418 				}
419 			}
420 		}
421 	}
422 
423 	/**
424 	 * See if element is the data of one of the elements in items.
425 	 *
426 	 * @param items
427 	 * @param element
428 	 * @return <code>true</code> if the element matches.
429 	 */
itemExists(Item[] items, Object element)430 	private boolean itemExists(Item[] items, Object element) {
431 		if (usingElementMap()) {
432 			Widget[] existingItems = findItems(element);
433 			// optimization for two common cases
434 			if (existingItems.length == 0) {
435 				return false;
436 			} else if (existingItems.length == 1) {
437 				if (items.length > 0 && existingItems[0] instanceof Item) {
438 					Item existingItem = (Item) existingItems[0];
439 					return getParentItem(existingItem) == getParentItem(items[0]);
440 				}
441 			}
442 		}
443 		for (Item item : items) {
444 			if (item.getData().equals(element)) {
445 				return true;
446 			}
447 		}
448 		return false;
449 	}
450 
451 	/**
452 	 * Returns the index where the item should be inserted. It uses sorter to
453 	 * determine the correct position, if sorter is not assigned, returns the
454 	 * index of the element after the last.
455 	 *
456 	 * @param items
457 	 *            the items to search
458 	 * @param comparator
459 	 *            The comparator to use.
460 	 * @param lastInsertion
461 	 *            the start index to start search for position from this allows
462 	 *            optimizing search for multiple elements that are sorted
463 	 *            themselves.
464 	 * @param element
465 	 *            element to find position for.
466 	 * @param parentPath
467 	 *            the tree path for the element's parent or <code>null</code>
468 	 *            if the element is a root element or the sorter is not a
469 	 *            {@link TreePathViewerSorter}
470 	 * @return the index to use when inserting the element.
471 	 *
472 	 */
473 
insertionPosition(Item[] items, ViewerComparator comparator, int lastInsertion, Object element, TreePath parentPath)474 	private int insertionPosition(Item[] items, ViewerComparator comparator,
475 			int lastInsertion, Object element, TreePath parentPath) {
476 
477 		int size = items.length;
478 		if (comparator == null) {
479 			return size;
480 		}
481 		int min = lastInsertion, max = size - 1;
482 
483 		while (min <= max) {
484 			int mid = (min + max) / 2;
485 			Object data = items[mid].getData();
486 			int compare = internalCompare(comparator, parentPath, data, element);
487 			if (compare == 0) {
488 				return mid;// Return if we already match
489 			}
490 			if (compare < 0) {
491 				min = mid + 1;
492 			} else {
493 				max = mid - 1;
494 			}
495 		}
496 		return min;
497 
498 	}
499 
500 	/**
501 	 * Returns the index where the item should be inserted. It uses sorter to
502 	 * determine the correct position, if sorter is not assigned, returns the
503 	 * index of the element after the last.
504 	 *
505 	 * @param parent
506 	 *            The parent widget
507 	 * @param sorter
508 	 *            The sorter to use.
509 	 * @param startIndex
510 	 *            the start index to start search for position from this allows
511 	 *            optimizing search for multiple elements that are sorted
512 	 *            themselves.
513 	 * @param element
514 	 *            element to find position for.
515 	 * @param currentSize
516 	 *            the current size of the collection
517 	 * @return the index to use when inserting the element.
518 	 *
519 	 */
520 
521 	/**
522 	 * Returns the index where the item should be inserted.
523 	 *
524 	 * @param parent
525 	 *            The parent widget the element will be inserted into.
526 	 * @param element
527 	 *            The element to insert.
528 	 * @return the index of the element
529 	 */
indexForElement(Widget parent, Object element)530 	protected int indexForElement(Widget parent, Object element) {
531 		ViewerComparator comparator = getComparator();
532 		TreePath parentPath = internalGetSorterParentPath(parent, comparator);
533 
534 		Item[] items = getChildren(parent);
535 		int count = items.length;
536 
537 		if (comparator == null) {
538 			return count;
539 		}
540 		int min = 0, max = count - 1;
541 
542 		while (min <= max) {
543 			int mid = (min + max) / 2;
544 			Object data = items[mid].getData();
545 			int compare = internalCompare(comparator, parentPath, data, element);
546 			if (compare == 0) {
547 				// find first item > element
548 				while (compare == 0) {
549 					++mid;
550 					if (mid >= count) {
551 						break;
552 					}
553 					data = items[mid].getData();
554 					compare = internalCompare(comparator, parentPath, data,
555 							element);
556 				}
557 				return mid;
558 			}
559 			if (compare < 0) {
560 				min = mid + 1;
561 			} else {
562 				max = mid - 1;
563 			}
564 		}
565 		return min;
566 	}
567 
568 	/**
569 	 * Return the tree path that should be used as the parent path for the given
570 	 * widget and sorter. A <code>null</code> is returned if either the sorter
571 	 * is not a {@link TreePathViewerSorter} or if the parent widget is not an
572 	 * {@link Item} (i.e. is the root of the tree).
573 	 *
574 	 * @param parent
575 	 *            the parent widget
576 	 * @param comparator
577 	 *            the sorter
578 	 * @return the tree path that should be used as the parent path for the
579 	 *         given widget and sorter
580 	 */
internalGetSorterParentPath(Widget parent, ViewerComparator comparator)581 	private TreePath internalGetSorterParentPath(Widget parent,
582 			ViewerComparator comparator) {
583 		TreePath path;
584 		if (comparator instanceof TreePathViewerSorter
585 				&& parent instanceof Item) {
586 			Item item = (Item) parent;
587 			path = getTreePathFromItem(item);
588 		} else {
589 			path = null;
590 		}
591 		return path;
592 	}
593 
594 	/**
595 	 * Compare the two elements using the given sorter. If the sorter is a
596 	 * {@link TreePathViewerSorter}, the provided tree path will be used. If
597 	 * the tree path is null and the sorter is a tree path sorter, then the
598 	 * elements are root elements
599 	 *
600 	 * @param comparator
601 	 *            the sorter
602 	 * @param parentPath
603 	 *            the path of the elements' parent
604 	 * @param e1
605 	 *            the first element
606 	 * @param e2
607 	 *            the second element
608 	 * @return the result of comparing the two elements
609 	 */
internalCompare(ViewerComparator comparator, TreePath parentPath, Object e1, Object e2)610 	private int internalCompare(ViewerComparator comparator,
611 			TreePath parentPath, Object e1, Object e2) {
612 		if (comparator instanceof TreePathViewerSorter) {
613 			TreePathViewerSorter tpvs = (TreePathViewerSorter) comparator;
614 			return tpvs.compare(this, parentPath, e1, e2);
615 		}
616 		return comparator.compare(this, e1, e2);
617 	}
618 
619 	@Override
getSortedChildren(Object parentElementOrTreePath)620 	protected Object[] getSortedChildren(Object parentElementOrTreePath) {
621 		Object[] result = getFilteredChildren(parentElementOrTreePath);
622 		ViewerComparator comparator = getComparator();
623 		if (parentElementOrTreePath != null
624 				&& comparator instanceof TreePathViewerSorter) {
625 			TreePathViewerSorter tpvs = (TreePathViewerSorter) comparator;
626 
627 			// be sure we're not modifying the original array from the model
628 			result = result.clone();
629 
630 			TreePath path = null;
631 			if (parentElementOrTreePath instanceof TreePath) {
632 				path = (TreePath) parentElementOrTreePath;
633 			} else {
634 				Object parent = parentElementOrTreePath;
635 				Widget w = internalGetWidgetToSelect(parent);
636 				if (w != null) {
637 					path = internalGetSorterParentPath(w, comparator);
638 				}
639 			}
640 			tpvs.sort(this, path, result);
641 		} else if (comparator != null) {
642 			// be sure we're not modifying the original array from the model
643 			result = result.clone();
644 			comparator.sort(this, result);
645 		}
646 		return result;
647 	}
648 
649 	/**
650 	 * Adds the given child element to this viewer as a child of the given
651 	 * parent element. If this viewer does not have a sorter, the element is
652 	 * added at the end of the parent's list of children; otherwise, the element
653 	 * is inserted at the appropriate position.
654 	 * <p>
655 	 * This method should be called (by the content provider) when a single
656 	 * element has been added to the model, in order to cause the viewer to
657 	 * accurately reflect the model. This method only affects the viewer, not
658 	 * the model. Note that there is another method for efficiently processing
659 	 * the simultaneous addition of multiple elements.
660 	 * </p>
661 	 *
662 	 * @param parentElementOrTreePath
663 	 *            the parent element or path
664 	 * @param childElement
665 	 *            the child element
666 	 */
add(Object parentElementOrTreePath, Object childElement)667 	public void add(Object parentElementOrTreePath, Object childElement) {
668 		add(parentElementOrTreePath, new Object[] { childElement });
669 	}
670 
671 	/**
672 	 * Adds the given SWT selection listener to the given SWT control.
673 	 *
674 	 * @param control
675 	 *            the SWT control
676 	 * @param listener
677 	 *            the SWT selection listener
678 	 * @deprecated
679 	 */
680 	@Deprecated
addSelectionListener(Control control, SelectionListener listener)681 	protected void addSelectionListener(Control control,
682 			SelectionListener listener) {
683 		// do nothing
684 	}
685 
686 	/**
687 	 * Adds a listener for expand and collapse events in this viewer. Has no
688 	 * effect if an identical listener is already registered.
689 	 *
690 	 * @param listener
691 	 *            a tree viewer listener
692 	 */
addTreeListener(ITreeViewerListener listener)693 	public void addTreeListener(ITreeViewerListener listener) {
694 		treeListeners.add(listener);
695 	}
696 
697 	/**
698 	 * Adds the given SWT tree listener to the given SWT control.
699 	 *
700 	 * @param control
701 	 *            the SWT control
702 	 * @param listener
703 	 *            the SWT tree listener
704 	 */
addTreeListener(Control control, TreeListener listener)705 	protected abstract void addTreeListener(Control control,
706 			TreeListener listener);
707 
708 	@Override
associate(Object element, Item item)709 	protected void associate(Object element, Item item) {
710 		Object data = item.getData();
711 		if (data != null && data != element && equals(data, element)) {
712 			// workaround for PR 1FV62BT
713 			// assumption: elements are equal but not identical
714 			// -> remove from map but don't touch children
715 			unmapElement(data, item);
716 			item.setData(element);
717 			mapElement(element, item);
718 		} else {
719 			// recursively disassociate all
720 			super.associate(element, item);
721 		}
722 	}
723 
724 	/**
725 	 * Collapses all nodes of the viewer's tree, starting with the root. This method
726 	 * is equivalent to <code>collapseToLevel(ALL_LEVELS)</code>.
727 	 */
collapseAll()728 	public void collapseAll() {
729 		Object root = getRoot();
730 		if (root != null) {
731 			collapseToLevel(root, ALL_LEVELS);
732 		}
733 	}
734 
735 	/**
736 	 * Collapses the subtree rooted at the given element or tree path to the given
737 	 * level.
738 	 * <p>
739 	 * Note that the default implementation of this method does turn redraw off via
740 	 * this operation via a call to <code>setRedraw</code>
741 	 * </p>
742 	 *
743 	 * @param elementOrTreePath the element or tree path
744 	 * @param level             non-negative level, or <code>ALL_LEVELS</code> to
745 	 *                          collapse all levels of the tree
746 	 */
collapseToLevel(Object elementOrTreePath, int level)747 	public void collapseToLevel(Object elementOrTreePath, int level) {
748 		Assert.isNotNull(elementOrTreePath);
749 		Control control = getControl();
750 		try {
751 			control.setRedraw(false);
752 			Widget w = internalGetWidgetToSelect(elementOrTreePath);
753 			if (w != null) {
754 				internalCollapseToLevel(w, level);
755 			}
756 		} finally {
757 			control.setRedraw(true);
758 		}
759 	}
760 
761 	/**
762 	 * Creates all children for the given widget.
763 	 * <p>
764 	 * The default implementation of this framework method assumes that
765 	 * <code>widget.getData()</code> returns the element corresponding to the
766 	 * node. Note: the node is not visually expanded! You may have to call
767 	 * <code>parent.setExpanded(true)</code>.
768 	 * </p>
769 	 *
770 	 * @param widget
771 	 *            the widget
772 	 */
createChildren(final Widget widget)773 	protected void createChildren(final Widget widget) {
774 		createChildren(widget, true);
775 	}
776 
777 	/**
778 	 * Creates all children for the given widget.
779 	 * <p>
780 	 * The default implementation of this framework method assumes that
781 	 * <code>widget.getData()</code> returns the element corresponding to the
782 	 * node. Note: the node is not visually expanded! You may have to call
783 	 * <code>parent.setExpanded(true)</code>.
784 	 * </p>
785 	 *
786 	 * @param widget
787 	 *            the widget
788 	 * @param materialize
789 	 * 			  true if children are expected to be fully materialized
790 	 */
createChildren(final Widget widget, boolean materialize)791 	void createChildren(final Widget widget, boolean materialize) {
792 		boolean oldBusy = isBusy();
793 		setBusy(true);
794 		try {
795 			final Item[] items = getChildren(widget);
796 			if (items != null && items.length > 0) {
797 				Object data = items[0].getData();
798 				if (data != null) {
799 					return; // children already there!
800 				}
801 			}
802 
803 			// fix for PR 1FW89L7:
804 			// don't complain and remove all "dummies" ...
805 			if (items != null) {
806 				for (Item item : items) {
807 					if (item.getData() != null) {
808 						disassociate(item);
809 						Assert.isTrue(item.getData() == null, "Second or later child is non -null");//$NON-NLS-1$
810 
811 					}
812 					item.dispose();
813 				}
814 			}
815 			Object d = widget.getData();
816 			if (d != null) {
817 				Object parentElement = d;
818 				Object[] children;
819 				if (isTreePathContentProvider() && widget instanceof Item) {
820 					TreePath path = getTreePathFromItem((Item) widget);
821 					children = getSortedChildren(path);
822 				} else {
823 					children = getSortedChildren(parentElement);
824 				}
825 				for (Object element : children) {
826 					createTreeItem(widget, element, -1);
827 				}
828 			}
829 		} finally {
830 			setBusy(oldBusy);
831 		}
832 	}
833 
834 	/**
835 	 * Creates a single item for the given parent and synchronizes it with the
836 	 * given element.
837 	 *
838 	 * @param parent
839 	 *            the parent widget
840 	 * @param element
841 	 *            the element
842 	 * @param index
843 	 *            if non-negative, indicates the position to insert the item
844 	 *            into its parent
845 	 */
createTreeItem(Widget parent, Object element, int index)846 	protected void createTreeItem(Widget parent, Object element, int index) {
847 		Item item = newItem(parent, SWT.NULL, index);
848 		updateItem(item, element);
849 		updatePlus(item, element);
850 	}
851 
852 	/**
853 	 * The <code>AbstractTreeViewer</code> implementation of this method also
854 	 * recurses over children of the corresponding element.
855 	 */
856 	@Override
disassociate(Item item)857 	protected void disassociate(Item item) {
858 		super.disassociate(item);
859 		// recursively unmapping the items is only required when
860 		// the hash map is used. In the other case disposing
861 		// an item will recursively dispose its children.
862 		if (usingElementMap()) {
863 			disassociateChildren(item);
864 		}
865 	}
866 
867 	/**
868 	 * Disassociates the children of the given SWT item from their corresponding
869 	 * elements.
870 	 *
871 	 * @param item
872 	 *            the widget
873 	 */
disassociateChildren(Item item)874 	private void disassociateChildren(Item item) {
875 		Item[] items = getChildren(item);
876 		for (Item child : items) {
877 			if (child.getData() != null) {
878 				disassociate(child);
879 			}
880 		}
881 	}
882 
883 	@Override
doFindInputItem(Object element)884 	protected Widget doFindInputItem(Object element) {
885 		// compare with root
886 		Object root = getRoot();
887 		if (root == null) {
888 			return null;
889 		}
890 
891 		if (equals(root, element)) {
892 			return getControl();
893 		}
894 		return null;
895 	}
896 
897 	@Override
doFindItem(Object element)898 	protected Widget doFindItem(Object element) {
899 		// compare with root
900 		Object root = getRoot();
901 		if (root == null) {
902 			return null;
903 		}
904 
905 		Item[] items = getChildren(getControl());
906 		if (items != null) {
907 			for (Item item : items) {
908 				Widget o = internalFindItem(item, element);
909 				if (o != null) {
910 					return o;
911 				}
912 			}
913 		}
914 		return null;
915 	}
916 
917 	/**
918 	 * Copies the attributes of the given element into the given SWT item.
919 	 *
920 	 * @param item
921 	 *            the SWT item
922 	 * @param element
923 	 *            the element
924 	 */
doUpdateItem(final Item item, Object element)925 	protected void doUpdateItem(final Item item, Object element) {
926 		if (item.isDisposed()) {
927 			unmapElement(element, item);
928 			return;
929 		}
930 
931 		int columnCount = doGetColumnCount();
932 		if (columnCount == 0)// If no columns are created then fake one
933 			columnCount = 1;
934 
935 		ViewerRow viewerRowFromItem = getViewerRowFromItem(item);
936 
937 		boolean isVirtual = (getControl().getStyle() & SWT.VIRTUAL) != 0;
938 
939 		// If the control is virtual, we cannot use the cached viewer row object. See bug 188663.
940 		if (isVirtual) {
941 			viewerRowFromItem = (ViewerRow) viewerRowFromItem.clone();
942 		}
943 
944 		for (int column = 0; column < columnCount; column++) {
945 			ViewerColumn columnViewer = getViewerColumn(column);
946 			ViewerCell cellToUpdate = updateCell(viewerRowFromItem, column,
947 					element);
948 
949 			// If the control is virtual, we cannot use the cached cell object. See bug 188663.
950 			if (isVirtual) {
951 				cellToUpdate = new ViewerCell(cellToUpdate.getViewerRow(), cellToUpdate.getColumnIndex(), element);
952 			}
953 
954 			columnViewer.refresh(cellToUpdate);
955 
956 			// clear cell (see bug 201280)
957 			updateCell(null, 0, null);
958 
959 			// As it is possible for user code to run the event
960 			// loop check here.
961 			if (item.isDisposed()) {
962 				unmapElement(element, item);
963 				return;
964 			}
965 
966 		}
967 	}
968 
969 	/**
970 	 * Returns <code>true</code> if the given list and array of items refer to
971 	 * the same model elements. Order is unimportant.
972 	 * <p>
973 	 * This method is not intended to be overridden by subclasses.
974 	 * </p>
975 	 *
976 	 * @param items
977 	 *            the list of items
978 	 * @param current
979 	 *            the array of items
980 	 * @return <code>true</code> if the refer to the same elements,
981 	 *         <code>false</code> otherwise
982 	 *
983 	 * @since 3.1 in TreeViewer, moved to AbstractTreeViewer in 3.3
984 	 */
isSameSelection(List<Item> items, Item[] current)985 	protected boolean isSameSelection(List<Item> items, Item[] current) {
986 		// If they are not the same size then they are not equivalent
987 		int n = items.size();
988 		if (n != current.length) {
989 			return false;
990 		}
991 
992 		CustomHashtable itemSet = newHashtable(n * 2 + 1);
993 		for (Item item : items) {
994 			Object element = item.getData();
995 			itemSet.put(element, element);
996 		}
997 
998 		// Go through the items of the current collection
999 		// If there is a mismatch return false
1000 		for (Item c : current) {
1001 			if (c.getData() == null || !itemSet.containsKey(c.getData())) {
1002 				return false;
1003 			}
1004 		}
1005 
1006 		return true;
1007 	}
1008 
1009 
1010 
1011 	@Override
doUpdateItem(Widget widget, Object element, boolean fullMap)1012 	protected void doUpdateItem(Widget widget, Object element, boolean fullMap) {
1013 		boolean oldBusy = isBusy();
1014 		setBusy(true);
1015 		try {
1016 			if (widget instanceof Item) {
1017 				Item item = (Item) widget;
1018 
1019 				// ensure that back pointer is correct
1020 				if (fullMap) {
1021 					associate(element, item);
1022 				} else {
1023 					Object data = item.getData();
1024 					if (data != null) {
1025 						unmapElement(data, item);
1026 					}
1027 					item.setData(element);
1028 					mapElement(element, item);
1029 				}
1030 
1031 				// update icon and label
1032 				SafeRunnable.run(new UpdateItemSafeRunnable(item, element));
1033 			}
1034 		} finally {
1035 			setBusy(oldBusy);
1036 		}
1037 	}
1038 
1039 	/**
1040 	 * Expands all nodes of the viewer's tree, starting with the root. This
1041 	 * method is equivalent to <code>expandToLevel(ALL_LEVELS)</code>.
1042 	 */
expandAll()1043 	public void expandAll() {
1044 		expandToLevel(ALL_LEVELS, true);
1045 	}
1046 
1047 	/**
1048 	 * Expands all nodes of the viewer's tree, starting with the root. This method
1049 	 * is equivalent to <code>expandToLevel(ALL_LEVELS)</code>.
1050 	 *
1051 	 * @param disableRedraw
1052 	 *            <code>true</code> when drawing operations should be disabled
1053 	 *            during expansion.
1054 	 * @since 3.14
1055 	 */
expandAll(boolean disableRedraw)1056 	public void expandAll(boolean disableRedraw) {
1057 		expandToLevel(ALL_LEVELS, disableRedraw);
1058 	}
1059 
1060 	/**
1061 	 * Expands the root of the viewer's tree to the given level.
1062 	 *
1063 	 * @param level
1064 	 *            non-negative level, or <code>ALL_LEVELS</code> to expand all
1065 	 *            levels of the tree
1066 	 */
expandToLevel(int level)1067 	public void expandToLevel(int level) {
1068 		expandToLevel(level, true);
1069 	}
1070 
1071 	/**
1072 	 * Expands the root of the viewer's tree to the given level.
1073 	 *
1074 	 * @param level         non-negative level, or <code>ALL_LEVELS</code> to expand
1075 	 *                      all levels of the tree
1076 	 * @param disableRedraw <code>true</code> when drawing operations should be
1077 	 *                      disabled during expansion. <code>true</code> when
1078 	 *                      drawing operations should be enabled during expansion.
1079 	 *                      Prefer using true as this results in a faster UI
1080 	 * @since 3.14
1081 	 */
expandToLevel(int level, boolean disableRedraw)1082 	public void expandToLevel(int level, boolean disableRedraw) {
1083 		BusyIndicator.showWhile(getControl().getDisplay(), () -> {
1084 			expandToLevel(getRoot(), level, disableRedraw);
1085 		});
1086 	}
1087 
1088 	/**
1089 	 * Expands all ancestors of the given element or tree path so that the given
1090 	 * element becomes visible in this viewer's tree control, and then expands
1091 	 * the subtree rooted at the given element to the given level.
1092 	 *
1093 	 * @param elementOrTreePath
1094 	 *            the element
1095 	 * @param level
1096 	 *            non-negative level, or <code>ALL_LEVELS</code> to expand all
1097 	 *            levels of the tree
1098 	 */
expandToLevel(Object elementOrTreePath, int level)1099 	public void expandToLevel(Object elementOrTreePath, int level) {
1100 		expandToLevel(elementOrTreePath, level, true);
1101 	}
1102 
1103 	/**
1104 	 * Expands all ancestors of the given element or tree path so that the given
1105 	 * element becomes visible in this viewer's tree control, and then expands the
1106 	 * subtree rooted at the given element to the given level.
1107 	 *
1108 	 * @param elementOrTreePath the element
1109 	 * @param level             non-negative level, or <code>ALL_LEVELS</code> to
1110 	 *                          expand all levels of the tree
1111 	 * @param disableRedraw     <code>true</code> when drawing operations should be
1112 	 *                          disabled during expansion. <code>false</code> when
1113 	 *                          drawing operations should be enabled during
1114 	 *                          expansion. Prefer true as this results in a faster
1115 	 *                          UI.
1116 	 * @since 3.14
1117 	 */
expandToLevel(Object elementOrTreePath, int level, boolean disableRedraw)1118 	public void expandToLevel(Object elementOrTreePath, int level, boolean disableRedraw) {
1119 		if (checkBusy())
1120 			return;
1121 		Control control = getControl();
1122 		try {
1123 			if (disableRedraw) {
1124 				control.setRedraw(false);
1125 			}
1126 			Widget w = internalExpand(elementOrTreePath, true);
1127 			if (w != null) {
1128 				internalExpandToLevel(w, level);
1129 			}
1130 		} finally {
1131 			if (disableRedraw) {
1132 				control.setRedraw(true);
1133 			}
1134 		}
1135 	}
1136 
1137 	/**
1138 	 * Fires a tree collapsed event. Only listeners registered at the time this
1139 	 * method is called are notified.
1140 	 *
1141 	 * @param event
1142 	 *            the tree expansion event
1143 	 * @see ITreeViewerListener#treeCollapsed
1144 	 */
fireTreeCollapsed(final TreeExpansionEvent event)1145 	protected void fireTreeCollapsed(final TreeExpansionEvent event) {
1146 		boolean oldBusy = isBusy();
1147 		setBusy(true);
1148 		try {
1149 			for (ITreeViewerListener l : treeListeners) {
1150 				SafeRunnable.run(new SafeRunnable() {
1151 					@Override
1152 					public void run() {
1153 						l.treeCollapsed(event);
1154 					}
1155 				});
1156 			}
1157 		} finally {
1158 			setBusy(oldBusy);
1159 		}
1160 	}
1161 
1162 	/**
1163 	 * Fires a tree expanded event. Only listeners registered at the time this
1164 	 * method is called are notified.
1165 	 *
1166 	 * @param event
1167 	 *            the tree expansion event
1168 	 * @see ITreeViewerListener#treeExpanded
1169 	 */
fireTreeExpanded(final TreeExpansionEvent event)1170 	protected void fireTreeExpanded(final TreeExpansionEvent event) {
1171 		boolean oldBusy = isBusy();
1172 		setBusy(true);
1173 		try {
1174 			for (ITreeViewerListener l : treeListeners) {
1175 				SafeRunnable.run(new SafeRunnable() {
1176 					@Override
1177 					public void run() {
1178 						l.treeExpanded(event);
1179 					}
1180 				});
1181 			}
1182 		} finally {
1183 			setBusy(oldBusy);
1184 		}
1185 	}
1186 
1187 	/**
1188 	 * Returns the auto-expand level.
1189 	 *
1190 	 * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of
1191 	 *         the tree are expanded automatically
1192 	 * @see #setAutoExpandLevel
1193 	 */
getAutoExpandLevel()1194 	public int getAutoExpandLevel() {
1195 		return expandToLevel;
1196 	}
1197 
1198 	/**
1199 	 * Returns the SWT child items for the given SWT widget.
1200 	 *
1201 	 * @param widget
1202 	 *            the widget
1203 	 * @return the child items
1204 	 */
getChildren(Widget widget)1205 	protected abstract Item[] getChildren(Widget widget);
1206 
1207 	/**
1208 	 * Get the child for the widget at index. Note that the default
1209 	 * implementation is not very efficient and should be overridden if this
1210 	 * class is implemented.
1211 	 *
1212 	 * @param widget
1213 	 *            the widget to check
1214 	 * @param index
1215 	 *            the index of the widget
1216 	 * @return Item or <code>null</code> if widget is not a type that can
1217 	 *         contain items.
1218 	 *
1219 	 * @throws ArrayIndexOutOfBoundsException
1220 	 *             if the index is not valid.
1221 	 * @since 3.1
1222 	 */
getChild(Widget widget, int index)1223 	protected Item getChild(Widget widget, int index) {
1224 		return getChildren(widget)[index];
1225 	}
1226 
1227 	/**
1228 	 * Returns whether the given SWT item is expanded or collapsed.
1229 	 *
1230 	 * @param item
1231 	 *            the item
1232 	 * @return <code>true</code> if the item is considered expanded and
1233 	 *         <code>false</code> if collapsed
1234 	 */
getExpanded(Item item)1235 	protected abstract boolean getExpanded(Item item);
1236 
1237 	/**
1238 	 * Returns a list of elements corresponding to expanded nodes in this
1239 	 * viewer's tree, including currently hidden ones that are marked as
1240 	 * expanded but are under a collapsed ancestor.
1241 	 * <p>
1242 	 * This method is typically used when preserving the interesting state of a
1243 	 * viewer; <code>setExpandedElements</code> is used during the restore.
1244 	 * </p>
1245 	 *
1246 	 * @return the array of expanded elements
1247 	 * @see #setExpandedElements
1248 	 */
getExpandedElements()1249 	public Object[] getExpandedElements() {
1250 		ArrayList<Item> items = new ArrayList<>();
1251 		internalCollectExpandedItems(items, getControl());
1252 		ArrayList<Object> result = new ArrayList<>(items.size());
1253 		for (Item item : items) {
1254 			Object data = item.getData();
1255 			if (data != null) {
1256 				result.add(data);
1257 			}
1258 		}
1259 		return result.toArray();
1260 	}
1261 
1262 	/**
1263 	 * Returns whether the node corresponding to the given element or tree path
1264 	 * is expanded or collapsed.
1265 	 *
1266 	 * @param elementOrTreePath
1267 	 *            the element
1268 	 * @return <code>true</code> if the node is expanded, and
1269 	 *         <code>false</code> if collapsed
1270 	 */
getExpandedState(Object elementOrTreePath)1271 	public boolean getExpandedState(Object elementOrTreePath) {
1272 		Assert.isNotNull(elementOrTreePath);
1273 		Widget item = internalGetWidgetToSelect(elementOrTreePath);
1274 		if (item instanceof Item) {
1275 			return getExpanded((Item) item);
1276 		}
1277 		return false;
1278 	}
1279 
1280 	/**
1281 	 * Returns the number of child items of the given SWT control.
1282 	 *
1283 	 * @param control
1284 	 *            the control
1285 	 * @return the number of children
1286 	 */
getItemCount(Control control)1287 	protected abstract int getItemCount(Control control);
1288 
1289 	/**
1290 	 * Returns the number of child items of the given SWT item.
1291 	 *
1292 	 * @param item
1293 	 *            the item
1294 	 * @return the number of children
1295 	 */
getItemCount(Item item)1296 	protected abstract int getItemCount(Item item);
1297 
1298 	/**
1299 	 * Returns the child items of the given SWT item.
1300 	 *
1301 	 * @param item
1302 	 *            the item
1303 	 * @return the child items
1304 	 */
getItems(Item item)1305 	protected abstract Item[] getItems(Item item);
1306 
1307 	/**
1308 	 * Returns the item after the given item in the tree, or <code>null</code>
1309 	 * if there is no next item.
1310 	 *
1311 	 * @param item
1312 	 *            the item
1313 	 * @param includeChildren
1314 	 *            <code>true</code> if the children are considered in
1315 	 *            determining which item is next, and <code>false</code> if
1316 	 *            subtrees are ignored
1317 	 * @return the next item, or <code>null</code> if none
1318 	 */
getNextItem(Item item, boolean includeChildren)1319 	protected Item getNextItem(Item item, boolean includeChildren) {
1320 		if (item == null) {
1321 			return null;
1322 		}
1323 		if (includeChildren && getExpanded(item)) {
1324 			Item[] children = getItems(item);
1325 			if (children != null && children.length > 0) {
1326 				return children[0];
1327 			}
1328 		}
1329 
1330 		// next item is either next sibling or next sibling of first
1331 		// parent that has a next sibling.
1332 		Item parent = getParentItem(item);
1333 		if (parent == null) {
1334 			return null;
1335 		}
1336 		Item[] siblings = getItems(parent);
1337 		if (siblings != null) {
1338 			if (siblings.length <= 1) {
1339 				return getNextItem(parent, false);
1340 			}
1341 
1342 			for (int i = 0; i < siblings.length; i++) {
1343 				if (siblings[i] == item && i < (siblings.length - 1)) {
1344 					return siblings[i + 1];
1345 				}
1346 			}
1347 		}
1348 		return getNextItem(parent, false);
1349 	}
1350 
1351 	/**
1352 	 * Returns the parent item of the given item in the tree, or
1353 	 * <code>null</code> if there is no parent item.
1354 	 *
1355 	 * @param item
1356 	 *            the item
1357 	 * @return the parent item, or <code>null</code> if none
1358 	 */
getParentItem(Item item)1359 	protected abstract Item getParentItem(Item item);
1360 
1361 	/**
1362 	 * Returns the item before the given item in the tree, or <code>null</code>
1363 	 * if there is no previous item.
1364 	 *
1365 	 * @param item
1366 	 *            the item
1367 	 * @return the previous item, or <code>null</code> if none
1368 	 */
getPreviousItem(Item item)1369 	protected Item getPreviousItem(Item item) {
1370 		// previous item is either right-most visible descendent of previous
1371 		// sibling or parent
1372 		Item parent = getParentItem(item);
1373 		if (parent == null) {
1374 			return null;
1375 		}
1376 		Item[] siblings = getItems(parent);
1377 		if (siblings.length == 0 || siblings[0] == item) {
1378 			return parent;
1379 		}
1380 		Item previous = siblings[0];
1381 		for (int i = 1; i < siblings.length; i++) {
1382 			if (siblings[i] == item) {
1383 				return rightMostVisibleDescendent(previous);
1384 			}
1385 			previous = siblings[i];
1386 		}
1387 		return null;
1388 	}
1389 
1390 	@Override
getRawChildren(Object parentElementOrTreePath)1391 	protected Object[] getRawChildren(Object parentElementOrTreePath) {
1392 		boolean oldBusy = isBusy();
1393 		setBusy(true);
1394 		try {
1395 			Object parent;
1396 			TreePath path;
1397 			if (parentElementOrTreePath instanceof TreePath) {
1398 				path = (TreePath) parentElementOrTreePath;
1399 				parent = path.getLastSegment();
1400 			} else {
1401 				parent = parentElementOrTreePath;
1402 				path = null;
1403 			}
1404 			if (parent != null) {
1405 				if (equals(parent, getRoot())) {
1406 					return super.getRawChildren(parent);
1407 				}
1408 				IContentProvider cp = getContentProvider();
1409 				if (cp instanceof ITreePathContentProvider) {
1410 					ITreePathContentProvider tpcp = (ITreePathContentProvider) cp;
1411 					if (path == null) {
1412 						// A path was not provided so try and find one
1413 						Widget w = findItem(parent);
1414 						if (w instanceof Item) {
1415 							Item item = (Item) w;
1416 							path = getTreePathFromItem(item);
1417 						}
1418 						if (path == null) {
1419 							path = new TreePath(new Object[] { parent });
1420 						}
1421 					}
1422 					Object[] result = tpcp.getChildren(path);
1423 					if (result != null) {
1424 						assertElementsNotNull(parent, result);
1425 						return result;
1426 					}
1427 				} else if (cp instanceof ITreeContentProvider) {
1428 					ITreeContentProvider tcp = (ITreeContentProvider) cp;
1429 					Object[] result = tcp.getChildren(parent);
1430 					if (result != null) {
1431 						assertElementsNotNull(parent, result);
1432 						return result;
1433 					}
1434 				}
1435 			}
1436 			return new Object[0];
1437 		} finally {
1438 			setBusy(oldBusy);
1439 		}
1440 	}
1441 
1442 	/**
1443 	 * Asserts that the given array of elements is itself non- <code>null</code>
1444 	 * and contains no <code>null</code> elements.
1445 	 *
1446 	 * @param parent
1447 	 *            the parent element
1448 	 * @param elements
1449 	 *            the array to check
1450 	 *
1451 	 * @see #assertElementsNotNull(Object[])
1452 	 */
assertElementsNotNull(Object parent, Object[] elements)1453 	private void assertElementsNotNull(Object parent, Object[] elements) {
1454 		Assert.isNotNull(elements);
1455 		for (Object element : elements) {
1456 			Assert.isNotNull(element);
1457 		}
1458 
1459 		if (InternalPolicy.DEBUG_LOG_EQUAL_VIEWER_ELEMENTS
1460 				&& elements.length > 1) {
1461 			CustomHashtable elementSet = newHashtable(elements.length * 2);
1462 			for (Object element : elements) {
1463 				Object old = elementSet.put(element, element);
1464 				if (old != null) {
1465 					String message = "Sibling elements in viewer must not be equal:\n  " //$NON-NLS-1$
1466 							+ old + ",\n  " + element + ",\n  parent: " + parent; //$NON-NLS-1$ //$NON-NLS-2$
1467 					Policy.getLog().log(
1468 							new Status(IStatus.WARNING, Policy.JFACE, message,
1469 									new RuntimeException()));
1470 					return;
1471 				}
1472 			}
1473 		}
1474 	}
1475 
1476 	/**
1477 	 * Returns all selected items for the given SWT control.
1478 	 *
1479 	 * @param control
1480 	 *            the control
1481 	 * @return the list of selected items
1482 	 */
getSelection(Control control)1483 	protected abstract Item[] getSelection(Control control);
1484 
1485 	@SuppressWarnings("rawtypes")
1486 	@Override
getSelectionFromWidget()1487 	protected List getSelectionFromWidget() {
1488 		Widget[] items = getSelection(getControl());
1489 		List<Object> list = new ArrayList<>(items.length);
1490 		for (Widget item : items) {
1491 			Object e = item.getData();
1492 			if (e != null) {
1493 				list.add(e);
1494 			}
1495 		}
1496 		return list;
1497 	}
1498 
1499 	/*
1500 	 * Overridden in AbstractTreeViewer to fix bug 108102 (code copied from
1501 	 * StructuredViewer to avoid introducing new API)
1502 	 */
1503 	@Override
handleDoubleSelect(SelectionEvent event)1504 	protected void handleDoubleSelect(SelectionEvent event) {
1505 		// handle case where an earlier selection listener disposed the control.
1506 		Control control = getControl();
1507 		if (control != null && !control.isDisposed()) {
1508 			// If the double-clicked element can be obtained from the event, use
1509 			// it
1510 			// otherwise get it from the control. Some controls like List do
1511 			// not have the notion of item.
1512 			// For details, see bug 90161 [Navigator] DefaultSelecting folders
1513 			// shouldn't always expand first one
1514 			ISelection selection;
1515 			if (event.item != null && event.item.getData() != null) {
1516 
1517 				// changes to fix bug 108102 follow
1518 				TreePath treePath = getTreePathFromItem((Item) event.item);
1519 				selection = new TreeSelection(treePath);
1520 				// end of changes
1521 
1522 			} else {
1523 				selection = getSelection();
1524 				updateSelection(selection);
1525 			}
1526 			fireDoubleClick(new DoubleClickEvent(this, selection));
1527 		}
1528 	}
1529 
1530 	/**
1531 	 * Handles a tree collapse event from the SWT widget.
1532 	 *
1533 	 * @param event
1534 	 *            the SWT tree event
1535 	 */
handleTreeCollapse(TreeEvent event)1536 	protected void handleTreeCollapse(TreeEvent event) {
1537 		if (event.item.getData() != null) {
1538 			fireTreeCollapsed(new TreeExpansionEvent(this, event.item.getData()));
1539 		}
1540 	}
1541 
1542 	/**
1543 	 * Handles a tree expand event from the SWT widget.
1544 	 *
1545 	 * @param event
1546 	 *            the SWT tree event
1547 	 */
handleTreeExpand(TreeEvent event)1548 	protected void handleTreeExpand(TreeEvent event) {
1549 		createChildren(event.item);
1550 		if (event.item.getData() != null) {
1551 			fireTreeExpanded(new TreeExpansionEvent(this, event.item.getData()));
1552 		}
1553 	}
1554 
1555 	@Override
hookControl(Control control)1556 	protected void hookControl(Control control) {
1557 		super.hookControl(control);
1558 		addTreeListener(control, new TreeListener() {
1559 			@Override
1560 			public void treeExpanded(TreeEvent event) {
1561 				handleTreeExpand(event);
1562 			}
1563 
1564 			@Override
1565 			public void treeCollapsed(TreeEvent event) {
1566 				handleTreeCollapse(event);
1567 			}
1568 		});
1569 	}
1570 
1571 	@Override
inputChanged(Object input, Object oldInput)1572 	protected void inputChanged(Object input, Object oldInput) {
1573 		preservingSelection(() -> {
1574 			Control tree = getControl();
1575 			tree.setRedraw(false);
1576 			try {
1577 				removeAll(tree);
1578 				tree.setData(getRoot());
1579 				internalInitializeTree(tree);
1580 			} finally {
1581 				tree.setRedraw(true);
1582 			}
1583 		});
1584 	}
1585 
1586 	/**
1587 	 * Initializes the tree with root items, expanding to the appropriate
1588 	 * level if necessary.
1589 	 *
1590 	 * @param tree the tree control
1591 	 * @since 3.3
1592 	 */
internalInitializeTree(Control tree)1593 	protected void internalInitializeTree(Control tree) {
1594 		createChildren(tree);
1595 		internalExpandToLevel(tree, expandToLevel);
1596 	}
1597 
1598 	/**
1599 	 * Recursively collapses the subtree rooted at the given widget to the given
1600 	 * level.
1601 	 *
1602 	 * @param widget the widget
1603 	 * @param level  non-negative level, or <code>ALL_LEVELS</code> to collapse all
1604 	 *               levels of the tree
1605 	 */
internalCollapseToLevel(Widget widget, int level)1606 	protected void internalCollapseToLevel(Widget widget, int level) {
1607 		if (level == ALL_LEVELS || level > 0) {
1608 
1609 			if (widget instanceof Item) {
1610 				Item item = (Item) widget;
1611 				setExpanded(item, false);
1612 				Object element = item.getData();
1613 				if (element != null && level == ALL_LEVELS) {
1614 					if (optionallyPruneChildren(item, element)) {
1615 						return;
1616 					}
1617 				}
1618 			}
1619 
1620 			if (level == ALL_LEVELS || level > 1) {
1621 				Item[] children = getChildren(widget);
1622 				if (children != null) {
1623 					int nextLevel = (level == ALL_LEVELS ? ALL_LEVELS
1624 							: level - 1);
1625 					for (Item element : children) {
1626 						internalCollapseToLevel(element, nextLevel);
1627 					}
1628 				}
1629 			}
1630 		}
1631 	}
1632 
1633 	/**
1634 	 * Recursively collects all expanded items from the given widget.
1635 	 *
1636 	 * @param result
1637 	 *            a list (element type: <code>Item</code>) into which to
1638 	 *            collect the elements
1639 	 * @param widget
1640 	 *            the widget
1641 	 */
internalCollectExpandedItems(List<Item> result, Widget widget)1642 	private void internalCollectExpandedItems(List<Item> result, Widget widget) {
1643 		Item[] items = getChildren(widget);
1644 		for (Item item : items) {
1645 			// Disregard dummy nodes (see bug 287765)
1646 			if (item.getData() != null) {
1647 				if (getExpanded(item)) {
1648 					result.add(item);
1649 				}
1650 				internalCollectExpandedItems(result, item);
1651 			}
1652 		}
1653 	}
1654 
1655 	/**
1656 	 * Tries to create a path of tree items for the given element or tree path.
1657 	 * This method recursively walks up towards the root of the tree and in the
1658 	 * case of an element (rather than a tree path) assumes that
1659 	 * <code>getParent</code> returns the correct parent of an element.
1660 	 *
1661 	 * @param elementOrPath
1662 	 *            the element
1663 	 * @param expand
1664 	 *            <code>true</code> if all nodes on the path should be
1665 	 *            expanded, and <code>false</code> otherwise
1666 	 * @return Widget
1667 	 */
internalExpand(Object elementOrPath, boolean expand)1668 	protected Widget internalExpand(Object elementOrPath, boolean expand) {
1669 
1670 		if (elementOrPath == null) {
1671 			return null;
1672 		}
1673 
1674 		Widget w = internalGetWidgetToSelect(elementOrPath);
1675 		if (w == null) {
1676 			if (equals(elementOrPath, getRoot())) { // stop at root
1677 				return null;
1678 			}
1679 			// my parent has to create me
1680 			Object parent = getParentElement(elementOrPath);
1681 			if (parent != null) {
1682 				Widget pw = internalExpand(parent, false);
1683 				if (pw != null) {
1684 					// let my parent create me
1685 					createChildren(pw);
1686 					Object element = internalToElement(elementOrPath);
1687 					w = internalFindChild(pw, element);
1688 				}
1689 			}
1690 		}
1691 		if (expand && w instanceof Item) {
1692 			// expand parent items top-down
1693 			Item item = getParentItem((Item) w);
1694 			LinkedList<Item> toExpandList = new LinkedList<>();
1695 			while (item != null) {
1696 				if (!getExpanded(item)) {
1697 					toExpandList.addFirst(item);
1698 				}
1699 				item = getParentItem(item);
1700 			}
1701 			for (Item toExpand : toExpandList) {
1702 				setExpanded(toExpand, true);
1703 			}
1704 		}
1705 		return w;
1706 	}
1707 
1708 	/**
1709 	 * If the argument is a tree path, returns its last segment, otherwise
1710 	 * return the argument
1711 	 *
1712 	 * @param elementOrPath
1713 	 *            an element or a tree path
1714 	 * @return the element, or the last segment of the tree path
1715 	 */
internalToElement(Object elementOrPath)1716 	private Object internalToElement(Object elementOrPath) {
1717 		if (elementOrPath instanceof TreePath) {
1718 			return ((TreePath) elementOrPath).getLastSegment();
1719 		}
1720 		return elementOrPath;
1721 	}
1722 
1723 	/**
1724 	 * This method takes a tree path or an element. If the argument is not a tree
1725 	 * path, returns the parent of the given element or <code>null</code> if the
1726 	 * parent is not known. If the argument is a tree path with more than one
1727 	 * segment, returns its parent tree path, otherwise returns <code>null</code>.
1728 	 *
1729 	 * @param elementOrTreePath the element or path to find parent for
1730 	 * @return the parent element, or parent path, or <code>null</code>
1731 	 *
1732 	 * @since 3.2
1733 	 */
getParentElement(Object elementOrTreePath)1734 	protected Object getParentElement(Object elementOrTreePath) {
1735 		if (elementOrTreePath instanceof TreePath) {
1736 			TreePath treePath = (TreePath) elementOrTreePath;
1737 			return (treePath).getParentPath();
1738 		}
1739 		IContentProvider cp = getContentProvider();
1740 		if (cp instanceof ITreePathContentProvider) {
1741 			ITreePathContentProvider tpcp = (ITreePathContentProvider) cp;
1742 			TreePath[] paths = tpcp.getParents(elementOrTreePath);
1743 			if (paths.length > 0) {
1744 				if (paths[0].getSegmentCount() == 0) {
1745 					return getRoot();
1746 				}
1747 				return paths[0].getLastSegment();
1748 			}
1749 		}
1750 		if (cp instanceof ITreeContentProvider) {
1751 			ITreeContentProvider tcp = (ITreeContentProvider) cp;
1752 			return tcp.getParent(elementOrTreePath);
1753 		}
1754 		return null;
1755 	}
1756 
1757 	/**
1758 	 * Returns the widget to be selected for the given element or tree path.
1759 	 *
1760 	 * @param elementOrTreePath
1761 	 *            the element or tree path to select
1762 	 * @return the widget to be selected, or <code>null</code> if not found
1763 	 *
1764 	 * @since 3.1
1765 	 */
internalGetWidgetToSelect(Object elementOrTreePath)1766 	protected Widget internalGetWidgetToSelect(Object elementOrTreePath) {
1767 		if (elementOrTreePath instanceof TreePath) {
1768 			TreePath treePath = (TreePath) elementOrTreePath;
1769 			if (treePath.getSegmentCount() == 0) {
1770 				return getControl();
1771 			}
1772 			Widget[] candidates = findItems(treePath.getLastSegment());
1773 			for (Widget candidate : candidates) {
1774 				if (!(candidate instanceof Item)) {
1775 					continue;
1776 				}
1777 				if (treePath.equals(getTreePathFromItem((Item) candidate), getComparer())) {
1778 					return candidate;
1779 				}
1780 			}
1781 			return null;
1782 		}
1783 		return findItem(elementOrTreePath);
1784 	}
1785 
1786 	/**
1787 	 * Recursively expands the subtree rooted at the given widget to the given
1788 	 * level.
1789 	 * <p>
1790 	 * Note that the default implementation of this method does not call
1791 	 * <code>setRedraw</code>.
1792 	 * </p>
1793 	 *
1794 	 * @param widget the widget
1795 	 * @param level  non-negative level, or <code>ALL_LEVELS</code> to collapse all
1796 	 *               levels of the tree
1797 	 */
internalExpandToLevel(Widget widget, int level)1798 	protected void internalExpandToLevel(Widget widget, int level) {
1799 		if (level == ALL_LEVELS || level > 0) {
1800 			if (widget instanceof Item && widget.getData() != null
1801 					&& !isExpandable((Item) widget, null, widget.getData())) {
1802 				return;
1803 			}
1804 			createChildren(widget, false);
1805 			if (widget instanceof Item) {
1806 				setExpanded((Item) widget, true);
1807 			}
1808 			if (level == ALL_LEVELS || level > 1) {
1809 				Item[] children = getChildren(widget);
1810 				if (children != null) {
1811 					int newLevel = (level == ALL_LEVELS ? ALL_LEVELS
1812 							: level - 1);
1813 					for (Item element : children) {
1814 						internalExpandToLevel(element, newLevel);
1815 					}
1816 				}
1817 			}
1818 		}
1819 	}
1820 
1821 	/**
1822 	 * Non-recursively tries to find the given element as a child of the given
1823 	 * parent (item or tree).
1824 	 *
1825 	 * @param parent
1826 	 *            the parent item
1827 	 * @param element
1828 	 *            the element
1829 	 * @return Widget
1830 	 */
internalFindChild(Widget parent, Object element)1831 	private Widget internalFindChild(Widget parent, Object element) {
1832 		Item[] items = getChildren(parent);
1833 		for (Item item : items) {
1834 			Object data = item.getData();
1835 			if (data != null && equals(data, element)) {
1836 				return item;
1837 			}
1838 		}
1839 		return null;
1840 	}
1841 
1842 	/**
1843 	 * Recursively tries to find the given element.
1844 	 *
1845 	 * @param parent
1846 	 *            the parent item
1847 	 * @param element
1848 	 *            the element
1849 	 * @return Widget
1850 	 */
internalFindItem(Item parent, Object element)1851 	private Widget internalFindItem(Item parent, Object element) {
1852 
1853 		// compare with node
1854 		Object data = parent.getData();
1855 		if (data != null) {
1856 			if (equals(data, element)) {
1857 				return parent;
1858 			}
1859 		}
1860 		// recurse over children
1861 		Item[] items = getChildren(parent);
1862 		for (Item item : items) {
1863 			Widget o = internalFindItem(item, element);
1864 			if (o != null) {
1865 				return o;
1866 			}
1867 		}
1868 		return null;
1869 	}
1870 
1871 	@Override
internalRefresh(Object element)1872 	protected void internalRefresh(Object element) {
1873 		internalRefresh(element, true);
1874 	}
1875 
1876 	@Override
internalRefresh(Object element, boolean updateLabels)1877 	protected void internalRefresh(Object element, boolean updateLabels) {
1878 		// If element is null, do a full refresh.
1879 		if (element == null) {
1880 			internalRefresh(getControl(), getRoot(), true, updateLabels);
1881 			return;
1882 		}
1883 		Widget[] items = findItems(element);
1884 		if (items.length != 0) {
1885 			for (Widget item : items) {
1886 				// pick up structure changes too
1887 				internalRefresh(item, element, true, updateLabels);
1888 			}
1889 		}
1890 	}
1891 
1892 	/**
1893 	 * Refreshes the tree starting at the given widget.
1894 	 * <p>
1895 	 * EXPERIMENTAL. Not to be used except by JDT. This method was added to
1896 	 * support JDT's explorations into grouping by working sets. This method
1897 	 * cannot be removed without breaking binary backwards compatibility, but
1898 	 * should not be called by clients.
1899 	 * </p>
1900 	 *
1901 	 * @param widget
1902 	 *            the widget
1903 	 * @param element
1904 	 *            the element
1905 	 * @param doStruct
1906 	 *            <code>true</code> if structural changes are to be picked up,
1907 	 *            and <code>false</code> if only label provider changes are of
1908 	 *            interest
1909 	 * @param updateLabels
1910 	 *            <code>true</code> to update labels for existing elements,
1911 	 *            <code>false</code> to only update labels as needed, assuming
1912 	 *            that labels for existing elements are unchanged.
1913 	 * @since 3.1
1914 	 */
internalRefresh(Widget widget, Object element, boolean doStruct, boolean updateLabels)1915 	protected void internalRefresh(Widget widget, Object element,
1916 			boolean doStruct, boolean updateLabels) {
1917 
1918 		if (widget instanceof Item) {
1919 			if (doStruct) {
1920 				updatePlus((Item) widget, element);
1921 			}
1922 			if (updateLabels || !equals(element, widget.getData())) {
1923 				doUpdateItem(widget, element, true);
1924 			} else {
1925 				associate(element, (Item) widget);
1926 			}
1927 		}
1928 
1929 		if (doStruct) {
1930 			internalRefreshStruct(widget, element, updateLabels);
1931 		} else {
1932 			Item[] children = getChildren(widget);
1933 			if (children != null) {
1934 				for (Item item : children) {
1935 					Object data = item.getData();
1936 					if (data != null) {
1937 						internalRefresh(item, data, doStruct, updateLabels);
1938 					}
1939 				}
1940 			}
1941 		}
1942 	}
1943 
1944 	/**
1945 	 * Update the structure and recurse. Items are updated in updateChildren, as
1946 	 * needed.
1947 	 *
1948 	 * @param widget
1949 	 * @param element
1950 	 * @param updateLabels
1951 	 */
internalRefreshStruct(Widget widget, Object element, boolean updateLabels)1952 	/* package */void internalRefreshStruct(Widget widget, Object element,
1953 			boolean updateLabels) {
1954 		updateChildren(widget, element, null, updateLabels);
1955 		Item[] children = getChildren(widget);
1956 		if (children != null) {
1957 			for (Item item : children) {
1958 				Object data = item.getData();
1959 				if (data != null) {
1960 					internalRefreshStruct(item, data, updateLabels);
1961 				}
1962 			}
1963 		}
1964 	}
1965 
1966 	/**
1967 	 * Removes the given elements from this viewer.
1968 	 * <p>
1969 	 * EXPERIMENTAL. Not to be used except by JDT. This method was added to
1970 	 * support JDT's explorations into grouping by working sets. This method
1971 	 * cannot be removed without breaking binary backwards compatibility, but
1972 	 * should not be called by clients.
1973 	 * </p>
1974 	 *
1975 	 * @param elementsOrPaths
1976 	 *            the elements or element paths to remove
1977 	 * @since 3.1
1978 	 */
internalRemove(Object[] elementsOrPaths)1979 	protected void internalRemove(Object[] elementsOrPaths) {
1980 		Object input = getInput();
1981 		for (Object element : elementsOrPaths) {
1982 			if (equals(element, input)) {
1983 				setInput(null);
1984 				return;
1985 			}
1986 			Widget[] childItems = internalFindItems(element);
1987 			if (childItems.length > 0) {
1988 				for (Widget childItem : childItems) {
1989 					if (childItem instanceof Item) {
1990 						disassociate((Item) childItem);
1991 						childItem.dispose();
1992 					}
1993 				}
1994 			} else {
1995 				// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=210747
1996 				Object parent = getParentElement(element);
1997 				if (parent != null
1998 						&& !equals(parent, getRoot())
1999 						&& !(parent instanceof TreePath && ((TreePath) parent)
2000 								.getSegmentCount() == 0)) {
2001 					Widget[] parentItems = internalFindItems(parent);
2002 					for (Widget parentItem : parentItems) {
2003 						if (parentItem instanceof Item) {
2004 							updatePlus((Item) parentItem, parent);
2005 						}
2006 					}
2007 				}
2008 			}
2009 		}
2010 	}
2011 
2012 	/**
2013 	 * Removes the given elements from this viewer, whenever those elements
2014 	 * appear as children of the given parent.
2015 	 *
2016 	 * @param parent the parent element
2017 	 * @param elements
2018 	 *            the elements to remove
2019 	 * @since 3.1
2020 	 */
internalRemove(Object parent, Object[] elements)2021 	protected void internalRemove(Object parent, Object[] elements) {
2022 
2023 		CustomHashtable toRemove = new CustomHashtable(getComparer());
2024 		for (Object element : elements) {
2025 			toRemove.put(element, element);
2026 		}
2027 
2028 		// Find each place the parent appears in the tree
2029 		Widget[] parentItemArray = findItems(parent);
2030 		for (Widget parentItem : parentItemArray) {
2031 			// May happen if parent element is a descendent of of a previously
2032 			// removed element
2033 			if (parentItem.isDisposed())
2034 				continue;
2035 
2036 			// Iterate over the child items and remove each one
2037 			Item[] children = getChildren(parentItem);
2038 
2039 			if (children.length == 1 && children[0].getData() == null &&
2040 					parentItem instanceof Item) { // dummy node
2041 				// Remove plus if parent element has no children
2042 				updatePlus((Item) parentItem, parent);
2043 			} else {
2044 				for (Item child : children) {
2045 					Object data = child.getData();
2046 					if (data != null && toRemove.containsKey(data)) {
2047 						disassociate(child);
2048 						child.dispose();
2049 					}
2050 				}
2051 			}
2052 		}
2053 	}
2054 
2055 	/**
2056 	 * Sets the expanded state of all items to correspond to the given set of
2057 	 * expanded elements.
2058 	 *
2059 	 * @param expandedElements
2060 	 *            the set (element type: <code>Object</code>) of elements
2061 	 *            which are expanded
2062 	 * @param widget
2063 	 *            the widget
2064 	 */
internalSetExpanded(CustomHashtable expandedElements, Widget widget)2065 	private void internalSetExpanded(CustomHashtable expandedElements,
2066 			Widget widget) {
2067 		Item[] items = getChildren(widget);
2068 		for (Item item : items) {
2069 			Object data = item.getData();
2070 			if (data != null) {
2071 				// remove the element to avoid an infinite loop
2072 				// if the same element appears on a child item
2073 				boolean expanded = expandedElements.remove(data) != null;
2074 				if (expanded != getExpanded(item)) {
2075 					if (expanded) {
2076 						createChildren(item);
2077 					}
2078 					setExpanded(item, expanded);
2079 				}
2080 			}
2081 			if (expandedElements.size() > 0) {
2082 				internalSetExpanded(expandedElements, item);
2083 			}
2084 		}
2085 	}
2086 
2087 	/**
2088 	 * Sets the expanded state of all items to correspond to the given set of
2089 	 * expanded tree paths.
2090 	 *
2091 	 * @param expandedTreePaths
2092 	 *            the set (element type: <code>TreePath</code>) of elements
2093 	 *            which are expanded
2094 	 * @param widget
2095 	 *            the widget
2096 	 */
internalSetExpandedTreePaths( CustomHashtable expandedTreePaths, Widget widget, TreePath currentPath)2097 	private void internalSetExpandedTreePaths(
2098 			CustomHashtable expandedTreePaths, Widget widget,
2099 			TreePath currentPath) {
2100 		Item[] items = getChildren(widget);
2101 		for (Item item : items) {
2102 			Object data = item.getData();
2103 			TreePath childPath = data == null ? null : currentPath
2104 					.createChildPath(data);
2105 			if (data != null && childPath != null) {
2106 				// remove the element to avoid an infinite loop
2107 				// if the same element appears on a child item
2108 				boolean expanded = expandedTreePaths.remove(childPath) != null;
2109 				if (expanded != getExpanded(item)) {
2110 					if (expanded) {
2111 						createChildren(item);
2112 					}
2113 					setExpanded(item, expanded);
2114 				}
2115 			}
2116 			internalSetExpandedTreePaths(expandedTreePaths, item, childPath);
2117 		}
2118 	}
2119 
2120 	/**
2121 	 * Return whether the tree node representing the given element or path can
2122 	 * be expanded. Clients should query expandability by path if the viewer's
2123 	 * content provider is an {@link ITreePathContentProvider}.
2124 	 * <p>
2125 	 * The default implementation of this framework method calls
2126 	 * <code>hasChildren</code> on this viewer's content provider. It may be
2127 	 * overridden if necessary.
2128 	 * </p>
2129 	 * @see #setExpandPreCheckFilters(boolean)
2130 	 * @param elementOrTreePath
2131 	 *            the element or path
2132 	 * @return <code>true</code> if the tree node representing the given
2133 	 *         element can be expanded, or <code>false</code> if not
2134 	 */
isExpandable(Object elementOrTreePath)2135 	public boolean isExpandable(Object elementOrTreePath) {
2136 		Object element;
2137 		TreePath path;
2138 		if (elementOrTreePath instanceof TreePath) {
2139 			path = (TreePath) elementOrTreePath;
2140 			element = path.getLastSegment();
2141 		} else {
2142 			element = elementOrTreePath;
2143 			path = null;
2144 		}
2145 		IContentProvider cp = getContentProvider();
2146 		if (cp instanceof ITreePathContentProvider) {
2147 			ITreePathContentProvider tpcp = (ITreePathContentProvider) cp;
2148 			if (path == null) {
2149 				// A path was not provided so try and find one
2150 				Widget w = findItem(element);
2151 				if (w instanceof Item) {
2152 					Item item = (Item) w;
2153 					path = getTreePathFromItem(item);
2154 				}
2155 				if (path == null) {
2156 					path = new TreePath(new Object[] { element });
2157 				}
2158 			}
2159 			boolean hasChildren = tpcp.hasChildren(path);
2160 			if (hasChildren && isExpandableCheckFilters && hasFilters()) {
2161 				return getFilteredChildren(path).length > 0;
2162 			}
2163 			return hasChildren;
2164 		}
2165 		if (cp instanceof ITreeContentProvider) {
2166 			ITreeContentProvider tcp = (ITreeContentProvider) cp;
2167 			boolean hasChildren = tcp.hasChildren(element);
2168 			if (hasChildren && isExpandableCheckFilters && hasFilters()) {
2169 				return getFilteredChildren(element).length > 0;
2170 			}
2171 			return hasChildren;
2172 		}
2173 		return false;
2174 	}
2175 
2176 	/**
2177 	 * Return whether the given element is expandable.
2178 	 *
2179 	 * @param item
2180 	 *            the tree item for the element
2181 	 * @param parentPath
2182 	 *            the parent path if it is known or <code>null</code> if it
2183 	 *            needs to be determines
2184 	 * @param element
2185 	 *            the element
2186 	 * @return whether the given element is expandable
2187 	 */
isExpandable(Item item, TreePath parentPath, Object element)2188 	private boolean isExpandable(Item item, TreePath parentPath, Object element) {
2189 		Object elementOrTreePath = element;
2190 		if (isTreePathContentProvider()) {
2191 			if (parentPath != null) {
2192 				elementOrTreePath = parentPath.createChildPath(element);
2193 			} else {
2194 				elementOrTreePath = getTreePathFromItem(item);
2195 			}
2196 		}
2197 		return isExpandable(elementOrTreePath);
2198 	}
2199 
2200 	@Override
labelProviderChanged()2201 	protected void labelProviderChanged() {
2202 		// we have to walk the (visible) tree and update every item
2203 		Control tree = getControl();
2204 		tree.setRedraw(false);
2205 		// don't pick up structure changes, but do force label updates
2206 		internalRefresh(tree, getRoot(), false, true);
2207 		tree.setRedraw(true);
2208 	}
2209 
2210 	/**
2211 	 * Creates a new item.
2212 	 *
2213 	 * @param parent
2214 	 *            the parent widget
2215 	 * @param style
2216 	 *            SWT style bits
2217 	 * @param index
2218 	 *            if non-negative, indicates the position to insert the item
2219 	 *            into its parent
2220 	 * @return the newly-created item
2221 	 */
newItem(Widget parent, int style, int index)2222 	protected abstract Item newItem(Widget parent, int style, int index);
2223 
2224 	/**
2225 	 * Removes the given elements from this viewer. The selection is updated if
2226 	 * required.
2227 	 * <p>
2228 	 * This method should be called (by the content provider) when elements have
2229 	 * been removed from the model, in order to cause the viewer to accurately
2230 	 * reflect the model. This method only affects the viewer, not the model.
2231 	 * </p>
2232 	 *
2233 	 * @param elementsOrTreePaths the elements to remove
2234 	 */
remove(final Object... elementsOrTreePaths)2235 	public void remove(final Object... elementsOrTreePaths) {
2236 		assertElementsNotNull(elementsOrTreePaths);
2237 		if (elementsOrTreePaths.length == 0) {
2238 			return;
2239 		}
2240 		if (checkBusy())
2241 			return;
2242 		preservingSelection(() -> internalRemove(elementsOrTreePaths));
2243 	}
2244 
2245 	/**
2246 	 * Removes the given elements from this viewer whenever they appear as
2247 	 * children of the given parent element. If the given elements also appear
2248 	 * as children of some other parent, the other parent will remain unchanged.
2249 	 * The selection is updated if required.
2250 	 * <p>
2251 	 * This method should be called (by the content provider) when elements have
2252 	 * been removed from the model, in order to cause the viewer to accurately
2253 	 * reflect the model. This method only affects the viewer, not the model.
2254 	 * </p>
2255 	 *
2256 	 * @param parent
2257 	 *            the parent of the elements to remove
2258 	 * @param elements
2259 	 *            the elements to remove
2260 	 *
2261 	 * @since 3.2
2262 	 */
remove(final Object parent, final Object... elements)2263 	public void remove(final Object parent, final Object... elements) {
2264 		assertElementsNotNull(elements);
2265 		if (elements.length == 0) {
2266 			return;
2267 		}
2268 		if (checkBusy())
2269 			return;
2270 		preservingSelection(() -> internalRemove(parent, elements));
2271 	}
2272 
2273 	/**
2274 	 * Removes the given element from the viewer. The selection is updated if
2275 	 * necessary.
2276 	 * <p>
2277 	 * This method should be called (by the content provider) when a single
2278 	 * element has been removed from the model, in order to cause the viewer to
2279 	 * accurately reflect the model. This method only affects the viewer, not
2280 	 * the model. Note that there is another method for efficiently processing
2281 	 * the simultaneous removal of multiple elements.
2282 	 * </p>
2283 	 *
2284 	 * @param elementsOrTreePaths
2285 	 *            the element
2286 	 */
remove(Object elementsOrTreePaths)2287 	public void remove(Object elementsOrTreePaths) {
2288 		remove(new Object[] { elementsOrTreePaths });
2289 	}
2290 
2291 	/**
2292 	 * Removes all items from the given control.
2293 	 *
2294 	 * @param control
2295 	 *            the control
2296 	 */
removeAll(Control control)2297 	protected abstract void removeAll(Control control);
2298 
2299 	/**
2300 	 * Removes a listener for expand and collapse events in this viewer. Has no
2301 	 * effect if an identical listener is not registered.
2302 	 *
2303 	 * @param listener
2304 	 *            a tree viewer listener
2305 	 */
removeTreeListener(ITreeViewerListener listener)2306 	public void removeTreeListener(ITreeViewerListener listener) {
2307 		treeListeners.remove(listener);
2308 	}
2309 
2310 	/**
2311 	 * This implementation of reveal() reveals the given element or tree path.
2312 	 */
2313 	@Override
reveal(Object elementOrTreePath)2314 	public void reveal(Object elementOrTreePath) {
2315 		Assert.isNotNull(elementOrTreePath);
2316 		Widget w = internalExpand(elementOrTreePath, true);
2317 		if (w instanceof Item) {
2318 			showItem((Item) w);
2319 		}
2320 	}
2321 
2322 	/**
2323 	 * Returns the rightmost visible descendent of the given item. Returns the
2324 	 * item itself if it has no children.
2325 	 *
2326 	 * @param item
2327 	 *            the item to compute the descendent of
2328 	 * @return the rightmost visible descendent or the item itself if it has no
2329 	 *         children
2330 	 */
rightMostVisibleDescendent(Item item)2331 	private Item rightMostVisibleDescendent(Item item) {
2332 		Item[] children = getItems(item);
2333 		if (getExpanded(item) && children != null && children.length > 0) {
2334 			return rightMostVisibleDescendent(children[children.length - 1]);
2335 		}
2336 		return item;
2337 	}
2338 
2339 	@Override
scrollDown(int x, int y)2340 	public Item scrollDown(int x, int y) {
2341 		Item current = getItem(x, y);
2342 		if (current != null) {
2343 			Item next = getNextItem(current, true);
2344 			showItem(next == null ? current : next);
2345 			return next;
2346 		}
2347 		return null;
2348 	}
2349 
2350 	@Override
scrollUp(int x, int y)2351 	public Item scrollUp(int x, int y) {
2352 		Item current = getItem(x, y);
2353 		if (current != null) {
2354 			Item previous = getPreviousItem(current);
2355 			showItem(previous == null ? current : previous);
2356 			return previous;
2357 		}
2358 		return null;
2359 	}
2360 
2361 	/**
2362 	 * Sets the auto-expand level to be used when the input of the viewer is set
2363 	 * using {@link #setInput(Object)}. The value 0 means that there is no
2364 	 * auto-expand; 1 means that the invisible root element is expanded (since
2365 	 * most concrete subclasses do not show the root element, there is usually
2366 	 * no practical difference between using the values 0 and 1); 2 means that
2367 	 * top-level elements are expanded, but not their children; 3 means that
2368 	 * top-level elements are expanded, and their children, but not
2369 	 * grandchildren; and so on.
2370 	 * <p>
2371 	 * The value <code>ALL_LEVELS</code> means that all subtrees should be
2372 	 * expanded.
2373 	 * </p>
2374 	 * <p>
2375 	 * Note that in previous releases, the Javadoc for this method had an off-by
2376 	 * one error. See bug 177669 for details.
2377 	 * </p>
2378 	 *
2379 	 * @param level
2380 	 *            non-negative level, or <code>ALL_LEVELS</code> to expand all
2381 	 *            levels of the tree
2382 	 */
setAutoExpandLevel(int level)2383 	public void setAutoExpandLevel(int level) {
2384 		expandToLevel = level;
2385 	}
2386 
2387 	/**
2388 	 * Sets the content provider used by this <code>AbstractTreeViewer</code>.
2389 	 * <p>
2390 	 * Content providers for abstract tree viewers must implement either
2391 	 * {@link ITreeContentProvider} or {@link ITreePathContentProvider}.
2392 	 */
2393 	@Override
setContentProvider(IContentProvider provider)2394 	public void setContentProvider(IContentProvider provider) {
2395 		// the actual check is in assertContentProviderType
2396 		super.setContentProvider(provider);
2397 	}
2398 
2399 	@Override
assertContentProviderType(IContentProvider provider)2400 	protected void assertContentProviderType(IContentProvider provider) {
2401 		Assert.isTrue(provider instanceof ITreeContentProvider
2402 				|| provider instanceof ITreePathContentProvider,
2403 				"Instances of AbstractTreeViewer must have a content provider " //$NON-NLS-1$
2404 						+ "of type ITreeContentProvider or ITreePathContentProvider"); //$NON-NLS-1$
2405 	}
2406 
2407 	/**
2408 	 * Sets the expand state of the given item.
2409 	 *
2410 	 * @param item
2411 	 *            the item
2412 	 * @param expand
2413 	 *            the expand state of the item
2414 	 */
setExpanded(Item item, boolean expand)2415 	protected abstract void setExpanded(Item item, boolean expand);
2416 
2417 	/**
2418 	 * Sets which nodes are expanded in this viewer's tree. The given list
2419 	 * contains the elements that are to be expanded; all other nodes are to be
2420 	 * collapsed.
2421 	 * <p>
2422 	 * This method is typically used when restoring the interesting state of a
2423 	 * viewer captured by an earlier call to <code>getExpandedElements</code>.
2424 	 * </p>
2425 	 *
2426 	 * @param elements
2427 	 *            the array of expanded elements
2428 	 * @see #getExpandedElements
2429 	 */
setExpandedElements(Object... elements)2430 	public void setExpandedElements(Object... elements) {
2431 		assertElementsNotNull(elements);
2432 		if (checkBusy()) {
2433 			return;
2434 		}
2435 		CustomHashtable expandedElements = newHashtable(elements.length * 2 + 1);
2436 		for (Object element : elements) {
2437 			// Ensure item exists for element. This will materialize items for
2438 			// each element and their parents, if possible. This is important
2439 			// to support expanding of inner tree nodes without necessarily
2440 			// expanding their parents.
2441 			internalExpand(element, false);
2442 			expandedElements.put(element, element);
2443 		}
2444 		// this will traverse all existing items, and create children for
2445 		// elements that need to be expanded. If the tree contains multiple
2446 		// equal elements, and those are in the set of elements to be expanded,
2447 		// only the first item found for each element will be expanded.
2448 		internalSetExpanded(expandedElements, getControl());
2449 	}
2450 
2451 	/**
2452 	 * Sets which nodes are expanded in this viewer's tree. The given list
2453 	 * contains the tree paths that are to be expanded; all other nodes are to
2454 	 * be collapsed.
2455 	 * <p>
2456 	 * This method is typically used when restoring the interesting state of a
2457 	 * viewer captured by an earlier call to <code>getExpandedTreePaths</code>.
2458 	 * </p>
2459 	 *
2460 	 * @param treePaths
2461 	 *            the array of expanded tree paths
2462 	 * @see #getExpandedTreePaths()
2463 	 *
2464 	 * @since 3.2
2465 	 */
setExpandedTreePaths(TreePath... treePaths)2466 	public void setExpandedTreePaths(TreePath... treePaths) {
2467 		assertElementsNotNull((Object[]) treePaths);
2468 		if (checkBusy())
2469 			return;
2470 		final IElementComparer comparer = getComparer();
2471 		IElementComparer treePathComparer = new IElementComparer() {
2472 
2473 			@Override
2474 			public boolean equals(Object a, Object b) {
2475 				return ((TreePath) a).equals(((TreePath) b), comparer);
2476 			}
2477 
2478 
2479 			@Override
2480 			public int hashCode(Object element) {
2481 				return ((TreePath) element).hashCode(comparer);
2482 			}
2483 		};
2484 		CustomHashtable expandedTreePaths = new CustomHashtable(
2485 				treePaths.length * 2 + 1, treePathComparer);
2486 		for (TreePath treePath : treePaths) {
2487 			// Ensure item exists for element. This will materialize items for
2488 			// each element and their parents, if possible. This is important
2489 			// to support expanding of inner tree nodes without necessarily
2490 			// expanding their parents.
2491 			internalExpand(treePath, false);
2492 			expandedTreePaths.put(treePath, treePath);
2493 		}
2494 		// this will traverse all existing items, and create children for
2495 		// elements that need to be expanded. If the tree contains multiple
2496 		// equal elements, and those are in the set of elements to be expanded,
2497 		// only the first item found for each element will be expanded.
2498 		internalSetExpandedTreePaths(expandedTreePaths, getControl(),
2499 				new TreePath(new Object[0]));
2500 	}
2501 
2502 	/**
2503 	 * Sets whether the node corresponding to the given element or tree path is
2504 	 * expanded or collapsed.
2505 	 *
2506 	 * @param elementOrTreePath
2507 	 *            the element
2508 	 * @param expanded
2509 	 *            <code>true</code> if the node is expanded, and
2510 	 *            <code>false</code> if collapsed
2511 	 */
setExpandedState(Object elementOrTreePath, boolean expanded)2512 	public void setExpandedState(Object elementOrTreePath, boolean expanded) {
2513 		Assert.isNotNull(elementOrTreePath);
2514 		if (checkBusy())
2515 			return;
2516 		Widget item = internalExpand(elementOrTreePath, false);
2517 		if (item instanceof Item) {
2518 			if (expanded) {
2519 				createChildren(item);
2520 			}
2521 			setExpanded((Item) item, expanded);
2522 		}
2523 	}
2524 
2525 	/**
2526 	 * Sets the selection to the given list of items.
2527 	 *
2528 	 * @param items
2529 	 *            list of items (element type:
2530 	 *            <code>org.eclipse.swt.widgets.Item</code>)
2531 	 */
setSelection(List<Item> items)2532 	protected abstract void setSelection(List<Item> items);
2533 
2534 	/**
2535 	 * This implementation of setSelectionToWidget accepts a list of elements or
2536 	 * a list of tree paths.
2537 	 */
2538 	@Override
setSelectionToWidget(@uppressWarningsR) List v, boolean reveal)2539 	protected void setSelectionToWidget(@SuppressWarnings("rawtypes") List v, boolean reveal) {
2540 		if (v == null) {
2541 			setSelection(new ArrayList<>(0));
2542 			return;
2543 		}
2544 		int size = v.size();
2545 		List<Item> newSelection = new ArrayList<>(size);
2546 		for (int i = 0; i < size; ++i) {
2547 			Object elementOrTreePath = v.get(i);
2548 			// Use internalExpand since item may not yet be created. See
2549 			// 1G6B1AR.
2550 			Widget w = internalExpand(elementOrTreePath, false);
2551 			if (w instanceof Item) {
2552 				newSelection.add((Item) w);
2553 			} else if (w == null && elementOrTreePath instanceof TreePath) {
2554 				TreePath treePath = (TreePath) elementOrTreePath;
2555 				Object element = treePath.getLastSegment();
2556 				if (element != null) {
2557 					w = internalExpand(element, false);
2558 					if (w instanceof Item) {
2559 						newSelection.add((Item) w);
2560 					}
2561 				}
2562 			}
2563 		}
2564 		setSelection(newSelection);
2565 
2566 		// Although setting the selection in the control should reveal it,
2567 		// setSelection may be a no-op if the selection is unchanged,
2568 		// so explicitly reveal items in the selection here.
2569 		// See bug 100565 for more details.
2570 		if (reveal && newSelection.size() > 0) {
2571 			// Iterate backwards so the first item in the list
2572 			// is the one guaranteed to be visible
2573 			for (int i = (newSelection.size()-1); i >= 0; i--) {
2574 				showItem(newSelection.get(i));
2575 			}
2576 		}
2577 	}
2578 
2579 	/**
2580 	 * Shows the given item.
2581 	 *
2582 	 * @param item
2583 	 *            the item
2584 	 */
showItem(Item item)2585 	protected abstract void showItem(Item item);
2586 
2587 	/**
2588 	 * Updates the tree items to correspond to the child elements of the given
2589 	 * parent element. If null is passed for the children, this method obtains
2590 	 * them (only if needed).
2591 	 *
2592 	 * @param widget
2593 	 *            the widget
2594 	 * @param parent
2595 	 *            the parent element
2596 	 * @param elementChildren
2597 	 *            the child elements, or null
2598 	 * @deprecated this is no longer called by the framework
2599 	 */
2600 	@Deprecated
updateChildren(Widget widget, Object parent, Object[] elementChildren)2601 	protected void updateChildren(Widget widget, Object parent,
2602 			Object[] elementChildren) {
2603 		updateChildren(widget, parent, elementChildren, true);
2604 	}
2605 
2606 	/**
2607 	 * Updates the tree items to correspond to the child elements of the given
2608 	 * parent element. If null is passed for the children, this method obtains
2609 	 * them (only if needed).
2610 	 *
2611 	 * @param widget
2612 	 *            the widget
2613 	 * @param parent
2614 	 *            the parent element
2615 	 * @param elementChildren
2616 	 *            the child elements, or null
2617 	 * @param updateLabels
2618 	 *            <code>true</code> to update labels for existing elements,
2619 	 *            <code>false</code> to only update labels as needed, assuming
2620 	 *            that labels for existing elements are unchanged.
2621 	 * @since 2.1
2622 	 */
updateChildren(Widget widget, Object parent, Object[] elementChildren, boolean updateLabels)2623 	private void updateChildren(Widget widget, Object parent,
2624 			Object[] elementChildren, boolean updateLabels) {
2625 		// optimization! prune collapsed subtrees
2626 		if (widget instanceof Item) {
2627 			Item ti = (Item) widget;
2628 			if (!getExpanded(ti)) {
2629 				if (optionallyPruneChildren(ti, parent)) {
2630 					// children were pruned, nothing left to do
2631 					return;
2632 				}
2633 				// The following code is being executed if children were not pruned.
2634 				// This is (as of 3.5) only the case for CheckboxTreeViewer.
2635 				Item[] its = getItems(ti);
2636 				if (isExpandable(ti, null, parent)) {
2637 					if (its.length == 0) {
2638 						// need dummy node
2639 						newItem(ti, SWT.NULL, -1);
2640 						return;
2641 					} else if (its.length == 1 && its[0].getData() == null) {
2642 						// dummy node exists, nothing left to do
2643 						return;
2644 					}
2645 					// else fall through to normal update code below
2646 				} else {
2647 					for (Item it : its) {
2648 						if (it.getData() != null) {
2649 							disassociate(it);
2650 						}
2651 						it.dispose();
2652 					}
2653 					// nothing left to do
2654 					return;
2655 				}
2656 			}
2657 		}
2658 
2659 		// If the children weren't passed in, get them now since they're needed
2660 		// below.
2661 		if (elementChildren == null) {
2662 			if (isTreePathContentProvider() && widget instanceof Item) {
2663 				TreePath path = getTreePathFromItem((Item) widget);
2664 				elementChildren = getSortedChildren(path);
2665 			} else {
2666 				elementChildren = getSortedChildren(parent);
2667 			}
2668 		}
2669 
2670 		Control tree = getControl();
2671 
2672 		// WORKAROUND
2673 		int oldCnt = -1;
2674 		if (widget == tree) {
2675 			oldCnt = getItemCount(tree);
2676 		}
2677 
2678 		Item[] items = getChildren(widget);
2679 
2680 		// save the expanded elements
2681 		CustomHashtable expanded = newHashtable(CustomHashtable.DEFAULT_CAPACITY); // assume
2682 																					// num
2683 																					// expanded
2684 																					// is
2685 																					// small
2686 		for (Item item : items) {
2687 			if (getExpanded(item)) {
2688 				Object element = item.getData();
2689 				if (element != null) {
2690 					expanded.put(element, element);
2691 				}
2692 			}
2693 		}
2694 
2695 		int min = Math.min(elementChildren.length, items.length);
2696 
2697 		// dispose of surplus items, optimizing for the case where elements have
2698 		// been deleted but not reordered, or all elements have been removed.
2699 		int numItemsToDispose = items.length - min;
2700 		if (numItemsToDispose > 0) {
2701 			CustomHashtable children = newHashtable(elementChildren.length * 2);
2702 			for (Object elementChild : elementChildren) {
2703 				children.put(elementChild, elementChild);
2704 			}
2705 			int i = 0;
2706 			while (numItemsToDispose > 0 && i < items.length) {
2707 				Object data = items[i].getData();
2708 				if (data == null || !children.containsKey(data)) {
2709 					if (data != null) {
2710 						disassociate(items[i]);
2711 					}
2712 					items[i].dispose();
2713 					if (i + 1 < items.length) {
2714 						// The components at positions i+1 through
2715 						// items.length-1 in the source array are copied into
2716 						// positions i through items.length-2
2717 						System.arraycopy(items, i + 1, items, i, items.length - (i+1));
2718 					}
2719 					numItemsToDispose--;
2720 				} else {
2721 					i++;
2722 				}
2723 			}
2724 		}
2725 
2726 		// compare first min items, and update item if necessary
2727 		// need to do it in two passes:
2728 		// 1: disassociate old items
2729 		// 2: associate new items
2730 		// because otherwise a later disassociate can remove a mapping made for
2731 		// a previous associate,
2732 		// making the map inconsistent
2733 		for (int i = 0; i < min; ++i) {
2734 			Item item = items[i];
2735 			Object oldElement = item.getData();
2736 			if (oldElement != null) {
2737 				Object newElement = elementChildren[i];
2738 				if (newElement != oldElement) {
2739 					if (equals(newElement, oldElement)) {
2740 						// update the data to be the new element, since
2741 						// although the elements
2742 						// may be equal, they may still have different labels
2743 						// or children
2744 						Object data = item.getData();
2745 						if (data != null) {
2746 							unmapElement(data, item);
2747 						}
2748 						item.setData(newElement);
2749 						mapElement(newElement, item);
2750 					} else {
2751 						disassociate(item);
2752 						// Clear the text and image to force a label update
2753 						item.setImage(null);
2754 						item.setText("");//$NON-NLS-1$
2755 
2756 					}
2757 				}
2758 			}
2759 		}
2760 
2761 		for (int i = 0; i < min; ++i) {
2762 			Item item = items[i];
2763 			Object newElement = elementChildren[i];
2764 			if (item.getData() == null) {
2765 				// old and new elements are not equal
2766 				associate(newElement, item);
2767 				updatePlus(item, newElement);
2768 				updateItem(item, newElement);
2769 			} else {
2770 				// old and new elements are equal
2771 				updatePlus(item, newElement);
2772 				if (updateLabels) {
2773 					updateItem(item, newElement);
2774 				} else {
2775 					associate(newElement, item);
2776 				}
2777 			}
2778 		}
2779 
2780 		// Restore expanded state for items that changed position.
2781 		// Make sure setExpanded is called after updatePlus, since
2782 		// setExpanded(false) fails if item has no children.
2783 		// Need to call setExpanded for both expanded and unexpanded
2784 		// cases since the expanded state can change either way.
2785 		// This needs to be done in a second loop, see bug 148025.
2786 		for (int i = 0; i < min; ++i) {
2787 			Item item = items[i];
2788 			Object newElement = elementChildren[i];
2789 			setExpanded(item, expanded.containsKey(newElement));
2790 		}
2791 
2792 		// add any remaining elements
2793 		if (min < elementChildren.length) {
2794 			for (int i = min; i < elementChildren.length; ++i) {
2795 				createTreeItem(widget, elementChildren[i], -1);
2796 			}
2797 
2798 			// Need to restore expanded state in a separate pass
2799 			// because createTreeItem does not return the new item.
2800 			// Avoid doing this unless needed.
2801 			if (expanded.size() > 0) {
2802 				// get the items again, to include the new items
2803 				items = getChildren(widget);
2804 				for (int i = min; i < elementChildren.length; ++i) {
2805 					// Restore expanded state for items that changed position.
2806 					// Make sure setExpanded is called after updatePlus (called
2807 					// in createTreeItem), since
2808 					// setExpanded(false) fails if item has no children.
2809 					// Only need to call setExpanded if element was expanded
2810 					// since new items are initially unexpanded.
2811 					if (expanded.containsKey(elementChildren[i])) {
2812 						setExpanded(items[i], true);
2813 					}
2814 				}
2815 			}
2816 		}
2817 
2818 		// WORKAROUND
2819 		if (widget == tree && oldCnt == 0 && getItemCount(tree) != 0) {
2820 			// System.out.println("WORKAROUND setRedraw");
2821 			tree.setRedraw(false);
2822 			tree.setRedraw(true);
2823 		}
2824 	}
2825 
2826 	/** Returns true if children were pruned */
optionallyPruneChildren(Item item, Object element)2827 	/*package*/ boolean optionallyPruneChildren(Item item, Object element) {
2828 		// need a dummy node if element is expandable;
2829 		// but try to avoid recreating the dummy node
2830 		boolean needDummy = isExpandable(item, null, element);
2831 		boolean haveDummy = false;
2832 		// remove all children
2833 		Item[] items = getItems(item);
2834 		for (Item child : items) {
2835 			if (child.getData() != null) {
2836 				disassociate(child);
2837 				child.dispose();
2838 			} else {
2839 				if (needDummy && !haveDummy) {
2840 					haveDummy = true;
2841 				} else {
2842 					child.dispose();
2843 				}
2844 			}
2845 		}
2846 		if (needDummy && !haveDummy) {
2847 			newItem(item, SWT.NULL, -1);
2848 		}
2849 		return true;
2850 	}
2851 
2852 	/**
2853 	 * Not to be called by clients. Return the items to be refreshed as part of
2854 	 * an update. elementChildren are the new elements.
2855 	 *
2856 	 * @param widget widget to get children for
2857 	 * @param elementChildren unused
2858 	 * @since 3.4
2859 	 * @return Item[]
2860 	 *
2861 	 * @deprecated This method was inadvertently released as API but is not
2862 	 *             intended to be called by clients.
2863 	 */
2864 	@Deprecated
getChildren(Widget widget, Object[] elementChildren)2865 	public Item[] getChildren(Widget widget,  Object[] elementChildren) {
2866 		return getChildren(widget);
2867 	}
2868 
2869 	/**
2870 	 * Updates the "+"/"-" icon of the tree node from the given element. It
2871 	 * calls <code>isExpandable</code> to determine whether an element is
2872 	 * expandable.
2873 	 *
2874 	 * @param item
2875 	 *            the item
2876 	 * @param element
2877 	 *            the element
2878 	 */
updatePlus(Item item, Object element)2879 	protected void updatePlus(Item item, Object element) {
2880 		boolean hasPlus = getItemCount(item) > 0;
2881 		boolean needsPlus = isExpandable(item, null, element);
2882 		boolean removeAll = false;
2883 		boolean addDummy = false;
2884 		Object data = item.getData();
2885 		if (data != null && equals(element, data)) {
2886 			// item shows same element
2887 			if (hasPlus != needsPlus) {
2888 				if (needsPlus) {
2889 					addDummy = true;
2890 				} else {
2891 					removeAll = true;
2892 				}
2893 			}
2894 		} else {
2895 			// item shows different element
2896 			removeAll = true;
2897 			addDummy = needsPlus;
2898 
2899 			// we cannot maintain expand state so collapse it
2900 			setExpanded(item, false);
2901 		}
2902 		if (removeAll) {
2903 			// remove all children
2904 			Item[] items = getItems(item);
2905 			for (Item item2 : items) {
2906 				if (item2.getData() != null) {
2907 					disassociate(item2);
2908 				}
2909 				item2.dispose();
2910 			}
2911 		}
2912 		if (addDummy) {
2913 			newItem(item, SWT.NULL, -1); // append a dummy
2914 		}
2915 	}
2916 
2917 	/**
2918 	 * Gets the expanded elements that are visible to the user. An expanded
2919 	 * element is only visible if the parent is expanded.
2920 	 *
2921 	 * @return the visible expanded elements
2922 	 * @since 2.0
2923 	 */
getVisibleExpandedElements()2924 	public Object[] getVisibleExpandedElements() {
2925 		ArrayList<Object> v = new ArrayList<>();
2926 		internalCollectVisibleExpanded(v, getControl());
2927 		return v.toArray();
2928 	}
2929 
internalCollectVisibleExpanded(ArrayList<Object> result, Widget widget)2930 	private void internalCollectVisibleExpanded(ArrayList<Object> result, Widget widget) {
2931 		Item[] items = getChildren(widget);
2932 		for (Item item : items) {
2933 			if (getExpanded(item)) {
2934 				Object data = item.getData();
2935 				if (data != null) {
2936 					result.add(data);
2937 				}
2938 				// Only recurse if it is expanded - if
2939 				// not then the children aren't visible
2940 				internalCollectVisibleExpanded(result, item);
2941 			}
2942 		}
2943 	}
2944 
2945 	/**
2946 	 * Returns the tree path for the given item.
2947 	 *
2948 	 * @param item item to get path for
2949 	 * @return {@link TreePath}
2950 	 *
2951 	 * @since 3.2
2952 	 */
getTreePathFromItem(Item item)2953 	protected TreePath getTreePathFromItem(Item item) {
2954 		LinkedList<Object> segments = new LinkedList<>();
2955 		while (item != null) {
2956 			Object segment = item.getData();
2957 			Assert.isNotNull(segment);
2958 			segments.addFirst(segment);
2959 			item = getParentItem(item);
2960 		}
2961 		return new TreePath(segments.toArray());
2962 	}
2963 
2964 	/**
2965 	 * The <code>AbstractTreeViewer</code> implementation of this method returns
2966 	 * the result as an <code>ITreeSelection</code>.
2967 	 * <p>
2968 	 * Call {@link #getStructuredSelection()} instead to get an instance of
2969 	 * <code>ITreeSelection</code> directly.
2970 	 * </p>
2971 	 * Subclasses do not typically override this method, but implement
2972 	 * <code>getSelectionFromWidget(List)</code> instead. If they override this
2973 	 * method, they should return an <code>ITreeSelection</code> as well.
2974 	 *
2975 	 * @since 3.2
2976 	 */
2977 	@Override
getSelection()2978 	public ISelection getSelection() {
2979 		Control control = getControl();
2980 		if (control == null || control.isDisposed()) {
2981 			return TreeSelection.EMPTY;
2982 		}
2983 		Widget[] items = getSelection(getControl());
2984 		ArrayList<TreePath> list = new ArrayList<>(items.length);
2985 		for (Widget item : items) {
2986 			if (item.getData() != null) {
2987 				list.add(getTreePathFromItem((Item) item));
2988 			}
2989 		}
2990 		return new TreeSelection(list.toArray(new TreePath[list.size()]), getComparer());
2991 	}
2992 
2993 	/**
2994 	 * Returns the <code>ITreeSelection</code> of this viewer.
2995 	 * <p>
2996 	 * Subclasses whose {@link #getSelection()} specifies to return a more
2997 	 * specific type should also override this method and return that type.
2998 	 * </p>
2999 	 *
3000 	 * @return ITreeSelection
3001 	 * @throws ClassCastException
3002 	 *             if the selection of the viewer is not an instance of
3003 	 *             ITreeSelection
3004 	 * @since 3.11
3005 	 */
3006 	@Override
getStructuredSelection()3007 	public ITreeSelection getStructuredSelection() throws ClassCastException {
3008 		ISelection selection = getSelection();
3009 		if (selection instanceof ITreeSelection) {
3010 			return (ITreeSelection) selection;
3011 		}
3012 		throw new ClassCastException(
3013 				getClass().getName() + " should return an instance of ITreeSelection from its getSelection() method."); //$NON-NLS-1$
3014 	}
3015 
3016 	@Override
setSelectionToWidget(ISelection selection, boolean reveal)3017 	protected void setSelectionToWidget(ISelection selection, boolean reveal) {
3018 		if (selection instanceof ITreeSelection) {
3019 			ITreeSelection treeSelection = (ITreeSelection) selection;
3020 			setSelectionToWidget(Arrays.asList(treeSelection.getPaths()), reveal);
3021 		} else {
3022 			super.setSelectionToWidget(selection, reveal);
3023 		}
3024 	}
3025 
3026 	/**
3027 	 * Returns a list of tree paths corresponding to expanded nodes in this
3028 	 * viewer's tree, including currently hidden ones that are marked as
3029 	 * expanded but are under a collapsed ancestor.
3030 	 * <p>
3031 	 * This method is typically used when preserving the interesting state of a
3032 	 * viewer; <code>setExpandedElements</code> is used during the restore.
3033 	 * </p>
3034 	 *
3035 	 * @return the array of expanded tree paths
3036 	 * @see #setExpandedElements
3037 	 *
3038 	 * @since 3.2
3039 	 */
getExpandedTreePaths()3040 	public TreePath[] getExpandedTreePaths() {
3041 		ArrayList<Item> items = new ArrayList<>();
3042 		internalCollectExpandedItems(items, getControl());
3043 		ArrayList<TreePath> result = new ArrayList<>(items.size());
3044 		for (Item item : items) {
3045 			TreePath treePath = getTreePathFromItem(item);
3046 			if (treePath != null) {
3047 				result.add(treePath);
3048 			}
3049 		}
3050 		return result.toArray(new TreePath[items.size()]);
3051 	}
3052 
isTreePathContentProvider()3053 	private boolean isTreePathContentProvider() {
3054 		return getContentProvider() instanceof ITreePathContentProvider;
3055 	}
3056 
3057 	/**
3058 	 * Inserts the given element as a new child element of the given parent
3059 	 * element at the given position. If this viewer has a sorter, the position
3060 	 * is ignored and the element is inserted at the correct position in the
3061 	 * sort order.
3062 	 * <p>
3063 	 * This method should be called (by the content provider) when elements have
3064 	 * been added to the model, in order to cause the viewer to accurately
3065 	 * reflect the model. This method only affects the viewer, not the model.
3066 	 * </p>
3067 	 *
3068 	 * @param parentElementOrTreePath
3069 	 *            the parent element, or the tree path to the parent
3070 	 * @param element
3071 	 *            the element
3072 	 * @param position
3073 	 *            a 0-based position relative to the model, or -1 to indicate
3074 	 *            the last position
3075 	 *
3076 	 * @since 3.2
3077 	 */
insert(Object parentElementOrTreePath, Object element, int position)3078 	public void insert(Object parentElementOrTreePath, Object element,
3079 			int position) {
3080 		Assert.isNotNull(parentElementOrTreePath);
3081 		Assert.isNotNull(element);
3082 		if (checkBusy())
3083 			return;
3084 		if (getComparator() != null || hasFilters()) {
3085 			add(parentElementOrTreePath, new Object[] { element });
3086 			return;
3087 		}
3088 		Widget[] items;
3089 		if (internalIsInputOrEmptyPath(parentElementOrTreePath)) {
3090 			items = new Widget[] { getControl() };
3091 		} else {
3092 			items = internalFindItems(parentElementOrTreePath);
3093 		}
3094 
3095 		for (Widget widget : items) {
3096 			if (widget instanceof Item) {
3097 				Item item = (Item) widget;
3098 
3099 				Item[] childItems = getChildren(item);
3100 				if (getExpanded(item)
3101 						|| (childItems.length > 0 && childItems[0].getData() != null)) {
3102 					// item has real children, go ahead and add
3103 					int insertionPosition = position;
3104 					if (insertionPosition == -1) {
3105 						insertionPosition = getItemCount(item);
3106 					}
3107 
3108 					createTreeItem(item, element, insertionPosition);
3109 				} else {
3110 					Object parentElement = parentElementOrTreePath;
3111 					if (element instanceof TreePath)
3112 						parentElement = ((TreePath) parentElement).getLastSegment();
3113 					updatePlus(item, parentElement);
3114 				}
3115 			} else {
3116 				int insertionPosition = position;
3117 				if (insertionPosition == -1) {
3118 					insertionPosition = getItemCount((Control) widget);
3119 				}
3120 
3121 				createTreeItem(widget, element, insertionPosition);
3122 			}
3123 		}
3124 	}
3125 
3126 	@Override
getColumnViewerOwner(int columnIndex)3127 	protected Widget getColumnViewerOwner(int columnIndex) {
3128 		// Return null by default
3129 		return null;
3130 	}
3131 
3132 	/**
3133 	 * This implementation of {@link #getItemAt(Point)} returns null to ensure
3134 	 * API backwards compatibility. Subclasses should override.
3135 	 *
3136 	 * @since 3.3
3137 	 */
3138 	@Override
getItemAt(Point point)3139 	protected Item getItemAt(Point point) {
3140 		return null;
3141 	}
3142 
3143 	/**
3144 	 * This implementation of {@link #createViewerEditor()} returns null to ensure
3145 	 * API backwards compatibility. Subclasses should override.
3146 	 *
3147 	 * @since 3.3
3148 	 */
3149 	@Override
createViewerEditor()3150 	protected ColumnViewerEditor createViewerEditor() {
3151 		return null;
3152 	}
3153 
3154 	/**
3155 	 * Returns the number of columns of this viewer.
3156 	 * <p><b>Subclasses should overwrite this method, which has a default
3157 	 * implementation (returning 0) for API backwards compatility reasons</b></p>
3158 	 *
3159 	 * @return the number of columns
3160 	 *
3161 	 * @since 3.3
3162 	 */
3163 	@Override
doGetColumnCount()3164 	protected int doGetColumnCount() {
3165 		return 0;
3166 	}
3167 
3168 
3169 	/**
3170 	 * This implementation of buildLabel handles tree paths as well as elements.
3171 	 *
3172 	 * @param updateLabel
3173 	 *            the ViewerLabel to collect the result in
3174 	 * @param elementOrPath
3175 	 *            the element or tree path for which a label should be built
3176 	 *
3177 	 * @see org.eclipse.jface.viewers.StructuredViewer#buildLabel(org.eclipse.jface.viewers.ViewerLabel,
3178 	 *      java.lang.Object)
3179 	 */
3180 	@Override
buildLabel(ViewerLabel updateLabel, Object elementOrPath)3181 	protected void buildLabel(ViewerLabel updateLabel, Object elementOrPath) {
3182 		Object element;
3183 		if (elementOrPath instanceof TreePath) {
3184 			TreePath path = (TreePath) elementOrPath;
3185 			IBaseLabelProvider provider = getLabelProvider();
3186 			if (provider instanceof ITreePathLabelProvider) {
3187 				ITreePathLabelProvider pprov = (ITreePathLabelProvider) provider;
3188 				buildLabel(updateLabel, path, pprov);
3189 				return;
3190 			}
3191 			element = path.getLastSegment();
3192 		} else {
3193 			element = elementOrPath;
3194 		}
3195 		super.buildLabel(updateLabel, element);
3196 	}
3197 
3198 	/**
3199 	 * Returns true if the given object is either the input or an empty tree path.
3200 	 *
3201 	 * @param elementOrTreePath an element which could either be the viewer's input, or a tree path
3202 	 *
3203 	 * @return <code>true</code> if the given object is either the input or an empty tree path,
3204 	 * <code>false</code> otherwise.
3205 	 * @since 3.3
3206 	 */
internalIsInputOrEmptyPath(final Object elementOrTreePath)3207 	final protected boolean internalIsInputOrEmptyPath(final Object elementOrTreePath) {
3208 		if (elementOrTreePath.equals(getRoot()))
3209 			return true;
3210 		if (!(elementOrTreePath instanceof TreePath))
3211 			return false;
3212 		return ((TreePath) elementOrTreePath).getSegmentCount() == 0;
3213 	}
3214 
3215 	/*
3216 	 * Subclasses should implement
3217 	 */
3218 	@Override
getViewerRowFromItem(Widget item)3219 	protected ViewerRow getViewerRowFromItem(Widget item) {
3220 		return null;
3221 	}
3222 
3223 	/**
3224 	 * Instructs {@link #isExpandable(Object)} to consult filters to more accurately
3225 	 * determine if an item can be expanded.
3226 	 * <p>
3227 	 * Setting this value to <code>true</code> will affect performance of the tree
3228 	 * viewer.
3229 	 * </p><p>
3230 	 * To improve performance, by default the tree viewer does not consult filters when
3231 	 * determining if a tree node could be expanded.
3232 	 * </p>
3233 	 * @param checkFilters <code>true</code> to instruct tree viewer to consult filters
3234 	 * @see #isExpandable(Object)
3235 	 * @since 3.8
3236 	 */
setExpandPreCheckFilters(boolean checkFilters)3237 	public void setExpandPreCheckFilters(boolean checkFilters) {
3238 		if (checkFilters != isExpandableCheckFilters) {
3239 			this.isExpandableCheckFilters = checkFilters;
3240 			refresh();
3241 		}
3242 	}
3243 
3244 }
3245