org.eclipse.mat.ui.snapshot.views.inspector.InspectorView.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.mat.ui.snapshot.views.inspector.InspectorView.java

Source

/*******************************************************************************
 * Copyright (c) 2008, 2011 SAP AG 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
 *    IBM Corporation - accessibility related fixes 
 *    Chris Grindstaff
 *******************************************************************************/
package org.eclipse.mat.ui.snapshot.views.inspector;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

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.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.layout.TreeColumnLayout;
import org.eclipse.jface.resource.FontDescriptor;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.IFontProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.query.IContextObject;
import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.model.GCRootInfo;
import org.eclipse.mat.snapshot.model.IClass;
import org.eclipse.mat.snapshot.model.IInstance;
import org.eclipse.mat.snapshot.model.IObject;
import org.eclipse.mat.snapshot.model.IObjectArray;
import org.eclipse.mat.snapshot.model.IPrimitiveArray;
import org.eclipse.mat.ui.MemoryAnalyserPlugin;
import org.eclipse.mat.ui.Messages;
import org.eclipse.mat.ui.accessibility.AccessibleCompositeAdapter;
import org.eclipse.mat.ui.accessibility.AccessibleToolbarAdapter;
import org.eclipse.mat.ui.snapshot.ImageHelper;
import org.eclipse.mat.ui.snapshot.editor.HeapEditor;
import org.eclipse.mat.ui.snapshot.editor.ISnapshotEditorInput;
import org.eclipse.mat.ui.util.Copy;
import org.eclipse.mat.ui.util.PopupMenu;
import org.eclipse.mat.ui.util.QueryContextMenu;
import org.eclipse.mat.util.MessageUtil;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.MenuAdapter;
import org.eclipse.swt.events.MenuEvent;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.part.ViewPart;

public class InspectorView extends ViewPart implements IPartListener, ISelectionChangedListener {
    private HeapEditor editor;
    /* package */ISnapshot snapshot;

    private Composite top;
    private Composite visualViewer;
    private TableViewer topTableViewer;
    private CTabFolder tabFolder;
    private TableViewer attributesTable;
    private TableViewer staticsTable;
    private TreeViewer classHierarchyTree;
    private StyledText resolvedValue;
    private boolean pinSelection = false;
    private Font font;

    private List<Menu> contextMenus = new ArrayList<Menu>();

    boolean keepInSync = true;

    /* package */static class BaseNode {
        int objectId;

        public BaseNode(int objectId) {
            this.objectId = objectId;
        }
    }

    private class ObjectNode extends BaseNode {
        String label;
        int imageType;

        public ObjectNode(IObject object) {
            super(object.getObjectId());
            this.label = object.getTechnicalName();
            this.imageType = ImageHelper.getType(object);
        }

        public String getLabel() {
            return label;
        }

        public int getImageType() {
            return imageType;
        }
    }

    private static class TopTableLabelProvider extends LabelProvider {

        @Override
        public String getText(Object element) {
            if (element instanceof InfoItem)
                return ((InfoItem) element).getText();
            else if (element instanceof ObjectNode)
                return ((ObjectNode) element).getLabel();
            else if (element instanceof GCRootInfo[])
                return Messages.InspectorView_GCroot + GCRootInfo.getTypeSetAsString((GCRootInfo[]) element);
            else
                return "";//$NON-NLS-1$
        }

        @Override
        public Image getImage(Object element) {
            if (element instanceof InfoItem)
                return MemoryAnalyserPlugin.getDefault().getImage(((InfoItem) element).getDescriptor());
            else if (element instanceof ObjectNode)
                return ImageHelper.getImage(((ObjectNode) element).getImageType());
            else if (element instanceof GCRootInfo[])
                return MemoryAnalyserPlugin.getImage(ImageHelper.Decorations.GC_ROOT);
            else
                return null;
        }

    }

    private static final class TableContentProvider implements IStructuredContentProvider {
        Object[] elements;

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

