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 }