rx.internal.operators.OperatorGroupByUntil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rxjava-core Show documentation
Show all versions of rxjava-core Show documentation
rxjava-core developed by Netflix
The newest version!
/**
* 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.HashMap;
import java.util.List;
import java.util.Map;
import rx.Observable;
import rx.Observable.OnSubscribe;
import rx.Observable.Operator;
import rx.Subscriber;
import rx.Subscription;
import rx.functions.Func1;
import rx.observables.GroupedObservable;
import rx.observers.SerializedObserver;
import rx.observers.SerializedSubscriber;
import rx.subjects.PublishSubject;
import rx.subjects.Subject;
import rx.subscriptions.CompositeSubscription;
/**
* Groups the elements of an observable sequence according to a specified key selector, value selector and
* duration selector function.
*
* @see MSDN: Observable.GroupByUntil
* @see MSDN: Observable.GroupByUntil
*
* @param the source value type
* @param the group key type
* @param the value type of the groups
* @param the type of the duration
*/
public class OperatorGroupByUntil implements Operator, T> {
final Func1 super T, ? extends K> keySelector;
final Func1 super T, ? extends R> valueSelector;
final Func1 super GroupedObservable, ? extends Observable extends D>> durationSelector;
public OperatorGroupByUntil(
Func1 super T, ? extends K> keySelector,
Func1 super T, ? extends R> valueSelector,
Func1 super GroupedObservable, ? extends Observable extends D>> durationSelector) {
this.keySelector = keySelector;
this.valueSelector = valueSelector;
this.durationSelector = durationSelector;
}
@Override
public Subscriber super T> call(Subscriber super GroupedObservable> child) {
final SerializedSubscriber> s = new SerializedSubscriber>(child);
final CompositeSubscription csub = new CompositeSubscription();
child.add(csub);
return new Subscriber(child) {
final Object guard = new Object();
/** Guarded by guard. */
Map> groups = new HashMap>();
@Override
public void onStart() {
/*
* This operator does not support backpressure as splitting a stream effectively turns it into a "hot observable" and
* blocking any one group would block the entire parent stream. If backpressure is needed on individual groups then
* operators such as `onBackpressureDrop` or `onBackpressureBuffer` should be used.
*/
request(Long.MAX_VALUE);
}
final Subscriber self = this;
@Override
public void onNext(T t) {
K key;
R value;
try {
key = keySelector.call(t);
value = valueSelector.call(t);
} catch (Throwable e) {
onError(e);
return;
}
GroupSubject gs;
boolean newGroup = false;
synchronized (guard) {
if (groups == null) {
return;
}
gs = groups.get(key);
if (gs == null) {
gs = GroupSubject.create(key);
groups.put(key, gs);
newGroup = true;
}
}
if (newGroup) {
final GroupedObservable groupObs = gs.toObservable();
Observable extends D> durationObs;
try {
durationObs = durationSelector.call(groupObs);
} catch (Throwable e) {
onError(e);
return;
}
s.onNext(groupObs);
final K fKey = key;
Subscriber durationSub = new Subscriber() {
boolean once = true;
@Override
public void onNext(D t) {
onCompleted();
}
@Override
public void onError(Throwable e) {
self.onError(e);
}
@Override
public void onCompleted() {
if (once) {
once = false;
expire(fKey, this);
}
}
};
csub.add(durationSub);
durationObs.unsafeSubscribe(durationSub);
}
gs.onNext(value);
}
void expire(K key, Subscription subscription) {
GroupSubject g;
synchronized (guard) {
if (groups == null) {
return;
}
g = groups.remove(key);
}
if (g != null) {
g.onCompleted();
}
csub.remove(subscription);
}
@Override
public void onError(Throwable e) {
List> localGroups;
synchronized (guard) {
if (groups == null) {
return;
}
localGroups = new ArrayList>(groups.values());
groups = null;
}
for (GroupSubject g : localGroups) {
g.onError(e);
}
s.onError(e);
unsubscribe();
}
@Override
public void onCompleted() {
List> localGroups;
synchronized (guard) {
if (groups == null) {
return;
}
localGroups = new ArrayList>(groups.values());
groups = null;
}
for (GroupSubject g : localGroups) {
g.onCompleted();
}
s.onCompleted();
unsubscribe();
}
};
}
/**
* A grouped observable with subject-like behavior.
*
* @param the key type
* @param the value type
*/
public static final class GroupSubject extends Subscriber {
static GroupSubject create(K key) {
Subject publish = BufferUntilSubscriber.create();
return new GroupSubject(key, publish);
}
final Observable publishObservable;
final SerializedObserver publishSerial;
final K key;
public GroupSubject(K key, final Subject publish) {
this.key = key;
this.publishObservable = publish;
this.publishSerial = new SerializedObserver(publish);
}
/**
* @warn javadoc missing
* @return
*/
public GroupedObservable toObservable() {
return new GroupedObservable(key, new OnSubscribe() {
@Override
public void call(Subscriber super R> o) {
publishObservable.unsafeSubscribe(o);
}
});
}
@Override
public void onNext(R args) {
publishSerial.onNext(args);
}
@Override
public void onError(Throwable e) {
publishSerial.onError(e);
}
@Override
public void onCompleted() {
publishSerial.onCompleted();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy