Skip to content

Commit f55ef5c

Browse files
Merge pull request #160 from benjchristensen/mairbek_single-merge
Manual merge of mairbek/single Pull #157
2 parents ef3ff56 + 4c98d37 commit f55ef5c

File tree

3 files changed

+275
-0
lines changed

3 files changed

+275
-0
lines changed

language-adaptors/rxjava-groovy/src/test/groovy/rx/lang/groovy/ObservableTests.groovy

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,16 @@ def class ObservableTests {
244244
def val = Observable.toObservable("one", "two").lastOrDefault("default", { x -> x.length() > 3})
245245
assertEquals("default", val)
246246
}
247+
248+
public void testSingle1() {
249+
def s = Observable.toObservable("one").single({ x -> x.length() == 3})
250+
assertEquals("one", s)
251+
}
252+
253+
@Test(expected = IllegalStateException.class)
254+
public void testSingle2() {
255+
Observable.toObservable("one", "two").single({ x -> x.length() == 3})
256+
}
247257

248258
def class AsyncObservable implements Func1<Observer<Integer>, Subscription> {
249259

rxjava-core/src/main/java/rx/Observable.java

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,66 @@ public void call(Object args) {
408408
});
409409
}
410410

411+
/**
412+
* Returns the only element of an observable sequence and throws an exception if there is not exactly one element in the observable sequence.
413+
*
414+
* @return The single element in the observable sequence.
415+
*/
416+
public T single() {
417+
return single(this);
418+
}
419+
420+
/**
421+
* Returns the only element of an observable sequence that matches the predicate and throws an exception if there is not exactly one element in the observable sequence.
422+
*
423+
* @param predicate A predicate function to evaluate for elements in the sequence.
424+
* @return The single element in the observable sequence.
425+
*/
426+
public T single(Func1<T, Boolean> predicate) {
427+
return single(this, predicate);
428+
}
429+
430+
/**
431+
* Returns the only element of an observable sequence that matches the predicate and throws an exception if there is not exactly one element in the observable sequence.
432+
*
433+
* @param predicate A predicate function to evaluate for elements in the sequence.
434+
* @return The single element in the observable sequence.
435+
*/
436+
public T single(Object predicate) {
437+
return single(this, predicate);
438+
}
439+
440+
/**
441+
* Returns the only element of an observable sequence, or a default value if the observable sequence is empty.
442+
*
443+
* @param defaultValue default value for a sequence.
444+
* @return The single element in the observable sequence, or a default value if no value is found.
445+
*/
446+
public T singleOrDefault(T defaultValue) {
447+
return singleOrDefault(this, defaultValue);
448+
}
449+
450+
/**
451+
* Returns the only element of an observable sequence that matches the predicate, or a default value if no value is found.
452+
* @param defaultValue default value for a sequence.
453+
* @param predicate A predicate function to evaluate for elements in the sequence.
454+
* @return The single element in the observable sequence, or a default value if no value is found.
455+
*/
456+
public T singleOrDefault(T defaultValue, Func1<T, Boolean> predicate) {
457+
return singleOrDefault(this, defaultValue, predicate);
458+
}
459+
460+
/**
461+
* Returns the only element of an observable sequence that matches the predicate, or a default value if no value is found.
462+
*
463+
* @param defaultValue default value for a sequence.
464+
* @param predicate A predicate function to evaluate for elements in the sequence.
465+
* @return The single element in the observable sequence, or a default value if no value is found.
466+
*/
467+
public T singleOrDefault(T defaultValue, Object predicate) {
468+
return singleOrDefault(this, defaultValue, predicate);
469+
}
470+
411471
/**
412472
* Allow the {@link RxJavaErrorHandler} to receive the exception from onError.
413473
*
@@ -1520,6 +1580,7 @@ public static <T> Observable<T> takeWhile(final Observable<T> items, Func1<T, Bo
15201580
* @return
15211581
*/
15221582
public static <T> Observable<T> takeWhile(final Observable<T> items, Object predicate) {
1583+
@SuppressWarnings("rawtypes")
15231584
final FuncN _f = Functions.from(predicate);
15241585

15251586
return takeWhile(items, new Func1<T, Boolean>() {
@@ -1542,6 +1603,7 @@ public static <T> Observable<T> takeWhileWithIndex(final Observable<T> items, Fu
15421603
}
15431604

15441605
public static <T> Observable<T> takeWhileWithIndex(final Observable<T> items, Object predicate) {
1606+
@SuppressWarnings("rawtypes")
15451607
final FuncN _f = Functions.from(predicate);
15461608

15471609
return create(OperationTake.takeWhileWithIndex(items, new Func2<T, Integer, Boolean>() {
@@ -1648,6 +1710,127 @@ public Iterator<T> iterator() {
16481710
};
16491711
}
16501712

1713+
/**
1714+
* Returns the only element of an observable sequence and throws an exception if there is not exactly one element in the observable sequence.
1715+
*
1716+
* @param that
1717+
* the source Observable
1718+
* @return The single element in the observable sequence.
1719+
* @throws IllegalStateException
1720+
* if there is not exactly one element in the observable sequence
1721+
*/
1722+
public static <T> T single(Observable<T> that) {
1723+
return singleOrDefault(that, false, null);
1724+
}
1725+
1726+
/**
1727+
* Returns the only element of an observable sequence that matches the predicate and throws an exception if there is not exactly one element in the observable sequence.
1728+
*
1729+
* @param that
1730+
* the source Observable
1731+
* @param predicate
1732+
* A predicate function to evaluate for elements in the sequence.
1733+
* @return The single element in the observable sequence.
1734+
* @throws IllegalStateException
1735+
* if there is not exactly one element in the observable sequence that matches the predicate
1736+
*/
1737+
public static <T> T single(Observable<T> that, Func1<T, Boolean> predicate) {
1738+
return single(that.filter(predicate));
1739+
}
1740+
1741+
/**
1742+
* Returns the only element of an observable sequence that matches the predicate and throws an exception if there is not exactly one element in the observable sequence.
1743+
*
1744+
* @param that
1745+
* the source Observable
1746+
* @param predicate
1747+
* A predicate function to evaluate for elements in the sequence.
1748+
* @return The single element in the observable sequence.
1749+
* @throws IllegalStateException
1750+
* if there is not exactly one element in the observable sequence that matches the predicate
1751+
*/
1752+
public static <T> T single(Observable<T> that, Object predicate) {
1753+
@SuppressWarnings("rawtypes")
1754+
final FuncN _f = Functions.from(predicate);
1755+
1756+
return single(that, new Func1<T, Boolean>() {
1757+
@Override
1758+
public Boolean call(T t) {
1759+
return (Boolean) _f.call(t);
1760+
}
1761+
});
1762+
}
1763+
1764+
/**
1765+
* Returns the only element of an observable sequence, or a default value if the observable sequence is empty.
1766+
*
1767+
* @param that
1768+
* the source Observable
1769+
* @param defaultValue
1770+
* default value for a sequence.
1771+
* @return The single element in the observable sequence, or a default value if no value is found.
1772+
*/
1773+
public static <T> T singleOrDefault(Observable<T> that, T defaultValue) {
1774+
return singleOrDefault(that, true, defaultValue);
1775+
}
1776+
1777+
/**
1778+
* Returns the only element of an observable sequence that matches the predicate, or a default value if no value is found.
1779+
*
1780+
* @param that
1781+
* the source Observable
1782+
* @param defaultValue
1783+
* default value for a sequence.
1784+
* @param predicate
1785+
* A predicate function to evaluate for elements in the sequence.
1786+
* @return The single element in the observable sequence, or a default value if no value is found.
1787+
*/
1788+
public static <T> T singleOrDefault(Observable<T> that, T defaultValue, Func1<T, Boolean> predicate) {
1789+
return singleOrDefault(that.filter(predicate), defaultValue);
1790+
}
1791+
1792+
/**
1793+
* Returns the only element of an observable sequence that matches the predicate, or a default value if no value is found.
1794+
*
1795+
* @param that
1796+
* the source Observable
1797+
* @param defaultValue
1798+
* default value for a sequence.
1799+
* @param predicate
1800+
* A predicate function to evaluate for elements in the sequence.
1801+
* @return The single element in the observable sequence, or a default value if no value is found.
1802+
*/
1803+
public static <T> T singleOrDefault(Observable<T> that, T defaultValue, Object predicate) {
1804+
@SuppressWarnings("rawtypes")
1805+
final FuncN _f = Functions.from(predicate);
1806+
1807+
return singleOrDefault(that, defaultValue, new Func1<T, Boolean>() {
1808+
@Override
1809+
public Boolean call(T t) {
1810+
return (Boolean) _f.call(t);
1811+
}
1812+
});
1813+
}
1814+
1815+
private static <T> T singleOrDefault(Observable<T> that, boolean hasDefault, T defaultVal) {
1816+
Iterator<T> it = that.toIterable().iterator();
1817+
1818+
if (!it.hasNext()) {
1819+
if (hasDefault) {
1820+
return defaultVal;
1821+
}
1822+
throw new IllegalStateException("Expected single entry. Actually empty stream.");
1823+
}
1824+
1825+
T result = it.next();
1826+
1827+
if (it.hasNext()) {
1828+
throw new IllegalStateException("Expected single entry. Actually more than one entry.");
1829+
}
1830+
1831+
return result;
1832+
}
1833+
16511834
/**
16521835
* Converts an Iterable sequence to an Observable sequence.
16531836
*
@@ -2878,6 +3061,74 @@ public Boolean call(Integer args) {
28783061

28793062
assertEquals(-1, last);
28803063
}
3064+
3065+
public void testSingle() {
3066+
Observable<String> observable = toObservable("one");
3067+
assertEquals("one", observable.single());
3068+
}
3069+
3070+
@Test
3071+
public void testSingleDefault() {
3072+
Observable<String> observable = toObservable();
3073+
assertEquals("default", observable.singleOrDefault("default"));
3074+
}
3075+
3076+
@Test(expected = IllegalStateException.class)
3077+
public void testSingleDefaultWithMoreThanOne() {
3078+
Observable<String> observable = toObservable("one", "two", "three");
3079+
observable.singleOrDefault("default");
3080+
}
3081+
3082+
@Test
3083+
public void testSingleWithPredicateDefault() {
3084+
Observable<String> observable = toObservable("one", "two", "four");
3085+
assertEquals("four", observable.single(new Func1<String, Boolean>() {
3086+
@Override
3087+
public Boolean call(String s) {
3088+
return s.length() == 4;
3089+
}
3090+
}));
3091+
}
3092+
3093+
@Test(expected = IllegalStateException.class)
3094+
public void testSingleWrong() {
3095+
Observable<Integer> observable = toObservable(1, 2);
3096+
observable.single();
3097+
}
3098+
3099+
@Test(expected = IllegalStateException.class)
3100+
public void testSingleWrongPredicate() {
3101+
Observable<Integer> observable = toObservable(-1);
3102+
observable.single(new Func1<Integer, Boolean>() {
3103+
@Override
3104+
public Boolean call(Integer args) {
3105+
return args > 0;
3106+
}
3107+
});
3108+
}
3109+
3110+
@Test
3111+
public void testSingleDefaultPredicateMatchesNothing() {
3112+
Observable<String> observable = toObservable("one", "two");
3113+
String result = observable.singleOrDefault("default", new Func1<String, Boolean>() {
3114+
@Override
3115+
public Boolean call(String args) {
3116+
return args.length() == 4;
3117+
}
3118+
});
3119+
assertEquals("default", result);
3120+
}
3121+
3122+
@Test(expected = IllegalStateException.class)
3123+
public void testSingleDefaultPredicateMatchesMoreThanOne() {
3124+
Observable<String> observable = toObservable("one", "two");
3125+
String result = observable.singleOrDefault("default", new Func1<String, Boolean>() {
3126+
@Override
3127+
public Boolean call(String args) {
3128+
return args.length() == 3;
3129+
}
3130+
});
3131+
}
28813132

28823133
private static class TestException extends RuntimeException {
28833134

rxjava-core/src/main/java/rx/util/functions/Functions.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,4 +550,18 @@ public Void call(Object... args) {
550550
};
551551
}
552552

553+
@SuppressWarnings("unchecked")
554+
public static <T> Func1<T, Boolean> alwaysTrue() {
555+
return (Func1<T, Boolean>) AlwaysTrue.INSTANCE;
556+
}
557+
558+
private enum AlwaysTrue implements Func1<Object, Boolean> {
559+
INSTANCE;
560+
561+
@Override
562+
public Boolean call(Object o) {
563+
return true;
564+
}
565+
}
566+
553567
}

0 commit comments

Comments
 (0)