/******************************************************************************* * Copyright (c) 2008, 2015 Matthew Hall and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Matthew Hall - initial API and implementation (bug 194734) * Stefan Xenos <sxenos@gmail.com> - Bug 335792 ******************************************************************************/ package org.eclipse.core.internal.databinding.property.value; import java.util.AbstractSet; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.eclipse.core.databinding.observable.Diffs; import org.eclipse.core.databinding.observable.IStaleListener; import org.eclipse.core.databinding.observable.ObservableTracker; import org.eclipse.core.databinding.observable.StaleEvent; import org.eclipse.core.databinding.observable.map.AbstractObservableMap; import org.eclipse.core.databinding.observable.map.IMapChangeListener; import org.eclipse.core.databinding.observable.map.IObservableMap; import org.eclipse.core.databinding.observable.map.MapChangeEvent; import org.eclipse.core.databinding.observable.map.MapDiff; import org.eclipse.core.databinding.property.IPropertyObservable; import org.eclipse.core.databinding.property.value.DelegatingValueProperty; import org.eclipse.core.internal.databinding.property.Util; /** * @param <S> * type of the source object * @param <I> * type of the intermediate values * @param <K> * type of the keys to the map * @param <V> * type of the values in the map * @since 1.2 */ public class MapDelegatingValueObservableMap<S, K, I extends S, V> extends AbstractObservableMap<K, V> implements IPropertyObservable<DelegatingValueProperty<S, V>> { private IObservableMap<K, I> masterMap; private DelegatingValueProperty<S, V> detailProperty; private DelegatingCache<S, I, V> cache; private Set<Map.Entry<K, V>> entrySet; class EntrySet extends AbstractSet<Map.Entry<K, V>> { @Override public Iterator<Map.Entry<K, V>> iterator() { return new Iterator<Map.Entry<K, V>>() { Iterator<Map.Entry<K, I>> it = masterMap.entrySet().iterator(); @Override public boolean hasNext() { getterCalled(); return it.hasNext(); } @Override public Map.Entry<K, V> next() { getterCalled(); Map.Entry<K, I> next = it.next(); return new MapEntry(next.getKey()); } @Override public void remove() { it.remove(); } }; } @Override public int size() { return masterMap.size(); } } class MapEntry implements Map.Entry<K, V> { private K key; MapEntry(K key) { this.key = key; } @Override public K getKey() { getterCalled(); return key; } @Override public V getValue() { getterCalled(); if (!masterMap.containsKey(key)) return null; I masterValue = masterMap.get(key); return cache.get(masterValue); } @Override public V setValue(V value) { checkRealm(); if (!masterMap.containsKey(key)) return null; I masterValue = masterMap.get(key); return cache.put(masterValue, value); } @Override public boolean equals(Object o) { getterCalled(); if (o == this) return true; if (o == null) return false; if (!(o instanceof Map.Entry)) return false; Map.Entry<?, ?> that = (Map.Entry<?, ?>) o; return Util.equals(this.getKey(), that.getKey()) && Util.equals(this.getValue(), that.getValue()); } @Override public int hashCode() { getterCalled(); Object value = getValue(); return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); } } private IMapChangeListener<K, I> masterListener = new IMapChangeListener<K, I>() { @Override public void handleMapChange(final MapChangeEvent<? extends K, ? extends I> event) { if (isDisposed()) return; cache.addAll(masterMap.values()); // Need both obsolete and new master values to convert diff MapDiff<K, V> diff = convertDiff(event.diff); cache.retainAll(masterMap.values()); fireMapChange(diff); } private MapDiff<K, V> convertDiff(MapDiff<? extends K, ? extends I> diff) { Map<K, V> oldValues = new HashMap<>(); Map<K, V> newValues = new HashMap<>(); Set<? extends K> addedKeys = diff.getAddedKeys(); for (K key : addedKeys) { I masterValue = diff.getNewValue(key); V newValue = cache.get(masterValue); newValues.put(key, newValue); } Set<? extends K> removedKeys = diff.getRemovedKeys(); for (K key : removedKeys) { I masterValue = diff.getOldValue(key); V oldValue = cache.get(masterValue); oldValues.put(key, oldValue); } Set<K> changedKeys = new HashSet<K>(diff.getChangedKeys()); for (Iterator<K> it = changedKeys.iterator(); it.hasNext();) { K key = it.next(); I oldMasterValue = diff.getOldValue(key); I newMasterValue = diff.getNewValue(key); V oldValue = cache.get(oldMasterValue); V newValue = cache.get(newMasterValue); if (Util.equals(oldValue, newValue)) { it.remove(); } else { oldValues.put(key, oldValue); newValues.put(key, newValue); } } return Diffs.createMapDiff(addedKeys, removedKeys, changedKeys, oldValues, newValues); } }; private IStaleListener staleListener = new IStaleListener() { @Override public void handleStale(StaleEvent staleEvent) { fireStale(); } }; /** * @param map * @param valueProperty */ public MapDelegatingValueObservableMap(IObservableMap<K, I> map, DelegatingValueProperty<S, V> valueProperty) { super(map.getRealm()); this.masterMap = map; this.detailProperty = valueProperty; this.cache = new DelegatingCache<S, I, V>(getRealm(), valueProperty) { @Override void handleValueChange(I masterElement, V oldValue, V newValue) { fireMapChange(keysFor(masterElement), oldValue, newValue); } }; cache.addAll(masterMap.values()); masterMap.addMapChangeListener(masterListener); masterMap.addStaleListener(staleListener); } @Override public Set<Map.Entry<K, V>> entrySet() { getterCalled(); if (entrySet == null) entrySet = new EntrySet(); return entrySet; } private void getterCalled() { ObservableTracker.getterCalled(this); } @Override public V get(Object key) { getterCalled(); Object masterValue = masterMap.get(key); return cache.get(masterValue); } @Override public V put(K key, V value) { if (!masterMap.containsKey(key)) return null; I masterValue = masterMap.get(key); return cache.put(masterValue, value); } @Override public boolean isStale() { getterCalled(); return masterMap.isStale(); } @Override public Object getObserved() { return masterMap; } @Override public DelegatingValueProperty<S, V> getProperty() { return detailProperty; } @Override public Object getKeyType() { return masterMap.getKeyType(); } @Override public Object getValueType() { return detailProperty.getValueType(); } private Set<K> keysFor(I masterValue) { Set<K> keys = new HashSet<>(); for (Map.Entry<K, I> entry : masterMap.entrySet()) { if (entry.getValue() == masterValue) { keys.add(entry.getKey()); } } return keys; } private void fireMapChange(final Set<K> changedKeys, final V oldValue, final V newValue) { fireMapChange(new MapDiff<K, V>() { @Override public Set<K> getAddedKeys() { return Collections.emptySet(); } @Override public Set<K> getRemovedKeys() { return Collections.emptySet(); } @Override public Set<K> getChangedKeys() { return Collections.unmodifiableSet(changedKeys); } @Override public V getOldValue(Object key) { if (changedKeys.contains(key)) return oldValue; return null; } @Override public V getNewValue(Object key) { if (changedKeys.contains(key)) return newValue; return null; } }); } @Override public synchronized void dispose() { if (masterMap != null) { masterMap.removeMapChangeListener(masterListener); masterMap.removeStaleListener(staleListener); masterMap = null; } if (cache != null) { cache.dispose(); cache = null; } masterListener = null; detailProperty = null; super.dispose(); } }