Created
December 31, 2014 22:03
-
-
Save NightRa/856d11944b3e51967d1a to your computer and use it in GitHub Desktop.
JavaFX reinversion of memory control
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nightra.reversi.util | |
import scala.ref.WeakReference | |
import scalafx.beans.property.{Property, BooleanProperty, ObjectProperty} | |
import scalafx.beans.value.ObservableValue | |
import scalafx.collections.ObservableBuffer | |
import scalafx.event.subscriptions.Subscription | |
object JavaFXUtil { | |
/** | |
* Memory scheme: | |
* b = a.map(f) | |
* b a strong reference to a, | |
* while a does not hold a strong reference to b; | |
* If b loses all strong references, | |
* the change listener from a will be cleaned, | |
* and b will be garbage collected. | |
**/ | |
def weaklyBindOnChange[A, B](prop: ObservableValue[A, _], to: Property[B, _])(f: A => B): Subscription = { | |
to.onChange({prop;()}) // capture prop as a strong reference in to, so that it won't be released until to is released | |
val weakNewProp: WeakReference[Property[B, _]] = WeakReference(to) // Don't capture 'to in 'prop, so 'to can be cleaned without prop being cleaned. | |
lazy val subscription: Subscription = prop.onChange((_, _, _) => weakNewProp.get match { | |
case None => subscription.cancel() | |
case Some(newProp) => newProp.value = f(prop.value) | |
}) | |
subscription // Force the lazy val to tie the recursive knot. | |
} | |
def liftObservableList[A](prop: ObjectProperty[ObservableBuffer[A]]): ObservableBuffer[A] = { | |
val newList = ObservableBuffer(prop.value) | |
newList.onChange({ prop; () }) // capture the original property in memory. | |
val weakNewList = WeakReference(newList) | |
lazy val subscription: Subscription = | |
prop.onChange { (_, _, _) => | |
weakNewList.get match { | |
case None => subscription.cancel() | |
case Some(solidNewList) => | |
newList.clear() | |
newList ++= prop.value | |
} | |
} | |
subscription // Force the lazy val to tie the recursive knot. | |
newList | |
} | |
/** | |
* Nice that the whole basic hierarchy is here: | |
* Functor, | |
* Applicative, | |
* and Monad. | |
* Actually needed each and every one of them. | |
**/ | |
def flattenProp[A](prop: ObservableValue[ObservableValue[A, _], _]): ObjectProperty[A] = { | |
val newProp: ObjectProperty[A] = ObjectProperty(prop.value.value) | |
var lastSubscription: Subscription = null | |
newProp.onChange({ prop; () }) | |
val weakNewProp = WeakReference(newProp) | |
lazy val subscription: Subscription = prop.onChange { (_, _, _) => | |
val innerProp = prop.value | |
if (lastSubscription != null) | |
lastSubscription.cancel() | |
weakNewProp.get match { | |
case None => subscription.cancel() | |
case Some(solidNewProp) => lastSubscription = weaklyBindOnChange(innerProp, newProp)(s => s) | |
} | |
} | |
subscription | |
newProp | |
} | |
def mapProp[A, B](prop: ObservableValue[A, _])(f: A => B): ObjectProperty[B] = { | |
val newProp: ObjectProperty[B] = ObjectProperty(f(prop.value)) | |
weaklyBindOnChange(prop, newProp)(f) | |
newProp // Strong reference. | |
} | |
def map2Prop[A, B, C](prop1: ObservableValue[A, _], prop2: ObservableValue[B, _])(f: (A, B) => C): ObjectProperty[C] = { | |
val newProp: ObjectProperty[C] = ObjectProperty(f(prop1.value, prop2.value)) | |
weaklyBindOnChange(prop1, newProp)(s1 => f(s1, prop2.value)) | |
weaklyBindOnChange(prop2, newProp)(s2 => f(prop1.value, s2)) | |
newProp // Strong reference. | |
} | |
def map3Prop[A, B, C, D](prop1: ObservableValue[A, _], prop2: ObservableValue[B, _], prop3: ObservableValue[C, _])(f: (A, B, C) => D): ObjectProperty[D] = { | |
val newProp: ObjectProperty[D] = ObjectProperty(f(prop1.value, prop2.value, prop3.value)) | |
weaklyBindOnChange(prop1, newProp)(s1 => f(s1, prop2.value, prop3.value)) | |
weaklyBindOnChange(prop2, newProp)(s2 => f(prop1.value, s2, prop3.value)) | |
weaklyBindOnChange(prop3, newProp)(s3 => f(prop1.value, prop2.value, s3)) | |
newProp // Strong reference. | |
} | |
def merge[A](props: Vector[ObjectProperty[A]]): ObjectProperty[A] = { | |
val newProp: ObjectProperty[A] = ObjectProperty(props.head.value) | |
props.foreach(prop => weaklyBindOnChange(prop, newProp)(s => s)) | |
newProp // Strong reference. | |
} | |
def toBooleanProp(prop: ObjectProperty[Boolean]): BooleanProperty = { | |
val newProp: BooleanProperty = BooleanProperty(prop.value) | |
weaklyBindOnChange(prop, newProp)(s => s) | |
newProp // Strong reference. | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment