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 com.r3corda.explorer.formatters.Formatter
import javafx.beans.binding.Bindings import javafx.beans.binding.Bindings
import javafx.beans.value.ObservableValue import javafx.beans.value.ObservableValue
import javafx.scene.Node
import javafx.scene.control.ListCell
import javafx.scene.control.TableCell import javafx.scene.control.TableCell
import javafx.scene.control.TableColumn import javafx.scene.control.TableColumn
import javafx.scene.control.TableView import javafx.scene.control.TableView
@ -54,3 +56,21 @@ fun <S> TableView<S>.singleRowSelection() = Bindings.createObjectBinding({
SingleRowSelection.Selected(selectionModel.selectedItems[0]) SingleRowSelection.Selected(selectionModel.selectedItems[0])
} }
}, arrayOf(selectionModel.selectedItems)) }, 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.model.SettingsModel
import com.r3corda.explorer.ui.* import com.r3corda.explorer.ui.*
import javafx.beans.binding.Bindings import javafx.beans.binding.Bindings
import javafx.beans.property.ReadOnlyObjectWrapper
import javafx.beans.property.ReadOnlyStringWrapper
import javafx.beans.value.ObservableValue import javafx.beans.value.ObservableValue
import javafx.collections.FXCollections import javafx.collections.FXCollections
import javafx.collections.ObservableList import javafx.collections.ObservableList
@ -323,16 +321,16 @@ class CashViewer : View() {
cashViewerTableIssuerCurrency.setCellValueFactory { cashViewerTableIssuerCurrency.setCellValueFactory {
val node = it.value.value val node = it.value.value
when (node) { when (node) {
ViewerNode.Root -> ReadOnlyStringWrapper("") ViewerNode.Root -> "".lift()
is ViewerNode.IssuerNode -> ReadOnlyStringWrapper(node.issuer.toString()) is ViewerNode.IssuerNode -> node.issuer.toString().lift()
is ViewerNode.CurrencyNode -> node.amount.map { it.token.toString() } is ViewerNode.CurrencyNode -> node.amount.map { it.token.toString() }
} }
} }
cashViewerTableLocalCurrency.setCellValueFactory { cashViewerTableLocalCurrency.setCellValueFactory {
val node = it.value.value val node = it.value.value
when (node) { when (node) {
ViewerNode.Root -> ReadOnlyObjectWrapper(null) ViewerNode.Root -> null.lift()
is ViewerNode.IssuerNode -> ReadOnlyObjectWrapper(null) is ViewerNode.IssuerNode -> null.lift()
is ViewerNode.CurrencyNode -> node.amount.map { it } is ViewerNode.CurrencyNode -> node.amount.map { it }
} }
} }
@ -344,7 +342,7 @@ class CashViewer : View() {
cashViewerTableEquiv.setCellValueFactory { cashViewerTableEquiv.setCellValueFactory {
val node = it.value.value val node = it.value.value
when (node) { when (node) {
ViewerNode.Root -> ReadOnlyObjectWrapper(null) ViewerNode.Root -> null.lift()
is ViewerNode.IssuerNode -> node.sumEquivAmount.map { it } is ViewerNode.IssuerNode -> node.sumEquivAmount.map { it }
is ViewerNode.CurrencyNode -> node.equivAmount.map { it } is ViewerNode.CurrencyNode -> node.equivAmount.map { it }
} }

View File

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