@@ -8,18 +8,20 @@ import scala.collection.SortedSet
8
8
import scala .collection .SortedMap
9
9
import org .junit .Ignore
10
10
import java .lang .reflect .Modifier
11
+ import java .util .Date
12
+ import java .util .Calendar
11
13
12
14
class CompletenessTest extends JUnitSuite {
13
15
14
16
val unnecessary = " [considered unnecessary in Scala land]"
15
17
16
18
val deprecated = " [deprecated in RxJava]"
17
19
18
- val averageProblem = " [We can't have a general average method because Scala's Numeric does not have " +
19
- " scalar multiplication (we would need to calculate (1.0/numberOfElements)*sum). " +
20
- " You can use fold instead to accumulate sum and numberOfElements and divide at the end.]"
20
+ val averageProblem = " [We can't have a general average method because Scala's ` Numeric` does not have " +
21
+ " scalar multiplication (we would need to calculate ` (1.0/numberOfElements)*sum` ). " +
22
+ " You can use ` fold` instead to accumulate ` sum` and ` numberOfElements` and divide at the end.]"
21
23
22
- val commentForFirstWithPredicate = " [use .filter(condition).first]"
24
+ val commentForFirstWithPredicate = " [use ` .filter(condition).first` ]"
23
25
24
26
val correspondence = defaultMethodCorrespondence ++ Map (
25
27
// manually added entries for Java instance methods
@@ -32,10 +34,10 @@ class CompletenessTest extends JUnitSuite {
32
34
" dematerialize()" -> " dematerialize(<:<[T, Notification[U]])" ,
33
35
" first(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate,
34
36
" firstOrDefault(T)" -> " firstOrElse(=> U)" ,
35
- " firstOrDefault(Func1[_ >: T, Boolean], T)" -> " [use .filter(condition).firstOrElse(default)]" ,
36
- " groupBy(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: R])" -> " groupBy(T => K) " ,
37
+ " firstOrDefault(Func1[_ >: T, Boolean], T)" -> " [use ` .filter(condition).firstOrElse(default)` ]" ,
38
+ " groupBy(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: R])" -> " [use ` groupBy` and `map`] " ,
37
39
" mapMany(Func1[_ >: T, _ <: Observable[_ <: R]])" -> " flatMap(T => Observable[R])" ,
38
- " mapWithIndex(Func2[_ >: T, Integer, _ <: R])" -> " [combine zipWithIndex with map or with a for comprehension]" ,
40
+ " mapWithIndex(Func2[_ >: T, Integer, _ <: R])" -> " [combine ` zipWithIndex` with ` map` or with a for comprehension]" ,
39
41
" onErrorResumeNext(Func1[Throwable, _ <: Observable[_ <: T]])" -> " onErrorResumeNext(Throwable => Observable[U])" ,
40
42
" onErrorResumeNext(Observable[_ <: T])" -> " onErrorResumeNext(Observable[U])" ,
41
43
" onErrorReturn(Func1[Throwable, _ <: T])" -> " onErrorReturn(Throwable => U)" ,
@@ -49,13 +51,13 @@ class CompletenessTest extends JUnitSuite {
49
51
" skip(Int)" -> " drop(Int)" ,
50
52
" skipWhile(Func1[_ >: T, Boolean])" -> " dropWhile(T => Boolean)" ,
51
53
" skipWhileWithIndex(Func2[_ >: T, Integer, Boolean])" -> unnecessary,
52
- " startWith(Iterable[T])" -> " [unnecessary because we can just use ++ instead]" ,
54
+ " startWith(Iterable[T])" -> " [unnecessary because we can just use `++` instead]" ,
53
55
" takeFirst()" -> " first" ,
54
56
" takeFirst(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate,
55
57
" takeLast(Int)" -> " takeRight(Int)" ,
56
58
" toList()" -> " toSeq" ,
57
- " toSortedList()" -> unnecessary ,
58
- " toSortedList(Func2[_ >: T, _ >: T, Integer])" -> unnecessary ,
59
+ " toSortedList()" -> " [Sorting is already done in Scala's collection library, use `.toSeq.map(_.sorted)`] " ,
60
+ " toSortedList(Func2[_ >: T, _ >: T, Integer])" -> " [Sorting is already done in Scala's collection library, use `.toSeq.map(_.sortWith(f))`] " ,
59
61
" where(Func1[_ >: T, Boolean])" -> " filter(T => Boolean)" ,
60
62
" window(Long, Long, TimeUnit)" -> " window(Duration, Duration)" ,
61
63
" window(Long, Long, TimeUnit, Scheduler)" -> " window(Duration, Duration, Scheduler)" ,
@@ -81,24 +83,24 @@ class CompletenessTest extends JUnitSuite {
81
83
" mergeDelayError(Observable[_ <: T], Observable[_ <: T])" -> " mergeDelayError(Observable[U])" ,
82
84
" mergeDelayError(Observable[_ <: Observable[_ <: T]])" -> " flattenDelayError(<:<[Observable[T], Observable[Observable[U]]])" ,
83
85
" range(Int, Int)" -> " apply(Range)" ,
84
- " sequenceEqual(Observable[_ <: T], Observable[_ <: T])" -> " [use (first zip second) map (p => p._1 == p._2)]" ,
85
- " sequenceEqual(Observable[_ <: T], Observable[_ <: T], Func2[_ >: T, _ >: T, Boolean])" -> " [use (first zip second) map (p => equality(p._1, p._2))]" ,
86
+ " sequenceEqual(Observable[_ <: T], Observable[_ <: T])" -> " [use ` (first zip second) map (p => p._1 == p._2)` ]" ,
87
+ " sequenceEqual(Observable[_ <: T], Observable[_ <: T], Func2[_ >: T, _ >: T, Boolean])" -> " [use ` (first zip second) map (p => equality(p._1, p._2))` ]" ,
86
88
" sum(Observable[Integer])" -> " sum(Numeric[U])" ,
87
89
" sumDoubles(Observable[Double])" -> " sum(Numeric[U])" ,
88
90
" sumFloats(Observable[Float])" -> " sum(Numeric[U])" ,
89
91
" sumLongs(Observable[Long])" -> " sum(Numeric[U])" ,
90
92
" synchronize(Observable[T])" -> " synchronize" ,
91
93
" switchDo(Observable[_ <: Observable[_ <: T]])" -> deprecated,
92
94
" switchOnNext(Observable[_ <: Observable[_ <: T]])" -> " switch(<:<[Observable[T], Observable[Observable[U]]])" ,
93
- " zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> " [use instance method zip and map]" ,
94
- " zip(Observable[_ <: Observable[_]], FuncN[_ <: R])" -> " [use zip in companion object and map]" ,
95
- " zip(Iterable[_ <: Observable[_]], FuncN[_ <: R])" -> " [use zip in companion object and map]"
95
+ " zip(Observable[_ <: T1], Observable[_ <: T2], Func2[_ >: T1, _ >: T2, _ <: R])" -> " [use instance method ` zip` and ` map` ]" ,
96
+ " zip(Observable[_ <: Observable[_]], FuncN[_ <: R])" -> " [use ` zip` in companion object and ` map` ]" ,
97
+ " zip(Iterable[_ <: Observable[_]], FuncN[_ <: R])" -> " [use ` zip` in companion object and ` map` ]"
96
98
) ++ List .iterate(" T" , 9 )(s => s + " , T" ).map(
97
99
// all 9 overloads of startWith:
98
- " startWith(" + _ + " )" -> " [unnecessary because we can just use ++ instead]"
100
+ " startWith(" + _ + " )" -> " [unnecessary because we can just use `++` instead]"
99
101
).toMap ++ List .iterate(" Observable[_ <: T]" , 9 )(s => s + " , Observable[_ <: T]" ).map(
100
102
// concat 2-9
101
- " concat(" + _ + " )" -> " [unnecessary because we can use ++ instead or Observable(o1, o2, ...).concat]"
103
+ " concat(" + _ + " )" -> " [unnecessary because we can use `++` instead or ` Observable(o1, o2, ...).concat` ]"
102
104
).drop(1 ).toMap ++ List .iterate(" T" , 10 )(s => s + " , T" ).map(
103
105
// all 10 overloads of from:
104
106
" from(" + _ + " )" -> " apply(T*)"
@@ -109,10 +111,10 @@ class CompletenessTest extends JUnitSuite {
109
111
(" zip(" + obsArgs + " Func" + i + " [" + funcParams + " _ <: R])" , unnecessary)
110
112
}).toMap ++ List .iterate(" Observable[_ <: T]" , 9 )(s => s + " , Observable[_ <: T]" ).map(
111
113
// merge 3-9:
112
- " merge(" + _ + " )" -> " [unnecessary because we can use Observable(o1, o2, ...).flatten instead]"
114
+ " merge(" + _ + " )" -> " [unnecessary because we can use ` Observable(o1, o2, ...).flatten` instead]"
113
115
).drop(2 ).toMap ++ List .iterate(" Observable[_ <: T]" , 9 )(s => s + " , Observable[_ <: T]" ).map(
114
116
// mergeDelayError 3-9:
115
- " mergeDelayError(" + _ + " )" -> " [unnecessary because we can use Observable(o1, o2, ...).flattenDelayError instead]"
117
+ " mergeDelayError(" + _ + " )" -> " [unnecessary because we can use ` Observable(o1, o2, ...).flattenDelayError` instead]"
116
118
).drop(2 ).toMap ++ (3 to 9 ).map(i => {
117
119
// combineLatest 3-9:
118
120
val obsArgs = (1 to i).map(j => s " Observable[_ <: T $j], " ).mkString(" " )
@@ -257,6 +259,12 @@ class CompletenessTest extends JUnitSuite {
257
259
val status = if (bad == 0 ) " SUCCESS" else " BAD"
258
260
println(s " \n $status: $bad out of ${bad+ good} methods were not found in Scala Observable " )
259
261
}
262
+
263
+ def setTodoForMissingMethods (corresp : Map [String , String ]): Map [String , String ] = {
264
+ val actualMethods = getPublicInstanceAndCompanionMethods(typeOf[rx.lang.scala.Observable [_]]).toSet
265
+ for ((javaM, scalaM) <- corresp) yield
266
+ (javaM, if (actualMethods.contains(scalaM) || scalaM.charAt(0 ) == '[' ) scalaM else " [**TODO: missing**]" )
267
+ }
260
268
261
269
@ Test def checkJavaMethodPresence : Unit = {
262
270
println(" \n Testing that all mentioned Java methods exist" )
@@ -269,4 +277,54 @@ class CompletenessTest extends JUnitSuite {
269
277
checkMethodPresence(correspondence.values, typeOf[rx.lang.scala.Observable [_]])
270
278
}
271
279
280
+ def scalaToJavaSignature (s : String ) =
281
+ s.replaceAllLiterally(" _ <:" , " ? extends" )
282
+ .replaceAllLiterally(" _ >:" , " ? super" )
283
+ .replaceAllLiterally(" [" , " <" )
284
+ .replaceAllLiterally(" ]" , " >" )
285
+ .replaceAllLiterally(" Array<T>" , " T[]" )
286
+
287
+ def escapeJava (s : String ) =
288
+ s.replaceAllLiterally(" <" , " <" )
289
+ .replaceAllLiterally(" >" , " >" )
290
+
291
+ @ Test def printMarkdownCorrespondenceTable () {
292
+ def isInteresting (p : (String , String )): Boolean =
293
+ p._1.replaceAllLiterally(" ()" , " " ) != p._2
294
+ def groupingKey (p : (String , String )): (String , String ) =
295
+ (if (p._1.startsWith(" average" )) " average" else p._1.takeWhile(_ != '(' ), p._2)
296
+ def formatJavaCol (name : String , alternatives : Iterable [String ]): String = {
297
+ alternatives.toList.sorted.map(scalaToJavaSignature(_)).map(s => {
298
+ if (s.length > 50 ) {
299
+ val toolTip = escapeJava(s)
300
+ " <span title=\" " + toolTip + " \" ><code>" + name + " (...)</code></span>"
301
+ } else {
302
+ " `" + s + " `"
303
+ }
304
+ }).mkString(" <br/>" )
305
+ }
306
+ def formatScalaCol (s : String ): String =
307
+ if (s.startsWith(" [" ) && s.endsWith(" ]" )) s.drop(1 ).dropRight(1 ) else " `" + s + " `"
308
+ def escape (s : String ) = s.replaceAllLiterally(" [" , " <" ).replaceAllLiterally(" ]" , " >" )
309
+
310
+ println("""
311
+ ## Comparison of Scala Observable and Java Observable
312
+
313
+ Note:
314
+ * This table contains both static methods and instance methods.
315
+ * If a signature is too long, move your mouse over it to get the full signature.
316
+
317
+
318
+ | Java Method | Scala Method |
319
+ |-------------|--------------|""" )
320
+
321
+ val ps = setTodoForMissingMethods(correspondence)
322
+
323
+ (for (((javaName, scalaCol), pairs) <- ps.groupBy(groupingKey(_)).toList.sortBy(_._1._1)) yield {
324
+ " | " + formatJavaCol(javaName, pairs.map(_._1)) + " | " + formatScalaCol(scalaCol) + " |"
325
+ }).foreach(println(_))
326
+ println(s " \n This table was generated on ${Calendar .getInstance().getTime()}. " )
327
+ println(s " **Do not edit**. Instead, edit ` ${getClass().getCanonicalName()}`. " )
328
+ }
329
+
272
330
}
0 commit comments