Skip to content

Content of file SnapshotOutlinePage.java

/*******************************************************************************
 * Copyright (c) 2008, 2023 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 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    SAP AG - initial API and implementation
 *    Andrew Johnson/IBM Corporation - com.ibm.icu fixes, notes
 *******************************************************************************/
package org.eclipse.mat.ui.snapshot.views;

import java.io.File;
import java.io.Serializable;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.SnapshotInfo;
import org.eclipse.mat.snapshot.UnreachableObjectsHistogram;
import org.eclipse.mat.ui.accessibility.AccessibleCompositeAdapter;
import org.eclipse.mat.ui.internal.views.NotesView;
import org.eclipse.mat.ui.snapshot.editor.ISnapshotEditorInput;
import org.eclipse.mat.ui.util.Copy;
import org.eclipse.mat.util.MessageUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.part.Page;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.DecimalFormat;
import com.ibm.icu.text.NumberFormat;

public abstract class SnapshotOutlinePage extends Page implements IContentOutlinePage
{
    public static class HeapEditorOutlinePage extends SnapshotOutlinePage implements
                    ISnapshotEditorInput.IChangeListener
    {
        private ISnapshotEditorInput snapshotInput;

        public HeapEditorOutlinePage(ISnapshotEditorInput input)
        {
            this.snapshotInput = input;
            this.snapshotInput.addChangeListener(this);
        }

        @Override
        protected void createColumns()
        {
            super.createColumns();
            TreeColumn column = new TreeColumn(treeViewer.getTree(), SWT.LEFT);
            column.setText(Messages.baseline);
            column.setWidth(80);
        }

        public void onSnapshotLoaded(ISnapshot snapshot)
        {
            updateSnapshotInput();
        }

        public void onBaselineLoaded(ISnapshot snapshot)
        {
            updateSnapshotInput();
        }

        @Override
        protected SnapshotInfo getBaseline()
        {
            return this.snapshotInput.hasBaseline() ? this.snapshotInput.getBaseline().getSnapshotInfo() : null;
        }

        @Override
        protected SnapshotInfo getSnapshot()
        {
            if (snapshotInput == null)
                return null;
            else
                return snapshotInput.hasSnapshot() ? snapshotInput.getSnapshot().getSnapshotInfo() : null;
        }

        @Override
        protected IPath getSnapshotPath()
        {
            if (snapshotInput == null)
                return null;
            return this.snapshotInput.getPath();
        }

        @Override
        public void dispose()
        {
            if (snapshotInput != null)
            {
                snapshotInput.removeChangeListener(this);
                snapshotInput = null;
            }
            super.dispose();
        }
    }

    static class Label
    {
        String text;
        Object snapshotValue, baselineValue;

        public Label(String typeId, Object snapshotValue, Object baselineValue)
        {
            this.text = typeId;
            this.snapshotValue = snapshotValue;
            this.baselineValue = baselineValue;
        }

        public String getText()
        {
            return text;
        }

        public Object getBaselineValue()
        {
            return baselineValue;
        }

        public Object getSnapshotValue()
        {
            return snapshotValue;
        }
    }

    static class Category
    {
        String categoryId;
        protected List<Label> children = new ArrayList<Label>();

        public Category(String categoryId)
        {
            this.categoryId = categoryId;
        }

        public String getCategoryId()
        {
            return categoryId;
        }

        public void addChild(Label label)
        {
            children.add(label);
        }
    }

    static class OutlineContentProvider implements ITreeContentProvider
    {
        List<Object> elements;

        @SuppressWarnings("unchecked")
        public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
        {
            elements = (List<Object>) newInput;
        }

        public Object[] getElements(Object inputElement)
        {
            return elements.toArray();
        }

        public Object[] getChildren(Object parentElement)
        {
            return parentElement instanceof Category ? ((Category) parentElement).children.toArray() : null;
        }

        public Object getParent(Object element)
        {
            return null;
        }

        public boolean hasChildren(Object element)
        {
            return element instanceof Category;
        }

        public void dispose()
        {}
    }

    static class OutlineLabelProvider extends LabelProvider implements ITableLabelProvider
    {

        public Image getColumnImage(Object element, int columnIndex)
        {
            return null;
        }

