/**
* Copyright 2014 Netflix, Inc.
*
* 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 rx.internal.operators;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import rx.Observable.Operator;
import rx.Subscriber;
import rx.functions.Func0;
import rx.functions.Func1;
/**
* Maps the elements of the source observable into a multimap
* (Map<K, Collection<V>>) where each
* key entry has a collection of the source's values.
*
* @see <a href='https://github.com/Netflix/RxJava/issues/97'>Issue #97</a>
*/
public final class OperatorToMultimap<T, K, V> implements Operator<Map<K, Collection<V>>, T> {
/**
* The default multimap factory returning a HashMap.
*/
public static final class DefaultToMultimapFactory<K, V> implements Func0<Map<K, Collection<V>>> {
@Override
public Map<K, Collection<V>> call() {
return new HashMap<K, Collection<V>>();
}
}
/**
* The default collection factory for a key in the multimap returning
* an ArrayList independent of the key.
*/
public static final class DefaultMultimapCollectionFactory<K, V>
implements Func1<K, Collection<V>> {
@Override
public Collection<V> call(K t1) {
return new ArrayList<V>();
}
}
private final Func1<? super T, ? extends K> keySelector;
private final Func1<? super T, ? extends V> valueSelector;
private final Func0<? extends Map<K, Collection<V>>> mapFactory;
private final Func1<? super K, ? extends Collection<V>> collectionFactory;
/**
* ToMultimap with key selector, custom value selector,
* default HashMap factory and default ArrayList collection factory.
*/
public OperatorToMultimap(
Func1<? super T, ? extends K> keySelector,
Func1<? super T, ? extends V> valueSelector) {
this(keySelector, valueSelector,
new DefaultToMultimapFactory<K, V>(),
new DefaultMultimapCollectionFactory<K, V>());
}
/**
* ToMultimap with key selector, custom value selector,
* custom Map factory and default ArrayList collection factory.
*/
public OperatorToMultimap(
Func1<? super T, ? extends K> keySelector,
Func1<? super T, ? extends V> valueSelector,
Func0<? extends Map<K, Collection<V>>> mapFactory) {
this(keySelector, valueSelector,
mapFactory,
new DefaultMultimapCollectionFactory<K, V>());
}
/**
* ToMultimap with key selector, custom value selector,
* custom Map factory and custom collection factory.
*/
public OperatorToMultimap(
Func1<? super T, ? extends K> keySelector,
Func1<? super T, ? extends V> valueSelector,
Func0<? extends Map<K, Collection<V>>> mapFactory,
Func1<? super K, ? extends Collection<V>> collectionFactory) {
this.keySelector = keySelector;
this.valueSelector = valueSelector;
this.mapFactory = mapFactory;
this.collectionFactory = collectionFactory;
}
@Override
public Subscriber<? super T> call(final Subscriber<? super Map<K, Collection<V>>> subscriber) {
return new Subscriber<T>(subscriber) {
private Map<K, Collection<V>> map = mapFactory.call();
@Override
public void onStart() {
request(Long.MAX_VALUE);
}
@Override
public void onNext(T v) {
K key = keySelector.call(v);
V value = valueSelector.call(v);
Collection<V> collection = map.get(key);
if (collection == null) {
collection = collectionFactory.call(key);
map.put(key, collection);
}
collection.add(value);
}
@Override
public void onError(Throwable e) {
map = null;
subscriber.onError(e);
}
@Override
public void onCompleted() {
Map<K, Collection<V>> map0 = map;
map = null;
subscriber.onNext(map0);
subscriber.onCompleted();
}
};
}
}