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.map;
19 
20 import java.util.Collections;
21 import java.util.Map;
22 
23 import org.eclipse.core.databinding.observable.Diffs;
24 import org.eclipse.core.databinding.observable.Realm;
25 import org.eclipse.core.databinding.observable.map.IObservableMap;
26 import org.eclipse.core.databinding.observable.map.MapDiff;
27 import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
28 import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables;
29 import org.eclipse.core.databinding.observable.value.IObservableValue;
30 import org.eclipse.core.databinding.property.value.IValueProperty;
31 import org.eclipse.core.internal.databinding.identity.IdentityMap;
32 import org.eclipse.core.internal.databinding.property.MapPropertyDetailValuesMap;
33 
34 /**
35  * Abstract implementation of IMapProperty
36  *
37  * @param <S> type of the source object
38  * @param <K> type of the keys to the map
39  * @param <V> type of the values in the map
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 MapProperty<S, K, V> implements IMapProperty<S, K, V> {
45 
46 	/**
47 	 * By default, this method returns <code>Collections.EMPTY_MAP</code> in
48 	 * case the source object is <code>null</code>. Otherwise, this method
49 	 * delegates to {@link #doGetMap(Object)}.
50 	 *
51 	 * <p>
52 	 * Clients may override this method if they e.g. want to return a specific
53 	 * default map in case the source object is <code>null</code>.
54 	 * </p>
55 	 *
56 	 * @see #doGetMap(Object)
57 	 *
58 	 * @since 1.3
59 	 */
60 	@Override
getMap(S source)61 	public Map<K, V> getMap(S source) {
62 		if (source == null) {
63 			return Collections.emptyMap();
64 		}
65 		return Collections.unmodifiableMap(doGetMap(source));
66 	}
67 
68 	/**
69 	 * Returns a Map with the current contents of the source's map property
70 	 *
71 	 * @param source
72 	 *            the property source
73 	 * @return a Map with the current contents of the source's map property
74 	 * @since 1.6
75 	 * @noreference This method is not intended to be referenced by clients.
76 	 */
doGetMap(S source)77 	protected Map<K, V> doGetMap(S source) {
78 		IObservableMap<K, V> observable = observe(source);
79 		try {
80 			return new IdentityMap<>(observable);
81 		} finally {
82 			observable.dispose();
83 		}
84 	}
85 
86 	/**
87 	 * @since 1.3
88 	 */
89 	@Override
setMap(S source, Map<K, V> map)90 	public final void setMap(S source, Map<K, V> map) {
91 		if (source != null) {
92 			doSetMap(source, map);
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 map
102 	 *            the new map
103 	 * @since 1.6
104 	 * @noreference This method is not intended to be referenced by clients.
105 	 */
doSetMap(S source, Map<K, V> map)106 	protected void doSetMap(S source, Map<K, V> map) {
107 		MapDiff<K, V> diff = Diffs.computeMapDiff(doGetMap(source), map);
108 		doUpdateMap(source, diff);
109 	}
110 
111 	/**
112 	 * @since 1.3
113 	 */
114 	@Override
updateMap(S source, MapDiff<K, V> diff)115 	public final void updateMap(S source, MapDiff<K, V> diff) {
116 		if (source != null) {
117 			doUpdateMap(source, diff);
118 		}
119 	}
120 
121 	/**
122 	 * Updates the property on the source with the specified change.
123 	 *
124 	 * @param source
125 	 *            the property source
126 	 * @param diff
127 	 *            a diff describing the change
128 	 * @since 1.6
129 	 * @noreference This method is not intended to be referenced by clients.
130 	 */
doUpdateMap(S source, MapDiff<K, V> diff)131 	protected void doUpdateMap(S source, MapDiff<K, V> diff) {
132 		IObservableMap<K, V> observable = observe(source);
133 		try {
134 			diff.applyTo(observable);
135 		} finally {
136 			observable.dispose();
137 		}
138 	}
139 
140 	@Override
observe(S source)141 	public IObservableMap<K, V> observe(S source) {
142 		return observe(Realm.getDefault(), source);
143 	}
144 
145 	@Override
mapFactory()146 	public IObservableFactory<S, IObservableMap<K, V>> mapFactory() {
147 		return this::observe;
148 	}
149 
150 	@Override
mapFactory(final Realm realm)151 	public IObservableFactory<S, IObservableMap<K, V>> mapFactory(final Realm realm) {
152 		return target -> observe(realm, target);
153 	}
154 
155 	@Override
observeDetail(IObservableValue<U> master)156 	public <U extends S> IObservableMap<K, V> observeDetail(IObservableValue<U> master) {
157 		return MasterDetailObservables.detailMap(master, mapFactory(master.getRealm()), getKeyType(), getValueType());
158 	}
159 
160 	@Override
values(IValueProperty<? super V, T> detailValues)161 	public final <T> IMapProperty<S, K, T> values(IValueProperty<? super V, T> detailValues) {
162 		return new MapPropertyDetailValuesMap<>(this, detailValues);
163 	}
164 }