-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented Observable.x(ConversionFunc) to allow external extensions…
… to Observables.
- Loading branch information
Aaron Tull
committed
Jul 20, 2015
1 parent
14f82de
commit 3bc8457
Showing
4 changed files
with
299 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package rx; | ||
|
||
import rx.Observable.OnSubscribe; | ||
|
||
/** | ||
* Converts the values emitted by an Observable's OnSubscribe function to a value. | ||
* | ||
* @param <T> the type of values to be consumed | ||
* @param <R> the return type | ||
*/ | ||
public interface ConversionFunc<T, R> { | ||
/** | ||
* Converts the data produced by the provided {@code OnSubscribe function} to a value. | ||
* | ||
* @param onSubscribe a function that produces data to a Subscriber, usually wrapped by an Observable. | ||
* @return an instance of {@code R} | ||
*/ | ||
public R convert(OnSubscribe<T> onSubscribe); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
package rx; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.concurrent.ConcurrentLinkedQueue; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
import java.util.concurrent.atomic.AtomicReference; | ||
|
||
import static junit.framework.Assert.*; | ||
|
||
import org.junit.Test; | ||
|
||
import rx.Observable.OnSubscribe; | ||
import rx.Observable.Operator; | ||
import rx.exceptions.OnErrorNotImplementedException; | ||
import rx.functions.Func1; | ||
import rx.functions.Func2; | ||
import rx.internal.operators.OperatorFilter; | ||
import rx.internal.operators.OperatorMap; | ||
import rx.observers.TestSubscriber; | ||
import rx.schedulers.Schedulers; | ||
|
||
public class ObservableConversionTest { | ||
|
||
public static class Cylon {} | ||
|
||
public static class Jail { | ||
Object cylon; | ||
|
||
Jail(Object cylon) { | ||
this.cylon = cylon; | ||
} | ||
} | ||
|
||
public static class CylonDetectorObservable<T> { | ||
protected OnSubscribe<T> onSubscribe; | ||
|
||
public static <T> CylonDetectorObservable<T> create(OnSubscribe<T> onSubscribe) { | ||
return new CylonDetectorObservable<T>(onSubscribe); | ||
} | ||
|
||
protected CylonDetectorObservable(OnSubscribe<T> onSubscribe) { | ||
this.onSubscribe = onSubscribe; | ||
} | ||
|
||
public void subscribe(Subscriber<T> subscriber) { | ||
onSubscribe.call(subscriber); | ||
} | ||
|
||
public <R> CylonDetectorObservable<R> lift(Operator<? extends R, ? super T> operator) { | ||
return x(new RobotConversionFunc<T, R>(operator)); | ||
} | ||
|
||
public <R, O> O x(ConversionFunc<T, O> operator) { | ||
return operator.convert(onSubscribe); | ||
} | ||
|
||
public <R> CylonDetectorObservable<? extends R> compose(Func1<CylonDetectorObservable<? super T>, CylonDetectorObservable<? extends R>> transformer) { | ||
return transformer.call(this); | ||
} | ||
|
||
public final CylonDetectorObservable<T> beep(Func1<? super T, Boolean> predicate) { | ||
return lift(new OperatorFilter<T>(predicate)); | ||
} | ||
|
||
public final <R> CylonDetectorObservable<R> boop(Func1<? super T, ? extends R> func) { | ||
return lift(new OperatorMap<T, R>(func)); | ||
} | ||
|
||
public CylonDetectorObservable<String> DESTROY() { | ||
return boop(new Func1<T, String>() { | ||
@Override | ||
public String call(T t) { | ||
Object cylon = ((Jail) t).cylon; | ||
throwOutTheAirlock(cylon); | ||
if (t instanceof Jail) { | ||
String name = cylon.toString(); | ||
return "Cylon '" + name + "' has been destroyed"; | ||
} | ||
else { | ||
return "Cylon 'anonymous' has been destroyed"; | ||
} | ||
}}); | ||
} | ||
|
||
private static void throwOutTheAirlock(Object cylon) { | ||
// ... | ||
} | ||
} | ||
|
||
public static class RobotConversionFunc<T, R> implements ConversionFunc<T, CylonDetectorObservable<R>> { | ||
private Operator<? extends R, ? super T> operator; | ||
|
||
public RobotConversionFunc(Operator<? extends R, ? super T> operator) { | ||
this.operator = operator; | ||
} | ||
|
||
@Override | ||
public CylonDetectorObservable<R> convert(final OnSubscribe<T> onSubscribe) { | ||
return CylonDetectorObservable.create(new OnSubscribe<R>() { | ||
@Override | ||
public void call(Subscriber<? super R> o) { | ||
try { | ||
Subscriber<? super T> st = operator.call(o); | ||
try { | ||
st.onStart(); | ||
onSubscribe.call(st); | ||
} catch (OnErrorNotImplementedException e) { | ||
throw e; | ||
} catch (Throwable e) { | ||
st.onError(e); | ||
} | ||
} catch (OnErrorNotImplementedException e) { | ||
throw e; | ||
} catch (Throwable e) { | ||
o.onError(e); | ||
} | ||
|
||
}}); | ||
} | ||
} | ||
|
||
public static class ConvertToCylonDetector<T> implements ConversionFunc<T, CylonDetectorObservable<T>> { | ||
@Override | ||
public CylonDetectorObservable<T> convert(final OnSubscribe<T> onSubscribe) { | ||
return CylonDetectorObservable.create(onSubscribe); | ||
} | ||
} | ||
|
||
public static class ConvertToObservable<T> implements ConversionFunc<T, Observable<T>> { | ||
@Override | ||
public Observable<T> convert(final OnSubscribe<T> onSubscribe) { | ||
return Observable.create(onSubscribe); | ||
} | ||
} | ||
|
||
@Test | ||
public void testConversionBetweenObservableClasses() { | ||
final TestSubscriber<String> subscriber = new TestSubscriber<String>(new Subscriber<String>(){ | ||
|
||
@Override | ||
public void onCompleted() { | ||
System.out.println("Complete"); | ||
} | ||
|
||
@Override | ||
public void onError(Throwable e) { | ||
System.out.println("error: " + e.getMessage()); | ||
e.printStackTrace(); | ||
} | ||
|
||
@Override | ||
public void onNext(String t) { | ||
System.out.println(t); | ||
}}); | ||
List<Object> crewOfBattlestarGalactica = Arrays.asList(new Object[] {"William Adama", "Laura Roslin", "Lee Adama", new Cylon()}); | ||
Observable.from(crewOfBattlestarGalactica) | ||
.x(new ConvertToCylonDetector<Object>()) | ||
.beep(new Func1<Object, Boolean>(){ | ||
@Override | ||
public Boolean call(Object t) { | ||
return t instanceof Cylon; | ||
}}) | ||
.boop(new Func1<Object, Object>() { | ||
@Override | ||
public Jail call(Object cylon) { | ||
return new Jail(cylon); | ||
}}) | ||
.DESTROY() | ||
.x(new ConvertToObservable<String>()) | ||
.reduce("Cylon Detector finished. Report:\n", new Func2<String, String, String>() { | ||
@Override | ||
public String call(String a, String n) { | ||
return a + n + "\n"; | ||
}}) | ||
.subscribe(subscriber); | ||
subscriber.assertNoErrors(); | ||
subscriber.assertCompleted(); | ||
} | ||
|
||
@Test | ||
public void testConvertToConcurrentQueue() { | ||
final AtomicReference<Throwable> thrown = new AtomicReference<Throwable>(null); | ||
final AtomicBoolean isFinished = new AtomicBoolean(false); | ||
ConcurrentLinkedQueue<Integer> queue = Observable.range(0,5) | ||
.flatMap(new Func1<Integer, Observable<Integer>>(){ | ||
@Override | ||
public Observable<Integer> call(final Integer i) { | ||
return Observable.range(0, 5) | ||
.observeOn(Schedulers.io()) | ||
.map(new Func1<Integer, Integer>(){ | ||
@Override | ||
public Integer call(Integer k) { | ||
try { | ||
Thread.sleep(System.currentTimeMillis() % 100); | ||
} catch (InterruptedException e) { | ||
e.printStackTrace(); | ||
} | ||
return i + k; | ||
}}); | ||
}}) | ||
.x(new ConversionFunc<Integer, ConcurrentLinkedQueue<Integer>>() { | ||
@Override | ||
public ConcurrentLinkedQueue<Integer> convert(OnSubscribe<Integer> onSubscribe) { | ||
final ConcurrentLinkedQueue<Integer> q = new ConcurrentLinkedQueue<Integer>(); | ||
onSubscribe.call(new Subscriber<Integer>(){ | ||
@Override | ||
public void onCompleted() { | ||
isFinished.set(true); | ||
} | ||
|
||
@Override | ||
public void onError(Throwable e) { | ||
thrown.set(e); | ||
} | ||
|
||
@Override | ||
public void onNext(Integer t) { | ||
q.add(t); | ||
}}); | ||
return q; | ||
}}); | ||
|
||
int x = 0; | ||
while(!isFinished.get()) { | ||
Integer i = queue.poll(); | ||
if (i != null) { | ||
x++; | ||
System.out.println(x + " item: " + i); | ||
} | ||
} | ||
assertEquals(null, thrown.get()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters