1 /*******************************************************************************
2  * Copyright (c) 2008, 2017 Matthew Hall 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  *     Matthew Hall - initial API and implementation (bug 194734)
13  *     Matthew Hall - bugs 265561, 262287, 268203, 268688, 301774
14  *     Stefan Xenos <sxenos@gmail.com> - Bug 335792
15  ******************************************************************************/
16 
17 package org.eclipse.core.internal.databinding.property.set;
18 
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.ConcurrentModificationException;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.Set;
25 
26 import org.eclipse.core.databinding.observable.Diffs;
27 import org.eclipse.core.databinding.observable.Realm;
28 import org.eclipse.core.databinding.observable.set.AbstractObservableSet;
29 import org.eclipse.core.databinding.observable.set.SetDiff;
30 import org.eclipse.core.databinding.property.INativePropertyListener;
31 import org.eclipse.core.databinding.property.IPropertyObservable;
32 import org.eclipse.core.databinding.property.SimplePropertyEvent;
33 import org.eclipse.core.databinding.property.set.SimpleSetProperty;
34 
35 /**
36  * @param <S>
37  *            type of the source object
38  * @param <E>
39  *            type of the elements in the set
40  * @since 1.2
41  *
42  */
43 public class SimplePropertyObservableSet<S, E> extends AbstractObservableSet<E>
44 		implements IPropertyObservable<SimpleSetProperty<S, E>> {
45 	private S source;
46 	private SimpleSetProperty<S, E> property;
47 
48 	private volatile boolean updating = false;
49 
50 	private volatile int modCount = 0;
51 
52 	private INativePropertyListener<S> listener;
53 
54 	private Set<E> cachedSet;
55 	private boolean stale;
56 
57 	/**
58 	 * @param realm
59 	 * @param source
60 	 * @param property
61 	 */
SimplePropertyObservableSet(Realm realm, S source, SimpleSetProperty<S, E> property)62 	public SimplePropertyObservableSet(Realm realm, S source, SimpleSetProperty<S, E> property) {
63 		super(realm);
64 		this.source = source;
65 		this.property = property;
66 	}
67 
68 	@Override
firstListenerAdded()69 	protected void firstListenerAdded() {
70 		if (!isDisposed() && listener == null) {
71 			listener = property.adaptListener(event -> {
72 				if (!isDisposed() && !updating) {
73 					getRealm().exec(() -> {
74 						if (event.type == SimplePropertyEvent.CHANGE) {
75 							modCount++;
76 							notifyIfChanged(event.diff);
77 						} else if (event.type == SimplePropertyEvent.STALE && !stale) {
78 							stale = true;
79 							fireStale();
80 						}
81 					});
82 				}
83 			});
84 		}
85 
86 		getRealm().exec(() -> {
87 			cachedSet = new HashSet<>(getSet());
88 			stale = false;
89 
90 			if (listener != null)
91 				listener.addTo(source);
92 		});
93 	}
94 
95 	@Override
lastListenerRemoved()96 	protected void lastListenerRemoved() {
97 		if (listener != null)
98 			listener.removeFrom(source);
99 
100 		cachedSet.clear();
101 		cachedSet = null;
102 		stale = false;
103 	}
104 
105 	@Override
getWrappedSet()106 	protected Set<E> getWrappedSet() {
107 		return getSet();
108 	}
109 
110 	@Override
getElementType()111 	public Object getElementType() {
112 		return property.getElementType();
113 	}
114 
115 	// Queries
116 
getSet()117 	private Set<E> getSet() {
118 		return property.getSet(source);
119 	}
120 
121 	@Override
contains(Object o)122 	public boolean contains(Object o) {
123 		getterCalled();
124 		return getSet().contains(o);
125 	}
126 
127 	@Override
containsAll(Collection<?> c)128 	public boolean containsAll(Collection<?> c) {
129 		getterCalled();
130 		return getSet().containsAll(c);
131 	}
132 
133 	@Override
isEmpty()134 	public boolean isEmpty() {
135 		getterCalled();
136 		return getSet().isEmpty();
137 	}
138 
139 	@Override
toArray()140 	public Object[] toArray() {
141 		getterCalled();
142 		return getSet().toArray();
143 	}
144 
145 	@Override
toArray(T[] a)146 	public <T> T[] toArray(T[] a) {
147 		getterCalled();
148 		return getSet().toArray(a);
149 	}
150 
151 	// Single change operations
152 
updateSet(Set<E> set, SetDiff<E> diff)153 	private void updateSet(Set<E> set, SetDiff<E> diff) {
154 		if (!diff.isEmpty()) {
155 			boolean wasUpdating = updating;
156 			updating = true;
157 			try {
158 				property.updateSet(source, diff);
159 				modCount++;
160 			} finally {
161 				updating = wasUpdating;
162 			}
163 
164 			notifyIfChanged(null);
165 		}
166 	}
167 
168 	@Override
add(E o)169 	public boolean add(E o) {
170 		checkRealm();
171 
172 		Set<E> set = getSet();
173 		if (set.contains(o))
174 			return false;
175 
176 		SetDiff<E> diff = Diffs.createSetDiff(Collections.singleton(o), Collections.<E>emptySet());
177 		updateSet(set, diff);
178 
179 		return true;
180 	}
181 
182 	@Override
iterator()183 	public Iterator<E> iterator() {
184 		getterCalled();
185 		return new Iterator<E>() {
186 			int expectedModCount = modCount;
187 			Set<E> set = new HashSet<>(getSet());
188 			Iterator<E> iterator = set.iterator();
189 			E last = null;
190 
191 			@Override
192 			public boolean hasNext() {
193 				getterCalled();
194 				checkForComodification();
195 				return iterator.hasNext();
196 			}
197 
198 			@Override
199 			public E next() {
200 				getterCalled();
201 				checkForComodification();
202 				last = iterator.next();
203 				return last;
204 			}
205 
206 			@Override
207 			public void remove() {
208 				checkRealm();
209 				checkForComodification();
210 
211 				SetDiff<E> diff = Diffs.createSetDiff(Collections.<E>emptySet(), Collections.singleton(last));
212 				updateSet(set, diff);
213 
214 				iterator.remove(); // stay in sync
215 
216 				last = null;
217 				expectedModCount = modCount;
218 			}
219 
220 			private void checkForComodification() {
221 				if (expectedModCount != modCount)
222 					throw new ConcurrentModificationException();
223 			}
224 		};
225 	}
226 
227 	@Override
remove(Object o)228 	public boolean remove(Object o) {
229 		getterCalled();
230 
231 		Set<E> set = getSet();
232 		if (!set.contains(o))
233 			return false;
234 
235 		@SuppressWarnings("unchecked")
236 		// if o is contained, it is an E
237 		SetDiff<E> diff = Diffs.createSetDiff(Collections.<E>emptySet(), Collections.singleton((E) o));
238 		updateSet(set, diff);
239 
240 		return true;
241 	}
242 
243 	// Bulk change operations
244 
245 	@Override
addAll(Collection<? extends E> c)246 	public boolean addAll(Collection<? extends E> c) {
247 		getterCalled();
248 
249 		if (c.isEmpty())
250 			return false;
251 
252 		Set<E> set = getSet();
253 		if (set.containsAll(c))
254 			return false;
255 
256 		Set<E> additions = new HashSet<>(c);
257 		additions.removeAll(set);
258 
259 		if (additions.isEmpty())
260 			return false;
261 
262 		SetDiff<E> diff = Diffs.createSetDiff(additions, Collections.<E>emptySet());
263 		updateSet(set, diff);
264 
265 		return true;
266 	}
267 
268 	@Override
removeAll(Collection<?> c)269 	public boolean removeAll(Collection<?> c) {
270 		getterCalled();
271 
272 		if (c.isEmpty())
273 			return false;
274 
275 		Set<E> set = getSet();
276 		if (set.isEmpty())
277 			return false;
278 
279 		Set<Object> removals = new HashSet<>(c);
280 		removals.retainAll(set);
281 		@SuppressWarnings("unchecked")
282 		// because we have removed everything that is not an E
283 		Set<E> typedRemovals = (Set<E>) removals;
284 
285 		if (removals.isEmpty())
286 			return false;
287 
288 		SetDiff<E> diff = Diffs.createSetDiff(Collections.<E>emptySet(), typedRemovals);
289 		updateSet(set, diff);
290 
291 		return true;
292 	}
293 
294 	@Override
retainAll(Collection<?> c)295 	public boolean retainAll(Collection<?> c) {
296 		getterCalled();
297 
298 		Set<E> set = getSet();
299 		if (set.isEmpty())
300 			return false;
301 
302 		if (c.isEmpty()) {
303 			clear();
304 			return true;
305 		}
306 
307 		Set<E> removals = new HashSet<>(set);
308 		removals.removeAll(c);
309 
310 		if (removals.isEmpty())
311 			return false;
312 
313 		SetDiff<E> diff = Diffs.createSetDiff(Collections.<E>emptySet(), removals);
314 		updateSet(set, diff);
315 
316 		return true;
317 	}
318 
319 	@Override
clear()320 	public void clear() {
321 		getterCalled();
322 
323 		Set<E> set = getSet();
324 		if (set.isEmpty())
325 			return;
326 
327 		SetDiff<E> diff = Diffs.createSetDiff(Collections.<E>emptySet(), set);
328 		updateSet(set, diff);
329 	}
330 
notifyIfChanged(SetDiff<E> diff)331 	private void notifyIfChanged(SetDiff<E> diff) {
332 		if (hasListeners()) {
333 			Set<E> oldSet = cachedSet;
334 			Set<E> newSet = cachedSet = new HashSet<>(getSet());
335 			if (diff == null)
336 				diff = Diffs.computeSetDiff(oldSet, newSet);
337 			if (!diff.isEmpty() || stale) {
338 				stale = false;
339 				fireSetChange(diff);
340 			}
341 		}
342 	}
343 
344 	@Override
isStale()345 	public boolean isStale() {
346 		getterCalled();
347 		return stale;
348 	}
349 
350 	@Override
equals(Object o)351 	public boolean equals(Object o) {
352 		getterCalled();
353 		return getSet().equals(o);
354 	}
355 
356 	@Override
hashCode()357 	public int hashCode() {
358 		getterCalled();
359 		return getSet().hashCode();
360 	}
361 
362 	@Override
getObserved()363 	public Object getObserved() {
364 		return source;
365 	}
366 
367 	@Override
getProperty()368 	public SimpleSetProperty<S, E> getProperty() {
369 		return property;
370 	}
371 
372 	@Override
dispose()373 	public synchronized void dispose() {
374 		if (!isDisposed()) {
375 			if (listener != null)
376 				listener.removeFrom(source);
377 			property = null;
378 			source = null;
379 			listener = null;
380 			stale = false;
381 		}
382 		super.dispose();
383 	}
384 }
385