        public String getColumnText(Object element, int columnIndex)
        {
            if (element instanceof Category)
            {
                return columnIndex == 0 ? ((Category) element).getCategoryId() : null;
            }
            else if (element instanceof Label)
            {
                Label label = (Label) element;
                if (columnIndex == 0)
                {
                    return label.getText();
                }
                else
                {
                    Object obj = columnIndex == 1 ? label.getSnapshotValue() : label.getBaselineValue();
                    if (obj == null)
                    {
                        return null;
                    }
                    else if (Messages.identifier_size.equals(label.getText()))
                    {
                        int identifierSize = ((Integer) obj).intValue();
                        return MessageUtil.format(Messages.identifier_format, identifierSize);
                    }
                    else if ((obj instanceof Long) || (obj instanceof Integer))
                    {
                        return ((Number) obj).longValue() == 0 ? null : NumberFormat.getInstance().format(obj);
                    }
                    else if (obj instanceof Double)
                    {
                        DecimalFormat format = new DecimalFormat("#,##0.0"); //$NON-NLS-1$
                        NumberFormat nf = NumberFormat.getNumberInstance();
                        if (nf instanceof DecimalFormat)
                        {
                            // Use the locale formatter
                            DecimalFormat df = (DecimalFormat)nf;
                            df.setMinimumFractionDigits(1);
                            df.setMaximumFractionDigits(1);
                            format = df;
                        }
                        return MessageUtil.format(MessageUtil.format(Messages.size_format, format.format(obj)));
                    }
                    else if (obj instanceof Date)
                    {
                        return Messages.date.equals(label.getText()) ? DateFormat.getDateInstance().format(obj)
                                        : DateFormat.getTimeInstance().format(obj);
                    }
                    else
                    {
                        return String.valueOf(obj);
                    }
                }
            }

            return null;
        }
    }

    protected TreeViewer treeViewer;

    @Override
    public void createControl(Composite parent)
    {
        treeViewer = new TreeViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.MULTI);

        createColumns();

        AccessibleCompositeAdapter.access(treeViewer.getTree());

        treeViewer.getTree().setLinesVisible(true);
        treeViewer.getTree().setHeaderVisible(true);

        treeViewer.setContentProvider(new OutlineContentProvider());
        treeViewer.setLabelProvider(new OutlineLabelProvider());

        Action copyAction = new Action()
        {
            @Override
            public void run()
            {
                Copy.copyToClipboard(treeViewer.getTree());
            }
        };
        getSite().getActionBars().setGlobalActionHandler(ActionFactory.COPY.getId(), copyAction);
        getSite().getActionBars().updateActionBars();

