diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowStackSnapshot.kt b/core/src/main/kotlin/net/corda/core/flows/FlowStackSnapshot.kt
index c1866adcc8..5ab6afd37c 100644
--- a/core/src/main/kotlin/net/corda/core/flows/FlowStackSnapshot.kt
+++ b/core/src/main/kotlin/net/corda/core/flows/FlowStackSnapshot.kt
@@ -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 = listOf()
) {
data class Frame(
diff --git a/experimental/intellij-plugin/build.gradle b/experimental/intellij-plugin/build.gradle
index 083a2e9dfe..1174bb03d3 100644
--- a/experimental/intellij-plugin/build.gradle
+++ b/experimental/intellij-plugin/build.gradle
@@ -26,6 +26,7 @@ intellij {
}
dependencies {
+ compile project(':core')
// For JSON
compile "com.fasterxml.jackson.core:jackson-databind:2.8.5"
}
diff --git a/experimental/intellij-plugin/src/main/kotlin/net/corda/ideaplugin/toolwindow/CordaFlowToolWindow.kt b/experimental/intellij-plugin/src/main/kotlin/net/corda/ideaplugin/toolwindow/CordaFlowToolWindow.kt
index d87eddcd14..1e6fb1d9a0 100644
--- a/experimental/intellij-plugin/src/main/kotlin/net/corda/ideaplugin/toolwindow/CordaFlowToolWindow.kt
+++ b/experimental/intellij-plugin/src/main/kotlin/net/corda/ideaplugin/toolwindow/CordaFlowToolWindow.kt
@@ -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
}
diff --git a/experimental/intellij-plugin/src/main/kotlin/net/corda/ideaplugin/toolwindow/FlowSnapshotTreeDataManager.kt b/experimental/intellij-plugin/src/main/kotlin/net/corda/ideaplugin/toolwindow/FlowSnapshotTreeDataManager.kt
index 90953d01ce..d4d6f4ae05 100644
--- a/experimental/intellij-plugin/src/main/kotlin/net/corda/ideaplugin/toolwindow/FlowSnapshotTreeDataManager.kt
+++ b/experimental/intellij-plugin/src/main/kotlin/net/corda/ideaplugin/toolwindow/FlowSnapshotTreeDataManager.kt
@@ -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)
}
}
}
}
-}
\ No newline at end of file
+}
+
+class Descriptor(val value:Any? = null, val icon: Icon? = null, val label:String? = null)
\ No newline at end of file
diff --git a/experimental/intellij-plugin/src/main/kotlin/net/corda/ideaplugin/toolwindow/FlowTreeDataManager.kt b/experimental/intellij-plugin/src/main/kotlin/net/corda/ideaplugin/toolwindow/FlowTreeDataManager.kt
index 82054a0b60..9dfaaf0a2e 100644
--- a/experimental/intellij-plugin/src/main/kotlin/net/corda/ideaplugin/toolwindow/FlowTreeDataManager.kt
+++ b/experimental/intellij-plugin/src/main/kotlin/net/corda/ideaplugin/toolwindow/FlowTreeDataManager.kt
@@ -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.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.findByFile(file: File) = find { it.file == file }
internal fun addToModelAndRefresh(model: DefaultTreeModel,
child: DefaultMutableTreeNode,
diff --git a/test-utils/src/main/kotlin/net/corda/testing/FlowStackSnapshot.kt b/test-utils/src/main/kotlin/net/corda/testing/FlowStackSnapshot.kt
index 13ec465284..1423ffdec8 100644
--- a/test-utils/src/main/kotlin/net/corda/testing/FlowStackSnapshot.kt
+++ b/test-utils/src/main/kotlin/net/corda/testing/FlowStackSnapshot.kt
@@ -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? {