blob: baa57b0d462c1bc8b6b4d879c9a8f24f53218d81 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2007 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Matthew Hall - bugs 241585, 247394, 226289
*******************************************************************************/
package org.eclipse.core.databinding.observable.map;
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.set.IObservableSet;
import org.eclipse.core.databinding.observable.set.ISetChangeListener;
import org.eclipse.core.databinding.observable.set.SetChangeEvent;
/**
* Maps objects to one of their attributes. Tracks changes to the underlying
* observable set of objects (keys), as well as changes to attribute values.
*/
public abstract class ComputedObservableMap extends AbstractObservableMap {
private IObservableSet keySet;
private Object valueType;
private ISetChangeListener setChangeListener = new ISetChangeListener() {
public void handleSetChange(SetChangeEvent event) {
Set addedKeys = new HashSet(event.diff.getAdditions());
Set removedKeys = new HashSet(event.diff.getRemovals());
Map oldValues = new HashMap();
Map newValues = new HashMap();
for (Iterator it = removedKeys.iterator(); it.hasNext();) {
Object removedKey = it.next();
Object oldValue = doGet(removedKey);
unhookListener(removedKey);
if (oldValue != null) {
oldValues.put(removedKey, oldValue);
}
}
for (Iterator it = addedKeys.iterator(); it.hasNext();) {
Object addedKey = it.next();
hookListener(addedKey);
Object newValue = doGet(addedKey);
newValues.put(addedKey, newValue);
}
fireMapChange(Diffs.createMapDiff(addedKeys, removedKeys,
Collections.EMPTY_SET, oldValues, newValues));
}
};
private Set entrySet = new EntrySet();
private class EntrySet extends AbstractSet {
public Iterator iterator() {
final Iterator keyIterator = keySet.iterator();
return new Iterator() {
public boolean hasNext() {
return keyIterator.hasNext();
}
public Object next() {
final Object key = keyIterator.next();
return new Map.Entry() {
public Object getKey() {
return key;
}
public Object getValue() {
return get(getKey());
}
public Object setValue(Object value) {
return put(getKey(), value);
}
};
}
public void remove() {
keyIterator.remove();
}
};
}
public int size() {
return keySet.size();
}
}
/**
* @param keySet
*/
public ComputedObservableMap(IObservableSet keySet) {
this(keySet, null);
}
/**
* @param keySet
* @param valueType
* @since 1.2
*/
public ComputedObservableMap(IObservableSet keySet, Object valueType) {
super(keySet.getRealm());
this.keySet = keySet;
this.valueType = valueType;
}
/**
* @deprecated Subclasses are no longer required to call this method.
*/
protected void init() {
}
protected void firstListenerAdded() {
hookListeners();
}
protected void lastListenerRemoved() {
unhookListeners();
}
private void hookListeners() {
if (keySet != null) {
keySet.addSetChangeListener(setChangeListener);
for (Iterator it = this.keySet.iterator(); it.hasNext();) {
Object key = it.next();
hookListener(key);
}
}
}
private void unhookListeners() {
if (keySet != null) {
keySet.removeSetChangeListener(setChangeListener);
Object[] keys = keySet.toArray();
for (int i = 0; i < keys.length; i++) {
unhookListener(keys[i]);
}
}
}
protected final void fireSingleChange(Object key, Object oldValue,
Object newValue) {
fireMapChange(Diffs.createMapDiffSingleChange(key, oldValue, newValue));
}
public Object getKeyType() {
return keySet.getElementType();
}
public Object getValueType() {
return valueType;
}
public Set entrySet() {
return entrySet;
}
public Set keySet() {
return keySet;
}
final public Object get(Object key) {
if (!keySet.contains(key))
return null;
return doGet(key);
}
final public Object put(Object key, Object value) {
if (!keySet.contains(key))
return null;
return doPut(key, value);
}
/**
* @param removedKey
*/
protected abstract void unhookListener(Object removedKey);
/**
* @param addedKey
*/
protected abstract void hookListener(Object addedKey);
/**
* @param key
* @return the value for the given key
*/
protected abstract Object doGet(Object key);
/**
* @param key
* @param value
* @return the old value for the given key
*/
protected abstract Object doPut(Object key, Object value);
public synchronized void dispose() {
unhookListeners();
entrySet = null;
keySet = null;
setChangeListener = null;
super.dispose();
}
}