Index: src/org/eclipse/mat/ui/Messages.java =================================================================== --- src/org/eclipse/mat/ui/Messages.java (revision 386) +++ src/org/eclipse/mat/ui/Messages.java (working copy) @@ -96,7 +96,8 @@ public static String InspectorView_LinkWithSnapshot; public static String InspectorView_Name; public static String InspectorView_noGCRoot; - public static String InspectorView_PinTab; + public static String InspectorView_Path; + public static String InspectorView_PinTab; public static String InspectorView_retainedSize; public static String InspectorView_shallowSize; public static String InspectorView_Statics; @@ -266,6 +267,8 @@ public static String OverviewPane_ComponentReport; public static String OverviewPane_NoPieChartAvailable; public static String InspectorView_GCroot; + public static String InspectorView_ReferenceName; + public static String InspectorView_ReferingObject; static { Index: src/org/eclipse/mat/ui/messages.properties =================================================================== --- src/org/eclipse/mat/ui/messages.properties (revision 386) +++ src/org/eclipse/mat/ui/messages.properties (working copy) @@ -90,6 +90,7 @@ InspectorView_LinkWithSnapshot=Link with Snapshot InspectorView_Name=Name InspectorView_noGCRoot=no GC root +InspectorView_Path=Shortest Path InspectorView_PinTab=Pin Tab InspectorView_retainedSize={0,number} (retained size) InspectorView_shallowSize={0,number} (shallow size) @@ -260,3 +261,5 @@ OverviewPane_ComponentReport=Component Report OverviewPane_NoPieChartAvailable=BIRT Chart Engine (>2.2.2) not available. No pie today. Check-out the Dominator Tree or Top Consumers. InspectorView_GCroot=GC root: +InspectorView_ReferenceName=Reference Name +InspectorView_ReferingObject=Referring Object Index: src/org/eclipse/mat/ui/snapshot/views/inspector/InspectorContextProvider.java =================================================================== --- src/org/eclipse/mat/ui/snapshot/views/inspector/InspectorContextProvider.java (revision 386) +++ src/org/eclipse/mat/ui/snapshot/views/inspector/InspectorContextProvider.java (working copy) @@ -69,6 +69,18 @@ } }; } + else if (row instanceof PathNode) + { + final PathNode node = (PathNode) row; + + return new IContextObject() + { + public int getObjectId() + { + return node.getObjectId(); + } + }; + } return null; } catch (SnapshotException e) Index: src/org/eclipse/mat/ui/snapshot/views/inspector/InspectorView.java =================================================================== --- src/org/eclipse/mat/ui/snapshot/views/inspector/InspectorView.java (revision 386) +++ src/org/eclipse/mat/ui/snapshot/views/inspector/InspectorView.java (working copy) @@ -47,6 +47,7 @@ import org.eclipse.jface.viewers.Viewer; import org.eclipse.mat.SnapshotException; import org.eclipse.mat.query.IContextObject; +import org.eclipse.mat.snapshot.IPathsFromGCRootsComputer; import org.eclipse.mat.snapshot.ISnapshot; import org.eclipse.mat.snapshot.model.GCRootInfo; import org.eclipse.mat.snapshot.model.IClass; @@ -106,6 +107,7 @@ private CTabFolder tabFolder; private TableViewer attributesTable; private TableViewer staticsTable; + private TableViewer pathTable; private TreeViewer classHierarchyTree; private boolean pinSelection = false; private Font font; @@ -504,6 +506,11 @@ classHierarchyTab.setText(Messages.InspectorView_ClassHierarchy); classHierarchyTree = createHierarchyTree(tabFolder); classHierarchyTab.setControl(classHierarchyTree.getTree().getParent()); + + CTabItem pathTab = new CTabItem(tabFolder, SWT.NULL); + pathTab.setText(Messages.InspectorView_Path); + pathTable = createPathTable(tabFolder); + pathTab.setControl(pathTable.getTable().getParent()); tabFolder.setSelection(0); } @@ -524,6 +531,42 @@ return classHierarchyTree; } + + private TableViewer createPathTable(Composite parent) + { + Composite composite = new Composite(parent, SWT.NONE); + TableColumnLayout columnLayout = new TableColumnLayout(); + composite.setLayout(columnLayout); + GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(composite); + + final TableViewer viewer = new TableViewer(composite, SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION); + Table table = viewer.getTable(); + viewer.setContentProvider(new FieldsContentProvider()); + viewer.setLabelProvider(new PathLabelProvider(this, table.getFont())); + + getViewSite().getActionBars().setGlobalActionHandler(ActionFactory.COPY.getId(), new Action() + { + @Override + public void run() + { + Copy.copyToClipboard(viewer.getControl()); + } + }); + + TableColumn tableColumn = new TableColumn(table, SWT.LEFT); + tableColumn.setWidth(80); + tableColumn.setText(Messages.InspectorView_ReferenceName); + columnLayout.setColumnData(tableColumn, new ColumnWeightData(30, 40)); + + tableColumn = new TableColumn(table, SWT.LEFT); + tableColumn.setWidth(250); + tableColumn.setText(Messages.InspectorView_ReferingObject); + columnLayout.setColumnData(tableColumn, new ColumnWeightData(60, 250, true)); + + table.setHeaderVisible(true); + + return viewer; + } private TableViewer createTable(Composite parent) { @@ -572,6 +615,7 @@ createMenu(attributesTable); createMenu(topTableViewer); createMenu(classHierarchyTree); + createMenu(pathTable); } private void createMenu(StructuredViewer viewer) @@ -808,6 +852,9 @@ // prepare attributes final LazyFields attributeFields = prepareAttributes(object); + + // prepare path + final LazyFields pathFields = preparePath(object); // update visual viewer final Object toShow = prepareVisualInfo(object); @@ -820,6 +867,7 @@ topTableViewer.setData("input", objectId);//$NON-NLS-1$ staticsTable.setInput(staticFields); attributesTable.setInput(attributeFields); + pathTable.setInput(pathFields); updateVisualViewer(toShow); IClass input = object instanceof IClass ? (IClass) object : object.getClazz(); @@ -952,6 +1000,15 @@ return fields; } + + private LazyFields preparePath(final IObject object) throws SnapshotException + { + IPathsFromGCRootsComputer pathComputer = snapshot.getPathsFromGCRoots(object.getObjectId(), null); + int[] path = pathComputer.getNextShortestPath(); + + return new LazyFields.Path(object, path); + } + private LazyFields prepareStaticFields(final IObject object) { @@ -1105,6 +1162,11 @@ { classHierarchyTree.setInput(new Object[] { new Object() }); } + + if (pathTable.getContentProvider() != null) + { + pathTable.setInput(LazyFields.EMPTY); + } for (Menu menu : contextMenus) { Index: src/org/eclipse/mat/ui/snapshot/views/inspector/PathNode.java =================================================================== --- src/org/eclipse/mat/ui/snapshot/views/inspector/PathNode.java (revision 0) +++ src/org/eclipse/mat/ui/snapshot/views/inspector/PathNode.java (revision 0) @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2008 SAP AG. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * SAP AG - initial API and implementation + *******************************************************************************/ +package org.eclipse.mat.ui.snapshot.views.inspector; + +import org.eclipse.swt.graphics.Image; + +/* package */class PathNode +{ + String name; + int objectId; + Image icon; + + public PathNode(String name, int objectId, Image icon) + { + this.name = name; + this.objectId = objectId; + this.icon = icon; + } + + public String getName() + { + return name; + } + + public int getObjectId() + { + return objectId; + } + + public Image getIcon() + { + return icon; + } +} Index: src/org/eclipse/mat/ui/snapshot/views/inspector/LazyFields.java =================================================================== --- src/org/eclipse/mat/ui/snapshot/views/inspector/LazyFields.java (revision 386) +++ src/org/eclipse/mat/ui/snapshot/views/inspector/LazyFields.java (working copy) @@ -11,6 +11,7 @@ package org.eclipse.mat.ui.snapshot.views.inspector; import java.lang.ref.WeakReference; +import java.net.URL; import java.util.ArrayList; import java.util.List; @@ -24,7 +25,10 @@ import org.eclipse.mat.snapshot.model.IPrimitiveArray; import org.eclipse.mat.snapshot.model.NamedReference; import org.eclipse.mat.snapshot.model.ObjectReference; +import org.eclipse.mat.snapshot.query.Icons; +import org.eclipse.mat.ui.MemoryAnalyserPlugin; import org.eclipse.mat.ui.Messages; +import org.eclipse.swt.graphics.Image; /* package */abstract class LazyFields { @@ -202,6 +206,58 @@ } } } + + /* package */static class Path extends LazyFields + { + private int length; + int[] path; + + public Path(IObject object, int[] path) + { + super(object); + this.length = path.length; + this.path = path; + } + + public int getSize() + { + return length; + } + + @Override + protected Object createElement(IObject object, int index) + { + try + { + ISnapshot snapshot = object.getSnapshot(); + String refName = getReferenceName(index, snapshot); + URL imageURL = index == 0 ? Icons.forObject(snapshot, path[index]) : Icons.inbound(snapshot, path[index]); + Image icon = MemoryAnalyserPlugin.getDefault().getImage(imageURL); + return new PathNode(refName, path[index], icon); + } + catch (SnapshotException e) { + // as fallback create the node with just the objectId + return new PathNode("", path[index], null); + } + } + + private String getReferenceName(int index, ISnapshot snapshot) throws SnapshotException + { + if (index == 0) + return ""; + + IObject from = snapshot.getObject(path[index]); + + List references = from.getOutboundReferences(); + for (NamedReference namedReference : references) + { + if (namedReference.getObjectId() == path[index - 1]) + return namedReference.getName(); + } + + return ""; + } + } // ////////////////////////////////////////////////////////////// // private helpers Index: src/org/eclipse/mat/ui/snapshot/views/inspector/PathLabelProvider.java =================================================================== --- src/org/eclipse/mat/ui/snapshot/views/inspector/PathLabelProvider.java (revision 0) +++ src/org/eclipse/mat/ui/snapshot/views/inspector/PathLabelProvider.java (revision 0) @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2008 SAP AG. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * SAP AG - initial API and implementation + *******************************************************************************/ +package org.eclipse.mat.ui.snapshot.views.inspector; + +import org.eclipse.jface.resource.FontDescriptor; +import org.eclipse.jface.viewers.ITableFontProvider; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.mat.SnapshotException; +import org.eclipse.mat.snapshot.model.IObject; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; + +class PathLabelProvider extends LabelProvider implements ITableLabelProvider, ITableFontProvider +{ + private final InspectorView inspectorView; + private Font italicFont; + private Font boldFont; + + public PathLabelProvider(InspectorView inspectorView, Font defaultFont) + { + this.inspectorView = inspectorView; + FontDescriptor fontDescriptor = FontDescriptor.createFrom(defaultFont); + this.italicFont = fontDescriptor.setStyle(SWT.ITALIC).createFont(Display.getDefault()); + this.boldFont = fontDescriptor.setStyle(SWT.BOLD).createFont(Display.getDefault()); + } + + public Image getColumnImage(Object element, int columnIndex) + { + if (element instanceof PathNode) + { + if (columnIndex == 0) + { + return ((PathNode) element).getIcon(); + } + } + return null; + } + + public String getColumnText(Object element, int columnIndex) + { + + if (element instanceof PathNode) + { + PathNode node = ((PathNode) element); + switch (columnIndex) + { + case 0: + return node.getName(); + case 1: + return getObjectLabel(node.getObjectId()); + } + } + + return null; + } + + private String getObjectLabel(int objectId) + { + try + { + IObject object = this.inspectorView.snapshot.getObject(objectId); + String text = object.getDisplayName(); + return text; + } + catch (SnapshotException e) + { + // $JL-EXC$ + return "";//$NON-NLS-1$ + } + } + + public Font getFont(Object element, int columnIndex) + { + if (columnIndex == 0) + { + return boldFont; + } + else + { + return null; + } + } + + @Override + public void dispose() + { + italicFont.dispose(); + boldFont.dispose(); + } + +}