Changing flow stack snapshot tree node labeling (#26)

* Changing flow stack snapshot tree node labeling

* Removing stackObjects node from the hierarchy
This commit is contained in:
mkit 2017-08-17 12:16:10 +01:00 committed by GitHub
parent a3ab62341c
commit 551a26d0a3
6 changed files with 53 additions and 42 deletions

View File

@ -57,7 +57,7 @@ private class FlowStackSnapshotDefaultFactory : FlowStackSnapshotFactory {
*/
data class FlowStackSnapshot constructor(
val timestamp: Long = System.currentTimeMillis(),
val flowClass: Class<*>? = null,
val flowClass: String? = null,
val stackFrames: List<Frame> = listOf()
) {
data class Frame(

View File

@ -26,6 +26,7 @@ intellij {
}
dependencies {
compile project(':core')
// For JSON
compile "com.fasterxml.jackson.core:jackson-databind:2.8.5"
}

View File

@ -8,7 +8,6 @@ import com.intellij.ui.components.JBScrollPane
import com.intellij.ui.content.Content
import com.intellij.ui.content.ContentFactory
import com.intellij.ui.treeStructure.Tree
import net.corda.ideaplugin.module.CordaModuleType
import java.awt.*
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
@ -187,10 +186,10 @@ class CordaFlowToolWindow : ToolWindowFactory {
private class SnapshotTreeRenderer : DefaultTreeCellRenderer() {
override fun getTreeCellRendererComponent(tree: JTree?, value: Any?, sel: Boolean, expanded: Boolean, leaf: Boolean, row: Int, hasFocus: Boolean): Component {
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus)
val descriptor = (value as DefaultMutableTreeNode).userObject as SnapshotDataDescriptor
val descriptor = (value as DefaultMutableTreeNode).userObject as Descriptor
icon = descriptor.icon
if (!descriptor.key.isNullOrEmpty()) {
text = "${descriptor.key}: ${descriptor.data.toString()}"
if (!descriptor.label.isNullOrEmpty()) {
text = descriptor.label + if (leaf) ": ${descriptor.value?.toString()}" else ""
}
return this
}

View File

@ -3,6 +3,7 @@ package net.corda.ideaplugin.toolwindow
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.intellij.icons.AllIcons
import net.corda.core.flows.FlowStackSnapshot
import java.io.File
import javax.swing.Icon
import javax.swing.JTree
@ -10,13 +11,6 @@ import javax.swing.tree.DefaultMutableTreeNode
import javax.swing.tree.DefaultTreeModel
import javax.swing.tree.MutableTreeNode
/**
* Snapshot tree data descriptor. It is used as userObject in the [DefaultMutableTreeNode] class.
*/
class SnapshotDataDescriptor(val data: Any?, val icon: Icon, val key: String? = null) {
override fun toString(): String = data?.toString() ?: "null"
}
/**
* Manager class for flow snapshots. It is responsible for parsing data read from a snapshot file and constructing
* tree model from it.
@ -27,7 +21,7 @@ class FlowSnapshotTreeDataManager(tree: JTree) {
}
// Root node for the snapshot hierarchy, which is an empty node.
private val root = DefaultMutableTreeNode(SnapshotDataDescriptor(null, AllIcons.Json.Object))
private val root = DefaultMutableTreeNode(Descriptor())
// Snapshot tree model
private val snapshotModel = DefaultTreeModel(root)
@ -62,8 +56,8 @@ class FlowSnapshotTreeDataManager(tree: JTree) {
* the model is updated accordingly.
*/
fun addNodeToSnapshotModel(snapshotFile: File) {
val insertionIndex = -(root.childNodes().map {
(it.userObject as SnapshotDataDescriptor).key
val insertionIndex = -(root.childNodes.map {
it.file?.name
}.binarySearch(extractFileName(snapshotFile))) - 1
insertNodeToSnapshotModel(snapshotFile, insertionIndex)
}
@ -72,8 +66,9 @@ class FlowSnapshotTreeDataManager(tree: JTree) {
* Removes the snapshot file from the snapshot hierarchy. The model is also updated after this operation.
*/
fun removeNodeFromSnapshotModel(snapshotFile: File) {
val node = root.childNodes().find {
(it.userObject as SnapshotDataDescriptor).data == extractFileName(snapshotFile)
val snapshotFileName = extractFileName(snapshotFile)
val node = root.childNodes.find {
it.file?.name == snapshotFileName
} as MutableTreeNode?
if (node != null) {
snapshotModel.removeNodeFromParent(node)
@ -83,7 +78,23 @@ class FlowSnapshotTreeDataManager(tree: JTree) {
}
private fun insertNodeToSnapshotModel(snapshotFile: File, insertionIndex: Int = -1) {
buildChildrenModel(mapper.readTree(snapshotFile), root, extractFileName(snapshotFile), insertionIndex)
val fileNode = DefaultMutableTreeNode(Descriptor(snapshotFile, AllIcons.FileTypes.Custom, snapshotFile.name))
val snapshot = mapper.readValue(snapshotFile, FlowStackSnapshot::class.java)
fileNode.add(DefaultMutableTreeNode(Descriptor(snapshot.timestamp, AllIcons.Debugger.Db_primitive, "timestamp")))
fileNode.add(DefaultMutableTreeNode(Descriptor(snapshot.flowClass, AllIcons.Debugger.Db_primitive, "flowClass")))
val framesNode = DefaultMutableTreeNode(Descriptor(icon = AllIcons.Debugger.Db_array, label = "stackFrames"))
fileNode.add(framesNode)
snapshot.stackFrames.forEach {
val ste = it.stackTraceElement!!
val label = "${ste.className}.${ste.methodName}(line:${ste.lineNumber}) - ${ste.fileName}"
val frameNode = DefaultMutableTreeNode(Descriptor(icon = AllIcons.Debugger.StackFrame, label = label))
framesNode.add(frameNode)
it.stackObjects.mapIndexed { index: Int, stackItem: Any? ->
buildChildrenModel(mapper.convertValue(stackItem, JsonNode::class.java), frameNode, index.toString())
}
}
addToModelAndRefresh(snapshotModel, fileNode, root, insertionIndex)
}
private fun extractFileName(file: File): String {
@ -92,26 +103,26 @@ class FlowSnapshotTreeDataManager(tree: JTree) {
private fun buildChildrenModel(
node: JsonNode?,
parent: DefaultMutableTreeNode, key: String? = null,
insertionIndex: Int = -1) {
parent: DefaultMutableTreeNode, label: String? = null) {
val child: DefaultMutableTreeNode
if (node == null || !node.isContainerNode) {
child = DefaultMutableTreeNode(SnapshotDataDescriptor(node, AllIcons.Debugger.Db_primitive, key))
addToModelAndRefresh(snapshotModel, child, parent, insertionIndex)
parent.add(DefaultMutableTreeNode(Descriptor(node, AllIcons.Debugger.Db_primitive, label)))
} else {
if (node.isArray) {
child = DefaultMutableTreeNode(SnapshotDataDescriptor(key, AllIcons.Debugger.Db_array))
addToModelAndRefresh(snapshotModel, child, parent, insertionIndex)
child = DefaultMutableTreeNode(Descriptor(icon = AllIcons.Debugger.Db_array, label = label))
parent.add(child)
node.mapIndexed { index: Int, item: JsonNode? ->
buildChildrenModel(item, child, index.toString())
}
} else {
child = DefaultMutableTreeNode(SnapshotDataDescriptor(key, AllIcons.Json.Object))
addToModelAndRefresh(snapshotModel, child, parent, insertionIndex)
child = DefaultMutableTreeNode(Descriptor(icon = AllIcons.Json.Object, label = label))
parent.add(child)
node.fields().forEach {
buildChildrenModel(it.value, child, it.key)
}
}
}
}
}
}
class Descriptor(val value:Any? = null, val icon: Icon? = null, val label:String? = null)

View File

@ -39,7 +39,7 @@ class FlowTreeDataManager(val tree: JTree, val snapshotModel: FlowSnapshotTreeDa
* Builds the flow directory hierarchy with the root being associated with the passed [flowsDirectory].
* If the parameter is missing the function rebuilds current hierarchy and reloads (refreshes) current model.
*/
fun loadFlows(flowsDirectory: File? = root.userObjectAsFile()) {
fun loadFlows(flowsDirectory: File? = root.file) {
root.userObject = flowsDirectory ?: return
root.removeAllChildren()
@ -81,8 +81,8 @@ class FlowTreeDataManager(val tree: JTree, val snapshotModel: FlowSnapshotTreeDa
}
private fun isSelected(dir: File?): Boolean {
val node = tree.selectedNode()
return node != null && dir == node.userObjectAsFile()
val node = tree.selectedNode
return node != null && dir == node.file
}
private fun startWatching(dir: File) {
@ -100,18 +100,18 @@ class FlowTreeDataManager(val tree: JTree, val snapshotModel: FlowSnapshotTreeDa
val parent = dir.parentFile
if (parent.name == FLOWS_DIRECTORY) {
// if a date directory has been added this means that that its parent is [FLOWS_DIRECTORY]
insertDateDirectory(dir, findInsertionIndex(root.childNodes(), dir.name))
insertDateDirectory(dir, findInsertionIndex(root.childNodes, dir.name))
} else if (parent.parentFile.name == FLOWS_DIRECTORY) {
// if a flow directory has been added this means that that the parent of its parent is [FLOWS_DIRECTORY]
val parentNode = root.childNodes().findByFile(parent) ?: return
flowModel.insertNodeInto(DefaultMutableTreeNode(dir), parentNode, findInsertionIndex(parentNode.childNodes(), dir.name))
val parentNode = root.childNodes.findByFile(parent) ?: return
flowModel.insertNodeInto(DefaultMutableTreeNode(dir), parentNode, findInsertionIndex(parentNode.childNodes, dir.name))
startWatching(dir)
}
}
private fun removeNodeFromFlowModel(dir: File) {
val selectedNode = tree.selectedNode()
if (selectedNode != null && selectedNode.userObjectAsFile() == dir) {
val selectedNode = tree.selectedNode
if (selectedNode != null && selectedNode.file == dir) {
// Reload flows if the [dir] is currently selected
loadFlows()
return
@ -123,10 +123,10 @@ class FlowTreeDataManager(val tree: JTree, val snapshotModel: FlowSnapshotTreeDa
parentNode = root
} else if (parent.parentFile.name == FLOWS_DIRECTORY) {
// if a flow directory has been added this means that that the parent of its parent is [FLOWS_DIRECTORY]
parentNode = root.childNodes().findByFile(parent)
parentNode = root.childNodes.findByFile(parent)
}
if (parentNode != null) {
val node = parentNode.childNodes().findByFile(dir) ?: return
val node = parentNode.childNodes.findByFile(dir) ?: return
flowModel.removeNodeFromParent(node)
}
}
@ -206,10 +206,10 @@ class FlowTreeDataManager(val tree: JTree, val snapshotModel: FlowSnapshotTreeDa
}
}
internal fun DefaultMutableTreeNode.userObjectAsFile() = userObject as? File
internal fun DefaultMutableTreeNode.childNodes() = children().toList().mapNotNull { it as? DefaultMutableTreeNode }
private fun List<DefaultMutableTreeNode>.findByFile(file: File) = find { it.userObjectAsFile() == file }
private fun JTree.selectedNode() = lastSelectedPathComponent as? DefaultMutableTreeNode
internal val DefaultMutableTreeNode.file get() = userObject as? File
internal val DefaultMutableTreeNode.childNodes get() = children().toList().mapNotNull { it as? DefaultMutableTreeNode }
private val JTree.selectedNode get() = lastSelectedPathComponent as? DefaultMutableTreeNode
private fun List<DefaultMutableTreeNode>.findByFile(file: File) = find { it.file == file }
internal fun addToModelAndRefresh(model: DefaultTreeModel,
child: DefaultMutableTreeNode,

View File

@ -70,7 +70,7 @@ class FlowStackSnapshotFactoryImpl : FlowStackSnapshotFactory {
Frame(element, listOf())
}
}
return FlowStackSnapshot(flowClass = flowClass, stackFrames = frames)
return FlowStackSnapshot(flowClass = flowClass.name, stackFrames = frames)
}
private fun getInstrumentedAnnotation(element: StackTraceElement): Instrumented? {