explorer: Some more cleanup

This commit is contained in:
Andras Slemmer 2016-09-16 12:24:00 +01:00
parent 118d5c485e
commit 486b6cc414
3 changed files with 104 additions and 115 deletions

View File

@ -3,6 +3,8 @@ package com.r3corda.explorer.ui
import com.r3corda.explorer.formatters.Formatter
import javafx.beans.binding.Bindings
import javafx.beans.value.ObservableValue
import javafx.scene.Node
import javafx.scene.control.ListCell
import javafx.scene.control.TableCell
import javafx.scene.control.TableColumn
import javafx.scene.control.TableView
@ -54,3 +56,21 @@ fun <S> TableView<S>.singleRowSelection() = Bindings.createObjectBinding({
SingleRowSelection.Selected(selectionModel.selectedItems[0])
}
}, arrayOf(selectionModel.selectedItems))
fun <S, T> TableColumn<S, T>.setCustomCellFactory(toNode: (T) -> Node) {
setCellFactory {
object : TableCell<S, T>() {
init {
text = null
}
override fun updateItem(value: T?, empty: Boolean) {
super.updateItem(value, empty)
graphic = if (value != null && !empty) {
toNode(value)
} else {
null
}
}
}
}
}

View File

@ -15,8 +15,6 @@ import com.r3corda.explorer.model.ReportingCurrencyModel
import com.r3corda.explorer.model.SettingsModel
import com.r3corda.explorer.ui.*
import javafx.beans.binding.Bindings
import javafx.beans.property.ReadOnlyObjectWrapper
import javafx.beans.property.ReadOnlyStringWrapper
import javafx.beans.value.ObservableValue
import javafx.collections.FXCollections
import javafx.collections.ObservableList
@ -323,16 +321,16 @@ class CashViewer : View() {
cashViewerTableIssuerCurrency.setCellValueFactory {
val node = it.value.value
when (node) {
ViewerNode.Root -> ReadOnlyStringWrapper("")
is ViewerNode.IssuerNode -> ReadOnlyStringWrapper(node.issuer.toString())
ViewerNode.Root -> "".lift()
is ViewerNode.IssuerNode -> node.issuer.toString().lift()
is ViewerNode.CurrencyNode -> node.amount.map { it.token.toString() }
}
}
cashViewerTableLocalCurrency.setCellValueFactory {
val node = it.value.value
when (node) {
ViewerNode.Root -> ReadOnlyObjectWrapper(null)
is ViewerNode.IssuerNode -> ReadOnlyObjectWrapper(null)
ViewerNode.Root -> null.lift()
is ViewerNode.IssuerNode -> null.lift()
is ViewerNode.CurrencyNode -> node.amount.map { it }
}
}
@ -344,7 +342,7 @@ class CashViewer : View() {
cashViewerTableEquiv.setCellValueFactory {
val node = it.value.value
when (node) {
ViewerNode.Root -> ReadOnlyObjectWrapper(null)
ViewerNode.Root -> null.lift()
is ViewerNode.IssuerNode -> node.sumEquivAmount.map { it }
is ViewerNode.CurrencyNode -> node.equivAmount.map { it }
}

View File

@ -1,11 +1,8 @@
package com.r3corda.explorer.views
import com.r3corda.client.fxutils.ChosenList
import com.r3corda.client.fxutils.*
import com.r3corda.client.model.*
import com.r3corda.contracts.asset.Cash
import com.r3corda.core.contracts.Amount
import com.r3corda.core.contracts.CommandData
import com.r3corda.core.contracts.withoutIssuer
import com.r3corda.core.contracts.*
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.SecureHash
@ -21,7 +18,6 @@ import com.r3corda.explorer.sign
import com.r3corda.explorer.ui.*
import com.r3corda.node.services.monitor.ServiceToClientEvent
import javafx.beans.binding.Bindings
import javafx.beans.property.ReadOnlyObjectWrapper
import javafx.beans.value.ObservableValue
import javafx.collections.FXCollections
import javafx.collections.ObservableList
@ -123,15 +119,15 @@ class TransactionViewer: View() {
/**
* We map the gathered data about transactions almost one-to-one to the nodes.
*/
private val viewerNodes = EasyBind.map(gatheredTransactionDataList) {
private val viewerNodes = gatheredTransactionDataList.map {
ViewerNode(
transactionId = EasyBind.map(it.transaction) { it?.id },
transactionId = it.transaction.map { it?.id },
fiberId = it.fiberId,
clientUuid = it.uuid,
/**
* We can't really do any better based on uuid, we need to store explicit data for this TODO
*/
originator = EasyBind.map(it.uuid) { uuid ->
originator = it.uuid.map { uuid ->
if (uuid == null) {
"Someone"
} else {
@ -142,57 +138,31 @@ class TransactionViewer: View() {
protocolStatus = it.protocolStatus,
stateMachineStatus = it.stateMachineStatus,
statusUpdated = it.lastUpdate,
commandTypes = EasyBind.map(it.transaction) {
commandTypes = it.transaction.map {
val commands = mutableSetOf<Class<CommandData>>()
it?.commands?.forEach {
commands.add(it.value.javaClass)
}
commands
},
totalValueEquiv = EasyBind.combine(myIdentity, reportingExchange, it.transaction) { identity, exchange, transaction ->
transaction?.let { calculateTotalEquiv(setOf(identity.owningKey), exchange.first, exchange.second, transaction) }
},
totalValueEquiv = ::calculateTotalEquiv.lift(myIdentity, reportingExchange, it.transaction),
transaction = it.transaction,
allEvents = it.allEvents
)
}
/**
* We calculate the total value by subtracting relevant input states and adding relevant output states, as long as they're cash
*/
private fun calculateTotalEquiv(
relevantPublicKeys: Set<PublicKey>,
reportingCurrency: Currency,
exchange: (Amount<Currency>) -> Amount<Currency>,
transaction: LedgerTransaction): AmountDiff<Currency> {
var sum = 0L
transaction.inputs.forEach {
val contractState = it.state.data
if (contractState is Cash.State && relevantPublicKeys.contains(contractState.owner)) {
sum -= exchange(contractState.amount.withoutIssuer()).quantity
}
}
transaction.outputs.forEach {
val contractState = it.data
if (contractState is Cash.State && relevantPublicKeys.contains(contractState.owner)) {
sum += exchange(contractState.amount.withoutIssuer()).quantity
}
}
return AmountDiff.fromLong(sum, reportingCurrency)
}
/**
* The detail panes are only filled out if a transaction is selected
*/
private val selectedViewerNode = transactionViewTable.singleRowSelection()
private val selectedTransaction = EasyBind.monadic(selectedViewerNode).flatMap<LedgerTransaction?, SingleRowSelection<ViewerNode>> {
private val selectedTransaction = selectedViewerNode.bind {
when (it) {
is SingleRowSelection.None -> ReadOnlyObjectWrapper(null)
is SingleRowSelection.None -> null.lift()
is SingleRowSelection.Selected -> it.node.transaction
}
}
private val inputStateNodes = ChosenList<StateNode>(EasyBind.map(selectedTransaction) {
private val inputStateNodes = ChosenList(selectedTransaction.map {
if (it == null) {
FXCollections.emptyObservableList<StateNode>()
} else {
@ -200,7 +170,7 @@ class TransactionViewer: View() {
}
})
private val outputStateNodes = ChosenList<StateNode>(EasyBind.map(selectedTransaction) {
private val outputStateNodes = ChosenList(selectedTransaction.map {
if (it == null) {
FXCollections.emptyObservableList<StateNode>()
} else {
@ -210,7 +180,7 @@ class TransactionViewer: View() {
}
})
private val signatures = ChosenList<PublicKey>(EasyBind.map(selectedTransaction) {
private val signatures = ChosenList(selectedTransaction.map {
if (it == null) {
FXCollections.emptyObservableList<PublicKey>()
} else {
@ -218,7 +188,7 @@ class TransactionViewer: View() {
}
})
private val lowLevelEvents = ChosenList(EasyBind.map(selectedViewerNode) {
private val lowLevelEvents = ChosenList(selectedViewerNode.map {
when (it) {
is SingleRowSelection.None -> FXCollections.emptyObservableList<ServiceToClientEvent>()
is SingleRowSelection.Selected -> it.node.allEvents
@ -237,8 +207,8 @@ class TransactionViewer: View() {
private val onlyTransactionsTableShown = FXCollections.observableArrayList<Node>(
transactionViewTable
)
private val topSplitPaneNodesShown = ChosenList<Node>(
EasyBind.map(selectedViewerNode) { selection ->
private val topSplitPaneNodesShown = ChosenList(
selectedViewerNode.map { selection ->
if (selection is SingleRowSelection.None<*>) {
onlyTransactionsTableShown
} else {
@ -260,45 +230,45 @@ class TransactionViewer: View() {
statesAmount: TableColumn<StateNode, Long>,
statesEquiv: TableColumn<StateNode, Amount<Currency>>
) {
statesCountLabel.textProperty().bind(EasyBind.map(Bindings.size(states)) { "$it" })
statesCountLabel.textProperty().bind(Bindings.size(states).map { "$it" })
Bindings.bindContent(statesTable.items, states)
statesId.setCellValueFactory { ReadOnlyObjectWrapper(it.value.stateRef.toString()) }
statesType.setCellValueFactory { ReadOnlyObjectWrapper(it.value.transactionState.data.javaClass) }
statesId.setCellValueFactory { it.value.stateRef.toString().lift() }
statesType.setCellValueFactory { it.value.transactionState.data.javaClass.lift() }
statesOwner.setCellValueFactory {
val state = it.value.transactionState.data
if (state is OwnableState) {
ReadOnlyObjectWrapper(state.owner.toStringShort())
state.owner.toStringShort().lift()
} else {
ReadOnlyObjectWrapper("???")
"???".lift()
}
}
statesLocalCurrency.setCellValueFactory {
val state = it.value.transactionState.data
if (state is Cash.State) {
ReadOnlyObjectWrapper(state.amount.token.product)
state.amount.token.product.lift()
} else {
ReadOnlyObjectWrapper(null)
null.lift()
}
}
statesAmount.setCellValueFactory {
val state = it.value.transactionState.data
if (state is Cash.State) {
ReadOnlyObjectWrapper(state.amount.quantity)
state.amount.quantity.lift()
} else {
ReadOnlyObjectWrapper(null)
null.lift()
}
}
statesAmount.setCellFactory(NumberFormatter.longComma.toTableCellFactory())
statesEquiv.setCellValueFactory {
val state = it.value.transactionState.data
if (state is Cash.State) {
EasyBind.map(reportingExchange) { exchange ->
reportingExchange.map { exchange ->
exchange.second(state.amount.withoutIssuer())
}
} else {
ReadOnlyObjectWrapper(null)
null.lift()
}
}
}
@ -313,65 +283,37 @@ class TransactionViewer: View() {
Math.floor(tableWidthWithoutPaddingAndBorder.toDouble() / transactionViewTable.columns.size).toInt()
}
transactionViewTransactionId.setCellValueFactory { EasyBind.map (it.value.transactionId) { "${it ?: ""}" } }
transactionViewFiberId.setCellValueFactory { EasyBind.map (it.value.fiberId) { "${it?: ""}" } }
transactionViewClientUuid.setCellValueFactory { EasyBind.map (it.value.clientUuid) { "${it ?: ""}" } }
transactionViewProtocolStatus.setCellValueFactory { EasyBind.map(it.value.protocolStatus) { "${it ?: ""}" } }
transactionViewTransactionId.setCellValueFactory { it.value.transactionId.map { "${it ?: ""}" } }
transactionViewFiberId.setCellValueFactory { it.value.fiberId.map { "${it?: ""}" } }
transactionViewClientUuid.setCellValueFactory { it.value.clientUuid.map { "${it ?: ""}" } }
transactionViewProtocolStatus.setCellValueFactory { it.value.protocolStatus.map { "${it ?: ""}" } }
transactionViewTransactionStatus.setCellValueFactory { it.value.transactionStatus }
// TODO reduce clutter
transactionViewTransactionStatus.setCellFactory {
object : TableCell<ViewerNode, TransactionCreateStatus?>() {
val label = Label()
override fun updateItem(
value: TransactionCreateStatus?,
empty: Boolean
) {
super.updateItem(value, empty)
if (value == null || empty) {
graphic = null
text = null
} else {
graphic = label
val backgroundFill = when (value) {
is TransactionCreateStatus.Started -> BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY)
is TransactionCreateStatus.Failed -> BackgroundFill(Color.SALMON, CornerRadii.EMPTY, Insets.EMPTY)
null -> BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY)
}
label.background = Background(backgroundFill)
label.text = "$value"
}
}
transactionViewTransactionStatus.setCustomCellFactory {
val label = Label()
val backgroundFill = when (it) {
is TransactionCreateStatus.Started -> BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY)
is TransactionCreateStatus.Failed -> BackgroundFill(Color.SALMON, CornerRadii.EMPTY, Insets.EMPTY)
null -> BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY)
}
label.background = Background(backgroundFill)
label.text = "$it"
label
}
// TODO reduce clutter
transactionViewStateMachineStatus.setCellValueFactory { it.value.stateMachineStatus }
transactionViewStateMachineStatus.setCellFactory {
object : TableCell<ViewerNode, StateMachineStatus?>() {
val label = Label()
override fun updateItem(
value: StateMachineStatus?,
empty: Boolean
) {
super.updateItem(value, empty)
if (value == null || empty) {
graphic = null
text = null
} else {
graphic = label
val backgroundFill = when (value) {
is StateMachineStatus.Added -> BackgroundFill(Color.LIGHTYELLOW, CornerRadii.EMPTY, Insets.EMPTY)
is StateMachineStatus.Removed -> BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY)
null -> BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY)
}
label.background = Background(backgroundFill)
label.text = "$value"
}
}
transactionViewStateMachineStatus.setCustomCellFactory {
val label = Label()
val backgroundFill = when (it) {
is StateMachineStatus.Added -> BackgroundFill(Color.LIGHTYELLOW, CornerRadii.EMPTY, Insets.EMPTY)
is StateMachineStatus.Removed -> BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY)
null -> BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY)
}
label.background = Background(backgroundFill)
label.text = "$it"
label
}
transactionViewCommandTypes.setCellValueFactory {
EasyBind.map(it.value.commandTypes) { it.map { it.simpleName }.joinToString(",") }
it.value.commandTypes.map { it.map { it.simpleName }.joinToString(",") }
}
transactionViewTotalValueEquiv.setCellValueFactory<ViewerNode, AmountDiff<Currency>> { it.value.totalValueEquiv }
transactionViewTotalValueEquiv.cellFactory = object : Formatter<AmountDiff<Currency>> {
@ -411,8 +353,8 @@ class TransactionViewer: View() {
// Low level events
Bindings.bindContent(lowLevelEventsTable.items, lowLevelEvents)
lowLevelEventsTimestamp.setCellValueFactory { ReadOnlyObjectWrapper(it.value.time) }
lowLevelEventsEvent.setCellValueFactory { ReadOnlyObjectWrapper(it.value) }
lowLevelEventsTimestamp.setCellValueFactory { it.value.time.lift() }
lowLevelEventsEvent.setCellValueFactory { it.value.lift() }
lowLevelEventsTable.setColumnPrefWidthPolicy { tableWidthWithoutPaddingAndBorder, column ->
Math.floor(tableWidthWithoutPaddingAndBorder.toDouble() / lowLevelEventsTable.columns.size).toInt()
}
@ -422,3 +364,32 @@ class TransactionViewer: View() {
})
}
}
/**
* We calculate the total value by subtracting relevant input states and adding relevant output states, as long as they're cash
*/
private fun calculateTotalEquiv(
identity: Party,
reportingCurrencyExchange: Pair<Currency, (Amount<Currency>) -> Amount<Currency>>,
transaction: LedgerTransaction?): AmountDiff<Currency>? {
if (transaction == null) {
return null
}
var sum = 0L
val (reportingCurrency, exchange) = reportingCurrencyExchange
val publicKey = identity.owningKey
transaction.inputs.forEach {
val contractState = it.state.data
if (contractState is Cash.State && publicKey == contractState.owner) {
sum -= exchange(contractState.amount.withoutIssuer()).quantity
}
}
transaction.outputs.forEach {
val contractState = it.data
if (contractState is Cash.State && publicKey == contractState.owner) {
sum += exchange(contractState.amount.withoutIssuer()).quantity
}
}
return AmountDiff.fromLong(sum, reportingCurrency)
}