@@ -8,18 +8,20 @@ import scala.collection.SortedSet
88import scala .collection .SortedMap
99import org .junit .Ignore
1010import java .lang .reflect .Modifier
11+ import java .util .Date
12+ import java .util .Calendar
1113
1214class CompletenessTest extends JUnitSuite {
1315
1416 val unnecessary = " [considered unnecessary in Scala land]"
1517
1618 val deprecated = " [deprecated in RxJava]"
1719
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.]"
2123
22- val commentForFirstWithPredicate = " [use .filter(condition).first]"
24+ val commentForFirstWithPredicate = " [use ` .filter(condition).first` ]"
2325
2426 val correspondence = defaultMethodCorrespondence ++ Map (
2527 // manually added entries for Java instance methods
@@ -32,10 +34,10 @@ class CompletenessTest extends JUnitSuite {
3234 " dematerialize()" -> " dematerialize(<:<[T, Notification[U]])" ,
3335 " first(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate,
3436 " 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`] " ,
3739 " 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]" ,
3941 " onErrorResumeNext(Func1[Throwable, _ <: Observable[_ <: T]])" -> " onErrorResumeNext(Throwable => Observable[U])" ,
4042 " onErrorResumeNext(Observable[_ <: T])" -> " onErrorResumeNext(Observable[U])" ,
4143 " onErrorReturn(Func1[Throwable, _ <: T])" -> " onErrorReturn(Throwable => U)" ,
@@ -49,13 +51,13 @@ class CompletenessTest extends JUnitSuite {
4951 " skip(Int)" -> " drop(Int)" ,
5052 " skipWhile(Func1[_ >: T, Boolean])" -> " dropWhile(T => Boolean)" ,
5153 " 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]" ,
5355 " takeFirst()" -> " first" ,
5456 " takeFirst(Func1[_ >: T, Boolean])" -> commentForFirstWithPredicate,
5557 " takeLast(Int)" -> " takeRight(Int)" ,
5658 " 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))`] " ,
5961 " where(Func1[_ >: T, Boolean])" -> " filter(T => Boolean)" ,
6062 " window(Long, Long, TimeUnit)" -> " window(Duration, Duration)" ,
6163 " window(Long, Long, TimeUnit, Scheduler)" -> " window(Duration, Duration, Scheduler)" ,
@@ -81,24 +83,24 @@ class CompletenessTest extends JUnitSuite {
8183 " mergeDelayError(Observable[_ <: T], Observable[_ <: T])" -> " mergeDelayError(Observable[U])" ,
8284 " mergeDelayError(Observable[_ <: Observable[_ <: T]])" -> " flattenDelayError(<:<[Observable[T], Observable[Observable[U]]])" ,
8385 " 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))` ]" ,
8688 " sum(Observable[Integer])" -> " sum(Numeric[U])" ,
8789 " sumDoubles(Observable[Double])" -> " sum(Numeric[U])" ,
8890 " sumFloats(Observable[Float])" -> " sum(Numeric[U])" ,
8991 " sumLongs(Observable[Long])" -> " sum(Numeric[U])" ,
9092 " synchronize(Observable[T])" -> " synchronize" ,
9193 " switchDo(Observable[_ <: Observable[_ <: T]])" -> deprecated,
9294 " 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` ]"
9698 ) ++ List .iterate(" T" , 9 )(s => s + " , T" ).map(
9799 // all 9 overloads of startWith:
98- " startWith(" + _ + " )" -> " [unnecessary because we can just use ++ instead]"
100+ " startWith(" + _ + " )" -> " [unnecessary because we can just use `++` instead]"
99101 ).toMap ++ List .iterate(" Observable[_ <: T]" , 9 )(s => s + " , Observable[_ <: T]" ).map(
100102 // 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` ]"
102104 ).drop(1 ).toMap ++ List .iterate(" T" , 10 )(s => s + " , T" ).map(
103105 // all 10 overloads of from:
104106 " from(" + _ + " )" -> " apply(T*)"
@@ -109,10 +111,10 @@ class CompletenessTest extends JUnitSuite {
109111 (" zip(" + obsArgs + " Func" + i + " [" + funcParams + " _ <: R])" , unnecessary)
110112 }).toMap ++ List .iterate(" Observable[_ <: T]" , 9 )(s => s + " , Observable[_ <: T]" ).map(
111113 // 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]"
113115 ).drop(2 ).toMap ++ List .iterate(" Observable[_ <: T]" , 9 )(s => s + " , Observable[_ <: T]" ).map(
114116 // 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]"
116118 ).drop(2 ).toMap ++ (3 to 9 ).map(i => {
117119 // combineLatest 3-9:
118120 val obsArgs = (1 to i).map(j => s " Observable[_ <: T $j], " ).mkString(" " )
@@ -257,6 +259,12 @@ class CompletenessTest extends JUnitSuite {
257259 val status = if (bad == 0 ) " SUCCESS" else " BAD"
258260 println(s " \n $status: $bad out of ${bad+ good} methods were not found in Scala Observable " )
259261 }
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+ }
260268
261269 @ Test def checkJavaMethodPresence : Unit = {
262270 println(" \n Testing that all mentioned Java methods exist" )
@@ -269,4 +277,54 @@ class CompletenessTest extends JUnitSuite {
269277 checkMethodPresence(correspondence.values, typeOf[rx.lang.scala.Observable [_]])
270278 }
271279
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+
272330}
0 commit comments