blob: af8203c8a4c57e4fdf7849e77477b119dad2c015 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.idea.editors.hprof.views;
import com.android.tools.idea.actions.EditMultipleSourcesAction;
import com.android.tools.idea.actions.PsiFileAndLineNavigation;
import com.android.tools.idea.editors.allocations.ColumnTreeBuilder;
import com.android.tools.idea.editors.hprof.descriptors.*;
import com.android.tools.perflib.heap.*;
import com.intellij.debugger.engine.DebugProcessEvents;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.engine.SuspendManagerImpl;
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.ui.impl.DebuggerTreeRenderer;
import com.intellij.debugger.ui.impl.tree.TreeBuilder;
import com.intellij.debugger.ui.impl.tree.TreeBuilderNode;
import com.intellij.debugger.ui.impl.watch.DebuggerTree;
import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
import com.intellij.debugger.ui.impl.watch.DefaultNodeDescriptor;
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl;
import com.intellij.debugger.ui.tree.NodeDescriptor;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.PopupHandler;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.components.JBList;
import com.sun.jdi.request.EventRequest;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.util.*;
import java.util.List;
public class InstancesTreeView implements DataProvider {
public static final String TREE_NAME = "HprofInstancesTree";
private static final int NODES_PER_EXPANSION = 100;
@NotNull private Project myProject;
@NotNull private DebuggerTree myDebuggerTree;
@NotNull private JComponent myColumnTree;
@NotNull private DebugProcessImpl myDebugProcess;
@SuppressWarnings("NullableProblems") @NotNull private volatile SuspendContextImpl myDummySuspendContext;
@NotNull private Heap myHeap;
@Nullable private ClassObj myClassObj;
@Nullable private Comparator<DebuggerTreeNodeImpl> myComparator;
@NotNull private SortOrder mySortOrder = SortOrder.UNSORTED;
public InstancesTreeView(@NotNull Project project, @NotNull final SelectionModel selectionModel) {
myProject = project;
myDebuggerTree = new DebuggerTree(project) {
@Override
protected void build(DebuggerContextImpl context) {
DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl)getModel().getRoot();
Instance instance = ((InstanceFieldDescriptorImpl)root.getDescriptor()).getInstance();
addChildren(root, null, instance);
}
@Override
public Object getData(@NonNls String dataId) {
return InstancesTreeView.this.getData(dataId);
}
};
myDebuggerTree.getComponent().setName(TREE_NAME);
myHeap = selectionModel.getHeap();
myDebugProcess = new DebugProcessEvents(project);
final SuspendManagerImpl suspendManager = new SuspendManagerImpl(myDebugProcess);
myDebugProcess.getManagerThread().invokeAndWait(new DebuggerCommandImpl() {
@Override
protected void action() throws Exception {
myDummySuspendContext = suspendManager.pushSuspendContext(EventRequest.SUSPEND_NONE, 1);
}
});
final TreeBuilder model = new TreeBuilder(myDebuggerTree) {
@Override
public void buildChildren(TreeBuilderNode node) {
final DebuggerTreeNodeImpl debuggerTreeNode = (DebuggerTreeNodeImpl)node;
NodeDescriptor descriptor = debuggerTreeNode.getDescriptor();
if (descriptor instanceof DefaultNodeDescriptor) {
return;
}
else if (descriptor instanceof ContainerDescriptorImpl) {
addContainerChildren(debuggerTreeNode, 0);
}
else {
InstanceFieldDescriptorImpl instanceDescriptor = (InstanceFieldDescriptorImpl)descriptor;
addChildren(debuggerTreeNode, instanceDescriptor.getHprofField(), instanceDescriptor.getInstance());
}
sortTree(debuggerTreeNode);
myDebuggerTree.treeDidChange();
}
@Override
public boolean isExpandable(TreeBuilderNode builderNode) {
return ((DebuggerTreeNodeImpl)builderNode).getDescriptor().isExpandable();
}
};
model.setRoot(myDebuggerTree.getNodeFactory().getDefaultNode());
model.addTreeModelListener(new TreeModelListener() {
@Override
public void treeNodesChanged(TreeModelEvent event) {
myDebuggerTree.hideTooltip();
}
@Override
public void treeNodesInserted(TreeModelEvent event) {
myDebuggerTree.hideTooltip();
}
@Override
public void treeNodesRemoved(TreeModelEvent event) {
myDebuggerTree.hideTooltip();
}
@Override
public void treeStructureChanged(TreeModelEvent event) {
myDebuggerTree.hideTooltip();
}
});
myDebuggerTree.setModel(model);
myDebuggerTree.setRootVisible(false);
myDebuggerTree.putClientProperty(DataManager.CLIENT_PROPERTY_DATA_PROVIDER, this);
JBList contextActionList = new JBList(new EditMultipleSourcesAction());
JBPopupFactory.getInstance().createListPopupBuilder(contextActionList);
final DefaultActionGroup popupGroup = new DefaultActionGroup(new EditMultipleSourcesAction());
myDebuggerTree.addMouseListener(new PopupHandler() {
@Override
public void invokePopup(Component comp, int x, int y) {
ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, popupGroup).getComponent().show(comp, x, y);
}
});
selectionModel.addListener(new SelectionModel.SelectionListener() {
@Override
public void onHeapChanged(@NotNull Heap heap) {
if (heap != myHeap) {
myHeap = heap;
if (myDebuggerTree.getMutableModel().getRoot() != null) {
onSelectionChanged();
}
}
}
@Override
public void onClassObjChanged(@Nullable ClassObj classObj) {
if (classObj != myClassObj) {
myClassObj = classObj;
onSelectionChanged();
}
}
@Override
public void onInstanceChanged(@Nullable Instance instance) {
}
private void onSelectionChanged() {
DebuggerTreeNodeImpl newRoot;
Instance singleChild = null;
if (myClassObj != null) {
ContainerDescriptorImpl containerDescriptor = new ContainerDescriptorImpl(myClassObj, myHeap.getId());
newRoot = DebuggerTreeNodeImpl.createNodeNoUpdate(myDebuggerTree, containerDescriptor);
if (containerDescriptor.getInstances().size() == 1) {
singleChild = containerDescriptor.getInstances().get(0);
}
}
else {
newRoot = myDebuggerTree.getNodeFactory().getDefaultNode();
}
myDebuggerTree.getMutableModel().setRoot(newRoot);
myDebuggerTree.treeChanged();
if (myDebuggerTree.getRowCount() > 0) {
myDebuggerTree.scrollRowToVisible(0);
}
if (singleChild != null) {
myDebuggerTree.setSelectionInterval(0 , 0);
selectionModel.setInstance(singleChild);
}
}
});
myDebuggerTree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
TreePath path = e.getPath();
if (path == null || path.getPathCount() < 2 || !e.isAddedPath()) {
selectionModel.setInstance(null);
return;
}
DebuggerTreeNodeImpl instanceNode = (DebuggerTreeNodeImpl)path.getPathComponent(1);
if (instanceNode.getDescriptor() instanceof InstanceFieldDescriptorImpl) {
InstanceFieldDescriptorImpl descriptor = (InstanceFieldDescriptorImpl)instanceNode.getDescriptor();
selectionModel.setInstance(descriptor.getInstance());
}
// Handle node expansions (this is present when the list is large).
DebuggerTreeNodeImpl lastPathNode = (DebuggerTreeNodeImpl)path.getLastPathComponent();
if (lastPathNode.getDescriptor() instanceof ExpansionDescriptorImpl) {
ExpansionDescriptorImpl expansionDescriptor = (ExpansionDescriptorImpl)lastPathNode.getDescriptor();
DebuggerTreeNodeImpl parentNode = lastPathNode.getParent();
myDebuggerTree.getMutableModel().removeNodeFromParent(lastPathNode);
if (parentNode.getDescriptor() instanceof ContainerDescriptorImpl) {
addContainerChildren(parentNode, expansionDescriptor.getStartIndex());
}
else if (parentNode.getDescriptor() instanceof InstanceFieldDescriptorImpl) {
InstanceFieldDescriptorImpl instanceFieldDescriptor = (InstanceFieldDescriptorImpl)parentNode.getDescriptor();
addChildren(parentNode, instanceFieldDescriptor.getHprofField(), instanceFieldDescriptor.getInstance(),
expansionDescriptor.getStartIndex());
}
sortTree(parentNode);
myDebuggerTree.getMutableModel().nodeStructureChanged(parentNode);
if (myComparator != null) {
myDebuggerTree.scrollPathToVisible(new TreePath(((DebuggerTreeNodeImpl)parentNode.getLastChild()).getPath()));
}
}
}
});
ColumnTreeBuilder builder = new ColumnTreeBuilder(myDebuggerTree)
.addColumn(
new ColumnTreeBuilder.ColumnBuilder()
.setName("Instance")
.setPreferredWidth(600)
.setHeaderAlignment(SwingConstants.LEFT)
.setComparator(new Comparator<DebuggerTreeNodeImpl>() {
@Override
public int compare(@NotNull DebuggerTreeNodeImpl a, @NotNull DebuggerTreeNodeImpl b) {
return getDefaultOrdering(a, b);
}
})
.setRenderer((DebuggerTreeRenderer)myDebuggerTree.getCellRenderer()))
.addColumn(
new ColumnTreeBuilder.ColumnBuilder()
.setName("Depth")
.setPreferredWidth(60)
.setHeaderAlignment(SwingConstants.RIGHT)
.setComparator(new Comparator<DebuggerTreeNodeImpl>() {
@Override
public int compare(DebuggerTreeNodeImpl a, DebuggerTreeNodeImpl b) {
int depthA = 0;
int depthB = 0;
if (a.getDescriptor() instanceof InstanceFieldDescriptorImpl) {
Instance instanceA = (Instance)((InstanceFieldDescriptorImpl)a.getDescriptor()).getValueData();
if (instanceA != null) {
depthA = instanceA.getDistanceToGcRoot();
}
}
if (b.getDescriptor() instanceof InstanceFieldDescriptorImpl) {
Instance instanceB = (Instance)((InstanceFieldDescriptorImpl)b.getDescriptor()).getValueData();
if (instanceB != null) {
depthB = instanceB.getDistanceToGcRoot();
}
}
if (depthA != depthB) {
return depthA - depthB;
}
else {
return getDefaultOrdering(a, b);
}
}
})
.setRenderer(new ColoredTreeCellRenderer() {
@Override
public void customizeCellRenderer(@NotNull JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
NodeDescriptorImpl nodeDescriptor = (NodeDescriptorImpl)((TreeBuilderNode)value).getUserObject();
if (nodeDescriptor instanceof InstanceFieldDescriptorImpl) {
InstanceFieldDescriptorImpl descriptor = (InstanceFieldDescriptorImpl)nodeDescriptor;
assert !descriptor.isPrimitive();
Instance instance = (Instance)descriptor.getValueData();
if (instance != null && instance.getDistanceToGcRoot() != Integer.MAX_VALUE) {
append(String.valueOf(instance.getDistanceToGcRoot()), SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
}
setTextAlign(SwingConstants.RIGHT);
}
})
)
.addColumn(
new ColumnTreeBuilder.ColumnBuilder()
.setName("Shallow Size")
.setPreferredWidth(80)
.setHeaderAlignment(SwingConstants.RIGHT)
.setComparator(new Comparator<DebuggerTreeNodeImpl>() {
@Override
public int compare(@NotNull DebuggerTreeNodeImpl a, @NotNull DebuggerTreeNodeImpl b) {
int sizeA = 0;
int sizeB = 0;
if (a.getDescriptor() instanceof InstanceFieldDescriptorImpl) {
Instance instanceA = (Instance)((InstanceFieldDescriptorImpl)a.getDescriptor()).getValueData();
if (instanceA != null) {
sizeA = instanceA.getSize();
}
}
if (b.getDescriptor() instanceof InstanceFieldDescriptorImpl) {
Instance instanceB = (Instance)((InstanceFieldDescriptorImpl)b.getDescriptor()).getValueData();
if (instanceB != null) {
sizeB = instanceB.getSize();
}
}
if (sizeA != sizeB) {
return sizeA - sizeB;
}
else {
return getDefaultOrdering(a, b);
}
}
})
.setRenderer(new ColoredTreeCellRenderer() {
@Override
public void customizeCellRenderer(@NotNull JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
NodeDescriptorImpl nodeDescriptor = (NodeDescriptorImpl)((TreeBuilderNode)value).getUserObject();
if (nodeDescriptor instanceof InstanceFieldDescriptorImpl) {
InstanceFieldDescriptorImpl descriptor = (InstanceFieldDescriptorImpl)nodeDescriptor;
assert !descriptor.isPrimitive();
Instance instance = (Instance)descriptor.getValueData();
if (instance != null) {
append(String.valueOf(instance.getSize()), SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
}
setTextAlign(SwingConstants.RIGHT);
}
})
)
.addColumn(
new ColumnTreeBuilder.ColumnBuilder()
.setName("Dominating Size")
.setPreferredWidth(80)
.setHeaderAlignment(SwingConstants.RIGHT)
.setComparator(new Comparator<DebuggerTreeNodeImpl>() {
@Override
public int compare(@NotNull DebuggerTreeNodeImpl a, @NotNull DebuggerTreeNodeImpl b) {
long sizeA = 0;
long sizeB = 0;
if (a.getDescriptor() instanceof InstanceFieldDescriptorImpl) {
Instance instanceA = (Instance)((InstanceFieldDescriptorImpl)a.getDescriptor()).getValueData();
if (instanceA != null && instanceA.getDistanceToGcRoot() != Integer.MAX_VALUE) {
sizeA = instanceA.getTotalRetainedSize();
}
}
if (b.getDescriptor() instanceof InstanceFieldDescriptorImpl) {
Instance instanceB = (Instance)((InstanceFieldDescriptorImpl)b.getDescriptor()).getValueData();
if (instanceB != null && instanceB.getDistanceToGcRoot() != Integer.MAX_VALUE) {
sizeB = instanceB.getTotalRetainedSize();
}
}
if (sizeA != sizeB) {
return (int)(sizeA - sizeB);
}
else {
return getDefaultOrdering(a, b);
}
}
})
.setRenderer(new ColoredTreeCellRenderer() {
@Override
public void customizeCellRenderer(@NotNull JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
NodeDescriptorImpl nodeDescriptor = (NodeDescriptorImpl)((TreeBuilderNode)value).getUserObject();
if (nodeDescriptor instanceof InstanceFieldDescriptorImpl) {
InstanceFieldDescriptorImpl descriptor = (InstanceFieldDescriptorImpl)nodeDescriptor;
assert !descriptor.isPrimitive();
Instance instance = (Instance)descriptor.getValueData();
if (instance != null && instance.getDistanceToGcRoot() != Integer.MAX_VALUE) {
append(String.valueOf(instance.getTotalRetainedSize()), SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
}
setTextAlign(SwingConstants.RIGHT);
}
})
);
//noinspection NullableProblems
builder.setTreeSorter(new ColumnTreeBuilder.TreeSorter<DebuggerTreeNodeImpl>() {
@Override
public void sort(@NotNull Comparator<DebuggerTreeNodeImpl> comparator, @NotNull SortOrder sortOrder) {
if (myComparator != comparator && mySortOrder != sortOrder) {
myComparator = comparator;
mySortOrder = sortOrder;
TreeBuilder mutableModel = myDebuggerTree.getMutableModel();
DebuggerTreeNodeImpl root = (DebuggerTreeNodeImpl)mutableModel.getRoot();
sortTree(root);
selectionModel.setSelectionLocked(true);
TreePath selectionPath = myDebuggerTree.getSelectionPath();
mutableModel.nodeStructureChanged(root);
myDebuggerTree.setSelectionPath(selectionPath);
myDebuggerTree.scrollPathToVisible(selectionPath);
selectionModel.setSelectionLocked(false);
}
}
});
myColumnTree = builder.build();
}
@NotNull
public JComponent getComponent() {
return myColumnTree;
}
private void sortTree(@NotNull DebuggerTreeNodeImpl node) {
if (myComparator == null) {
return;
}
// We don't want to accidentally build children, so we have to get the raw children instead.
Enumeration e = node.rawChildren();
if (e.hasMoreElements()) {
//noinspection unchecked
ArrayList<DebuggerTreeNodeImpl> builtChildren = Collections.list(e);
// First check if there's an expansion node. Remove if there is, and add it back at the end.
DebuggerTreeNodeImpl expansionNode = builtChildren.get(builtChildren.size() - 1);
if (expansionNode.getDescriptor() instanceof ExpansionDescriptorImpl) {
builtChildren.remove(builtChildren.size() - 1);
}
else {
expansionNode = null;
}
Collections.sort(builtChildren, myComparator);
node.removeAllChildren(); // Remove children after sorting, since the sort may depend on the parent information.
for (DebuggerTreeNodeImpl childNode : builtChildren) {
node.add(childNode);
sortTree(childNode);
}
if (expansionNode != null) {
node.add(expansionNode);
}
}
}
private int getDefaultOrdering(@NotNull DebuggerTreeNodeImpl a, @NotNull DebuggerTreeNodeImpl b) {
NodeDescriptorImpl parentDescriptor = a.getParent().getDescriptor();
if (parentDescriptor instanceof InstanceFieldDescriptorImpl) {
Instance parentInstance = ((InstanceFieldDescriptorImpl)parentDescriptor).getInstance();
if (parentInstance instanceof ArrayInstance) {
return getMemoryOrderingSortResult(a, b);
}
}
else if (parentDescriptor instanceof ContainerDescriptorImpl) {
return getMemoryOrderingSortResult(a, b);
}
return a.getDescriptor().getLabel().compareTo(b.getDescriptor().getLabel());
}
private int getMemoryOrderingSortResult(@NotNull DebuggerTreeNodeImpl a, @NotNull DebuggerTreeNodeImpl b) {
return (((HprofFieldDescriptorImpl)a.getDescriptor()).getMemoryOrdering() -
((HprofFieldDescriptorImpl)b.getDescriptor()).getMemoryOrdering()) ^
(mySortOrder == SortOrder.ASCENDING ? 1 : -1);
}
private void addContainerChildren(@NotNull DebuggerTreeNodeImpl node, int startIndex) {
ContainerDescriptorImpl containerDescriptor = (ContainerDescriptorImpl)node.getDescriptor();
List<Instance> instances = containerDescriptor.getInstances();
List<HprofFieldDescriptorImpl> descriptors = new ArrayList<HprofFieldDescriptorImpl>(NODES_PER_EXPANSION);
int currentIndex = startIndex;
int limit = currentIndex + NODES_PER_EXPANSION;
for (int loopCounter = currentIndex; loopCounter < instances.size() && currentIndex < limit; ++loopCounter) {
Instance instance = instances.get(loopCounter);
if (myHeap.getInstance(instance.getId()) != null) {
descriptors.add(new InstanceFieldDescriptorImpl(
myDebuggerTree.getProject(),
new Field(Type.OBJECT, Integer.toString(currentIndex)),
instance,
currentIndex));
++currentIndex;
}
}
HprofFieldDescriptorImpl.batchUpdateRepresentation(descriptors, myDebugProcess.getManagerThread(), myDummySuspendContext);
for (HprofFieldDescriptorImpl descriptor : descriptors) {
node.add(DebuggerTreeNodeImpl.createNodeNoUpdate(myDebuggerTree, descriptor));
}
if (currentIndex == limit) {
node.add(DebuggerTreeNodeImpl.createNodeNoUpdate(myDebuggerTree, new ExpansionDescriptorImpl("instances", limit, instances.size())));
}
}
private void addChildren(@NotNull DebuggerTreeNodeImpl node, @Nullable Field field, @Nullable Instance instance) {
addChildren(node, field, instance, 0);
}
private void addChildren(@NotNull DebuggerTreeNodeImpl node, @Nullable Field field, @Nullable Instance instance, int arrayStartIndex) {
if (instance == null) {
return;
}
// These local variables are used for adding an expansion node for array tree node expansion.
int currentArrayIndex = arrayStartIndex;
int limit = currentArrayIndex + NODES_PER_EXPANSION;
int arrayLength = 0;
List<HprofFieldDescriptorImpl> descriptors;
if (instance instanceof ClassInstance) {
ClassInstance classInstance = (ClassInstance)instance;
descriptors = new ArrayList<HprofFieldDescriptorImpl>(classInstance.getValues().size());
int i = 0;
for (ClassInstance.FieldValue entry : classInstance.getValues()) {
if (entry.getField().getType() == Type.OBJECT) {
descriptors.add(new InstanceFieldDescriptorImpl(myDebuggerTree.getProject(), entry.getField(), (Instance)entry.getValue(), i));
}
else {
descriptors.add(new PrimitiveFieldDescriptorImpl(myDebuggerTree.getProject(), entry.getField(), entry.getValue(), i));
}
++i;
}
}
else if (instance instanceof ArrayInstance) {
assert (field != null);
ArrayInstance arrayInstance = (ArrayInstance)instance;
Object[] values = arrayInstance.getValues();
descriptors = new ArrayList<HprofFieldDescriptorImpl>(values.length);
arrayLength = values.length;
if (arrayInstance.getArrayType() == Type.OBJECT) {
while (currentArrayIndex < arrayLength && currentArrayIndex < limit) {
descriptors.add(
new InstanceFieldDescriptorImpl(
myDebuggerTree.getProject(),
new Field(arrayInstance.getArrayType(), String.valueOf(currentArrayIndex)),
(Instance)values[currentArrayIndex], currentArrayIndex));
++currentArrayIndex;
}
}
else {
while (currentArrayIndex < arrayLength && currentArrayIndex < limit) {
descriptors.add(
new PrimitiveFieldDescriptorImpl(myDebuggerTree.getProject(),
new Field(arrayInstance.getArrayType(), String.valueOf(currentArrayIndex)), values[currentArrayIndex], currentArrayIndex));
++currentArrayIndex;
}
}
}
else {
throw new RuntimeException("Unimplemented Instance type in addChildren.");
}
HprofFieldDescriptorImpl.batchUpdateRepresentation(descriptors, myDebugProcess.getManagerThread(), myDummySuspendContext);
for (HprofFieldDescriptorImpl descriptor : descriptors) {
node.add(DebuggerTreeNodeImpl.createNodeNoUpdate(myDebuggerTree, descriptor));
}
if (currentArrayIndex == limit) {
node.add(DebuggerTreeNodeImpl.createNodeNoUpdate(myDebuggerTree, new ExpansionDescriptorImpl("array elements", limit, arrayLength)));
}
}
@Nullable
@Override
public Object getData(@NonNls String dataId) {
if (CommonDataKeys.NAVIGATABLE_ARRAY.is(dataId)) {
return getTargetFiles();
}
else if (CommonDataKeys.PROJECT.is(dataId)) {
return myProject;
}
return null;
}
@Nullable
private PsiFileAndLineNavigation[] getTargetFiles() {
Object node = myDebuggerTree.getSelectionPath().getLastPathComponent();
String className = null;
if (node instanceof DebuggerTreeNodeImpl) {
NodeDescriptorImpl nodeDescriptor = ((DebuggerTreeNodeImpl)node).getDescriptor();
if (nodeDescriptor instanceof InstanceFieldDescriptorImpl) {
Instance instance = ((InstanceFieldDescriptorImpl)nodeDescriptor).getInstance();
if (instance != null) {
if (instance instanceof ClassObj) {
className = ((ClassObj)instance).getClassName();
}
else {
className = instance.getClassObj().getClassName();
if (instance instanceof ArrayInstance) {
className = className.replace("[]", "");
}
}
}
}
}
return PsiFileAndLineNavigation.wrappersForClassName(myProject, className, 0);
}
}