diff --git a/rxjava-core/src/main/java/rx/Observable.java b/rxjava-core/src/main/java/rx/Observable.java index 746362ca78..d3c10fb37c 100644 --- a/rxjava-core/src/main/java/rx/Observable.java +++ b/rxjava-core/src/main/java/rx/Observable.java @@ -75,7 +75,6 @@ import rx.operators.OperationTimeInterval; import rx.operators.OperationTimer; import rx.operators.OperationToMap; -import rx.operators.OperationToMultimap; import rx.operators.OperationUsing; import rx.operators.OperationWindow; import rx.operators.OperatorAll; @@ -124,6 +123,7 @@ import rx.operators.OperatorTimeout; import rx.operators.OperatorTimeoutWithSelector; import rx.operators.OperatorTimestamp; +import rx.operators.OperatorToMultimap; import rx.operators.OperatorToObservableFuture; import rx.operators.OperatorToObservableList; import rx.operators.OperatorToObservableSortedList; @@ -7234,7 +7234,7 @@ public final Observable> toMap(Func1 ke * @see MSDN: Observable.ToLookup */ public final Observable>> toMultimap(Func1 keySelector) { - return create(OperationToMultimap.toMultimap(this, keySelector)); + return lift(new OperatorToMultimap(keySelector, Functions.identity())); } /** @@ -7254,7 +7254,7 @@ public final Observable>> toMultimap(Func1MSDN: Observable.ToLookup */ public final Observable>> toMultimap(Func1 keySelector, Func1 valueSelector) { - return create(OperationToMultimap.toMultimap(this, keySelector, valueSelector)); + return lift(new OperatorToMultimap(keySelector, valueSelector)); } /** @@ -7275,7 +7275,7 @@ public final Observable>> toMultimap(Func1RxJava Wiki: toMap() */ public final Observable>> toMultimap(Func1 keySelector, Func1 valueSelector, Func0>> mapFactory) { - return create(OperationToMultimap.toMultimap(this, keySelector, valueSelector, mapFactory)); + return lift(new OperatorToMultimap(keySelector, valueSelector, mapFactory)); } /** @@ -7298,7 +7298,7 @@ public final Observable>> toMultimap(Func1RxJava Wiki: toMap() */ public final Observable>> toMultimap(Func1 keySelector, Func1 valueSelector, Func0>> mapFactory, Func1> collectionFactory) { - return create(OperationToMultimap.toMultimap(this, keySelector, valueSelector, mapFactory, collectionFactory)); + return lift(new OperatorToMultimap(keySelector, valueSelector, mapFactory, collectionFactory)); } /** diff --git a/rxjava-core/src/main/java/rx/operators/OperationToMultimap.java b/rxjava-core/src/main/java/rx/operators/OperationToMultimap.java deleted file mode 100644 index 416c16acaf..0000000000 --- a/rxjava-core/src/main/java/rx/operators/OperationToMultimap.java +++ /dev/null @@ -1,214 +0,0 @@ -/** - * 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.operators; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -import rx.Observable; -import rx.Observable.OnSubscribeFunc; -import rx.Observer; -import rx.Subscriber; -import rx.Subscription; -import rx.functions.Func0; -import rx.functions.Func1; -import rx.functions.Functions; -import rx.subscriptions.Subscriptions; - -/** - * 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 Issue #97 - */ -public class OperationToMultimap { - /** - * ToMultimap with key selector, identitiy value selector, - * default HashMap factory and default ArrayList collection factory. - */ - public static OnSubscribeFunc>> toMultimap( - Observable source, - Func1 keySelector - ) { - return new ToMultimap( - source, keySelector, Functions. identity(), - new DefaultToMultimapFactory(), - new DefaultMultimapCollectionFactory()); - } - - /** - * ToMultimap with key selector, custom value selector, - * default HashMap factory and default ArrayList collection factory. - */ - public static OnSubscribeFunc>> toMultimap( - Observable source, - Func1 keySelector, - Func1 valueSelector - ) { - return new ToMultimap( - source, keySelector, valueSelector, - new DefaultToMultimapFactory(), - new DefaultMultimapCollectionFactory()); - } - - /** - * ToMultimap with key selector, custom value selector, - * custom Map factory and default ArrayList collection factory. - */ - public static OnSubscribeFunc>> toMultimap( - Observable source, - Func1 keySelector, - Func1 valueSelector, - Func0>> mapFactory - ) { - return new ToMultimap( - source, keySelector, valueSelector, - mapFactory, - new DefaultMultimapCollectionFactory()); - } - - /** - * ToMultimap with key selector, custom value selector, - * custom Map factory and custom collection factory. - */ - public static OnSubscribeFunc>> toMultimap( - Observable source, - Func1 keySelector, - Func1 valueSelector, - Func0>> mapFactory, - Func1> collectionFactory - ) { - return new ToMultimap( - source, keySelector, valueSelector, - mapFactory, - collectionFactory); - } - - /** - * The default multimap factory returning a HashMap. - */ - public static class DefaultToMultimapFactory implements Func0>> { - @Override - public Map> call() { - return new HashMap>(); - } - } - - /** - * The default collection factory for a key in the multimap returning - * an ArrayList independent of the key. - */ - public static class DefaultMultimapCollectionFactory - implements Func1> { - @Override - public Collection call(K t1) { - return new ArrayList(); - } - } - - /** - * Maps the elements of the source observable int a multimap customized - * by various selectors and factories. - */ - public static class ToMultimap implements OnSubscribeFunc>> { - private final Observable source; - private final Func1 keySelector; - private final Func1 valueSelector; - private final Func0>> mapFactory; - private final Func1> collectionFactory; - - public ToMultimap( - Observable source, - Func1 keySelector, - Func1 valueSelector, - Func0>> mapFactory, - Func1> collectionFactory) { - this.source = source; - this.keySelector = keySelector; - this.valueSelector = valueSelector; - this.mapFactory = mapFactory; - this.collectionFactory = collectionFactory; - } - - @Override - public Subscription onSubscribe(Observer>> t1) { - Map> map; - try { - map = mapFactory.call(); - } catch (Throwable t) { - t1.onError(t); - return Subscriptions.empty(); - } - return source.unsafeSubscribe(new ToMultimapObserver( - t1, keySelector, valueSelector, map, collectionFactory - )); - } - - /** - * Observer that collects the source values of Ts into a multimap. - */ - public static class ToMultimapObserver extends Subscriber { - private final Func1 keySelector; - private final Func1 valueSelector; - private final Func1> collectionFactory; - private Map> map; - private Observer>> t1; - - public ToMultimapObserver( - Observer>> t1, - Func1 keySelector, - Func1 valueSelector, - Map> map, - Func1> collectionFactory) { - this.t1 = t1; - this.keySelector = keySelector; - this.valueSelector = valueSelector; - this.collectionFactory = collectionFactory; - this.map = map; - } - - @Override - public void onNext(T args) { - K key = keySelector.call(args); - V value = valueSelector.call(args); - Collection 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; - t1.onError(e); - } - - @Override - public void onCompleted() { - Map> map0 = map; - map = null; - t1.onNext(map0); - t1.onCompleted(); - } - } - } -} diff --git a/rxjava-core/src/main/java/rx/operators/OperatorToMultimap.java b/rxjava-core/src/main/java/rx/operators/OperatorToMultimap.java new file mode 100644 index 0000000000..f0643dcd93 --- /dev/null +++ b/rxjava-core/src/main/java/rx/operators/OperatorToMultimap.java @@ -0,0 +1,137 @@ +/** + * 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.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 Issue #97 + */ +public final class OperatorToMultimap implements Operator>, T> { + /** + * The default multimap factory returning a HashMap. + */ + public static final class DefaultToMultimapFactory implements Func0>> { + @Override + public Map> call() { + return new HashMap>(); + } + } + + /** + * The default collection factory for a key in the multimap returning + * an ArrayList independent of the key. + */ + public static final class DefaultMultimapCollectionFactory + implements Func1> { + @Override + public Collection call(K t1) { + return new ArrayList(); + } + } + + private final Func1 keySelector; + private final Func1 valueSelector; + private final Func0>> mapFactory; + private final Func1> collectionFactory; + + /** + * ToMultimap with key selector, custom value selector, + * default HashMap factory and default ArrayList collection factory. + */ + public OperatorToMultimap( + Func1 keySelector, + Func1 valueSelector) { + this(keySelector, valueSelector, + new DefaultToMultimapFactory(), + new DefaultMultimapCollectionFactory()); + } + + /** + * ToMultimap with key selector, custom value selector, + * custom Map factory and default ArrayList collection factory. + */ + public OperatorToMultimap( + Func1 keySelector, + Func1 valueSelector, + Func0>> mapFactory) { + this(keySelector, valueSelector, + mapFactory, + new DefaultMultimapCollectionFactory()); + } + + /** + * ToMultimap with key selector, custom value selector, + * custom Map factory and custom collection factory. + */ + public OperatorToMultimap( + Func1 keySelector, + Func1 valueSelector, + Func0>> mapFactory, + Func1> collectionFactory) { + this.keySelector = keySelector; + this.valueSelector = valueSelector; + this.mapFactory = mapFactory; + this.collectionFactory = collectionFactory; + } + + @Override + public Subscriber call(final Subscriber>> subscriber) { + return new Subscriber(subscriber) { + private Map> map = mapFactory.call(); + + @Override + public void onNext(T v) { + K key = keySelector.call(v); + V value = valueSelector.call(v); + Collection 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> map0 = map; + map = null; + subscriber.onNext(map0); + subscriber.onCompleted(); + } + + }; + } +} \ No newline at end of file diff --git a/rxjava-core/src/test/java/rx/operators/OperationToMultimapTest.java b/rxjava-core/src/test/java/rx/operators/OperatorToMultimapTest.java similarity index 82% rename from rxjava-core/src/test/java/rx/operators/OperationToMultimapTest.java rename to rxjava-core/src/test/java/rx/operators/OperatorToMultimapTest.java index 2eb608d851..42dfb16fb4 100644 --- a/rxjava-core/src/test/java/rx/operators/OperationToMultimapTest.java +++ b/rxjava-core/src/test/java/rx/operators/OperatorToMultimapTest.java @@ -39,10 +39,10 @@ import rx.functions.Func0; import rx.functions.Func1; import rx.functions.Functions; -import rx.operators.OperationToMultimap.DefaultMultimapCollectionFactory; -import rx.operators.OperationToMultimap.DefaultToMultimapFactory; +import rx.operators.OperatorToMultimap.DefaultMultimapCollectionFactory; +import rx.operators.OperatorToMultimap.DefaultToMultimapFactory; -public class OperationToMultimapTest { +public class OperatorToMultimapTest { @Mock Observer objectObserver; @@ -68,7 +68,7 @@ public String call(String t1) { public void testToMultimap() { Observable source = Observable.from("a", "b", "cc", "dd"); - Observable>> mapped = Observable.create(OperationToMultimap.toMultimap(source, lengthFunc)); + Observable>> mapped = source.toMultimap(lengthFunc); Map> expected = new HashMap>(); expected.put(1, Arrays.asList("a", "b")); @@ -85,7 +85,7 @@ public void testToMultimap() { public void testToMultimapWithValueSelector() { Observable source = Observable.from("a", "b", "cc", "dd"); - Observable>> mapped = Observable.create(OperationToMultimap.toMultimap(source, lengthFunc, duplicate)); + Observable>> mapped = source.toMultimap(lengthFunc, duplicate); Map> expected = new HashMap>(); expected.put(1, Arrays.asList("aa", "bb")); @@ -114,10 +114,9 @@ protected boolean removeEldestEntry(Map.Entry> eldes } }; - Observable>> mapped = Observable.create( - OperationToMultimap.toMultimap(source, - lengthFunc, Functions. identity(), - mapFactory, new DefaultMultimapCollectionFactory())); + Observable>> mapped = source.toMultimap( + lengthFunc, Functions.identity(), + mapFactory, new DefaultMultimapCollectionFactory()); Map> expected = new HashMap>(); expected.put(2, Arrays.asList("cc", "dd")); @@ -146,10 +145,9 @@ public Collection call(Integer t1) { } }; - Observable>> mapped = Observable.create( - OperationToMultimap.toMultimap( - source, lengthFunc, Functions. identity(), - new DefaultToMultimapFactory(), collectionFactory)); + Observable>> mapped = source.toMultimap( + lengthFunc, Functions.identity(), + new DefaultToMultimapFactory(), collectionFactory); Map> expected = new HashMap>(); expected.put(2, Arrays.asList("cc", "dd")); @@ -176,7 +174,7 @@ public Integer call(String t1) { } }; - Observable>> mapped = Observable.create(OperationToMultimap.toMultimap(source, lengthFuncErr)); + Observable>> mapped = source.toMultimap(lengthFuncErr); Map> expected = new HashMap>(); expected.put(1, Arrays.asList("a", "b")); @@ -203,7 +201,7 @@ public String call(String t1) { } }; - Observable>> mapped = Observable.create(OperationToMultimap.toMultimap(source, lengthFunc, duplicateErr)); + Observable>> mapped = source.toMultimap(lengthFunc, duplicateErr); Map> expected = new HashMap>(); expected.put(1, Arrays.asList("aa", "bb")); @@ -227,8 +225,7 @@ public Map> call() { } }; - Observable>> mapped = Observable.create( - OperationToMultimap.toMultimap(source, lengthFunc, Functions. identity(), mapFactory)); + Observable>> mapped = source.toMultimap(lengthFunc, Functions.identity(), mapFactory); Map> expected = new HashMap>(); expected.put(2, Arrays.asList("cc", "dd")); @@ -257,9 +254,7 @@ public Collection call(Integer t1) { } }; - Observable>> mapped = Observable.create( - OperationToMultimap.toMultimap( - source, lengthFunc, Functions. identity(), new DefaultToMultimapFactory(), collectionFactory)); + Observable>> mapped = source.toMultimap(lengthFunc, Functions.identity(), new DefaultToMultimapFactory(), collectionFactory); Map> expected = new HashMap>(); expected.put(2, Arrays.asList("cc", "dd"));