        updateSnapshotInput();
    }

    protected void createColumns()
    {
        TreeColumn column = new TreeColumn(treeViewer.getTree(), SWT.LEFT);
        column.setText(Messages.col_property);
        column.setWidth(160);

        column = new TreeColumn(treeViewer.getTree(), SWT.LEFT);
        column.setText(Messages.col_file);
        column.setWidth(80);
    }

    protected void updateSnapshotInput()
    {
        List<Object> elements = new ArrayList<Object>();

        SnapshotInfo info = getSnapshot();

        if (info != null)
        {
            SnapshotInfo bInfo = getBaseline();

            if (bInfo == null)
                bInfo = new SnapshotInfo(null, null, null, 0, null, 0, 0, 0, 0, 0);

            // resource property (filename)
            int p = info.getPath().lastIndexOf(File.separatorChar);
            String filename = p >= 0 ? info.getPath().substring(p + 1) : info.getPath();
            String bFilename = null;
            if (bInfo.getPath() != null)
            {
                p = bInfo.getPath().lastIndexOf(File.separatorChar);
                bFilename = p >= 0 ? bInfo.getPath().substring(p + 1) : bInfo.getPath();
            }
            elements.add(new Label(Messages.resource, filename, bFilename));

            Category category = new Category(Messages.general_info);
            elements.add(category);

            category.addChild(new Label(Messages.format, info.getProperty("$heapFormat"), bInfo //$NON-NLS-1$
                            .getProperty("$heapFormat"))); //$NON-NLS-1$
            category.addChild(new Label(Messages.jvm_version, info.getJvmInfo(), bInfo.getJvmInfo()));
            category.addChild(new Label(Messages.time, info.getCreationDate(), bInfo.getCreationDate()));
            category.addChild(new Label(Messages.date, info.getCreationDate(), bInfo.getCreationDate()));
            category.addChild(new Label(Messages.identifier_size, info.getIdentifierSize(), bInfo.getIdentifierSize()));
            if (info.getIdentifierSize() == 8 || bInfo.getIdentifierSize() == 8) 
            {
                Boolean useCompressedOops = (Boolean) info.getProperty("$useCompressedOops"); //$NON-NLS-1$
                Boolean buseCompressedOops = (Boolean) bInfo.getProperty("$useCompressedOops"); //$NON-NLS-1$
                if (useCompressedOops != null || buseCompressedOops != null)
                {
                    category.addChild(new Label(Messages.use_compressed_oops, useCompressedOops != null ? useCompressedOops.toString() : "",  //$NON-NLS-1$
                                    buseCompressedOops != null ? buseCompressedOops.toString() : "")); //$NON-NLS-1$
                }
            }
            category.addChild(new Label(Messages.file_path, info.getPath(), bInfo.getPath()));
            Double fileLength = Double.valueOf((double) new File(info.getPath()).length() / (1024 * 1024));
            Double bFileLength = bInfo.getPath() != null ? Double.valueOf((double) new File(bInfo.getPath()).length()
                            / (1024 * 1024)) : null;
            category.addChild(new Label(Messages.file_length, fileLength, bFileLength));
            if (info.getProperty("$runtimeId") != null || bInfo.getProperty("$runtimeId") != null)  //$NON-NLS-1$//$NON-NLS-2$
            {
                category.addChild(new Label(Messages.identifier, info.getProperty("$runtimeId"), bInfo //$NON-NLS-1$
                                .getProperty("$runtimeId"))); //$NON-NLS-1$
            }
            Serializable discard_ratio = info.getProperty("discard_ratio"); //$NON-NLS-1$
            Serializable bDiscard_ratio = bInfo.getProperty("discard_ratio"); //$NON-NLS-1$
            if (discard_ratio instanceof Integer && (Integer)discard_ratio != 0 ||
                bDiscard_ratio instanceof Integer && (Integer)bDiscard_ratio != 0)
            {
                category.addChild(new Label(org.eclipse.mat.ui.Messages.UIPreferencePage_DiscardPercentage, discard_ratio, bDiscard_ratio));

                Serializable discard_pattern = info.getProperty("discard_pattern"); //$NON-NLS-1$
                Serializable bDiscard_pattern = bInfo.getProperty("discard_pattern"); //$NON-NLS-1$
                category.addChild(new Label(org.eclipse.mat.ui.Messages.UIPreferencePage_DiscardPattern, discard_pattern, bDiscard_pattern));

                Serializable discard_offset = info.getProperty("discard_offset"); //$NON-NLS-1$
                Serializable bDiscard_offset = bInfo.getProperty("discard_offset"); //$NON-NLS-1$
                category.addChild(new Label(org.eclipse.mat.ui.Messages.UIPreferencePage_DiscardOffset, discard_offset, bDiscard_offset));

                Serializable discard_seed = info.getProperty("discard_seed"); //$NON-NLS-1$
                Serializable bDiscard_seed = bInfo.getProperty("discard_seed"); //$NON-NLS-1$
                category.addChild(new Label(org.eclipse.mat.ui.Messages.UIPreferencePage_DiscardSeed, discard_seed, bDiscard_seed));
            }
            String note = getNotes(info);
            String noteb = getNotes(bInfo);
            if (note != null || noteb != null)
                category.addChild(new Label(Messages.notes, note, noteb));

            category = new Category(Messages.statistic_info);
            elements.add(category);

            category.addChild(new Label(Messages.heap, info.getUsedHeapSize(), bInfo.getUsedHeapSize()));
            category.addChild(new Label(Messages.number_of_objects, info.getNumberOfObjects(), bInfo
                            .getNumberOfObjects()));
            category.addChild(new Label(Messages.number_of_classes, info.getNumberOfClasses(), bInfo
                            .getNumberOfClasses()));
            category.addChild(new Label(Messages.number_of_classloaders, info.getNumberOfClassLoaders(), bInfo
                            .getNumberOfClassLoaders()));
            category.addChild(new Label(Messages.number_of_gc_roots, info.getNumberOfGCRoots(), bInfo
                            .getNumberOfGCRoots()));
            long u[] = unreachableObjects(info);
            long bU[] = unreachableObjects(bInfo);
            long unreachableObjects = u[0];
            long bUnreachableObjects = bU[0];
            if (unreachableObjects > 0 || bUnreachableObjects > 0)
            {
                category.addChild(new Label(Messages.unreachable_heap, u[1], bU[1]));
                category.addChild(new Label(Messages.number_of_unreachable_objects, unreachableObjects, bUnreachableObjects));
            }

        }
        else if (getSnapshotPath() != null)
        {
            // create tree from file information
            IPath path = getSnapshotPath();
            File osFile = path.toFile();

            elements.add(new Label(Messages.resource, path.lastSegment(), null));

            Category category = new Category(Messages.general_info);
            elements.add(category);
            category.addChild(new Label(Messages.format, null, null));
            category.addChild(new Label(Messages.jvm_version, null, null));
            category.addChild(new Label(Messages.time, new Date(osFile.lastModified()), null));
            category.addChild(new Label(Messages.date, new Date(osFile.lastModified()), null));
            category.addChild(new Label(Messages.identifier_size, null, null));
            category.addChild(new Label(Messages.file_path, path.toOSString(), null));

            final Double fileLength = Double.valueOf((double) osFile.length() / (1024 * 1024));
            category.addChild(new Label(Messages.file_length, fileLength, null));
            String note = getNotes(osFile);
            if (note != null)
                category.addChild(new Label(Messages.notes, note, null));
        }

        treeViewer.getTree().setRedraw(false);
        treeViewer.setInput(elements);
        treeViewer.expandAll();
        treeViewer.getTree().setRedraw(true);
    }

    private String getNotes(SnapshotInfo info)
    {
        if (info != null)
        {
            String path = info.getPath();
            String pfx = info.getPrefix();
            if (path == null)
                return null;
            // See SnapshotHistoryFile#openFile
            if (pfx != null && info.getProperty("$runtimeId") != null) //$NON-NLS-1$
            {
                return getNotes(new File(pfx + "index")); //$NON-NLS-1$
            }
            else
            {
                // New location
                String notes = getNotes(new File(pfx));
Null passed for non-null parameter of new java.io.File(String) in org.eclipse.mat.ui.snapshot.views.SnapshotOutlinePage.getNotes(SnapshotInfo)

This method call passes a null value for a non-null method parameter. Either the parameter is annotated as a parameter that should always be non-null, or analysis has shown that it will always be dereferenced.

if (notes != null) return notes; // Old location (might be the same) return getNotes(new File(path)); } } return null; } private String getNotes(File snapshotFile) { String notes; try { notes = NotesView.readNotes(snapshotFile); } catch (UncheckedIOException e) { return null; } // Just use the first line if (notes != null) return notes.split("\n", 2)[0]; //$NON-NLS-1$ return null; } private long[] unreachableObjects(SnapshotInfo info) { long discardedObjects = 0; long discardedHeap = 0; Serializable unreachable = info.getProperty(UnreachableObjectsHistogram.class.getName()); if (unreachable instanceof UnreachableObjectsHistogram) { UnreachableObjectsHistogram ur = (UnreachableObjectsHistogram)unreachable; for(UnreachableObjectsHistogram.Record r : ur.getRecords()) { discardedObjects += r.getObjectCount(); discardedHeap += r.getShallowHeapSize(); } } return new long[] {discardedObjects,discardedHeap}; } protected abstract IPath getSnapshotPath(); protected abstract SnapshotInfo getSnapshot(); protected abstract SnapshotInfo getBaseline(); @Override public Control getControl() { return treeViewer.getControl(); } @Override public void setFocus() { treeViewer.getControl().setFocus(); } public void addSelectionChangedListener(ISelectionChangedListener listener) { treeViewer.addSelectionChangedListener(listener); } public ISelection getSelection() { return treeViewer.getSelection(); } public void removeSelectionChangedListener(ISelectionChangedListener listener) { treeViewer.removeSelectionChangedListener(listener); } public void setSelection(ISelection selection) { treeViewer.setSelection(selection); } /* * (non-Javadoc) * @see * org.eclipse.ui.part.IPageBookViewPage#init(org.eclipse.ui.part.IPageSite) */ @Override public void init(IPageSite pageSite) { super.init(pageSite); pageSite.setSelectionProvider(this); } }