        public void dispose() {
        }

        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
            if (newInput instanceof Collection<?>) {
                this.elements = ((Collection<?>) newInput).toArray();
            } else {
                this.elements = (Object[]) newInput;
            }
        }
    }

    public class InfoItem extends BaseNode {
        private ImageDescriptor descriptor;
        private String text;

        public InfoItem(ImageDescriptor descriptor, String text) {
            this(-1, descriptor, text);
        }

        public InfoItem(int objectId, ImageDescriptor descriptor, String text) {
            super(objectId);
            this.descriptor = descriptor;
            this.text = text;
        }

        public ImageDescriptor getDescriptor() {
            return descriptor;
        }

        public String getText() {
            return text;
        }
    }

    private class MenuListener extends MenuAdapter {
        private Menu menu;
        private StructuredViewer viewer;

        public MenuListener(Menu menu, StructuredViewer viewer) {
            this.menu = menu;
            this.viewer = viewer;
        }

        @Override
        public void menuShown(MenuEvent e) {
            MenuItem[] items = menu.getItems();
            for (int ii = 0; ii < items.length; ii++)
                items[ii].dispose();

            PopupMenu popup = new PopupMenu();
            fillContextMenu(popup, viewer);
            popup.addToMenu(getViewSite().getActionBars().getStatusLineManager(), menu);
        }

    }

    private static class HierarchyTreeContentProvider implements ITreeContentProvider {
        LinkedList<IClass> supers;

        public Object[] getChildren(Object element) {
            int index = supers.indexOf(element);
            if (index >= 0 && index + 1 < supers.size())
                return new Object[] { supers.get(index + 1) };
            return ((IClass) element).getSubclasses().toArray();
        }

        public IClass getParent(Object element) {
            return ((IClass) element).getSuperClass();
        }

        public boolean hasChildren(Object element) {
            return !((IClass) element).getSubclasses().isEmpty();

        }

        public Object[] getElements(Object inputElement) {
            if (supers.isEmpty())
                return new Object[0];

            return new Object[] { supers.get(0) };
        }

        public void dispose() {
        }

        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
            supers = new LinkedList<IClass>();
            if (newInput instanceof IClass[]) {
                IClass[] input = (IClass[]) newInput;

                supers = new LinkedList<IClass>();
                supers.add(input[0]);

                while (input[0].hasSuperClass()) {
                    input[0] = input[0].getSuperClass();
                    supers.addFirst(input[0]);
                }
            }
        }
    }

    private class HierarchyLabelProvider extends LabelProvider implements IFontProvider {
        private int classId;

        public HierarchyLabelProvider(int classId) {
            super();
            this.classId = classId;
        }

        @Override
        public Image getImage(Object element) {
            return (element instanceof IClass) ? ImageHelper.getImage(ImageHelper.Type.CLASS) : null;
        }

        @Override
        public String getText(Object element) {
            return (element instanceof IClass) ? ((IClass) element).getName() : "";//$NON-NLS-1$
        }

        public Font getFont(Object element) {
            if (element instanceof IClass && ((IClass) element).getObjectId() == classId)
                return font;
            return null;
        }

    }

    // //////////////////////////////////////////////////////////////
    // view construction
    // //////////////////////////////////////////////////////////////

    @Override
    public void createPartControl(final Composite parent) {
        SashForm form = new SashForm(parent, SWT.VERTICAL);
        GridLayoutFactory.fillDefaults().numColumns(1).margins(0, 0).spacing(1, 1).applyTo(form);

        top = new Composite(form, SWT.TOP);
        GridLayoutFactory.fillDefaults().numColumns(1).margins(0, 0).spacing(1, 1).applyTo(top);

        IToolBarManager mgr = getViewSite().getActionBars().getToolBarManager();
        mgr.add(createSyncAction());

        FontDescriptor fontDescriptor = FontDescriptor.createFrom(JFaceResources.getDefaultFont());
        fontDescriptor = fontDescriptor.setStyle(SWT.BOLD);
        this.font = fontDescriptor.createFont(top.getDisplay());

        createTopTable(top);
        createTabFolder(top);
        createVisualViewer(form);
        form.setWeights(new int[] { 95, 5 });

        // add page listener
        getSite().getPage().addPartListener(this);

        hookContextMenu();
        showBootstrapPart();
    }

    private void createVisualViewer(Composite parent) {
        visualViewer = new Composite(parent, SWT.TOP);
        visualViewer.addPaintListener(new PaintListener() {
            public void paintControl(PaintEvent paintEvent) {
                Object toShow = visualViewer.getData("toShow");//$NON-NLS-1$
                if (toShow == null)
                    return;

                if (toShow instanceof Image) {
                    paintEvent.gc.drawImage((Image) toShow, 0, 0);
                } else if (toShow instanceof RGB) {
                    Color color = new Color(paintEvent.display, (RGB) toShow);
                    paintEvent.gc.setBackground(color);
                    paintEvent.gc.fillRectangle(0, 0, visualViewer.getSize().x, visualViewer.getSize().y);
                    color.dispose();
                }
            }
        });
        visualViewer.setVisible(false);
    }

    private Action createSyncAction() {
        Action syncAction = new Action(null, IAction.AS_CHECK_BOX) {

            @Override
            public void run() {
                if (!keepInSync) {
                    showBootstrapPart();
                    if (editor != null)
                        updateOnSelection(editor.getSelection());
                    keepInSync = true;
                } else {
                    keepInSync = false;
                }
                this.setChecked(!keepInSync);
            }

        };
        syncAction.setImageDescriptor(
                MemoryAnalyserPlugin.getImageDescriptor(MemoryAnalyserPlugin.ISharedImages.SYNCED));
        syncAction.setToolTipText(Messages.InspectorView_LinkWithSnapshot);

        return syncAction;

    }

    private void createTopTable(Composite parent) {
        Composite composite = new Composite(parent, SWT.NONE);
        topTableViewer = new TableViewer(composite, SWT.FULL_SELECTION | SWT.MULTI);

        Table table = topTableViewer.getTable();
        AccessibleCompositeAdapter.access(table);
        TableColumnLayout columnLayout = new TableColumnLayout();
        composite.setLayout(columnLayout);

        TableColumn column = new TableColumn(table, SWT.LEFT);
        columnLayout.setColumnData(column, new ColumnWeightData(100, 10));

        // on win32, item height is reported too low the first time around
        int itemHeight = table.getItemHeight() + 1;
        if (itemHeight < 17)
            itemHeight = 17;

        int scrollbarHeight = 2;
        if (!Platform.OS_WIN32.equals(Platform.getOS()))//$NON-NLS-1$
            scrollbarHeight = table.getHorizontalBar().getSize().y;

        int detailsHeight = 9 * itemHeight + scrollbarHeight;

        table.setHeaderVisible(false);
        table.setLinesVisible(false);
        topTableViewer.setLabelProvider(new TopTableLabelProvider());
        topTableViewer.setContentProvider(new TableContentProvider());

        GridDataFactory.fillDefaults().hint(SWT.DEFAULT, detailsHeight)//
                .grab(true, false).applyTo(composite);

    }

    private void createTabFolder(Composite parent) {
        tabFolder = new CTabFolder(parent, SWT.TOP | SWT.FLAT);
        GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(tabFolder);

        ToolBar toolBar = new ToolBar(tabFolder, SWT.HORIZONTAL | SWT.FLAT);
        // Add custom AccessibleAdapter, passing in associated ToolBar.
        toolBar.getAccessible().addAccessibleListener(new AccessibleToolbarAdapter(toolBar));
        tabFolder.setTopRight(toolBar);
        // set the height of the tab to display the tool bar correctly
        tabFolder.setTabHeight(Math.max(toolBar.computeSize(SWT.DEFAULT, SWT.DEFAULT).y, tabFolder.getTabHeight()));
        final ToolItem pinItem = new ToolItem(toolBar, SWT.CHECK);
        pinItem.setImage(MemoryAnalyserPlugin.getImage(MemoryAnalyserPlugin.ISharedImages.PINNED));
        pinItem.setToolTipText(Messages.InspectorView_PinTab);
        pinItem.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                pinSelection = !pinSelection;
                pinItem.setSelection(pinSelection);
            }
        });

        toolBar.pack();

        final CTabItem staticsTab = new CTabItem(tabFolder, SWT.NULL);
        staticsTab.setText(Messages.InspectorView_Statics);
        staticsTable = createTable(tabFolder);
        staticsTab.setControl(staticsTable.getTable().getParent());

        CTabItem instancesTab = new CTabItem(tabFolder, SWT.NULL);
        instancesTab.setText(Messages.InspectorView_Attributes);
        attributesTable = createTable(tabFolder);
        instancesTab.setControl(attributesTable.getTable().getParent());

        CTabItem classHierarchyTab = new CTabItem(tabFolder, SWT.NULL);
        classHierarchyTab.setText(Messages.InspectorView_ClassHierarchy);
        classHierarchyTree = createHierarchyTree(tabFolder);
        classHierarchyTab.setControl(classHierarchyTree.getTree().getParent());

        CTabItem valueTab = new CTabItem(tabFolder, SWT.NULL);
        valueTab.setText(Messages.InspectorView_Value);
        resolvedValue = createValue(tabFolder);
        valueTab.setControl(resolvedValue);

        getViewSite().getActionBars().setGlobalActionHandler(ActionFactory.COPY.getId(), new Action() {
            @Override
            public void run() {
                if (topTableViewer.getControl().isFocusControl()) {
                    Copy.copyToClipboard(topTableViewer.getControl());
                } else {
                    int s = tabFolder.getSelectionIndex();
                    switch (s) {
                    case 0:
                        Copy.copyToClipboard(staticsTable.getControl());
                        break;
                    case 1:
                        Copy.copyToClipboard(attributesTable.getControl());
                        break;
                    case 2:
                        Copy.copyToClipboard(classHierarchyTree.getControl());
                        break;
                    case 3:
                        String buffer = resolvedValue.getSelectionText();
                        Clipboard clipboard = new Clipboard(resolvedValue.getDisplay());
                        clipboard.setContents(new Object[] { buffer.toString() },
                                new Transfer[] { TextTransfer.getInstance() });
                        clipboard.dispose();
                        break;
                    default:
                        break;
                    }
                }
            }
        });

        tabFolder.setSelection(0);
    }

    private StyledText createValue(CTabFolder parent) {
        StyledText ret = new StyledText(parent, SWT.READ_ONLY | SWT.MULTI | SWT.WRAP);
        return ret;
    }

    private TreeViewer createHierarchyTree(CTabFolder parent) {
        Composite composite = new Composite(parent, SWT.NONE);
        TreeViewer classHierarchyTree = new TreeViewer(composite, SWT.FULL_SELECTION | SWT.MULTI);
        classHierarchyTree.setContentProvider(new HierarchyTreeContentProvider());
        classHierarchyTree.setLabelProvider(new HierarchyLabelProvider(-1));

        Tree tree = classHierarchyTree.getTree();
        AccessibleCompositeAdapter.access(tree);
        TreeColumnLayout columnLayout = new TreeColumnLayout();
        composite.setLayout(columnLayout);

        TreeColumn column = new TreeColumn(tree, SWT.LEFT);
        columnLayout.setColumnData(column, new ColumnWeightData(100, 10));

        return classHierarchyTree;
    }

    private TableViewer createTable(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 | SWT.MULTI);
        Table table = viewer.getTable();
        AccessibleCompositeAdapter.access(table);
        viewer.setContentProvider(new FieldsContentProvider());
        viewer.setLabelProvider(new FieldsLabelProvider(this, table.getFont()));

        TableColumn tableColumn = new TableColumn(table, SWT.LEFT);
        tableColumn.setText(Messages.InspectorView_Type);
        tableColumn.setWidth(50);
        columnLayout.setColumnData(tableColumn, new ColumnWeightData(10, 50, false));

        tableColumn = new TableColumn(table, SWT.LEFT);
        tableColumn.setWidth(80);
        tableColumn.setText(Messages.InspectorView_Name);
        columnLayout.setColumnData(tableColumn, new ColumnWeightData(30, 80));

        tableColumn = new TableColumn(table, SWT.LEFT);
        tableColumn.setWidth(250);
        tableColumn.setText(Messages.InspectorView_Value);
        columnLayout.setColumnData(tableColumn, new ColumnWeightData(60, 250, true));

        table.setHeaderVisible(true);

        return viewer;
    }

    private void hookContextMenu() {
        createMenu(staticsTable);
        createMenu(attributesTable);
        createMenu(topTableViewer);
        createMenu(classHierarchyTree);
    }

    private void createMenu(StructuredViewer viewer) {
        Menu menu = new Menu(viewer.getControl());
        menu.addMenuListener(new MenuListener(menu, viewer));
        viewer.getControl().setMenu(menu);
        contextMenus.add(menu);
    }

    private void fillContextMenu(PopupMenu manager, StructuredViewer viewer) {
        IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();

        if (editor != null) {
            InspectorContextProvider contextProvider = new InspectorContextProvider(snapshot);
            final IContextObject firstElement = contextProvider.getContext(selection.getFirstElement());

            IStructuredSelection editorSelection = (IStructuredSelection) editor.getSelection();
            final Object editorElement = editorSelection.getFirstElement();

            boolean isObject = firstElement != null && editorElement instanceof IContextObject
                    && firstElement.getObjectId() != ((IContextObject) editorElement).getObjectId();

            if (isObject) {
                manager.add(new Action(Messages.InspectorView_GoInto) {
                    @Override
                    public void run() {
                        updateOnSelection(new StructuredSelection(firstElement));
                    }
                });
                manager.addSeparator();
            }

            QueryContextMenu contextMenu = new QueryContextMenu(editor, contextProvider);
            contextMenu.addContextActions(manager, selection, viewer.getControl());
        }
    }

    private void showBootstrapPart() {
        IWorkbenchPage page = getSite().getPage();
        if (page != null)
            partActivated(page.getActiveEditor());
    }

    protected boolean isImportant(IWorkbenchPart part) {
        return part instanceof HeapEditor;
    }

    @Override
    public void dispose() {
        if (this.editor != null) {
            this.editor.removeSelectionChangedListener(this);
            this.editor = null;
        }

        for (Menu menu : contextMenus) {
            if (menu != null && !menu.isDisposed()) {
                menu.dispose();
                menu = null;
            }
        }

        if (font != null)
            font.dispose();

        getSite().getPage().removePartListener(this);
        super.dispose();
    }

    // //////////////////////////////////////////////////////////////
    // view life-cycle
    // //////////////////////////////////////////////////////////////

    @Override
    public void setFocus() {
        tabFolder.getSelection().getControl().setFocus();
    }

    public void partActivated(IWorkbenchPart part) {
        if (!isImportant(part)) {
            return;
        }

        if (!keepInSync)
            return;

        HeapEditor heapEditor = (HeapEditor) part;

        if (this.editor != heapEditor) {
            if (this.editor != null) {
                this.editor.removeSelectionChangedListener(this);
            }

            this.editor = heapEditor;

            final ISnapshotEditorInput input = heapEditor.getSnapshotInput();
            if (input.hasSnapshot()) {
                this.snapshot = input.getSnapshot();
            } else {
                this.snapshot = null;

                // snapshot is not yet available -> register to be informed once
                // the snapshot has been fully loaded
                input.addChangeListener(new ISnapshotEditorInput.IChangeListener() {

                    public void onBaselineLoaded(ISnapshot snapshot) {
                    }

                    public void onSnapshotLoaded(ISnapshot snapshot) {
                        if (InspectorView.this.snapshot == null)
                            InspectorView.this.snapshot = snapshot;

                        input.removeChangeListener(this);
                    }

                });
            }

            this.editor.addSelectionChangedListener(this);

            updateOnSelection(this.editor.getSelection());
        }
    }

    public void partBroughtToTop(IWorkbenchPart part) {
        partActivated(part);
    }

    public void partClosed(IWorkbenchPart part) {
        if (!isImportant(part)) {
            return;
        }

        HeapEditor heapEditor = (HeapEditor) part;

        if (this.editor == heapEditor) {
            this.editor.removeSelectionChangedListener(this);

            clearInput();

            this.snapshot = null;
            this.editor = null;

            if (!keepInSync) {
                keepInSync = true;
                showBootstrapPart();
            }
        }
    }

    public void partDeactivated(IWorkbenchPart part) {
    }

    public void partOpened(IWorkbenchPart part) {
    }

    public void selectionChanged(SelectionChangedEvent event) {
        if (keepInSync) {
            ISelection selection = event.getSelection();
            updateOnSelection(selection);
        }
    }

    private void updateOnSelection(ISelection selection) {
        IContextObject objectSet = null;

        if (selection instanceof IStructuredSelection) {
            Object object = ((IStructuredSelection) selection).getFirstElement();
            if (object instanceof IContextObject)
                objectSet = (IContextObject) object;
        }

        if (objectSet == null || objectSet.getObjectId() < 0) {
            clearInput();
        } else {
            final int objectId = objectSet.getObjectId();

            // do not update if the selection has not changed (double click)
            Object data = topTableViewer.getData("input"); //$NON-NLS-1$
            if (data != null) {
                int current = ((Integer) data).intValue();
                if (current == objectId)
                    return;
            }

            final ISnapshot savedSnapshot = snapshot;

            Job job = new Job(Messages.InspectorView_UpdateObjectDetails) {

                @Override
                protected IStatus run(IProgressMonitor monitor) {
                    try {
                        if (snapshot == null || savedSnapshot != snapshot)
                            return Status.OK_STATUS;

                        final IObject object = savedSnapshot.getObject(objectId);

                        // prepare object info
                        final List<Object> classInfos = prepareClassInfo(object);

                        // prepare static fields info
                        final LazyFields<?> staticFields = prepareStaticFields(object);

                        // prepare attributes
                        final LazyFields<?> attributeFields = prepareAttributes(object);

                        // update visual viewer
                        final Object toShow = prepareVisualInfo(object);

                        topTableViewer.getControl().getDisplay().asyncExec(new Runnable() {
                            public void run() {
                                topTableViewer.setInput(classInfos);
                                topTableViewer.setData("input", objectId);//$NON-NLS-1$
                                staticsTable.setInput(staticFields);
                                attributesTable.setInput(attributeFields);
                                updateVisualViewer(toShow);

                                IClass input = object instanceof IClass ? (IClass) object : object.getClazz();

                                try {
                                    classHierarchyTree.getTree().setRedraw(false);
                                    classHierarchyTree.setInput(null);
                                    classHierarchyTree
                                            .setLabelProvider(new HierarchyLabelProvider(input.getObjectId()));
                                    classHierarchyTree.setInput(new IClass[] { input });
                                    classHierarchyTree.expandAll();
                                } finally {
                                    classHierarchyTree.getTree().setRedraw(true);
                                }

                                String txt = object.getClassSpecificName();
                                resolvedValue.setText(txt != null ? txt : "");//$NON-NLS-1$

                                if (!pinSelection)// no tab pinned
                                {
                                    int selectionIndex = tabFolder.getSelectionIndex();
                                    if (selectionIndex <= 1) {
                                        int newSelectionIndex = (object instanceof IClass) ? 0 : 1;
                                        if (selectionIndex != newSelectionIndex)
                                            tabFolder.setSelection(newSelectionIndex);
                                    }
                                }

                            }

                            private void updateVisualViewer(Object toShow) {
                                Object previous = visualViewer.getData("toShow");//$NON-NLS-1$
                                if (previous instanceof Image)
                                    ((Image) previous).dispose();

                                if (toShow != null) {
                                    if (toShow instanceof ImageData) {
                                        Image image = new Image(visualViewer.getDisplay(), (ImageData) toShow);
                                        visualViewer.setData("toShow", image);//$NON-NLS-1$
                                    } else if (toShow instanceof RGB) {
                                        visualViewer.setData("toShow", toShow);//$NON-NLS-1$
                                    }
                                    visualViewer.redraw();
                                }
                                visualViewer.setVisible(toShow != null);
                                visualViewer.getParent().layout();
                            }
                        });

                        return Status.OK_STATUS;
                    } catch (SnapshotException e) {
                        return new Status(IStatus.ERROR, MemoryAnalyserPlugin.PLUGIN_ID,
                                Messages.InspectorView_ErrorUpdatingInspector, e);
                    }
                }

                private Object prepareVisualInfo(IObject object) throws SnapshotException {
                    String kind = object.getClazz().getName();

                    if ("org.eclipse.swt.graphics.RGB".equals(kind))//$NON-NLS-1$
                    {
                        Integer red = (Integer) object.resolveValue("red");//$NON-NLS-1$
                        Integer green = (Integer) object.resolveValue("green");//$NON-NLS-1$
                        Integer blue = (Integer) object.resolveValue("blue");//$NON-NLS-1$

                        if (red == null || green == null || blue == null)
                            return null;

                        return new RGB(red, green, blue);
                    } else if ("org.eclipse.swt.graphics.ImageData".equals(kind))//$NON-NLS-1$
                    {
                        IPrimitiveArray data = (IPrimitiveArray) object.resolveValue("data");//$NON-NLS-1$
                        Integer width = (Integer) object.resolveValue("width");//$NON-NLS-1$
                        Integer height = (Integer) object.resolveValue("height");//$NON-NLS-1$
                        Integer depth = (Integer) object.resolveValue("depth");//$NON-NLS-1$
                        Integer scanlinePad = (Integer) object.resolveValue("scanlinePad");//$NON-NLS-1$
                        Integer transparentPixel = (Integer) object.resolveValue("transparentPixel");//$NON-NLS-1$

                        if (data == null || width == null || height == null || depth == null || scanlinePad == null
                                || transparentPixel == null)
                            return null;

                        PaletteData paletteData = makePaletteData((IInstance) object.resolveValue("palette"));//$NON-NLS-1$
                        if (paletteData == null)
                            return null;

                        byte[] dataArray = (byte[]) data.getValueArray();
                        byte[] dataCopy = new byte[dataArray.length];
                        System.arraycopy(dataArray, 0, dataCopy, 0, dataArray.length);

                        ImageData imageData = new ImageData(width, height, depth, paletteData, scanlinePad,
                                dataCopy);
                        imageData.transparentPixel = transparentPixel;

                        IPrimitiveArray alphaBytes = (IPrimitiveArray) object.resolveValue("alphaData");//$NON-NLS-1$
                        if (alphaBytes != null) {
                            byte[] alphaDataArray = (byte[]) alphaBytes.getValueArray();
                            byte[] alphaDataCopy = new byte[alphaDataArray.length];
                            System.arraycopy(alphaDataArray, 0, alphaDataCopy, 0, alphaDataArray.length);
                            imageData.alphaData = alphaDataCopy;
                        }

                        return imageData;
                    }

                    return null;
                }

                private LazyFields<?> prepareAttributes(final IObject object) {
                    LazyFields<?> fields = null;
                    if (object instanceof IInstance)
                        fields = new LazyFields.Instance((IInstance) object);
                    else if (object instanceof IPrimitiveArray)
                        fields = new LazyFields.PrimitiveArray((IPrimitiveArray) object);
                    else if (object instanceof IObjectArray)
                        fields = new LazyFields.ObjectArray((IObjectArray) object);
                    else if (object instanceof IClass)
                        fields = new LazyFields.Class((IClass) object, true, false);
                    else
                        fields = LazyFields.EMPTY;

                    return fields;
                }

                private LazyFields<?> prepareStaticFields(final IObject object) {
                    LazyFields<?> fields = null;
                    if (object instanceof IClass)
                        fields = new LazyFields.Class((IClass) object, false, false);
                    else if (object instanceof IInstance)
                        fields = new LazyFields.Class(object.getClazz(), false, true);
                    else
                        fields = LazyFields.EMPTY;

                    return fields;
                }

                private List<Object> prepareClassInfo(final IObject object) throws SnapshotException {
                    List<Object> details = new ArrayList<Object>();

                    details.add(new InfoItem(object.getObjectId(),
                            MemoryAnalyserPlugin.getImageDescriptor(MemoryAnalyserPlugin.ISharedImages.ID), "0x"//$NON-NLS-1$
                                    + Long.toHexString(object.getObjectAddress())));

                    String className = object instanceof IClass ? ((IClass) object).getName()
                            : object.getClazz().getName();

                    int p = className.lastIndexOf('.');
                    if (p < 0) // primitive
                    {
                        InfoItem item = new InfoItem(object.getObjectId(),
                                ImageHelper.getImageDescriptor(ImageHelper.getType(object)), className);
                        details.add(item);
                        details.add(new InfoItem(
                                MemoryAnalyserPlugin.getImageDescriptor(MemoryAnalyserPlugin.ISharedImages.PACKAGE),
                                ""));//$NON-NLS-1$
                    } else {
                        details.add(new InfoItem(object.getObjectId(),
                                ImageHelper.getImageDescriptor(ImageHelper.getType(object)),
                                className.substring(p + 1)));
                        details.add(new InfoItem(
                                MemoryAnalyserPlugin.getImageDescriptor(MemoryAnalyserPlugin.ISharedImages.PACKAGE),
                                className.substring(0, p)));
                    }

                    details.add(new ObjectNode(object.getClazz()));

                    IClass superClass = object instanceof IClass ? ((IClass) object).getSuperClass()
                            : object.getClazz().getSuperClass();

                    if (superClass != null) {
                        details.add(
                                new InfoItem(superClass.getObjectId(),
                                        MemoryAnalyserPlugin
                                                .getImageDescriptor(MemoryAnalyserPlugin.ISharedImages.SUPERCLASS),
                                        superClass.getName()));
                    }

                    ISnapshot snapshot = object.getSnapshot();
                    if (object instanceof IClass)
                        details.add(new ObjectNode(snapshot.getObject(((IClass) object).getClassLoaderId())));
                    else
                        details.add(new ObjectNode(snapshot.getObject(object.getClazz().getClassLoaderId())));

                    details.add(new InfoItem(
                            MemoryAnalyserPlugin.getImageDescriptor(MemoryAnalyserPlugin.ISharedImages.SIZE),
                            MessageUtil.format(Messages.InspectorView_shallowSize, object.getUsedHeapSize())));
                    details.add(new InfoItem(
                            MemoryAnalyserPlugin.getImageDescriptor(MemoryAnalyserPlugin.ISharedImages.SIZE),
                            MessageUtil.format(Messages.InspectorView_retainedSize, object.getRetainedHeapSize())));

                    GCRootInfo[] gc = object.getGCRootInfo();
                    details.add(gc != null ? (Object) gc
                            : new InfoItem(MemoryAnalyserPlugin.getImageDescriptor(ImageHelper.Decorations.GC_ROOT),
                                    Messages.InspectorView_noGCRoot));
                    return details;
                }

                private PaletteData makePaletteData(IInstance palette) throws SnapshotException {
                    Boolean isDirect = (Boolean) palette.resolveValue("isDirect");//$NON-NLS-1$
                    if (isDirect == null)
                        return null;

                    if (isDirect) {
                        Integer redMask = (Integer) palette.resolveValue("redMask");//$NON-NLS-1$
                        Integer greenMask = (Integer) palette.resolveValue("greenMask");//$NON-NLS-1$
                        Integer blueMask = (Integer) palette.resolveValue("blueMask");//$NON-NLS-1$

                        if (redMask == null || greenMask == null || blueMask == null)
                            return null;

                        return new PaletteData(redMask, greenMask, blueMask);
                    } else {
                        IObjectArray array = (IObjectArray) palette.resolveValue("colors");//$NON-NLS-1$
                        if (array == null)
                            return null;

                        RGB[] rgbs = new RGB[array.getLength()];
                        long[] refs = array.getReferenceArray();
                        ISnapshot snapshot = palette.getSnapshot();
                        for (int ii = 0; ii < refs.length; ii++) {
                            int id = snapshot.mapAddressToId(refs[ii]);
                            IObject obj = snapshot.getObject(id);
                            if (obj == null)
                                return null;

                            Integer red = (Integer) obj.resolveValue("red");//$NON-NLS-1$
                            Integer green = (Integer) obj.resolveValue("green");//$NON-NLS-1$
                            Integer blue = (Integer) obj.resolveValue("blue");//$NON-NLS-1$

                            if (red == null || green == null || blue == null)
                                return null;

                            rgbs[ii] = new RGB(red, green, blue);
                        }

                        return new PaletteData(rgbs);
                    }
                }
            };
            job.schedule();
        }
    }

    private void clearInput() {
        topTableViewer.getControl().getDisplay().asyncExec(new Runnable() {
            public void run() {
                // fix: add one (dummy) row to each table so that the
                // ColumnViewer does not cache the last (real) row subject
                // which in turn often has a reference to the snapshot

                if (topTableViewer.getContentProvider() != null) {
                    topTableViewer.setInput(new Object[] { new Object() });
                    topTableViewer.setData("input", null); //$NON-NLS-1$
                }

                if (staticsTable.getContentProvider() != null) {
                    staticsTable.setInput(LazyFields.EMPTY);
                }

                if (attributesTable.getContentProvider() != null) {
                    attributesTable.setInput(LazyFields.EMPTY);
                }

                if (classHierarchyTree.getContentProvider() != null) {
                    classHierarchyTree.setInput(new Object[] { new Object() });
                }

                if (!resolvedValue.isDisposed()) {
                    resolvedValue.setText(""); //$NON-NLS-1$
                }

                for (Menu menu : contextMenus) {
                    if (!menu.isDisposed())
                        disposeItems(menu);
                }
            }
        });
    }

    private void disposeItems(Menu menu) {
        MenuItem[] items = menu.getItems();
        for (MenuItem menuItem : items) {
            if (!menuItem.isDisposed())
                menuItem.dispose();
        }
    }

}