Advertisement
Guest User

Untitled

a guest
Mar 19th, 2019
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.54 KB | None | 0 0
  1. type TransitionListener[A] = PartialFunction[(A, A), Unit]
  2. type EndListener[A] = PartialFunction[A, Unit]
  3.  
  4. /** Wraps the mutable value for subscriptions */
  5. trait Data[A] {
  6. /** Returns the latest value */
  7. def read: A
  8.  
  9. /** Adds the given listener that will be invoked on every update */
  10. def listen(code: TransitionListener[A], initialize: Boolean): Data[A]
  11.  
  12. /** Partially projects the wrapped value with given code */
  13. def partialMap[B](code: PartialFunction[A, B]): Data[Option[B]]
  14.  
  15. /** Maps the wrapped value with given code */
  16. def map[B](code: A => B): Data[B]
  17.  
  18. /** Combines the wrapper with another one for simultaneous subscriptions */
  19. def and[B](other: Data[B]): Data[(A, B)]
  20.  
  21.  
  22. /** Returns the latest value */
  23. def apply(): A = this.read
  24.  
  25. /** Adds the given listener that will be invoked on every update */
  26. def />>(code: TransitionListener[A]): Data[A] = {
  27. this.listen(code, initialize = true)
  28. }
  29.  
  30. /** Adds the given listener that will be invoked on every update */
  31. def />(code: EndListener[A]): Data[A] = {
  32. val listener: TransitionListener[A] = {
  33. case (before, after) if code.isDefinedAt(after) => code.apply(after)
  34. }
  35. this.listen(listener, initialize = true)
  36. }
  37.  
  38. /** Partially projects the wrapped value with given code */
  39. def /~[B](code: PartialFunction[A, B]): Data[Option[B]] = this.partialMap(code)
  40.  
  41. /** Combines the wrapper with another one for simultaneous subscriptions */
  42. def &&[B](other: Data[B]): Data[(A, B)] = this.and(other)
  43. }
  44.  
  45. /** Represents the wrapper that can mutate it's value */
  46. trait Writable[A] extends Data[A] {
  47. /** Mutates the current value into a given one */
  48. def write(a: A): A
  49. }
  50.  
  51. private class Implementation[A](default: A) extends Writable[A] with Logging {
  52. private var value: A = default
  53. private var listeners: List[TransitionListener[A]] = Nil
  54.  
  55. override def partialMap[B](code: PartialFunction[A, B]): Data[Option[B]] = {
  56. new Implementation[Option[B]](code.lift.apply(value)).mutate { source =>
  57. this.listen({ case (before, after) =>
  58. val current = source.read
  59. val next = code.lift.apply(after)
  60. if (current != next) source.write(next)
  61. }, initialize = true)
  62. }
  63. }
  64.  
  65. override def read: A = value
  66.  
  67. override def map[B](code: A => B): Data[B] = {
  68. new Implementation(code.apply(value)).mutate { source =>
  69. this.listen({ case (before, after) => source.write(code.apply(after)) }, initialize = true)
  70. }
  71. }
  72.  
  73. override def write(a: A): A = this.synchronized {
  74. val before = value
  75. val after = a
  76. value = after
  77. listeners.foreach { listener => listener.lift.apply(before, after) }
  78. value
  79. }
  80.  
  81. override def listen(code: TransitionListener[A], initialize: Boolean): Data[A] = this.synchronized {
  82. listeners = listeners :+ code
  83. if (initialize) {
  84. code.lift.apply(value, value)
  85. }
  86. this
  87. }
  88.  
  89. override def and[B](other: Data[B]): Data[(A, B)] = {
  90. val source = new Implementation((this.read, other.read))
  91. val handler: TransitionListener[Any] = source.synchronized {
  92. case _ => source.write(this.read, other.read)
  93. }
  94.  
  95. this.listen(handler, initialize = true)
  96. other.listen(handler, initialize = true)
  97. source
  98. }
  99. }
  100.  
  101. object Data {
  102. /** Creates the writable data source */
  103. def source[A](default: A): Writable[A] = new Implementation(default)
  104.  
  105. /** Creates the writable data source */
  106. def apply[A](default: A): Writable[A] = Data.source(default)
  107. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement