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 - bug 195222
14  *     Ovidio Mallo - bug 331348
15  *     Stefan Xenos <sxenos@gmail.com> - Bug 335792
16  ******************************************************************************/
17 
18 package org.eclipse.core.databinding.property.set;
19 
20 import java.util.Collections;
21 import java.util.Set;
22 
23 import org.eclipse.core.databinding.observable.Diffs;
24 import org.eclipse.core.databinding.observable.Realm;
25 import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
26 import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables;
27 import org.eclipse.core.databinding.observable.set.IObservableSet;
28 import org.eclipse.core.databinding.observable.set.SetDiff;
29 import org.eclipse.core.databinding.observable.value.IObservableValue;
30 import org.eclipse.core.databinding.property.map.IMapProperty;
31 import org.eclipse.core.databinding.property.value.IValueProperty;
32 import org.eclipse.core.internal.databinding.identity.IdentitySet;
33 import org.eclipse.core.internal.databinding.property.SetPropertyDetailValuesMap;
34 
35 /**
36  * Abstract implementation of ISetProperty
37  *
38  * @param <S> type of the source object
39  * @param <E> type of the elements in the set
40  * @since 1.2
41  * @implNote If methods are added to the interface which this class implements
42  *           then implementations of those methods must be added to this class.
43  */
44 public abstract class SetProperty<S, E> implements ISetProperty<S, E> {
45 
46 	/**
47 	 * By default, this method returns <code>Collections.EMPTY_SET</code> in
48 	 * case the source object is <code>null</code>. Otherwise, this method
49 	 * delegates to {@link #doGetSet(Object)}.
50 	 *
51 	 * <p>
52 	 * Clients may override this method if they e.g. want to return a specific
53 	 * default set in case the source object is <code>null</code>.
54 	 * </p>
55 	 *
56 	 * @see #doGetSet(Object)
57 	 *
58 	 * @since 1.3
59 	 */
60 	@Override
getSet(S source)61 	public Set<E> getSet(S source) {
62 		if (source == null) {
63 			return Collections.emptySet();
64 		}
65 		return Collections.unmodifiableSet(doGetSet(source));
66 	}
67 
68 	/**
69 	 * Returns a Set with the current contents of the source's set property
70 	 *
71 	 * @param source
72 	 *            the property source
73 	 * @return a Set with the current contents of the source's set property
74 	 * @since 1.6
75 	 * @noreference This method is not intended to be referenced by clients.
76 	 */
doGetSet(S source)77 	protected Set<E> doGetSet(S source) {
78 		IObservableSet<E> observable = observe(source);
79 		try {
80 			return new IdentitySet<>(observable);
81 		} finally {
82 			observable.dispose();
83 		}
84 	}
85 
86 	/**
87 	 * @since 1.3
88 	 */
89 	@Override
setSet(S source, Set<E> set)90 	public final void setSet(S source, Set<E> set) {
91 		if (source != null) {
92 			doSetSet(source, set);
93 		}
94 	}
95 
96 	/**
97 	 * Updates the property on the source with the specified change.
98 	 *
99 	 * @param source
100 	 *            the property source
101 	 * @param set
102 	 *            the new set
103 	 * @since 1.6
104 	 * @noreference This method is not intended to be referenced by clients.
105 	 */
doSetSet(S source, Set<E> set)106 	protected void doSetSet(S source, Set<E> set) {
107 		doUpdateSet(source, Diffs.computeSetDiff(doGetSet(source), set));
108 	}
109 
110 	/**
111 	 * @since 1.3
112 	 */
113 	@Override
updateSet(S source, SetDiff<E> diff)114 	public final void updateSet(S source, SetDiff<E> diff) {
115 		if (source != null && !diff.isEmpty()) {
116 			doUpdateSet(source, diff);
117 		}
118 	}
119 
120 	/**
121 	 * Updates the property on the source with the specified change.
122 	 *
123 	 * @param source
124 	 *            the property source
125 	 * @param diff
126 	 *            a diff describing the change
127 	 * @since 1.6
128 	 * @noreference This method is not intended to be referenced by clients.
129 	 */
doUpdateSet(S source, SetDiff<E> diff)130 	protected void doUpdateSet(S source, SetDiff<E> diff) {
131 		IObservableSet<E> observable = observe(source);
132 		try {
133 			diff.applyTo(observable);
134 		} finally {
135 			observable.dispose();
136 		}
137 	}
138 
139 	@Override
observe(S source)140 	public IObservableSet<E> observe(S source) {
141 		return observe(Realm.getDefault(), source);
142 	}
143 
144 	@Override
setFactory()145 	public IObservableFactory<S, IObservableSet<E>> setFactory() {
146 		return this::observe;
147 	}
148 
149 	@Override
setFactory(final Realm realm)150 	public IObservableFactory<S, IObservableSet<E>> setFactory(final Realm realm) {
151 		return target -> observe(realm, target);
152 	}
153 
154 	@Override
observeDetail(IObservableValue<U> master)155 	public <U extends S> IObservableSet<E> observeDetail(IObservableValue<U> master) {
156 		return MasterDetailObservables.detailSet(master, setFactory(master.getRealm()), getElementType());
157 	}
158 
159 	@Override
values(IValueProperty<? super E, T> detailValues)160 	public final <T> IMapProperty<S, E, T> values(IValueProperty<? super E, T> detailValues) {
161 		return new SetPropertyDetailValuesMap<>(this, detailValues);
162 	}
163 }
164