1 /*******************************************************************************
2  * Copyright (c) 2006, 2017 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  *     Stefan Xenos <sxenos@gmail.com> - Bug 335792
14  *******************************************************************************/
15 
16 package org.eclipse.core.databinding.observable.set;
17 
18 import java.util.HashSet;
19 import java.util.Set;
20 
21 import org.eclipse.core.databinding.observable.Diffs;
22 import org.eclipse.core.databinding.observable.Realm;
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.ListDiffEntry;
26 
27 /**
28  * Observable set backed by an observable list. The wrapped list must not
29  * contain duplicate elements.
30  *
31  * <p>
32  * This class is thread safe. All state accessing methods must be invoked from
33  * the {@link Realm#isCurrent() current realm}. Methods for adding and removing
34  * listeners may be invoked from any thread.
35  * </p>
36  *
37  * @param <E>
38  *            the type of elements in the collection
39  * @since 1.0
40  *
41  */
42 public class ListToSetAdapter<E> extends ObservableSet<E> {
43 
44 	private final IObservableList<E> list;
45 
46 	private IListChangeListener<E> listener = event -> {
47 		Set<E> added = new HashSet<>();
48 		Set<E> removed = new HashSet<>();
49 		ListDiffEntry<? extends E>[] differences = event.diff.getDifferences();
50 		for (ListDiffEntry<? extends E> entry : differences) {
51 			E element = entry.getElement();
52 			if (entry.isAddition()) {
53 				if (wrappedSet.add(element)) {
54 					if (!removed.remove(element))
55 						added.add(element);
56 				}
57 			} else {
58 				if (wrappedSet.remove(element)) {
59 					removed.add(element);
60 					added.remove(element);
61 				}
62 			}
63 		}
64 		fireSetChange(Diffs.createSetDiff(added, removed));
65 	};
66 
67 	/**
68 	 * @param list the list to adapt
69 	 */
ListToSetAdapter(IObservableList<E> list)70 	public ListToSetAdapter(IObservableList<E> list) {
71 		super(list.getRealm(), new HashSet<E>(), list.getElementType());
72 		this.list = list;
73 		wrappedSet.addAll(list);
74 		this.list.addListChangeListener(listener);
75 	}
76 
77 	@Override
dispose()78 	public synchronized void dispose() {
79 		super.dispose();
80 		if (list != null && listener != null) {
81 			list.removeListChangeListener(listener);
82 			listener = null;
83 		}
84 	}
85 
86 }
87