blob: c51c0c5c629ba95dd50d6920ccdda7636c3d1c15 [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
*******************************************************************************/
package org.eclipse.core.databinding.observable.set;
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.Realm;
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;
/**
*
* <p>
* This class is thread safe. All state accessing methods must be invoked from
* the {@link Realm#isCurrent() current realm}. Methods for adding and removing
* listeners may be invoked from any thread.
* </p>
*
* @since 1.0
*
*/
public class MappedSet extends ObservableSet {
private final IObservableMap wrappedMap;
/*
* Map from values (range elements) to Integer ref counts
*/
private Map valueCounts = new HashMap();
private ISetChangeListener domainListener = new ISetChangeListener() {
public void handleSetChange(SetChangeEvent event) {
Set additions = new HashSet();
for (Iterator it = event.diff.getAdditions().iterator(); it.hasNext();) {
Object added = it.next();
Object mapValue = wrappedMap.get(added);
if (handleAddition(mapValue)) {
additions.add(mapValue);
}
}
Set removals = new HashSet();
for (Iterator it = event.diff.getRemovals().iterator(); it.hasNext();) {
Object removed = it.next();
Object mapValue = wrappedMap.get(removed);
if (handleRemoval(mapValue)) {
removals.add(mapValue);
}
}
fireSetChange(Diffs.createSetDiff(additions, removals));
}
};
private IMapChangeListener mapChangeListener = new IMapChangeListener() {
public void handleMapChange(MapChangeEvent event) {
MapDiff diff = event.diff;
Set additions = new HashSet();
Set removals = new HashSet();
for (Iterator it = diff.getRemovedKeys().iterator(); it.hasNext();) {
Object key = it.next();
Object oldValue = diff.getOldValue(key);
if (handleRemoval(oldValue)) {
removals.add(oldValue);
}
}
for (Iterator it = diff.getChangedKeys().iterator(); it.hasNext();) {
Object key = it.next();
Object oldValue = diff.getOldValue(key);
Object newValue = diff.getNewValue(key);
if (handleRemoval(oldValue)) {
removals.add(oldValue);
}
if (handleAddition(newValue)) {
additions.add(newValue);
}
}
for (Iterator it = diff.getAddedKeys().iterator(); it.hasNext();) {
Object key = it.next();
Object newValue = diff.getNewValue(key);
if (handleAddition(newValue)) {
additions.add(newValue);
}
}
fireSetChange(Diffs.createSetDiff(additions, removals));
}
};
private IObservableSet input;
/**
* @param input
* @param map
*/
public MappedSet(IObservableSet input, IObservableMap map) {
super(input.getRealm(), Collections.EMPTY_SET, Object.class);
setWrappedSet(valueCounts.keySet());
this.wrappedMap = map;
this.input = input;
for (Iterator it = input.iterator(); it.hasNext();) {
Object element = it.next();
Object functionValue = wrappedMap.get(element);
handleAddition(functionValue);
}
input.addSetChangeListener(domainListener);
map.addMapChangeListener(mapChangeListener);
}
/**
* @param mapValue
* @return true if the given mapValue was an addition
*/
protected boolean handleAddition(Object mapValue) {
Integer count = (Integer) valueCounts.get(mapValue);
if (count == null) {
valueCounts.put(mapValue, new Integer(1));
return true;
}
valueCounts.put(mapValue, new Integer(count.intValue() + 1));
return false;
}
/**
* @param mapValue
* @return true if the given mapValue has been removed
*/
protected boolean handleRemoval(Object mapValue) {
Integer count = (Integer) valueCounts.get(mapValue);
if (count.intValue() <= 1) {
valueCounts.remove(mapValue);
return true;
}
valueCounts.put(mapValue, new Integer(count.intValue() - 1));
return false;
}
public synchronized void dispose() {
wrappedMap.removeMapChangeListener(mapChangeListener);
input.removeSetChangeListener(domainListener);
}
}