Skip to content

Commit de5c67e

Browse files
committed
Merge pull request #8 from retronym/topic/future-converters
Move API for FutureConverter to scala.compat.java8
2 parents be96e83 + d01ed9c commit de5c67e

File tree

6 files changed

+122
-104
lines changed

6 files changed

+122
-104
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,16 @@ class Test {
2121

2222
[More Examples / Documentation](src/test/java/scala/compat/java8/LambdaTest.java)
2323

24-
### Converters between `s.u.concurrent` and `j.u.concurrent`
24+
### Converters between `scala.concurrent` and `java.util.concurrent`
2525

26-
TODO
26+
[API](src/test/java/scala/compat/java8/FutureConverters.java)
27+
[Test Cases](src/test/java/scala/compat/java8/FutureConvertersTest.java)
2728

28-
### Converters for `j.u.function`
29+
### Converters for `java.util.function`
2930

3031
TODO
3132

32-
### Converters for `j.u.Stream`
33+
### Converters for `java.util.stream`
3334

3435
TODO
3536

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ inConfig(JavaDoc)(Defaults.configSettings) ++ Seq(
7171
),
7272
javacOptions in JavaDoc := Seq(),
7373
artifactName in packageDoc in JavaDoc := ((sv, mod, art) => "" + mod.name + "_" + sv.binary + "-" + mod.revision + "-javadoc.jar"),
74-
libraryDependencies += compilerPlugin("com.typesafe.genjavadoc" %% "genjavadoc-plugin" % "0.5" cross CrossVersion.full),
74+
libraryDependencies += compilerPlugin("com.typesafe.genjavadoc" % "genjavadoc-plugin_2.10.4" % "0.5"),
7575
scalacOptions in Compile <+= target map (t => "-P:genjavadoc:out=" + (t / "java"))
7676
)
7777

src/main/scala/scala/concurrent/java8/FutureConverter.scala renamed to src/main/scala/scala/compat/java8/FutureConverters.scala

Lines changed: 36 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
package scala.concurrent.java8
1+
package scala.compat.java8
22

