CombinedMapsView.java
package no.motif.maps;
import static no.motif.Base.containedIn;
import static no.motif.Base.first;
import static no.motif.Base.is;
import static no.motif.Base.where;
import static no.motif.Iterate.on;
import static no.motif.Maps.keyIn;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import no.motif.Maps;
import no.motif.f.Fn;
/**
* Documentation in {@link no.motif.Maps#combine(Map, Map) Maps.combine(..)}
*
* @param <K1> The key type of the first map, and the effective key type of the map.
* @param <V1> The value type of the first map
* @param <K2> The key type of the second map.
* @param <V2> The value type of the second map, and the effective value type of the map.
*
* @see no.motif.Maps#combine(Map, Map)
*/
public class CombinedMapsView<K1, V1, K2, V2> implements Map<K1, V2> {
private final Map<K1, V1> m1;
private final Map<K2, V2> m2;
private final Fn<? super V1, ? extends K2> connector;
private final Fn<Entry<?, V1>, ? extends K2> m1MappedValue;
public CombinedMapsView(Map<K1, V1> m1, Fn<? super V1, ? extends K2> connector, Map<K2, V2> m2) {
this.m1 = m1;
this.m2 = m2;
this.connector = connector;
this.m1MappedValue = first(Maps.<V1>value()).then(connector);
}
@Override
public int size() {
return on(m1.values()).map(connector).filter(containedIn(m2.keySet())).collect().size();
}
@Override
public boolean isEmpty() {
return m1.isEmpty()
|| m2.isEmpty()
|| on(m1).filter(where(m1MappedValue, keyIn(m2))).isEmpty();
}
@Override
public boolean containsKey(Object key) {
return m1.containsKey(key) && m2.containsKey(m1.get(key));
}
@Override
public boolean containsValue(Object value) {
if (m2.containsValue(value)) {
Entry<K2, V2> entry = on(m2).filter(where(Maps.<V2>value(), is(value))).head().get();
return m1.containsValue(entry.getKey());
}
return false;
}
@Override
public V2 get(Object key) {
if (!m1.containsKey(key)) return null;
else return m2.get(m1.get(key));
}
@Override
public V2 put(K1 key, V2 value) {
throw new UnsupportedOperationException("put is not supported");
}
@Override
public V2 remove(Object key) {
if (this.containsKey(key)) {
return m2.remove(m1.remove(key));
}
return null;
}
@Override
public void putAll(Map<? extends K1, ? extends V2> m) {
throw new UnsupportedOperationException("putAll is not supported");
}
@Override
public void clear() {
for (Entry<K1, V1> entry : on(m1).filter(where(m1MappedValue, keyIn(m2))).eval()) {
m1.remove(entry.getKey());
m2.remove(entry.getValue());
}
}
@Override
public Set<K1> keySet() {
return on(m1)
.filter(where(m1MappedValue, keyIn(m2)))
.map(Maps.<K1>key())
.collectIn(new LinkedHashSet<K1>());
}
@Override
public Collection<V2> values() {
return on(m2)
.filter(where(Maps.<K2>key(), containedIn(on(m1).map(m1MappedValue))))
.map(Maps.<V2>value())
.collect();
}
@Override
public Set<Entry<K1, V2>> entrySet() {
return on(m1)
.filter(where(m1MappedValue, keyIn(m2)))
.map(toEntry)
.collectIn(new LinkedHashSet<Entry<K1, V2>>());
}
private final Fn<Entry<K1, V1>, Entry<K1, V2>> toEntry = new Fn<Entry<K1, V1>, Entry<K1, V2>>() {
@Override
public Entry<K1, V2> $(Entry<K1, V1> entry) {
return new MapsViewEntry(entry.getKey(), connector.$(entry.getValue()));
}};
private final class MapsViewEntry implements Map.Entry<K1, V2> {
private final K1 key;
private final K2 key2;
public MapsViewEntry(K1 key, K2 key2) {
this.key = key;
this.key2 = key2;
}
@Override
public K1 getKey() {
return key;
}
@Override
public V2 getValue() {
return m2.get(key2);
}
@Override
public V2 setValue(V2 value) {
return m2.put(key2, value);
}
}
}