/* * Copyright 2015 Goldman Sachs. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gs.collections.impl.multimap; import java.util.Map; import com.gs.collections.api.RichIterable; import com.gs.collections.api.bag.Bag; import com.gs.collections.api.bag.MutableBag; import com.gs.collections.api.block.function.Function; import com.gs.collections.api.block.function.Function2; import com.gs.collections.api.block.predicate.Predicate; import com.gs.collections.api.block.predicate.Predicate2; import com.gs.collections.api.block.procedure.Procedure; import com.gs.collections.api.block.procedure.Procedure2; import com.gs.collections.api.map.MapIterable; import com.gs.collections.api.multimap.Multimap; import com.gs.collections.api.multimap.MutableMultimap; import com.gs.collections.api.tuple.Pair; import com.gs.collections.impl.UnmodifiableRichIterable; import com.gs.collections.impl.block.factory.Functions; import com.gs.collections.impl.factory.Bags; import com.gs.collections.impl.tuple.Tuples; public abstract class AbstractMultimap<K, V, C extends RichIterable<V>> implements Multimap<K, V> { private static final Function<AbstractMultimap<?, ?, ?>, ?> CREATE_COLLECTION_BLOCK = new Function<AbstractMultimap<?, ?, ?>, RichIterable<?>>() { public RichIterable<?> valueOf(AbstractMultimap<?, ?, ?> multimap) { return multimap.createCollection(); } }; protected abstract MapIterable<K, C> getMap(); /** * Creates the collection of values for a single key. * <p> * Collections with weak, soft, or phantom references are not supported. * Each call to {@code createCollection} should create a new instance. * <p> * The returned collection class determines whether duplicate key-value * pairs are allowed. * * @return an empty collection of values */ protected abstract C createCollection(); protected Function<AbstractMultimap<K, V, C>, C> createCollectionBlock() { return (Function<AbstractMultimap<K, V, C>, C>) (Function<?, ?>) CREATE_COLLECTION_BLOCK; } // Query Operations public boolean containsKey(Object key) { return this.getMap().containsKey(key); } public boolean containsValue(final Object value) { return this.getMap().anySatisfy(new Predicate<C>() { public boolean accept(C collection) { return collection.contains(value); } }); } public boolean containsKeyAndValue(Object key, Object value) { C collection = this.getMap().get(key); return collection != null && collection.contains(value); } // Views public RichIterable<K> keysView() { return this.getMap().keysView(); } public RichIterable<RichIterable<V>> multiValuesView() { return this.getMap().valuesView().collect(new Function<C, RichIterable<V>>() { public RichIterable<V> valueOf(C multiValue) { return UnmodifiableRichIterable.of(multiValue); } }); } public Bag<K> keyBag() { final MutableBag<K> bag = Bags.mutable.empty(); this.getMap().forEachKeyValue(new Procedure2<K, C>() { public void value(K key, C value) { bag.addOccurrences(key, value.size()); } }); return bag; } public RichIterable<V> valuesView() { return this.getMap().valuesView().flatCollect(Functions.<Iterable<V>>identity()); } public RichIterable<Pair<K, RichIterable<V>>> keyMultiValuePairsView() { return this.getMap().keyValuesView().collect(new Function<Pair<K, C>, Pair<K, RichIterable<V>>>() { public Pair<K, RichIterable<V>> valueOf(Pair<K, C> pair) { return Tuples.<K, RichIterable<V>>pair(pair.getOne(), UnmodifiableRichIterable.of(pair.getTwo())); } }); } public RichIterable<Pair<K, V>> keyValuePairsView() { return this.keyMultiValuePairsView().flatCollect(new Function<Pair<K, RichIterable<V>>, Iterable<Pair<K, V>>>() { public Iterable<Pair<K, V>> valueOf(Pair<K, RichIterable<V>> pair) { return pair.getTwo().collect(new KeyValuePairFunction<V, K>(pair.getOne())); } }); } // Comparison and hashing @Override public boolean equals(Object object) { if (object == this) { return true; } if (object instanceof Multimap) { Multimap<?, ?> that = (Multimap<?, ?>) object; return this.getMap().equals(that.toMap()); } return false; } /** * Returns the hash code for this multimap. * <p> * The hash code of a multimap is defined as the hash code of the map view, * as returned by {@link Multimap#toMap()}. * * @see Map#hashCode() */ @Override public int hashCode() { return this.getMap().hashCode(); } /** * Returns a string representation of the multimap, generated by calling * {@code toString} on the map returned by {@link Multimap#toMap()}. * * @return a string representation of the multimap */ @Override public String toString() { return this.getMap().toString(); } public boolean notEmpty() { return !this.isEmpty(); } public void forEachValue(final Procedure<? super V> procedure) { this.getMap().forEachValue(new Procedure<C>() { public void value(C collection) { collection.forEach(procedure); } }); } public void forEachKey(Procedure<? super K> procedure) { this.getMap().forEachKey(procedure); } public void forEachKeyValue(final Procedure2<K, V> procedure) { final Procedure2<V, K> innerProcedure = new Procedure2<V, K>() { public void value(V value, K key) { procedure.value(key, value); } }; this.getMap().forEachKeyValue(new Procedure2<K, C>() { public void value(K key, C collection) { collection.forEachWith(innerProcedure, key); } }); } public void forEachKeyMultiValues(Procedure2<K, ? super Iterable<V>> procedure) { this.getMap().forEachKeyValue(procedure); } public <R extends MutableMultimap<K, V>> R selectKeysValues(final Predicate2<? super K, ? super V> predicate, final R target) { this.getMap().forEachKeyValue(new Procedure2<K, C>() { public void value(final K key, C collection) { RichIterable<V> selectedValues = collection.select(new Predicate<V>() { public boolean accept(V value) { return predicate.accept(key, value); } }); target.putAll(key, selectedValues); } }); return target; } public <R extends MutableMultimap<K, V>> R rejectKeysValues(final Predicate2<? super K, ? super V> predicate, final R target) { this.getMap().forEachKeyValue(new Procedure2<K, C>() { public void value(final K key, C collection) { RichIterable<V> selectedValues = collection.reject(new Predicate<V>() { public boolean accept(V value) { return predicate.accept(key, value); } }); target.putAll(key, selectedValues); } }); return target; } public <R extends MutableMultimap<K, V>> R selectKeysMultiValues(final Predicate2<? super K, ? super Iterable<V>> predicate, final R target) { this.forEachKeyMultiValues(new Procedure2<K, Iterable<V>>() { public void value(K key, Iterable<V> collection) { if (predicate.accept(key, collection)) { target.putAll(key, collection); } } }); return target; } public <R extends MutableMultimap<K, V>> R rejectKeysMultiValues(final Predicate2<? super K, ? super Iterable<V>> predicate, final R target) { this.forEachKeyMultiValues(new Procedure2<K, Iterable<V>>() { public void value(K key, Iterable<V> collection) { if (!predicate.accept(key, collection)) { target.putAll(key, collection); } } }); return target; } private static final class KeyValuePairFunction<V, K> implements Function<V, Pair<K, V>> { private static final long serialVersionUID = 1L; private final K key; private KeyValuePairFunction(K key) { this.key = key; } public Pair<K, V> valueOf(V value) { return Tuples.pair(this.key, value); } } public <K2, V2, R extends MutableMultimap<K2, V2>> R collectKeysValues(final Function2<? super K, ? super V, Pair<K2, V2>> function, final R target) { this.getMap().forEachKeyValue(new Procedure2<K, C>() { public void value(final K key, C collection) { collection.forEach(new Procedure<V>() { public void value(V value) { Pair<K2, V2> pair = function.value(key, value); target.add(pair); } }); } }); return target; } public <V2, R extends MutableMultimap<K, V2>> R collectValues(final Function<? super V, ? extends V2> function, final R target) { this.getMap().forEachKeyValue(new Procedure2<K, C>() { public void value(K key, C collection) { target.putAll(key, collection.collect(function)); } }); return target; } }