client: Document models

This commit is contained in:
Andras Slemmer 2016-08-30 10:56:15 +01:00
parent cb956e0979
commit 0c8f58b2ac
5 changed files with 46 additions and 24 deletions

View File

@ -10,13 +10,20 @@ import javafx.collections.ObservableList
import kotlinx.support.jdk8.collections.removeIf
import org.reactfx.EventStream
class StatesDiff<out T : ContractState>(val added: Collection<StateAndRef<T>>, val removed: Collection<StateRef>)
class StatesDiff<out T : ContractState>(
val added: Collection<StateAndRef<T>>,
val removed: Collection<StateRef>
)
/**
* This model exposes the list of owned contract states.
*/
class ContractStateModel {
private val serviceToClient: EventStream<ServiceToClientEvent> by stream(WalletMonitorModel::serviceToClient)
private val outputStates = serviceToClient.filter(ServiceToClientEvent.OutputState::class.java)
val contractStatesDiff = outputStates.map { StatesDiff(it.produced, it.consumed) }
// We filter the diff first rather than the complete contract state list.
val cashStatesDiff = contractStatesDiff.map {
StatesDiff(it.added.filterIsInstance<StateAndRef<Cash.State>>(), it.removed)
}

View File

@ -2,6 +2,7 @@ package com.r3corda.client.model
import com.r3corda.core.contracts.Amount
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.value.ObservableValue
import java.util.*
@ -13,9 +14,12 @@ fun ExchangeRate.exchangeAmount(amount: Amount<Currency>, to: Currency) =
fun ExchangeRate.exchangeDouble(amount: Amount<Currency>, to: Currency) =
rate(amount.token, to) * amount.quantity
/**
* This model provides an exchange rate from arbitrary currency to arbitrary currency.
* TODO hook up an actual oracle
*/
class ExchangeRateModel {
// TODO hook up an actual oracle
val exchangeRate = SimpleObjectProperty<ExchangeRate>(object : ExchangeRate {
val exchangeRate: ObservableValue<ExchangeRate> = SimpleObjectProperty<ExchangeRate>(object : ExchangeRate {
override fun rate(from: Currency, to: Currency) = 1.0
})
}

View File

@ -12,13 +12,17 @@ import kotlin.reflect.KProperty
/**
* This file defines a global [Models] store and delegates to inject event streams/sinks. Note that all streams here
* are global to the app.
* are global.
*
* This allows us to decouple UI logic from stream initialisation and provides us with a central place to inspect data flows.
* It also allows detecting of looping logic by constructing a stream dependency graph TODO do this
* This allows decoupling of UI logic from stream initialisation and provides us with a central place to inspect data
* flows. It also allows detecting of looping logic by constructing a stream dependency graph TODO do this.
*
* General rule of thumb: A stream/observable should be a model if it may be reused several times.
*
* Usage:
* // Inject service -> client stream
* private val serviceToClient: EventStream<ServiceToClientEvent> by stream(WalletMonitorModel::serviceToClient)
*
* Usage: In your piece of UI use the [stream] and [sink] delegates to access external streams. If you have a reusable
* stream put it in a Model. See [NetworkModel] for an example
*/
inline fun <reified M : Any, T> stream(noinline streamProperty: (M) -> EventStream<T>) =
@ -40,7 +44,7 @@ inline fun <reified M : Any, T> observableList(noinline observableListProperty:
TrackedDelegate.ObservableListDelegate(M::class, observableListProperty)
inline fun <reified M : Any, T> observableListReadOnly(noinline observableListProperty: (M) -> ObservableList<out T>) =
TrackedDelegate.ObservableListDelegateReadOnly(M::class, observableListProperty)
TrackedDelegate.ObservableListReadOnlyDelegate(M::class, observableListProperty)
object Models {
private val modelStore = HashMap<KClass<*>, Any>()
@ -68,35 +72,34 @@ sealed class TrackedDelegate<M : Any>(val klass: KClass<M>) {
return streamProperty(Models.get(klass, thisRef.javaClass.kotlin))
}
}
class Sink<M : Any, T> (klass: KClass<M>, val sinkProperty: (M) -> EventSink<T>) : TrackedDelegate<M>(klass) {
operator fun getValue(thisRef: Any, property: KProperty<*>): EventSink<T> {
return sinkProperty(Models.get(klass, thisRef.javaClass.kotlin))
}
}
class ObservableValueDelegate<M : Any, T>(klass: KClass<M>, val sinkProperty: (M) -> ObservableValue<T>) : TrackedDelegate<M>(klass) {
class ObservableValueDelegate<M : Any, T>(klass: KClass<M>, val observableValueProperty: (M) -> ObservableValue<T>) : TrackedDelegate<M>(klass) {
operator fun getValue(thisRef: Any, property: KProperty<*>): ObservableValue<T> {
return sinkProperty(Models.get(klass, thisRef.javaClass.kotlin))
return observableValueProperty(Models.get(klass, thisRef.javaClass.kotlin))
}
}
class WritableValueDelegate<M : Any, T>(klass: KClass<M>, val sinkProperty: (M) -> WritableValue<T>) : TrackedDelegate<M>(klass) {
class WritableValueDelegate<M : Any, T>(klass: KClass<M>, val writableValueProperty: (M) -> WritableValue<T>) : TrackedDelegate<M>(klass) {
operator fun getValue(thisRef: Any, property: KProperty<*>): WritableValue<T> {
return sinkProperty(Models.get(klass, thisRef.javaClass.kotlin))
return writableValueProperty(Models.get(klass, thisRef.javaClass.kotlin))
}
}
class ObservableListDelegate<M : Any, T>(klass: KClass<M>, val sinkProperty: (M) -> ObservableList<T>) : TrackedDelegate<M>(klass) {
class ObservableListDelegate<M : Any, T>(klass: KClass<M>, val observableListProperty: (M) -> ObservableList<T>) : TrackedDelegate<M>(klass) {
operator fun getValue(thisRef: Any, property: KProperty<*>): ObservableList<T> {
return sinkProperty(Models.get(klass, thisRef.javaClass.kotlin))
return observableListProperty(Models.get(klass, thisRef.javaClass.kotlin))
}
}
class ObservableListDelegateReadOnly<M : Any, T>(klass: KClass<M>, val sinkProperty: (M) -> ObservableList<out T>) : TrackedDelegate<M>(klass) {
class ObservableListReadOnlyDelegate<M : Any, T>(klass: KClass<M>, val observableListReadOnlyProperty: (M) -> ObservableList<out T>) : TrackedDelegate<M>(klass) {
operator fun getValue(thisRef: Any, property: KProperty<*>): ObservableList<out T> {
return sinkProperty(Models.get(klass, thisRef.javaClass.kotlin))
return observableListReadOnlyProperty(Models.get(klass, thisRef.javaClass.kotlin))
}
}
class ObjectPropertyDelegate<M : Any, T>(klass: KClass<M>, val sinkProperty: (M) -> ObjectProperty<T>) : TrackedDelegate<M>(klass) {
class ObjectPropertyDelegate<M : Any, T>(klass: KClass<M>, val objectPropertyProperty: (M) -> ObjectProperty<T>) : TrackedDelegate<M>(klass) {
operator fun getValue(thisRef: Any, property: KProperty<*>): ObjectProperty<T> {
return sinkProperty(Models.get(klass, thisRef.javaClass.kotlin))
return objectPropertyProperty(Models.get(klass, thisRef.javaClass.kotlin))
}
}
}

View File

@ -58,6 +58,9 @@ data class TransactionCreateStateWritable(
override val lastUpdate: SimpleObjectProperty<Instant>
) : TransactionCreateState
/**
* This model provides an observable list of states relating to the creation of a transaction not yet on ledger.
*/
class TransactionCreateStateModel {
private val serviceToClient: EventStream<ServiceToClientEvent> by stream(WalletMonitorModel::serviceToClient)

View File

@ -1,17 +1,17 @@
package com.r3corda.client.model
import com.r3corda.client.WalletMonitorClient
import com.r3corda.core.contracts.Amount
import com.r3corda.core.contracts.ClientToServiceCommand
import com.r3corda.core.messaging.MessagingService
import com.r3corda.core.node.NodeInfo
import com.r3corda.node.services.monitor.ServiceToClientEvent
import javafx.beans.property.SimpleObjectProperty
import org.reactfx.EventSink
import org.reactfx.EventSource
import org.reactfx.EventStream
import java.util.*
/**
* This model exposes raw event streams to and from the [WalletMonitorService] through a [WalletMonitorClient]
*/
class WalletMonitorModel {
private val clientToServiceSource = EventSource<ClientToServiceCommand>()
val clientToService: EventSink<ClientToServiceCommand> = clientToServiceSource
@ -19,7 +19,12 @@ class WalletMonitorModel {
private val serviceToClientSource = EventSource<ServiceToClientEvent>()
val serviceToClient: EventStream<ServiceToClientEvent> = serviceToClientSource
// TODO provide an unsubscribe mechanism
/**
* Register for updates to/from a given wallet.
* @param messagingService The messaging to use for communication.
* @param walletMonitorNodeInfo the [Node] to connect to.
* TODO provide an unsubscribe mechanism
*/
fun register(messagingService: MessagingService, walletMonitorNodeInfo: NodeInfo) {
val monitorClient = WalletMonitorClient(
messagingService,