3+
import scala.concurrent.java8.FuturesConvertersImpl._
34
import scala.concurrent.{ Future, Promise, ExecutionContext, ExecutionContextExecutorService, ExecutionContextExecutor, impl }
45
import java.util.concurrent.{ CompletionStage, Executor, ExecutorService, CompletableFuture }
56
import scala.util.{ Try, Success, Failure }
@@ -33,54 +34,7 @@ import java.util.function.{ BiConsumer, Function ⇒ JF, Consumer, BiFunction }
3334
* final CompletionStage<Integer> cs2 = toJava(f2);
3435
* }}}
3536
*/
36-
object FutureConverter {
37-
38-
private class CF[T] extends CompletableFuture[T] with (Try[T] => Unit) {
39-
override def apply(t: Try[T]): Unit = t match {
40-
case Success(v) complete(v)
41-
case Failure(e) completeExceptionally(e)
42-
}
43-
44-
/*
45-
* Ensure that completions of this future cannot hold the Scala Future’s completer hostage.
46-
*/
47-
override def thenApply[U](fn: JF[_ >: T, _ <: U]): CompletableFuture[U] = thenApplyAsync(fn)
48-
override def thenAccept(fn: Consumer[_ >: T]): CompletableFuture[Void] = thenAcceptAsync(fn)
49-
override def thenRun(fn: Runnable): CompletableFuture[Void] = thenRunAsync(fn)
50-
override def thenCombine[U, V](cs: CompletionStage[_ <: U], fn: BiFunction[_ >: T, _ >: U, _ <: V]): CompletableFuture[V] = thenCombineAsync(cs, fn)
51-
override def thenAcceptBoth[U](cs: CompletionStage[_ <: U], fn: BiConsumer[_ >: T, _ >: U]): CompletableFuture[Void] = thenAcceptBothAsync(cs, fn)
52-
override def runAfterBoth(cs: CompletionStage[_], fn: Runnable): CompletableFuture[Void] = runAfterBothAsync(cs, fn)
53-
override def applyToEither[U](cs: CompletionStage[_ <: T], fn: JF[_ >: T, U]): CompletableFuture[U] = applyToEitherAsync(cs, fn)
54-
override def acceptEither(cs: CompletionStage[_ <: T], fn: Consumer[_ >: T]): CompletableFuture[Void] = acceptEitherAsync(cs, fn)
55-
override def runAfterEither(cs: CompletionStage[_], fn: Runnable): CompletableFuture[Void] = runAfterEitherAsync(cs, fn)
56-
override def thenCompose[U](fn: JF[_ >: T, _ <: CompletionStage[U]]): CompletableFuture[U] = thenComposeAsync(fn)
57-
override def whenComplete(fn: BiConsumer[_ >: T, _ >: Throwable]): CompletableFuture[T] = whenCompleteAsync(fn)
58-
override def handle[U](fn: BiFunction[_ >: T, Throwable, _ <: U]): CompletableFuture[U] = handleAsync(fn)
59-
override def exceptionally(fn: JF[Throwable, _ <: T]): CompletableFuture[T] = {
60-
val cf = new CompletableFuture[T]
61-
whenCompleteAsync(new BiConsumer[T, Throwable] {
62-
override def accept(t: T, e: Throwable): Unit = {
63-
if (e == null) cf.complete(t)
64-
else {
65-
val n: AnyRef =
66-
try {
67-
fn(e).asInstanceOf[AnyRef]
68-
} catch {
69-
case thr: Throwable cf.completeExceptionally(thr); this
70-
}
71-
if (n ne this) cf.complete(n.asInstanceOf[T])
72-
}
73-
}
74-
})
75-
cf
76-
}
77-
78-
override def toCompletableFuture(): CompletableFuture[T] =
79-
throw new UnsupportedOperationException("this CompletionStage represents a read-only Scala Future")
80-
81-
override def toString: String = super[CompletableFuture].toString
82-
}
83-
37+
object FutureConverters {
8438
/**
8539
* Returns a CompletionStage that will be completed with the same value or
8640
* exception as the given Scala Future when that completes. Since the Future is a read-only
@@ -98,18 +52,11 @@ object FutureConverter {
9852
*/
9953
def toJava[T](f: Future[T]): CompletionStage[T] = {
10054
val cf = new CF[T]
101-
implicit val ec = Future.InternalCallbackExecutor
55+
implicit val ec = InternalCallbackExecutor
10256
f onComplete cf
10357
cf
10458
}
10559

106-
private class P[T] extends impl.Promise.DefaultPromise[T] with BiConsumer[T, Throwable] {
107-
override def accept(v: T, e: Throwable): Unit = {
108-
if (e == null) complete(Success(v))
109-
else complete(Failure(e))
110-
}
111-
}
112-
11360
/**
11461
* Returns a Scala Future that will be completed with the same value or
11562
* exception as the given CompletionStage when that completes. Transformations
@@ -208,4 +155,36 @@ object FutureConverter {
208155
*/
209156
def failedPromise[T](ex: Throwable): Promise[T] = Promise.failed(ex)
210157

158+
implicit class futureToCompletionStage[T](val f: Future[T]) extends AnyVal {
159+
/**
160+
* Returns a CompletionStage that will be completed with the same value or
161+
* exception as the given Scala Future when that completes. Since the Future is a read-only
162+
* representation, this CompletionStage does not support the
163+
* <code>toCompletableFuture</code> method. The semantics of Scala Future
164+
* demand that all callbacks are invoked asynchronously by default, therefore
165+
* the returned CompletionStage routes all calls to synchronous
166+
* transformations to their asynchronous counterparts, i.e.
167+
* <code>thenRun</code> will internally call <code>thenRunAsync</code>.
168+
*
169+
* @param f The Scala Future which may eventually supply the completion for
170+
* the returned CompletionStage
171+
* @return a CompletionStage that runs all callbacks asynchronously and does
172+
* not support the CompletableFuture interface
173+
*/
174+
def toJava: CompletionStage[T] = FutureConverters.toJava(f)
175+
}
176+
177+
implicit class completionStageToFuture[T](val cs: CompletionStage[T]) extends AnyVal {
178+
/**
179+
* Returns a Scala Future that will be completed with the same value or
180+
* exception as the given CompletionStage when that completes. Transformations
181+
* of the returned Future are executed asynchronously as specified by the
182+
* ExecutionContext that is given to the combinator methods.
183+
*
184+
* @param cs The CompletionStage which may eventually supply the completion
185+
* for the returned Scala Future
186+
* @return a Scala Future that represents the CompletionStage's completion
187+
*/
188+
def toScala: Future[T] = FutureConverters.toScala(cs)
189+
}
211190
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package scala.concurrent.java8
2+
3+
// Located in this package to access private[concurrent] members
4+
5+
import scala.concurrent.{ Future, Promise, ExecutionContext, ExecutionContextExecutorService, ExecutionContextExecutor, impl }
6+
import java.util.concurrent.{ CompletionStage, Executor, ExecutorService, CompletableFuture }
7+
import scala.util.{ Try, Success, Failure }
8+
import java.util.function.{ BiConsumer, Function JF, Consumer, BiFunction }
9+
10+
private[scala] object FuturesConvertersImpl {
11+
def InternalCallbackExecutor = Future.InternalCallbackExecutor
12+
13+
class CF[T] extends CompletableFuture[T] with (Try[T] => Unit) {
14+
override def apply(t: Try[T]): Unit = t match {
15+
case Success(v) complete(v)
16+
case Failure(e) completeExceptionally(e)
17+
}
18+
19+
/*
20+
* Ensure that completions of this future cannot hold the Scala Future’s completer hostage.
21+
*/
22+
override def thenApply[U](fn: JF[_ >: T, _ <: U]): CompletableFuture[U] = thenApplyAsync(fn)
23+
24+
override def thenAccept(fn: Consumer[_ >: T]): CompletableFuture[Void] = thenAcceptAsync(fn)
25+
26+
override def thenRun(fn: Runnable): CompletableFuture[Void] = thenRunAsync(fn)
27+
28+
override def thenCombine[U, V](cs: CompletionStage[_ <: U], fn: BiFunction[_ >: T, _ >: U, _ <: V]): CompletableFuture[V] = thenCombineAsync(cs, fn)
29+
30+
override def thenAcceptBoth[U](cs: CompletionStage[_ <: U], fn: BiConsumer[_ >: T, _ >: U]): CompletableFuture[Void] = thenAcceptBothAsync(cs, fn)
31+
32+
override def runAfterBoth(cs: CompletionStage[_], fn: Runnable): CompletableFuture[Void] = runAfterBothAsync(cs, fn)
33+
34+
override def applyToEither[U](cs: CompletionStage[_ <: T], fn: JF[_ >: T, U]): CompletableFuture[U] = applyToEitherAsync(cs, fn)
35+
36+
override def acceptEither(cs: CompletionStage[_ <: T], fn: Consumer[_ >: T]): CompletableFuture[Void] = acceptEitherAsync(cs, fn)
37+
38+
override def runAfterEither(cs: CompletionStage[_], fn: Runnable): CompletableFuture[Void] = runAfterEitherAsync(cs, fn)
39+
40+
override def thenCompose[U](fn: JF[_ >: T, _ <: CompletionStage[U]]): CompletableFuture[U] = thenComposeAsync(fn)
41+
42+
override def whenComplete(fn: BiConsumer[_ >: T, _ >: Throwable]): CompletableFuture[T] = whenCompleteAsync(fn)
43+
44+
override def handle[U](fn: BiFunction[_ >: T, Throwable, _ <: U]): CompletableFuture[U] = handleAsync(fn)
45+
46+
override def exceptionally(fn: JF[Throwable, _ <: T]): CompletableFuture[T] = {
47+
val cf = new CompletableFuture[T]
48+
whenCompleteAsync(new BiConsumer[T, Throwable] {
49+
override def accept(t: T, e: Throwable): Unit = {
50+
if (e == null) cf.complete(t)
51+
else {
52+
val n: AnyRef =
53+
try {
54+
fn(e).asInstanceOf[AnyRef]
55+
} catch {
56+
case thr: Throwable cf.completeExceptionally(thr); this
57+
}
58+
if (n ne this) cf.complete(n.asInstanceOf[T])
59+
}
60+
}
61+
})
62+
cf
63+
}
64+
65+
override def toCompletableFuture(): CompletableFuture[T] =
66+
throw new UnsupportedOperationException("this CompletionStage represents a read-only Scala Future")
67+
68+
override def toString: String = super[CompletableFuture].toString
69+
}
70+
71+
class P[T] extends impl.Promise.DefaultPromise[T] with BiConsumer[T, Throwable] {
72+
override def accept(v: T, e: Throwable): Unit = {
73+
if (e == null) complete(Success(v))
74+
else complete(Failure(e))
75+
}
76+
}
77+
}

src/main/scala/scala/concurrent/java8/package.scala

Lines changed: 0 additions & 39 deletions
This file was deleted.

src/test/java/scala/concurrent/java8/BridgeTest.java renamed to src/test/java/scala/compat/java8/FutureConvertersTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package scala.concurrent.java8;
1+
package scala.compat.java8;
22

33
import java.util.concurrent.CompletableFuture;
44
import java.util.concurrent.CompletionStage;
@@ -12,9 +12,9 @@
1212

1313
import scala.concurrent.Future;
1414
import scala.concurrent.Promise;
15-
import static scala.concurrent.java8.FutureConverter.*;
15+
import static scala.compat.java8.FutureConverters.*;
1616

17-
public class BridgeTest {
17+
public class FutureConvertersTest {
1818

1919
@Test
2020
public void testToScalaSuccess() {

0 commit comments

Comments
 (0)