/******************************************************************************* | |
* Copyright (c) 2008, 2020 SAP AG, IBM Corporation and others. | |
* 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 | |
* Andrew Johnson (IBM Corporation) - comparisons | |
*******************************************************************************/ | |
package org.eclipse.mat.ui.snapshot.panes; | |
import java.util.ArrayList; | |
import java.util.Collections; | |
import java.util.Comparator; | |
import java.util.List; | |
import java.util.regex.Pattern; | |
import org.eclipse.core.runtime.IProgressMonitor; | |
import org.eclipse.core.runtime.IStatus; | |
import org.eclipse.core.runtime.Platform; | |
import org.eclipse.core.runtime.Status; | |
import org.eclipse.jface.viewers.ISelection; | |
import org.eclipse.jface.viewers.ISelectionChangedListener; | |
import org.eclipse.jface.viewers.ISelectionProvider; | |
import org.eclipse.jface.viewers.StructuredSelection; | |
import org.eclipse.mat.SnapshotException; | |
import org.eclipse.mat.internal.snapshot.SnapshotQueryContext; | |
import org.eclipse.mat.query.IResult; | |
import org.eclipse.mat.query.annotations.Category; | |
import org.eclipse.mat.query.registry.CommandLine; | |
import org.eclipse.mat.query.registry.QueryDescriptor; | |
import org.eclipse.mat.query.registry.QueryRegistry; | |
import org.eclipse.mat.query.registry.QueryResult; | |
import org.eclipse.mat.report.SpecFactory; | |
import org.eclipse.mat.snapshot.ISnapshot; | |
import org.eclipse.mat.snapshot.SnapshotInfo; | |
import org.eclipse.mat.ui.MemoryAnalyserPlugin; | |
import org.eclipse.mat.ui.Messages; | |
import org.eclipse.mat.ui.QueryExecution; | |
import org.eclipse.mat.ui.editor.AbstractEditorPane; | |
import org.eclipse.mat.ui.editor.AbstractPaneJob; | |
import org.eclipse.mat.ui.editor.EditorPaneRegistry; | |
import org.eclipse.mat.ui.editor.MultiPaneEditorSite; | |
import org.eclipse.mat.ui.snapshot.editor.HeapEditor; | |
import org.eclipse.mat.ui.snapshot.editor.HeapEditorPane; | |
import org.eclipse.mat.ui.util.ErrorHelper; | |
import org.eclipse.mat.ui.util.ProgressMonitorWrapper; | |
import org.eclipse.mat.util.MessageUtil; | |
import org.eclipse.mat.util.Units; | |
import org.eclipse.swt.graphics.Image; | |
import org.eclipse.swt.layout.FillLayout; | |
import org.eclipse.swt.widgets.Composite; | |
import org.eclipse.ui.PartInitException; | |
import org.eclipse.ui.cheatsheets.OpenCheatSheetAction; | |
import org.eclipse.ui.forms.events.HyperlinkEvent; | |
import org.eclipse.ui.forms.events.IHyperlinkListener; | |
import org.eclipse.ui.forms.widgets.FormText; | |
import org.eclipse.ui.forms.widgets.FormToolkit; | |
import org.eclipse.ui.forms.widgets.ScrolledForm; | |
import org.eclipse.ui.forms.widgets.Section; | |
import org.eclipse.ui.forms.widgets.TableWrapData; | |
import org.eclipse.ui.forms.widgets.TableWrapLayout; | |
public class OverviewPane extends HeapEditorPane implements IHyperlinkListener, ISelectionProvider | |
{ | |
private List<ISelectionChangedListener> listeners = new ArrayList<ISelectionChangedListener>(); | |
private ISelectionProvider delegate; | |
private FormToolkit toolkit; | |
private AbstractEditorPane pane; | |
private ScrolledForm form; | |
public void createPartControl(Composite parent) | |
{ | |
toolkit = new FormToolkit(parent.getDisplay()); | |
form = toolkit.createScrolledForm(parent); | |
// TableWrapLayout tends to give vertical scroll bars when needed | |
// but fits into horizontal space where possible | |
TableWrapLayout layout = new TableWrapLayout(); | |
layout.numColumns = 3; | |
layout.verticalSpacing = 20; | |
form.getBody().setLayout(layout); | |
Section section = createDetailsSection(); | |
TableWrapData td = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.FILL_GRAB, 1, 3); | |
section.setLayoutData(td); | |
section = createBiggestObjectsSection(); | |
td = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.FILL_GRAB, 1, 3); | |
section.setLayoutData(td); | |
section = createSidecarSection(); | |
if (section != null) | |
{ | |
td = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.FILL_GRAB, 1, 3); | |
section.setLayoutData(td); | |
} | |
section = createActionSection(); | |
td = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.FILL_GRAB, 1, 1); | |
section.setLayoutData(td); | |
section = createReportsSection(); | |
td = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.FILL_GRAB, 1, 1); | |
section.setLayoutData(td); | |
section = createStepByStepSection(); | |
td = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.FILL_GRAB, 1, 1); | |
section.setLayoutData(td); | |
form.reflow(true); | |
} | |
// ////////////////////////////////////////////////////////////// | |
// details | |
// ////////////////////////////////////////////////////////////// | |
private Section createSidecarSection() | |
{ | |
Section section = null; | |
FormText text = null; | |
List<QueryDescriptor> queries = QueryRegistry.instance().getQueries(Pattern.compile("supplement_.*")); //$NON-NLS-1$ | |
if (queries.isEmpty()) | |
return null; | |
Collections.sort(queries, new Comparator<QueryDescriptor>() | |
{ | |
public int compare(QueryDescriptor o1, QueryDescriptor o2) | |
{ | |
return o1.getName().compareTo(o2.getName()); | |
} | |
}); | |
StringBuilder buf = new StringBuilder(); | |
buf.append("<form>");//$NON-NLS-1$ | |
for (QueryDescriptor query : queries) | |
{ | |
if (query.accept(getQueryContext())) | |
{ | |
if (section == null) | |
{ | |
section = toolkit.createSection(form.getBody(), Section.TITLE_BAR | Section.EXPANDED | |
| Section.TWISTIE); | |
section.setText(Messages.OverviewPane_AdditionalInfo); | |
Composite sectionClient = toolkit.createComposite(section); | |
sectionClient.setLayout(new TableWrapLayout()); | |
text = toolkit.createFormText(sectionClient, true); | |
text.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); | |
text.addHyperlinkListener(this); | |
section.setClient(sectionClient); | |
} | |
addButton(buf, text, query.getIdentifier(), null, query.getName(), query.getHelp()); | |
} | |
} | |
buf.append("</form>");//$NON-NLS-1$ | |
if (text != null) | |
text.setText(buf.toString(), true, false); | |
return section; | |
} | |
private Section createDetailsSection() | |
{ | |
Section section = toolkit.createSection(form.getBody(), Section.TITLE_BAR | Section.EXPANDED | Section.TWISTIE); | |
section.setText(Messages.OverviewPane_Details); | |
Composite sectionClient = toolkit.createComposite(section); | |
sectionClient.setLayout(new TableWrapLayout()); | |
FormText text = toolkit.createFormText(sectionClient, true); | |
text.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); | |
SnapshotInfo info = getSnapshotInput().getSnapshot().getSnapshotInfo(); | |
StringBuilder buf = new StringBuilder(); | |
buf.append("<form>");//$NON-NLS-1$ | |
buf.append("<p>" + Messages.OverviewPane_Size);//$NON-NLS-1$ | |
long heapSize = info.getUsedHeapSize(); | |
String size = Units.Storage.of(heapSize).format(heapSize); | |
buf.append("<b>" + size + "</b>");//$NON-NLS-1$//$NON-NLS-2$ | |
buf.append(" " + Messages.OverviewPane_Classes); //$NON-NLS-1$ | |
buf.append("<b>" + formatNumber(info.getNumberOfClasses()) + "</b>");//$NON-NLS-1$//$NON-NLS-2$ | |
buf.append(" " + Messages.OverviewPane_Objects); //$NON-NLS-1$ | |
buf.append("<b>" + formatNumber(info.getNumberOfObjects()) + "</b>");//$NON-NLS-1$//$NON-NLS-2$ | |
buf.append(" " + Messages.OverviewPane_ClassLoader); //$NON-NLS-1$ | |
buf.append("<b>" + formatNumber(info.getNumberOfClassLoaders()) + "</b>");//$NON-NLS-1$//$NON-NLS-2$ | |
QueryDescriptor descriptor = QueryRegistry.instance().getQuery("unreachable_objects"); //$NON-NLS-1$ | |
if (descriptor != null && descriptor.accept(getQueryContext())) | |
{ | |
buf.append(" <a href=\"").append(descriptor.getIdentifier()) // //$NON-NLS-1$ | |
.append("\">").append(descriptor.getName()).append("</a>");//$NON-NLS-1$//$NON-NLS-2$ | |
} | |
buf.append("</p></form>");//$NON-NLS-1$ | |
text.setText(buf.toString(), true, false); | |
text.addHyperlinkListener(this); | |
section.setClient(sectionClient); | |
return section; | |
} | |
private String formatNumber(int number) | |
{ | |
return Units.Plain.of(number).format(number); | |
} | |
// ////////////////////////////////////////////////////////////// | |
// actions | |
// ////////////////////////////////////////////////////////////// | |
private Section createActionSection() | |
{ | |
Section section = toolkit.createSection(form.getBody(), Section.TITLE_BAR | Section.EXPANDED | Section.TWISTIE); | |
section.setText(Messages.OverviewPane_Actions); | |
Composite sectionClient = toolkit.createComposite(section); | |
sectionClient.setLayout(new TableWrapLayout()); | |
FormText text = toolkit.createFormText(sectionClient, true); | |
text.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); | |
StringBuilder buf = new StringBuilder(); | |
buf.append("<form>");//$NON-NLS-1$ | |
addButton(buf, text, "histogram", null, Messages.OverviewPane_Histogram, Messages.OverviewPane_HistogramInfo); //$NON-NLS-1$ | |
addButton(buf, text, "dominator_tree", null, Messages.OverviewPane_DominatorTree, //$NON-NLS-1$ | |
Messages.OverviewPane_DominatorTreeInfo); | |
addButton(buf, text, "top_consumers_html", null, Messages.OverviewPane_TopConsumers, //$NON-NLS-1$ | |
Messages.OverviewPane_TopConsumersInfo); | |
addButton(buf, text, "duplicate_classes", null, Messages.OverviewPane_DuplicateClasses, //$NON-NLS-1$ | |
Messages.OverviewPane_DuplicateClassesInfo); | |
buf.append("</form>");//$NON-NLS-1$ | |
text.setText(buf.toString(), true, false); | |
text.addHyperlinkListener(this); | |
section.setClient(sectionClient); | |
return section; | |
} | |
private Section createReportsSection() | |
{ | |
Section section = toolkit.createSection(form.getBody(), Section.TITLE_BAR | Section.EXPANDED | Section.TWISTIE); | |
section.setText(Messages.OverviewPane_Reports); | |
Composite sectionClient = toolkit.createComposite(section); | |
sectionClient.setLayout(new TableWrapLayout()); | |
FormText text = toolkit.createFormText(sectionClient, true); | |
StringBuilder buf = new StringBuilder(); | |
buf.append("<form>");//$NON-NLS-1$ | |
addReportsByPattern(text, buf, Pattern.compile(".*:suspects"));//$NON-NLS-1$ | |
addReportsByPattern(text, buf, Pattern.compile(".*:top_components"));//$NON-NLS-1$ | |
addReportsByPattern2(text, buf, Pattern.compile(".*:suspects2"));//$NON-NLS-1$ | |
buf.append("</form>");//$NON-NLS-1$ | |
text.setText(buf.toString(), true, false); | |
text.addHyperlinkListener(this); | |
section.setClient(sectionClient); | |
return section; | |
} | |
private void addReportsByPattern(FormText text, StringBuilder buf, Pattern pattern) | |
{ | |
for (SpecFactory.Report report : SpecFactory.instance().delegates()) | |
{ | |
if (pattern.matcher(report.getExtensionIdentifier()).matches()) | |
addButton(buf, text, "create_report", "default_report " + report.getExtensionIdentifier(), report//$NON-NLS-1$//$NON-NLS-2$ | |
.getName(), report.getDescription()); | |
} | |
} | |
private void addReportsByPattern2(FormText text, StringBuilder buf, Pattern pattern) | |
{ | |
for (SpecFactory.Report report : SpecFactory.instance().delegates()) | |
{ | |
if (pattern.matcher(report.getExtensionIdentifier()).matches()) | |
{ | |
String name = report.getName(); | |
// Allow hidden category report, but remove category | |
if (name.startsWith(Category.HIDDEN + "/")) //$NON-NLS-1$ | |
name = name.substring(Category.HIDDEN.length() + 1); | |
addButton(buf, text, "create_report", "comparison_report " + report.getExtensionIdentifier(), name, //$NON-NLS-1$//$NON-NLS-2$ | |
report.getDescription()); | |
} | |
} | |
} | |
private Section createStepByStepSection() | |
{ | |
Section section = toolkit.createSection(form.getBody(), Section.TITLE_BAR | Section.EXPANDED | Section.TWISTIE); | |
section.setText(Messages.OverviewPane_StepByStep); | |
Composite sectionClient = toolkit.createComposite(section); | |
sectionClient.setLayout(new TableWrapLayout()); | |
FormText text = toolkit.createFormText(sectionClient, true); | |
StringBuilder buf = new StringBuilder(); | |
buf.append("<form>");//$NON-NLS-1$ | |
addCheatSheetLink(buf, "org.eclipse.mat.tutorials.component_report", Messages.OverviewPane_ComponentReport, //$NON-NLS-1$ | |
Messages.OverviewPane_ComponentReportInfo); | |
buf.append("</form>");//$NON-NLS-1$ | |
text.setText(buf.toString(), true, false); | |
text.addHyperlinkListener(new IHyperlinkListener() | |
{ | |
public void linkActivated(HyperlinkEvent e) | |
{ | |
new OpenCheatSheetAction(String.valueOf(e.getHref())).run(); | |
} | |
public void linkEntered(HyperlinkEvent e) | |
{} | |
public void linkExited(HyperlinkEvent e) | |
{} | |
}); | |
section.setClient(sectionClient); | |
return section; | |
} | |
private void addCheatSheetLink(StringBuilder buf, String cheatSheetId, String title, String help) | |
{ | |
buf.append("<li style=\"text\" value=\"\">");//$NON-NLS-1$ | |
buf.append("<a href=\"").append(cheatSheetId).append("\">").append(title).append("</a>");//$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ | |
if (help != null) | |
buf.append(": ").append(help);//$NON-NLS-1$ | |
buf.append("</li>");//$NON-NLS-1$ | |
} | |
private void addButton(StringBuilder buf, FormText formText, String commandId, String command, String title, | |
String help) | |
{ | |
QueryDescriptor descriptor = QueryRegistry.instance().getQuery(commandId); | |
if (descriptor == null) | |
return; | |
if (command == null) | |
command = descriptor.getIdentifier(); | |
Image image = MemoryAnalyserPlugin.getDefault().getImage(descriptor); | |
if (image != null) | |
{ | |
buf.append("<li style=\"image\" value=\"").append(descriptor.getIdentifier()).append("\">");//$NON-NLS-1$//$NON-NLS-2$ | |
formText.setImage(descriptor.getIdentifier(), image); | |
} | |
else | |
{ | |
buf.append("<li style=\"text\" value=\"\">");//$NON-NLS-1$ | |
} | |
buf.append("<a href=\"").append(command).append("\">").append(title).append("</a>");//$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ | |
if (help != null) | |
buf.append(": ").append(help);//$NON-NLS-1$ | |
buf.append("</li>");//$NON-NLS-1$ | |
} | |
// ////////////////////////////////////////////////////////////// | |
// biggest objects | |
// ////////////////////////////////////////////////////////////// | |
private Section createBiggestObjectsSection() | |
{ | |
final Section section = toolkit.createSection(form.getBody(), Section.TITLE_BAR | Section.EXPANDED | |
| Section.TWISTIE); | |
section.setText(Messages.OverviewPane_BiggestObjectsByRetainedSIze); | |
final Composite sectionClient = toolkit.createComposite(section); | |
if (Platform.getBundle("org.eclipse.mat.chart.ui") == null)//$NON-NLS-1$ | |
{ | |
sectionClient.setLayout(new TableWrapLayout()); | |
FormText text = toolkit.createFormText(sectionClient, true); | |
StringBuilder buf = new StringBuilder(256); | |
buf.append("<form><li style=\"text\" value=\"\">"); //$NON-NLS-1$ | |
buf.append(MessageUtil.format(Messages.OverviewPane_NoPieChartAvailable, | |
"dominator_tree", "top_consumers_html")); //$NON-NLS-1$ //$NON-NLS-2$ | |
buf.append("</li></form>"); //$NON-NLS-1$ | |
text.setText(buf.toString(), true, false); | |
text.addHyperlinkListener(this); | |
} | |
else | |
{ | |
FillLayout layout = new FillLayout(); | |
sectionClient.setLayout(layout); | |
final ISnapshot snapshot = getSnapshotInput().getSnapshot(); | |
new AbstractPaneJob(Messages.OverviewPane_ExtractingBigObjects, this) | |
{ | |
@Override | |
protected IStatus doRun(IProgressMonitor monitor) | |
{ | |
try | |
{ | |
SnapshotQueryContext ctx = new SnapshotQueryContext(snapshot); | |
final IResult result = CommandLine.execute(ctx, "pie_biggest_objects",//$NON-NLS-1$ | |
new ProgressMonitorWrapper(monitor)); | |
sectionClient.getDisplay().asyncExec(new Runnable() | |
{ | |
public void run() | |
{ | |
try | |
{ | |
pane = EditorPaneRegistry.instance().createNewPane(result, null); | |
pane.init(getEditorSite(), getEditorInput()); | |
pane.createPartControl(sectionClient); | |
pane.initWithArgument(new QueryResult(null, "pie_biggest_objects", result));//$NON-NLS-1$ | |
form.reflow(true); | |
if (pane instanceof ISelectionProvider) | |
{ | |
delegate = (ISelectionProvider) pane; | |
for (ISelectionChangedListener l : listeners) | |
delegate.addSelectionChangedListener(l); | |
} | |
} | |
catch (PartInitException e) | |
{ | |
ErrorHelper.logThrowableAndShowMessage(e); | |
} | |
} | |
}); | |
return Status.OK_STATUS; | |
} | |
catch (SnapshotException e) | |
{ | |
return ErrorHelper.createErrorStatus(e); | |
} | |
} | |
}.schedule(); | |
} | |
section.setClient(sectionClient); | |
return section; | |
} | |
// ////////////////////////////////////////////////////////////// | |
// hyper-link listener | |
// ////////////////////////////////////////////////////////////// | |
public void linkActivated(HyperlinkEvent e) | |
{ | |
String command = String.valueOf(e.getHref()); | |
runCommand(command); | |
} | |
private void runCommand(String command) | |
{ | |
try | |
{ | |
HeapEditor heapEditor = (HeapEditor) ((MultiPaneEditorSite) getSite()).getMultiPageEditor(); | |
QueryExecution.executeCommandLine(heapEditor, this.getPaneState(), command); | |
} | |
catch (SnapshotException exp) | |
{ | |
ErrorHelper.logThrowableAndShowMessage(exp); | |
} | |
} | |
public void linkEntered(HyperlinkEvent e) | |
{} | |
public void linkExited(HyperlinkEvent e) | |
{} | |
// ////////////////////////////////////////////////////////////// | |
// misc | |
// ////////////////////////////////////////////////////////////// | |
public String getTitle() | |
{ | |
return Messages.OverviewPane_Overview; | |
} | |
@Override | |
public Image getTitleImage() | |
{ | |
return MemoryAnalyserPlugin.getImage(MemoryAnalyserPlugin.ISharedImages.INFO); | |
} | |
@Override | |
public void setFocus() | |
{ | |
form.setFocus(); | |
} | |
// ////////////////////////////////////////////////////////////// | |
// selection provider | |
// ////////////////////////////////////////////////////////////// | |
public void addSelectionChangedListener(ISelectionChangedListener listener) | |
{ | |
listeners.add(listener); | |
if (delegate != null) | |
delegate.addSelectionChangedListener(listener); | |
} | |
public void removeSelectionChangedListener(ISelectionChangedListener listener) | |
{ | |
listeners.remove(listener); | |
if (delegate != null) | |
delegate.removeSelectionChangedListener(listener); | |
} | |
public ISelection getSelection() | |
{ | |
if (delegate != null) | |
return delegate.getSelection(); | |
else | |
return StructuredSelection.EMPTY; | |
} | |
public void setSelection(ISelection selection) | |
{} | |
@Override | |
public void dispose() | |
{ | |
super.dispose(); | |
if (pane != null) | |
{ | |
pane.dispose(); | |
pane = null; | |
} | |
} | |
} |