1 /*******************************************************************************
2  * Copyright (c) 2006, 2015 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> - bugfix in: 214355
14  *     Matthew Hall - bugs 215531, 226765, 222991, 238296, 226292, 266038,
15  *                    283351
16  *******************************************************************************/
17 
18 package org.eclipse.jface.databinding.viewers;
19 
20 import java.util.Set;
21 
22 import org.eclipse.core.databinding.observable.IObservableCollection;
23 import org.eclipse.core.databinding.observable.list.IListChangeListener;
24 import org.eclipse.core.databinding.observable.list.IObservableList;
25 import org.eclipse.core.databinding.observable.list.ListChangeEvent;
26 import org.eclipse.core.databinding.observable.list.ListDiffVisitor;
27 import org.eclipse.core.databinding.observable.set.IObservableSet;
28 import org.eclipse.core.runtime.Assert;
29 import org.eclipse.jface.internal.databinding.viewers.ObservableCollectionContentProvider;
30 import org.eclipse.jface.internal.databinding.viewers.ViewerElementSet;
31 import org.eclipse.jface.viewers.AbstractListViewer;
32 import org.eclipse.jface.viewers.AbstractTableViewer;
33 import org.eclipse.jface.viewers.IStructuredContentProvider;
34 import org.eclipse.jface.viewers.Viewer;
35 
36 /**
37  * A {@link IStructuredContentProvider content provider} for
38  * {@link AbstractTableViewer} or {@link AbstractListViewer} that provides
39  * elements of an {@link IObservableList} when set as the viewer's input.
40  * Objects of this class listen for changes to the observable list, and will
41  * insert and remove viewer elements to reflect observed changes.
42  *
43  * @param <E> type of the values that are provided by this object TODO: Probably
44  *            remove this!
45  *
46  * @noextend This class is not intended to be subclassed by clients.
47  * @since 1.1
48  */
49 public class ObservableListContentProvider<E> implements IStructuredContentProvider {
50 	private ObservableCollectionContentProvider<E> impl;
51 
52 	private static class Impl<E> extends ObservableCollectionContentProvider<E> implements IListChangeListener<E> {
53 		private Viewer viewer;
54 
Impl(IViewerUpdater<E> explicitViewerUpdater)55 		Impl(IViewerUpdater<E> explicitViewerUpdater) {
56 			super(explicitViewerUpdater);
57 		}
58 
59 		@Override
inputChanged(Viewer viewer, Object oldInput, Object newInput)60 		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
61 			this.viewer = viewer;
62 			super.inputChanged(viewer, oldInput, newInput);
63 		}
64 
65 		@Override
checkInput(Object input)66 		protected void checkInput(Object input) {
67 			Assert.isTrue(input instanceof IObservableList,
68 					"This content provider only works with input of type IObservableList"); //$NON-NLS-1$
69 		}
70 
71 		@Override
addCollectionChangeListener(IObservableCollection<E> collection)72 		protected void addCollectionChangeListener(IObservableCollection<E> collection) {
73 			((IObservableList<E>) collection).addListChangeListener(this);
74 		}
75 
76 		@Override
removeCollectionChangeListener(IObservableCollection<E> collection)77 		protected void removeCollectionChangeListener(IObservableCollection<E> collection) {
78 			((IObservableList<E>) collection).removeListChangeListener(this);
79 		}
80 
81 		@Override
handleListChange(ListChangeEvent<? extends E> event)82 		public void handleListChange(ListChangeEvent<? extends E> event) {
83 			if (isViewerDisposed())
84 				return;
85 
86 			// Determine which elements were added and removed
87 			final Set<E> knownElementAdditions = ViewerElementSet.withComparer(comparer);
88 			final Set<E> knownElementRemovals = ViewerElementSet.withComparer(comparer);
89 			final boolean[] suspendRedraw = new boolean[] { false };
90 			event.diff.accept(new ListDiffVisitor<E>() {
91 				@Override
92 				public void handleAdd(int index, E element) {
93 					knownElementAdditions.add(element);
94 				}
95 
96 				@Override
97 				public void handleRemove(int index, E element) {
98 					knownElementRemovals.add(element);
99 				}
100 
101 				@Override
102 				public void handleMove(int oldIndex, int newIndex, E element) {
103 					suspendRedraw[0] = true;
104 					super.handleMove(oldIndex, newIndex, element);
105 				}
106 
107 				@Override
108 				public void handleReplace(int index, E oldElement, E newElement) {
109 					suspendRedraw[0] = true;
110 					super.handleReplace(index, oldElement, newElement);
111 				}
112 			});
113 			knownElementAdditions.removeAll(knownElements);
114 			knownElementRemovals.removeAll(event.getObservableList());
115 
116 			knownElements.addAll(knownElementAdditions);
117 			if (realizedElements != null) {
118 				realizedElements.removeAll(knownElementRemovals);
119 			}
120 
121 			if (suspendRedraw[0])
122 				viewer.getControl().setRedraw(false);
123 			try {
124 				event.diff.accept(new ListDiffVisitor<E>() {
125 					@Override
126 					public void handleAdd(int index, E element) {
127 						viewerUpdater.insert(element, index);
128 					}
129 
130 					@Override
131 					public void handleRemove(int index, E element) {
132 						viewerUpdater.remove(element, index);
133 					}
134 
135 					@Override
136 					public void handleReplace(int index, E oldElement, E newElement) {
137 						viewerUpdater.replace(oldElement, newElement, index);
138 					}
139 
140 					@Override
141 					public void handleMove(int oldIndex, int newIndex, E element) {
142 						viewerUpdater.move(element, oldIndex, newIndex);
143 					}
144 				});
145 			} finally {
146 				if (suspendRedraw[0])
147 					viewer.getControl().setRedraw(true);
148 			}
149 
150 			if (realizedElements != null) {
151 				realizedElements.addAll(knownElementAdditions);
152 			}
153 			knownElements.removeAll(knownElementRemovals);
154 		}
155 	}
156 
157 	/**
158 	 * Constructs an ObservableListContentProvider. Must be called from the
159 	 * display thread.
160 	 */
ObservableListContentProvider()161 	public ObservableListContentProvider() {
162 		this(null);
163 	}
164 
165 	/**
166 	 * Constructs an ObservableListContentProvider with the given viewer
167 	 * updater. Must be called from the display thread.
168 	 *
169 	 * @param viewerUpdater
170 	 *            the viewer updater to use when elements are added, removed,
171 	 *            moved or replaced in the input observable list.
172 	 * @since 1.3
173 	 */
ObservableListContentProvider(IViewerUpdater<E> viewerUpdater)174 	public ObservableListContentProvider(IViewerUpdater<E> viewerUpdater) {
175 		impl = new Impl<>(viewerUpdater);
176 	}
177 
178 	@Override
inputChanged(Viewer viewer, Object oldInput, Object newInput)179 	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
180 		impl.inputChanged(viewer, oldInput, newInput);
181 	}
182 
183 	@Override
getElements(Object inputElement)184 	public Object[] getElements(Object inputElement) {
185 		return impl.getElements(inputElement);
186 	}
187 
188 	/**
189 	 * Disposes of this content provider. This is called by the viewer when a
190 	 * content provider is replaced, or when the viewer itself is disposed.
191 	 * <p>
192 	 * The viewer should not be updated during this call, as it is in the
193 	 * process of being disposed.
194 	 * </p>
195 	 * <p>
196 	 * <em>Note:</em> Data binding content providers become unusable on
197 	 * disposal.
198 	 * </p>
199 	 */
200 	@Override
dispose()201 	public void dispose() {
202 		impl.dispose();
203 	}
204 
205 	/**
206 	 * Returns the set of elements known to this content provider. Label
207 	 * providers may track this set if they need to be notified about additions
208 	 * before the viewer sees the added element, and notified about removals
209 	 * after the element was removed from the viewer. This is intended for use
210 	 * by label providers, as it will always return the items that need labels.
211 	 *
212 	 * @return readableSet of items that will need labels
213 	 */
getKnownElements()214 	public IObservableSet<E> getKnownElements() {
215 		return impl.getKnownElements();
216 	}
217 
218 	/**
219 	 * Returns the set of known elements which have been realized in the viewer.
220 	 * Clients may track this set in order to perform custom actions on elements
221 	 * while they are known to be present in the viewer.
222 	 *
223 	 * @return the set of known elements which have been realized in the viewer.
224 	 * @since 1.3
225 	 */
getRealizedElements()226 	public IObservableSet<E> getRealizedElements() {
227 		return impl.getRealizedElements();
228 	}
229 }
230