org.eclipse.debug.internal.ui.memory.provisional.AbstractAsyncTableRendering.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.debug.internal.ui.memory.provisional.AbstractAsyncTableRendering.java

Source

/*******************************************************************************
 *  Copyright (c) 2006, 2011 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:
 *     IBM Corporation - initial API and implementation
 *     Teodor Madan (Freescale) -  Bug 292360 -  [Memory View] platform renderings do not implement correctly IMemoryRendering#getControl
 *     Teodor Madan (Freescale) -  Bug 292426 -  [Memory View] platform renderings cannot be repositioned from non-UI thread through calls to IRepositionableMemoryRendering#goToAddress 
 *     Teodor Madan (Freescale) & Jeremiah Swan (IBM) - Bug 300036 -  [Memory View] NPE in AbstractAsyncTableRendering#getSelectedAddress on rendering creation
 *******************************************************************************/

package org.eclipse.debug.internal.ui.memory.provisional;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Iterator;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IMemoryBlock;
import org.eclipse.debug.core.model.IMemoryBlockExtension;
import org.eclipse.debug.core.model.MemoryByte;
import org.eclipse.debug.internal.core.IInternalDebugCoreConstants;
import org.eclipse.debug.internal.ui.DebugUIMessages;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.internal.ui.IInternalDebugUIConstants;
import org.eclipse.debug.internal.ui.memory.IPersistableDebugElement;
import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IStatusMonitor;
import org.eclipse.debug.internal.ui.views.memory.MemoryViewUtil;
import org.eclipse.debug.internal.ui.views.memory.renderings.AbstractBaseTableRendering;
import org.eclipse.debug.internal.ui.views.memory.renderings.AbstractVirtualContentTableModel;
import org.eclipse.debug.internal.ui.views.memory.renderings.AsyncCopyTableRenderingAction;
import org.eclipse.debug.internal.ui.views.memory.renderings.AsyncPrintTableRenderingAction;
import org.eclipse.debug.internal.ui.views.memory.renderings.AsyncTableRenderingCellModifier;
import org.eclipse.debug.internal.ui.views.memory.renderings.AsyncTableRenderingViewer;
import org.eclipse.debug.internal.ui.views.memory.renderings.AsyncVirtualContentTableViewer;
import org.eclipse.debug.internal.ui.views.memory.renderings.CopyTableRenderingToClipboardAction;
import org.eclipse.debug.internal.ui.views.memory.renderings.FormatTableRenderingAction;
import org.eclipse.debug.internal.ui.views.memory.renderings.FormatTableRenderingDialog;
import org.eclipse.debug.internal.ui.views.memory.renderings.GoToAddressAction;
import org.eclipse.debug.internal.ui.views.memory.renderings.GoToAddressComposite;
import org.eclipse.debug.internal.ui.views.memory.renderings.IPresentationErrorListener;
import org.eclipse.debug.internal.ui.views.memory.renderings.IVirtualContentListener;
import org.eclipse.debug.internal.ui.views.memory.renderings.MemorySegment;
import org.eclipse.debug.internal.ui.views.memory.renderings.PendingPropertyChanges;
import org.eclipse.debug.internal.ui.views.memory.renderings.PrintTableRenderingAction;
import org.eclipse.debug.internal.ui.views.memory.renderings.ReformatAction;
import org.eclipse.debug.internal.ui.views.memory.renderings.ResetToBaseAddressAction;
import org.eclipse.debug.internal.ui.views.memory.renderings.TableRenderingContentDescriptor;
import org.eclipse.debug.internal.ui.views.memory.renderings.TableRenderingLine;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.memory.AbstractTableRendering;
import org.eclipse.debug.ui.memory.IMemoryBlockTablePresentation;
import org.eclipse.debug.ui.memory.IMemoryRendering;
import org.eclipse.debug.ui.memory.IMemoryRenderingContainer;
import org.eclipse.debug.ui.memory.IMemoryRenderingSite;
import org.eclipse.debug.ui.memory.IMemoryRenderingSynchronizationService;
import org.eclipse.debug.ui.memory.IMemoryRenderingType;
import org.eclipse.debug.ui.memory.IResettableMemoryRendering;
import org.eclipse.debug.ui.memory.MemoryRenderingElement;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.IBasicPropertyConstants;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.IFontProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter;
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.Font;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.dialogs.PropertyDialogAction;
import org.eclipse.ui.model.IWorkbenchAdapter;
import org.eclipse.ui.part.PageBook;
import org.eclipse.ui.progress.UIJob;

/**
 * Abstract implementation of a table rendering.
 * <p>
 * Clients should subclass from this class if they wish to provide a
 * table rendering.
 * </p>
 * <p>
 *
 * The label of the rendering is constructed by retrieving the expression from
 * <code>IMemoryBlockExtension</code>.  For IMemoryBlock, the label is constructed
 * using the memory block's start address.
 * 
 * This rendering manages the change states of its memory bytes if the memory
 * block does not opt to manage the change states.  For IMemoryBlockExtension, if
 * the memory block returns false when #supportsChangeManagement() is called, this
 * rendering will calculate the change state for each byte when its content is updated.
 * Clients may manages the change states of its memory block by returning true when
 * #supportsChangeManagement() is called.  This will cause this rendering to stop
 * calculating the change states of the memory block.  Instead it would rely on the
 * attributes returned in the MemoryByte array to determine if a byte has changed.
 * For IMemoryBlock, this rendering will manage the change states its content.   
 * 
 *  When firing change event, be aware of the following:
 *  - whenever a change event is fired, the content provider for Memory View
 *    view checks to see if memory has actually changed.  
 *  - If memory has actually changed, a refresh will commence.  Changes to the memory block
 *    will be computed and will be shown with the delta icons.
 *  - If memory has not changed, content will not be refreshed.  However, previous delta information 
 *      will be erased.  The screen will be refreshed to show that no memory has been changed.  (All
 *    delta icons will be removed.)
 *    
 * Please note that these APIs will be called multiple times by the Memory View.
 * To improve performance, debug adapters need to cache the content of its memory block and only
 * retrieve updated data when necessary.
 * </p>
    
 * @since 3.2
 */
public abstract class AbstractAsyncTableRendering extends AbstractBaseTableRendering
        implements IPropertyChangeListener, IResettableMemoryRendering {

    /**
     *  Property identifier for the selected address in a table rendering
     *  This property is used for synchronization between renderings.
     */
    public static final String PROPERTY_SELECTED_ADDRESS = AbstractTableRendering.PROPERTY_SELECTED_ADDRESS;

    /**
     * Property identifier for the column size in a table rendering
     * This property is used for synchronization between renderings.
     */
    public static final String PROPERTY_COL_SIZE = AbstractTableRendering.PROPERTY_COL_SIZE;

    /**
     * Property identifier for the top row address in a table rendering. 
     * This property is used for synchronization between renderings.
     */
    public static final String PROPERTY_TOP_ADDRESS = AbstractTableRendering.PROPERTY_TOP_ADDRESS;

    private static final String ID_ASYNC_TABLE_RENDERING_CONTEXT = "org.eclipse.debug.ui.memory.abstractasynctablerendering"; //$NON-NLS-1$
    private static final String ID_GO_TO_ADDRESS_COMMAND = "org.eclipse.debug.ui.command.gotoaddress"; //$NON-NLS-1$
    private static final String ID_NEXT_PAGE_COMMAND = "org.eclipse.debug.ui.command.nextpage"; //$NON-NLS-1$
    private static final String ID_PREV_PAGE_COMMAND = "org.eclipse.debug.ui.command.prevpage"; //$NON-NLS-1$

    /**
     * Property identifier for the row size in a table rendering
     * This property is used for synchronization between renderings.
     */
    public static final String PROPERTY_ROW_SIZE = AbstractTableRendering.PROPERTY_ROW_SIZE;

    private static final int DEFAULT_BUFFER_THRESHOLD = 1;

    private boolean fActivated = false;

    //   TODO:  review use of MemorySegment, need to be careful to ensure flexible hierarchy

    private class ToggleAddressColumnAction extends Action {

        public ToggleAddressColumnAction() {
            super();
            PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
                    IDebugUIConstants.PLUGIN_ID + ".ShowAddressColumnAction_context"); //$NON-NLS-1$
            updateActionLabel();
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jface.action.IAction#run()
         */
        public void run() {
            fIsShowAddressColumn = !fIsShowAddressColumn;
            if (!fIsShowAddressColumn) {
                fTableViewer.getTable().getColumn(0).setWidth(0);
            } else {
                fTableViewer.getTable().getColumn(0).pack();
            }
            updateActionLabel();
        }

        /**
         * 
         */
        private void updateActionLabel() {
            if (fIsShowAddressColumn) {
                setText(DebugUIMessages.ShowAddressColumnAction_0);
            } else {
                setText(DebugUIMessages.ShowAddressColumnAction_1);
            }
        }
    }

    private class NextPageAction extends Action {
        private NextPageAction() {
            super();
            setText(DebugUIMessages.AbstractTableRendering_4);
            PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
                    IDebugUIConstants.PLUGIN_ID + ".NextPageAction_context"); //$NON-NLS-1$ 
        }

        public void run() {
            BigInteger address = fContentDescriptor.getLoadAddress();
            address = address.add(BigInteger.valueOf(getPageSizeInUnits()));
            handlePageStartAddressChanged(address);
        }
    }

    private class PrevPageAction extends Action {
        private PrevPageAction() {
            super();
            setText(DebugUIMessages.AbstractTableRendering_6);
            PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
                    IDebugUIConstants.PLUGIN_ID + ".PrevPageAction_context"); //$NON-NLS-1$
        }

        public void run() {
            BigInteger address = fContentDescriptor.getLoadAddress();
            address = address.subtract(BigInteger.valueOf(getPageSizeInUnits()));
            handlePageStartAddressChanged(address);
        }
    }

    private class RenderingGoToAddressAction extends GoToAddressAction {
        public RenderingGoToAddressAction(IMemoryRenderingContainer container,
                AbstractBaseTableRendering rendering) {
            super(container, rendering);
        }

        public void run() {
            showGoToAddressComposite();
        }
    }

    private class SwitchPageJob extends UIJob {
        private Object fLock = new Object();
        private boolean fShowMessagePage = false;
        private String fMessage = IInternalDebugCoreConstants.EMPTY_STRING;

        private SwitchPageJob() {
            super("SwitchPageJob");//$NON-NLS-1$
            setSystem(true);
        }

        private void setShowMessagePage(boolean showMsg) {
            synchronized (fLock) {
                fShowMessagePage = showMsg;
            }
        }

        private void setMessage(String message) {
            synchronized (fLock) {
                fMessage = message;
            }
        }

        public IStatus runInUIThread(IProgressMonitor monitor) {

            if (fPageBook.isDisposed())
                return Status.OK_STATUS;

            String msgToShow = null;
            boolean showMsgPage = false;
            synchronized (fLock) {
                msgToShow = fMessage;
                showMsgPage = fShowMessagePage;
            }

            if (showMsgPage) {
                StyledText styleText = null;
                fShowMessage = true;

                styleText = fTextViewer.getTextWidget();

                if (styleText != null)
                    styleText.setText(msgToShow);
                fPageBook.showPage(fTextViewer.getControl());
            } else {
                fShowMessage = false;
                fPageBook.showPage(fTableViewer.getControl().getParent());
            }
            return Status.OK_STATUS;
        }
    }

    private class SerialByObjectRule implements ISchedulingRule {
        private Object fObject = null;

        public SerialByObjectRule(Object lock) {
            fObject = lock;
        }

        /* (non-Javadoc)
         * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
         */
        public boolean contains(ISchedulingRule rule) {
            return rule == this;
        }

        /* (non-Javadoc)
         * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule)
         */
        public boolean isConflicting(ISchedulingRule rule) {
            if (rule instanceof SerialByObjectRule) {
                SerialByObjectRule rRule = (SerialByObjectRule) rule;
                return fObject == rRule.fObject;
            }
            return false;
        }
    }

    private PageBook fPageBook;
    private AsyncTableRenderingViewer fTableViewer;
    private TextViewer fTextViewer;
    private Shell fToolTipShell;
    private MemoryViewPresentationContext fPresentationContext;
    private int fAddressableSize;
    private TableRenderingContentDescriptor fContentDescriptor;
    private int fBytePerLine;
    private int fColumnSize;
    private boolean fShowMessage = false;
    private String fLabel;
    private IWorkbenchAdapter fWorkbenchAdapter;
    private int fPageSize;
    private int fPreBufferSize = -1;
    private int fPostBufferSize = -1;
    private SashForm fSashForm;
    private GoToAddressComposite fGoToAddressComposite;

    // actions
    private GoToAddressAction fGoToAddressAction;
    private PrintTableRenderingAction fPrintViewTabAction;
    private CopyTableRenderingToClipboardAction fCopyToClipboardAction;
    private FormatTableRenderingAction fFormatRenderingAction;
    private ReformatAction fReformatAction;
    private ToggleAddressColumnAction fToggleAddressColumnAction;
    private ResetToBaseAddressAction fResetMemoryBlockAction;
    private PropertyDialogAction fPropertiesDialogAction;
    private NextPageAction fNextAction;
    private PrevPageAction fPrevAction;

    private ArrayList fContext = new ArrayList();
    private AbstractHandler fGoToAddressHandler;

    private AbstractHandler fNextPageHandler;
    private AbstractHandler fPrevPageHandler;

    private boolean fIsCreated = false;
    private boolean fIsDisposed = false;
    private boolean fIsShowAddressColumn = true;

    private SwitchPageJob fSwitchPageJob = new SwitchPageJob();
    private boolean fError = false;

    private PendingPropertyChanges fPendingSyncProperties;

    // list of menu listeners for popupMenuMgr.  
    private ArrayList fMenuListeners;
    private MenuManager fMenuMgr;

    private ISchedulingRule serialByRenderingRule = new SerialByObjectRule(this);

    /** 
     * Identifier for an empty group preceding all context menu actions 
     * (value <code>"popUpBegin"</code>).
     */
    public static final String EMPTY_MEMORY_GROUP = "popUpBegin"; //$NON-NLS-1$

    /**
     * Identifier for an empty group following navigation actions in the rendering
     * (value <code>navigationGroup</code>).
     */
    public static final String EMPTY_NAVIGATION_GROUP = "navigationGroup"; //$NON-NLS-1$

    /**
     * Identifier for an empty group following actions that are only applicable in
     * non-auto loading mode
     * (value <code>nonAutoLoadGroup</code>).
     */
    public static final String EMPTY_NON_AUTO_LOAD_GROUP = "nonAutoLoadGroup"; //$NON-NLS-1$

    /**
     * Identifier for an empty group following properties actions
     * (value <code>propertyGroup</code>).
     */
    public static final String EMPTY_PROPERTY_GROUP = "propertyGroup"; //$NON-NLS-1$

    private ISelectionChangedListener fViewerSelectionChangedListener = new ISelectionChangedListener() {
        public void selectionChanged(SelectionChangedEvent event) {
            updateSyncTopAddress(getTopVisibleAddress());
            updateSyncSelectedAddress(getSelectedAddress());
        }
    };

    private SelectionAdapter fScrollBarSelectionListener = new SelectionAdapter() {
        public void widgetSelected(SelectionEvent e) {
            updateSyncTopAddress(getTopVisibleAddress());
        }
    };

    private IModelChangedListener fModelChangedListener = new IModelChangedListener() {
        public void modelChanged(IModelDelta delta, IModelProxy proxy) {
            if (delta.getElement() == getMemoryBlock()) {
                showTable();
                updateRenderingLabel(isVisible());
            }
        }
    };

    private IVirtualContentListener fViewerListener = new IVirtualContentListener() {

        private int startThreshold;
        private int endThreshold;

        public void handledAtBufferStart() {
            if (getMemoryBlock() instanceof IMemoryBlockExtension) {
                if (isDynamicLoad() && startThreshold != 0) {
                    BigInteger address = getTopVisibleAddress();
                    if (address != null && !isAtTopLimit())
                        reloadTable(address);
                }
            }
        }

        public void handleAtBufferEnd() {
            if (getMemoryBlock() instanceof IMemoryBlockExtension) {
                if (isDynamicLoad() && endThreshold != 0) {
                    BigInteger address = getTopVisibleAddress();
                    if (address != null && !isAtBottomLimit())
                        reloadTable(address);
                }
            }
        }

        public int getThreshold(int bufferEndOrStart) {

            int threshold = DEFAULT_BUFFER_THRESHOLD;

            if (bufferEndOrStart == IVirtualContentListener.BUFFER_START) {
                if (threshold > getPreBufferSize()) {
                    threshold = getPreBufferSize();
                }
            } else {
                if (threshold > getPostBufferSize()) {
                    threshold = getPostBufferSize();
                }
            }

            if (bufferEndOrStart == IVirtualContentListener.BUFFER_START)
                startThreshold = threshold;
            else
                endThreshold = threshold;

            return threshold;
        }
    };

    private IPresentationErrorListener fPresentationErrorListener = new IPresentationErrorListener() {
        public void handlePresentationFailure(IStatusMonitor monitor, IStatus status) {
            showMessage(status.getMessage());
        }
    };

    /**
     * Constructs a new table rendering of the specified type.
     * 
     * @param renderingId memory rendering type identifier
     */
    public AbstractAsyncTableRendering(String renderingId) {
        super(renderingId);
    }

    /* (non-Javadoc)
     * @see org.eclipse.debug.ui.memory.IResettableMemoryRendering#resetRendering()
     */
    public void resetRendering() throws DebugException {
        if (!fIsCreated)
            return;

        BigInteger baseAddress = fContentDescriptor.getContentBaseAddress();

        fTableViewer.setSelection(baseAddress);
        reloadTable(baseAddress);
        fTableViewer.setTopIndex(baseAddress);
        if (!isDynamicLoad()) {
            updateSyncPageStartAddress(baseAddress);
        }

        updateSyncSelectedAddress(baseAddress);
        updateSyncTopAddress(baseAddress);
    }

    public Control createControl(Composite parent) {

        fPageBook = new PageBook(parent, SWT.NONE);
        createMessagePage(fPageBook);
        createTableViewer(fPageBook);
        addListeners();

        return fPageBook;
    }

    /**
     * Create the error page of this rendering
     * @param parent the parent to add the page to
     */
    private void createMessagePage(Composite parent) {
        if (fTextViewer == null) {
            fTextViewer = new TextViewer(parent, SWT.WRAP);
            fTextViewer.setDocument(new Document());
            StyledText styleText = fTextViewer.getTextWidget();
            styleText.setEditable(false);
            styleText.setEnabled(false);
        }
    }

    /**
     * @param parent the parent composite
     */
    private void createTableViewer(final Composite parent) {
        StringBuffer buffer = new StringBuffer();
        IMemoryRenderingType type = DebugUITools.getMemoryRenderingManager().getRenderingType(getRenderingId());
        buffer.append(type.getLabel());
        buffer.append(": "); //$NON-NLS-1$
        buffer.append(DebugUIMessages.AbstractAsyncTableRendering_2);

        Job job = new Job(buffer.toString()) {

            protected IStatus run(IProgressMonitor monitor) {

                // gather information from memory block
                initAddressableSize();
                final BigInteger topVisibleAddress = getInitialTopVisibleAddress();
                BigInteger mbBaseAddress = null;
                try {
                    mbBaseAddress = getMemoryBlockBaseAddress();
                } catch (DebugException e) {
                    fError = true;
                    showMessage(e.getMessage());
                }

                // if it takes too long to get the base address, and user has canceled
                // remove this rendering.
                if (monitor.isCanceled()) {
                    getMemoryRenderingContainer().removeMemoryRendering(AbstractAsyncTableRendering.this);
                    return Status.CANCEL_STATUS;
                }

                final BigInteger finalMbBaseAddress = mbBaseAddress;
                final BigInteger initialSelectedAddress = getInitialSelectedAddress();

                if (monitor.isCanceled()) {
                    getMemoryRenderingContainer().removeMemoryRendering(AbstractAsyncTableRendering.this);
                    return Status.CANCEL_STATUS;
                }

                createContentDescriptor(topVisibleAddress);

                // if it takes too long to get other information, and user has canceled
                // remove this rendering.
                if (monitor.isCanceled()) {
                    getMemoryRenderingContainer().removeMemoryRendering(AbstractAsyncTableRendering.this);
                    return Status.CANCEL_STATUS;
                }

                // batch update on UI thread
                UIJob uiJob = new UIJob("Create Table Viewer UI Job") { //$NON-NLS-1$
                    public IStatus runInUIThread(IProgressMonitor progressMonitor) {

                        if (fPageBook.isDisposed())
                            return Status.OK_STATUS;

                        fSashForm = new SashForm(parent, SWT.VERTICAL);
                        fTableViewer = new AsyncTableRenderingViewer(AbstractAsyncTableRendering.this, fSashForm,
                                SWT.VIRTUAL | SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.HIDE_SELECTION
                                        | SWT.BORDER);

                        GridData data = new GridData(GridData.FILL_BOTH);
                        fTableViewer.getControl().setLayoutData(data);

                        createGoToAddressComposite(fSashForm);
                        hideGotoAddressComposite();

                        IMemoryRenderingSite site = getMemoryRenderingContainer().getMemoryRenderingSite();
                        IMemoryRenderingContainer container = getMemoryRenderingContainer();
                        fPresentationContext = new MemoryViewPresentationContext(site, container,
                                AbstractAsyncTableRendering.this);
                        fTableViewer.setContext(fPresentationContext);

                        // must call this after the context is created as the information is stored in the context
                        getDynamicLoadFromPreference();
                        getPageSizeFromPreference();

                        int numberOfLines = getNumLinesToLoad();
                        fContentDescriptor.setNumLines(numberOfLines);

                        if (numberOfLines == 0) {
                            // if the table viewer is not initialized yet, add listener
                            // and load table when we can get the height of the table
                            fTableViewer.getTable().addPaintListener(new PaintListener() {
                                public void paintControl(PaintEvent e) {
                                    fTableViewer.getTable().removePaintListener(this);
                                    fContentDescriptor.setNumLines(getNumLinesToLoad());
                                    refresh();
                                }
                            });
                        }

                        BigInteger baseAddress = finalMbBaseAddress;
                        if (baseAddress == null)
                            baseAddress = BigInteger.ZERO;

                        if (!(getMemoryBlock() instanceof IMemoryBlockExtension) || !isDynamicLoad()) {
                            // If not extended memory block, do not create any buffer
                            // no scrolling
                            fContentDescriptor.setPreBuffer(0);
                            fContentDescriptor.setPostBuffer(0);
                        }

                        setupInitialFormat();
                        fTableViewer.setCellModifier(newInternalCellModifier());
                        fTableViewer.getTable().setHeaderVisible(true);
                        fTableViewer.getTable().setLinesVisible(true);
                        fTableViewer.addPresentationErrorListener(fPresentationErrorListener);
                        fTableViewer.setInput(getMemoryBlock());
                        fTableViewer.resizeColumnsToPreferredSize();
                        fTableViewer.setTopIndex(topVisibleAddress);

                        fTableViewer.setSelection(initialSelectedAddress);

                        // SET UP FONT      
                        // set to a non-proportional font
                        fTableViewer.getTable()
                                .setFont(JFaceResources.getFont(IInternalDebugUIConstants.FONT_NAME));

                        if (!fError)
                            showTable();

                        fTableViewer.addVirtualContentListener(fViewerListener);

                        // create context menu
                        // create pop up menu for the rendering
                        createActions();
                        IMenuListener menuListener = new IMenuListener() {
                            public void menuAboutToShow(IMenuManager mgr) {
                                fillContextMenu(mgr);
                            }
                        };
                        createPopupMenu(fTableViewer.getControl(), menuListener);
                        createPopupMenu(fTableViewer.getCursor(), menuListener);

                        fTableViewer.addSelectionChangedListener(fViewerSelectionChangedListener);
                        fTableViewer.getTable().getVerticalBar().addSelectionListener(fScrollBarSelectionListener);

                        // add resize listener to figure out number of visible lines 
                        // so that the viewer get refreshed with the correct number of lines on the
                        // next refresh request
                        fTableViewer.getTable().addListener(SWT.Resize, new Listener() {
                            public void handleEvent(Event event) {
                                if (!fTableViewer.getTable().isDisposed())
                                    fContentDescriptor.setNumLines(getNumLinesToLoad());
                            }
                        });

                        createToolTip();

                        if (isActivated()) {
                            activatePageActions();
                        }

                        // now the rendering is successfully created
                        fIsCreated = true;

                        return Status.OK_STATUS;
                    }
                };
                uiJob.setSystem(true);
                uiJob.schedule();

                return Status.OK_STATUS;

            }
        };

        job.schedule();
    }

    /**
     * Create popup menu for this rendering
     * @param control - control to create the popup menu for
     * @param menuListener - listener to notify when popup menu is about to show
     */
    private void createPopupMenu(Control control, IMenuListener menuListener) {
        IMemoryRenderingContainer container = getMemoryRenderingContainer();
        if (fMenuMgr == null) {
            fMenuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
            fMenuMgr.setRemoveAllWhenShown(true);
            IMemoryRenderingSite site = container.getMemoryRenderingSite();
            String menuId = container.getId();

            ISelectionProvider selProvider = site.getSite().getSelectionProvider();

            addMenuListener(menuListener);

            site.getSite().registerContextMenu(menuId, fMenuMgr, selProvider);
        }

        addMenuListener(menuListener);

        Menu popupMenu = fMenuMgr.createContextMenu(control);
        control.setMenu(popupMenu);
    }

    private void addMenuListener(IMenuListener menuListener) {
        if (fMenuListeners == null)
            fMenuListeners = new ArrayList();

        if (!fMenuListeners.contains(menuListener)) {
            fMenuMgr.addMenuListener(menuListener);
            fMenuListeners.add(menuListener);
        }
    }

    private BigInteger getInitialSelectedAddress() {
        // figure out selected address 
        BigInteger selectedAddress = (BigInteger) getSynchronizedProperty(
                AbstractAsyncTableRendering.PROPERTY_SELECTED_ADDRESS);
        if (selectedAddress == null) {
            if (getMemoryBlock() instanceof IMemoryBlockExtension) {
                try {
                    selectedAddress = ((IMemoryBlockExtension) getMemoryBlock()).getBigBaseAddress();
                } catch (DebugException e) {
                    selectedAddress = BigInteger.ZERO;
                }

                if (selectedAddress == null) {
                    selectedAddress = BigInteger.ZERO;
                }

            } else {
                long address = getMemoryBlock().getStartAddress();
                selectedAddress = BigInteger.valueOf(address);
            }
        }
        return selectedAddress;
    }

    private void addListeners() {
        DebugUIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this);
        addRenderingToSyncService();
        JFaceResources.getFontRegistry().addListener(this);
    }

    private void removeListeners() {
        DebugUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this);
        removeRenderingFromSyncService();
        JFaceResources.getFontRegistry().removeListener(this);

        if (fMenuListeners != null) {
            Iterator iter = fMenuListeners.iterator();
            while (iter.hasNext()) {
                fMenuMgr.removeMenuListener((IMenuListener) iter.next());
            }

            fMenuListeners.clear();
        }
    }

    private void addRenderingToSyncService() {
        IMemoryRenderingSynchronizationService syncService = getMemoryRenderingContainer().getMemoryRenderingSite()
                .getSynchronizationService();

        if (syncService == null)
            return;

        syncService.addPropertyChangeListener(this, null);
    }

    private void removeRenderingFromSyncService() {
        IMemoryRenderingSynchronizationService syncService = getMemoryRenderingContainer().getMemoryRenderingSite()
                .getSynchronizationService();

        if (syncService == null)
            return;

        syncService.removePropertyChangeListener(this);
    }

    private void initAddressableSize() {
        // set up addressable size and figure out number of bytes required per line
        fAddressableSize = -1;
        try {
            if (getMemoryBlock() instanceof IMemoryBlockExtension)
                fAddressableSize = ((IMemoryBlockExtension) getMemoryBlock()).getAddressableSize();
            else
                fAddressableSize = 1;
        } catch (DebugException e1) {
            DebugUIPlugin.log(e1);
            // log error and default to 1
            fAddressableSize = 1;
            return;

        }
        if (fAddressableSize < 1) {
            DebugUIPlugin.logErrorMessage("Invalid addressable size"); //$NON-NLS-1$
            fAddressableSize = 1;
        }
    }

    private BigInteger getInitialTopVisibleAddress() {
        BigInteger topVisibleAddress = (BigInteger) getSynchronizedProperty(
                AbstractAsyncTableRendering.PROPERTY_TOP_ADDRESS);
        if (topVisibleAddress == null) {
            if (getMemoryBlock() instanceof IMemoryBlockExtension) {
                try {
                    topVisibleAddress = ((IMemoryBlockExtension) getMemoryBlock()).getBigBaseAddress();
                } catch (DebugException e1) {
                    topVisibleAddress = new BigInteger("0"); //$NON-NLS-1$
                }
            } else {
                topVisibleAddress = BigInteger.valueOf(getMemoryBlock().getStartAddress());
            }
        }
        return topVisibleAddress;
    }

    private void setupInitialFormat() {

        boolean validated = validateInitialFormat();

        if (!validated) {
            // pop up dialog to ask user for default values
            StringBuffer msgBuffer = new StringBuffer(DebugUIMessages.AbstractTableRendering_20);
            msgBuffer.append(" "); //$NON-NLS-1$
            msgBuffer.append(this.getLabel());
            msgBuffer.append("\n\n"); //$NON-NLS-1$
            msgBuffer.append(DebugUIMessages.AbstractTableRendering_16);
            msgBuffer.append("\n"); //$NON-NLS-1$
            msgBuffer.append(DebugUIMessages.AbstractTableRendering_18);
            msgBuffer.append("\n\n"); //$NON-NLS-1$

            int bytePerLine = fBytePerLine;
            int columnSize = fColumnSize;

            // initialize this value to populate the dialog properly
            fBytePerLine = getDefaultRowSize() / getAddressableSize();
            fColumnSize = getDefaultColumnSize() / getAddressableSize();

            FormatTableRenderingDialog dialog = new FormatTableRenderingDialog(this, DebugUIPlugin.getShell());
            dialog.openError(msgBuffer.toString());

            // restore to original value before formatting
            fBytePerLine = bytePerLine;
            fColumnSize = columnSize;

            bytePerLine = dialog.getRowSize() * getAddressableSize();
            columnSize = dialog.getColumnSize() * getAddressableSize();

            format(bytePerLine, columnSize);
        } else {
            // Row size is stored as number of addressable units in preference store
            int bytePerLine = getDefaultRowSize();
            // column size is now stored as number of addressable units
            int columnSize = getDefaultColumnSize();

            // format memory block with specified "bytesPerLine" and "columnSize"   
            boolean ok = format(bytePerLine, columnSize);

            if (!ok) {
                // this is to ensure that the rest of the rendering can be created
                // and we can recover from a format error
                format(bytePerLine, bytePerLine);
            }
        }
    }

    private boolean validateInitialFormat() {
        int rowSize = getDefaultRowSize();
        int columnSize = getDefaultColumnSize();

        if (rowSize < columnSize || rowSize % columnSize != 0 || rowSize == 0 || columnSize == 0) {
            return false;
        }
        return true;
    }

    /* (non-Javadoc)
     * @see org.eclipse.debug.ui.memory.IMemoryRendering#getControl()
     */
    public Control getControl() {
        return fPageBook;
    }

    /* (non-Javadoc)
     * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
     */
    public void propertyChange(PropertyChangeEvent event) {
        if (!fIsCreated)
            return;

        // if memory view table font has changed
        if (event.getProperty().equals(IInternalDebugUIConstants.FONT_NAME)) {
            if (!fIsDisposed) {
                Font memoryViewFont = JFaceResources.getFont(IInternalDebugUIConstants.FONT_NAME);
                setFont(memoryViewFont);
            }
            return;
        }

        Object evtSrc = event.getSource();

        // always update page size, only refresh if the table is visible
        if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE)) {
            getPageSizeFromPreference();
        }
        if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PRE_BUFFER_SIZE)) {
            getPreBufferSizeFromPreference();
            fContentDescriptor.setPreBuffer(getPreBufferSize());
        }
        if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_POST_BUFFER_SIZE)) {
            getPostBufferSizeFromPreference();
            fContentDescriptor.setPostBuffer(getPostBufferSize());
        }

        // do not handle event if the rendering is displaying an error
        // or if it's not visible
        if (isDisplayingError() || !isVisible()) {
            handlePropertiesChangeWhenHidden(event);
            return;
        }

        if (event.getProperty().equals(IDebugUIConstants.PREF_PADDED_STR)
                || event.getProperty().equals(IDebugUIConstants.PREF_CHANGED_DEBUG_ELEMENT_COLOR)
                || event.getProperty().equals(IDebugUIConstants.PREF_MEMORY_HISTORY_KNOWN_COLOR)
                || event.getProperty().equals(IDebugUIConstants.PREF_MEMORY_HISTORY_UNKNOWN_COLOR)) {
            if (!fIsDisposed) {
                fTableViewer.refresh(false);
            }
            return;
        }

        if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PRE_BUFFER_SIZE)
                || event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_POST_BUFFER_SIZE)) {
            if (!fIsDisposed) {
                fTableViewer.refresh(true);
            }
            return;
        }

        if (event.getProperty().equals(IDebugPreferenceConstants.PREF_DYNAMIC_LOAD_MEM)) {
            handleDyanicLoadChanged();
            return;
        }

        if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE)) {
            if (!isDynamicLoad()) {
                int pageSize = DebugUIPlugin.getDefault().getPreferenceStore()
                        .getInt(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE);
                handlePageSizeChanged(pageSize);
            }
            return;
        }

        if (evtSrc == this)
            return;

        if (evtSrc instanceof IMemoryRendering) {
            IMemoryRendering rendering = (IMemoryRendering) evtSrc;
            IMemoryBlock memoryBlock = rendering.getMemoryBlock();

            // do not handle event from renderings displaying other memory blocks
            if (memoryBlock != getMemoryBlock())
                return;
        }

        String propertyName = event.getProperty();
        Object value = event.getNewValue();

        if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_SELECTED_ADDRESS)
                && value instanceof BigInteger) {
            selectedAddressChanged((BigInteger) value);
        } else if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_COL_SIZE) && value instanceof Integer) {
            columnSizeChanged(((Integer) value).intValue());
        } else if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_ROW_SIZE) && value instanceof Integer) {
            rowSizeChanged(((Integer) value).intValue());
        } else if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_TOP_ADDRESS)
                && value instanceof BigInteger) {
            topVisibleAddressChanged((BigInteger) value);
        } else if (propertyName.equals(IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS)
                && value instanceof BigInteger) {
            handlePageStartAddressChanged((BigInteger) value);
        }
    }

    /**
     * @param pageSize the new page size
     */
    private void handlePageSizeChanged(int pageSize) {
        fPageSize = pageSize;
        // only refresh if in non-auto-load mode
        fContentDescriptor.setNumLines(pageSize);
        refresh();
    }

    private void handlePropertiesChangeWhenHidden(PropertyChangeEvent event) {
        if (fPendingSyncProperties == null)
            return;

        String propertyName = event.getProperty();
        Object value = event.getNewValue();

        if (event.getSource() instanceof IMemoryRendering) {
            IMemoryRendering rendering = (IMemoryRendering) event.getSource();
            if (rendering == this || rendering.getMemoryBlock() != getMemoryBlock()) {
                return;
            }
        }

        if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_COL_SIZE) && value instanceof Integer) {
            fPendingSyncProperties.setColumnSize(((Integer) value).intValue());
        } else if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_ROW_SIZE) && value instanceof Integer) {
            fPendingSyncProperties.setRowSize(((Integer) value).intValue());
        }

        else if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_SELECTED_ADDRESS)
                && value instanceof BigInteger) {
            fPendingSyncProperties.setSelectedAddress((BigInteger) value);
        } else if (propertyName.equals(AbstractAsyncTableRendering.PROPERTY_TOP_ADDRESS)
                && value instanceof BigInteger) {
            fPendingSyncProperties.setTopVisibleAddress((BigInteger) value);
        } else if (propertyName.equals(IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS)
                && value instanceof BigInteger) {
            fPendingSyncProperties.setPageStartAddress((BigInteger) value);
        } else if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE)) {
            int pageSize = DebugUIPlugin.getDefault().getPreferenceStore()
                    .getInt(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE);
            fPendingSyncProperties.setPageSize(pageSize);
        }
    }

    private void topVisibleAddressChanged(final BigInteger address) {
        final Runnable runnable = new Runnable() {
            public void run() {
                if (fTableViewer.getTable().isDisposed())
                    return;

                doTopVisibleAddressChanged(address);
            }
        };
        runOnUIThread(runnable);
    }

    /**
     * @param address the changed address
     */
    private void doTopVisibleAddressChanged(final BigInteger address) {
        if (fIsDisposed)
            return;

        if (!isDynamicLoad()) {
            fTableViewer.setTopIndex(address);
            fTableViewer.topIndexChanged();
            return;
        }

        if (!isAtTopBuffer(address) && !isAtBottomBuffer(address)) {
            fTableViewer.setTopIndex(address);
            fTableViewer.topIndexChanged();
        } else {
            reloadTable(address);
        }
    }

    private boolean isAtBottomBuffer(BigInteger address) {
        int idx = fTableViewer.indexOf(address);
        if (idx < 0)
            return true;

        int bottomIdx = idx + getNumberOfVisibleLines();
        int elementsCnt = fTableViewer.getVirtualContentModel().getElements().length;
        int numLinesLeft = elementsCnt - bottomIdx;

        if (numLinesLeft < fViewerListener.getThreshold(IVirtualContentListener.BUFFER_END))
            return true;

        return false;
    }

    private boolean isAtTopBuffer(BigInteger address) {
        int topIdx = fTableViewer.indexOf(address);
        if (topIdx < fViewerListener.getThreshold(IVirtualContentListener.BUFFER_START))
            return true;

        return false;
    }

    private void runOnUIThread(final Runnable runnable) {
        if (Display.getCurrent() != null) {
            runnable.run();
        } else {
            UIJob job = new UIJob("Async Table Rendering UI Job") { //$NON-NLS-1$

                public IStatus runInUIThread(IProgressMonitor monitor) {
                    runnable.run();
                    return Status.OK_STATUS;
                }
            };
            job.setSystem(true);
            job.schedule();
        }
    }

    private void selectedAddressChanged(final BigInteger address) {
        Runnable runnable = new Runnable() {

            public void run() {

                if (fTableViewer.getTable().isDisposed())
                    return;

                // call this to make the table viewer to reload when needed
                int i = fTableViewer.indexOf(address);
                if (i < 0) {
                    // the model may not have been populated yet,
                    // try to predict if the address will be in the buffer
                    boolean contained = isAddressBufferred(address);
                    if (!contained)
                        topVisibleAddressChanged(address);
                }
                fTableViewer.setSelection(address);
            }
        };

        runOnUIThread(runnable);
    }

    private boolean isAddressBufferred(BigInteger address) {
        // figure out the buffer top address
        BigInteger loadAddress = fContentDescriptor.getLoadAddress();
        loadAddress = MemoryViewUtil.alignToBoundary(loadAddress, getAddressableUnitPerLine());
        int unitPerLine = getAddressableUnitPerLine();

        loadAddress = loadAddress.subtract(BigInteger.valueOf(getPreBufferSize() * unitPerLine));

        // figure out the buffer end address
        int numLines = fContentDescriptor.getNumLines();
        BigInteger bufferEnd = loadAddress
                .add(BigInteger.valueOf(fContentDescriptor.getPostBuffer() * unitPerLine));
        bufferEnd = bufferEnd.add(BigInteger.valueOf(numLines * unitPerLine + unitPerLine));

        // see if the address is contained based on current content descriptor
        if (address.compareTo(loadAddress) >= 0 && address.compareTo(bufferEnd) <= 0)
            return true;

        return false;
    }

    private void setFont(Font font) {
        // set font
        fTableViewer.getTable().setFont(font);
        fTableViewer.getCursor().setFont(font);
    }

    private int getDefaultColumnSize() {

        // default to global preference store
        IPreferenceStore prefStore = DebugUITools.getPreferenceStore();
        int columnSize = prefStore.getInt(IDebugPreferenceConstants.PREF_COLUMN_SIZE);
        // actual column size is number of addressable units * size of the addressable unit
        columnSize = columnSize * getAddressableSize();

        // check synchronized col size
        Integer colSize = (Integer) getSynchronizedProperty(AbstractAsyncTableRendering.PROPERTY_COL_SIZE);
        if (colSize != null) {
            // column size is stored as actual number of bytes in synchronizer
            int syncColSize = colSize.intValue();
            if (syncColSize > 0) {
                columnSize = syncColSize;
            }
        } else {
            IPersistableDebugElement elmt = (IPersistableDebugElement) getMemoryBlock()
                    .getAdapter(IPersistableDebugElement.class);
            int defaultColSize = -1;

            if (elmt != null) {
                if (elmt.supportsProperty(this, IDebugPreferenceConstants.PREF_COL_SIZE_BY_MODEL))
                    defaultColSize = getDefaultFromPersistableElement(
                            IDebugPreferenceConstants.PREF_COL_SIZE_BY_MODEL);
            }

            if (defaultColSize <= 0) {
                // if not provided, get default by model
                defaultColSize = getDefaultColumnSizeByModel(getMemoryBlock().getModelIdentifier());
            }

            if (defaultColSize > 0)
                columnSize = defaultColSize * getAddressableSize();
        }
        return columnSize;
    }

    private int getDefaultRowSize() {

        int rowSize = DebugUITools.getPreferenceStore().getInt(IDebugPreferenceConstants.PREF_ROW_SIZE);
        int bytePerLine = rowSize * getAddressableSize();

        // check synchronized row size
        Integer size = (Integer) getSynchronizedProperty(AbstractAsyncTableRendering.PROPERTY_ROW_SIZE);
        if (size != null) {
            // row size is stored as actual number of bytes in synchronizer
            int syncRowSize = size.intValue();
            if (syncRowSize > 0) {
                bytePerLine = syncRowSize;
            }
        } else {
            int defaultRowSize = -1;
            IPersistableDebugElement elmt = (IPersistableDebugElement) getMemoryBlock()
                    .getAdapter(IPersistableDebugElement.class);
            if (elmt != null) {
                if (elmt.supportsProperty(this, IDebugPreferenceConstants.PREF_ROW_SIZE_BY_MODEL)) {
                    defaultRowSize = getDefaultFromPersistableElement(
                            IDebugPreferenceConstants.PREF_ROW_SIZE_BY_MODEL);
                    return defaultRowSize * getAddressableSize();
                }
            }

            if (defaultRowSize <= 0)
                // no synchronized property, ask preference store by id
                defaultRowSize = getDefaultRowSizeByModel(getMemoryBlock().getModelIdentifier());

            if (defaultRowSize > 0)
                bytePerLine = defaultRowSize * getAddressableSize();
        }
        return bytePerLine;
    }

    /**
     * Returns the addressable size of this rendering's memory block in bytes.
     * 
     * @return the addressable size of this rendering's memory block in bytes
     */
    public int getAddressableSize() {
        return fAddressableSize;
    }

    private Object getSynchronizedProperty(String propertyId) {
        IMemoryRenderingSynchronizationService syncService = getMemoryRenderingContainer().getMemoryRenderingSite()
                .getSynchronizationService();

        if (syncService == null)
            return null;

        return syncService.getProperty(getMemoryBlock(), propertyId);
    }

    private int getDefaultFromPersistableElement(String propertyId) {
        int defaultValue = -1;
        IPersistableDebugElement elmt = (IPersistableDebugElement) getMemoryBlock()
                .getAdapter(IPersistableDebugElement.class);
        if (elmt != null) {
            try {
                Object valueMB = elmt.getProperty(this, propertyId);
                if (valueMB != null && !(valueMB instanceof Integer)) {
                    IStatus status = DebugUIPlugin.newErrorStatus("Model returned invalid type on " + propertyId, //$NON-NLS-1$
                            null);
                    DebugUIPlugin.log(status);
                }

                if (valueMB != null) {
                    Integer value = (Integer) valueMB;
                    defaultValue = value.intValue();
                }
            } catch (CoreException e) {
                DebugUIPlugin.log(e);
            }
        }
        return defaultValue;
    }

    /**
     * @param modelId the {@link String} model id
     * @return default number of addressable units per line for the model
     */
    private int getDefaultRowSizeByModel(String modelId) {
        int row = DebugUITools.getPreferenceStore().getInt(getRowPrefId(modelId));
        if (row == 0) {
            DebugUITools.getPreferenceStore().setValue(getRowPrefId(modelId),
                    IDebugPreferenceConstants.PREF_ROW_SIZE_DEFAULT);
        }

        row = DebugUITools.getPreferenceStore().getInt(getRowPrefId(modelId));
        return row;

    }

    /**
     * @param modelId the {@link String} model id
     * @return default number of addressable units per column for the model
     */
    private int getDefaultColumnSizeByModel(String modelId) {
        int col = DebugUITools.getPreferenceStore().getInt(getColumnPrefId(modelId));
        if (col == 0) {
            DebugUITools.getPreferenceStore().setValue(getColumnPrefId(modelId),
                    IDebugPreferenceConstants.PREF_COLUMN_SIZE_DEFAULT);
        }

        col = DebugUITools.getPreferenceStore().getInt(getColumnPrefId(modelId));
        return col;
    }

    private String getRowPrefId(String modelId) {
        String rowPrefId = IDebugPreferenceConstants.PREF_ROW_SIZE + ":" + modelId; //$NON-NLS-1$
        return rowPrefId;
    }

    private String getColumnPrefId(String modelId) {
        String colPrefId = IDebugPreferenceConstants.PREF_COLUMN_SIZE + ":" + modelId; //$NON-NLS-1$
        return colPrefId;
    }

    /**
     * Format view tab based on the bytes per line and column.
     * 
     * @param bytesPerLine - number of bytes per line, possible values: (1 / 2 / 4 / 8 / 16 / 32 / 64/ 128) * addressableSize
     * @param columnSize - number of bytes per column, possible values: (1 / 2 / 4 / 8 / 16 / 32/ 64 / 128) * addressableSize
     * @return true if format is successful, false, otherwise
     */
    public boolean format(int bytesPerLine, int columnSize) {

        // bytes per cell must be divisible to bytesPerLine
        if (bytesPerLine % columnSize != 0) {
            return false;
        }

        if (bytesPerLine < columnSize) {
            return false;
        }

        // do not format if the view tab is already in that format
        if (fBytePerLine == bytesPerLine && fColumnSize == columnSize) {
            return false;
        }

        fBytePerLine = bytesPerLine;
        fColumnSize = columnSize;
        formatViewer();

        updateSyncRowSize();
        updateSyncColSize();

        return true;
    }

    /**
     * Returns the number of addressable units per row.
     *  
     * @return number of addressable units per row
     */
    public int getAddressableUnitPerLine() {
        return fBytePerLine / getAddressableSize();
    }

    /**
     * Returns the number of addressable units per column.
     * 
     * @return number of addressable units per column
     */
    public int getAddressableUnitPerColumn() {
        return fColumnSize / getAddressableSize();
    }

    /**
     * This method estimates the number of visible lines in the rendering
     * table.  
     * @return estimated number of visible lines in the table
     */
    private int getNumberOfVisibleLines() {
        if (fTableViewer == null)
            return -1;

        Table table = fTableViewer.getTable();
        int height = fTableViewer.getTable().getSize().y;

        // when table is not yet created, height is zero
        if (height == 0) {
            // make use of the table viewer to estimate table size
            height = fTableViewer.getTable().getParent().getSize().y;
        }

        if (height == 0) {
            return 0;
        }

        // height of border
        int border = fTableViewer.getTable().getHeaderHeight();

        // height of scroll bar
        int scroll = fTableViewer.getTable().getHorizontalBar().getSize().y;

        // height of table is table's area minus border and scroll bar height      
        height = height - border - scroll;

        // calculate number of visible lines
        int lineHeight = getMinTableItemHeight(table);

        int numberOfLines = height / lineHeight;

        if (numberOfLines <= 0)
            return 0;

        return numberOfLines;
    }

    private int getMinTableItemHeight(Table table) {

        // Hack to get around Linux GTK problem.
        // On Linux GTK, table items have variable item height as
        // carriage returns are actually shown in a cell.  Some rows will be
        // taller than others.  When calculating number of visible lines, we
        // need to find the smallest table item height.  Otherwise, the rendering
        // underestimates the number of visible lines.  As a result the rendering
        // will not be able to get more memory as needed.
        if (MemoryViewUtil.isLinuxGTK()) {
            // check each of the items and find the minimum
            TableItem[] items = table.getItems();
            int minHeight = table.getItemHeight();
            for (int i = 0; i < items.length; i++) {
                if (items[i].getData() != null)
                    minHeight = Math.min(items[i].getBounds(0).height, minHeight);
            }

            return minHeight;

        }
        return table.getItemHeight();
    }

    private BigInteger getMemoryBlockBaseAddress() throws DebugException {
        if (getMemoryBlock() instanceof IMemoryBlockExtension)
            return ((IMemoryBlockExtension) getMemoryBlock()).getBigBaseAddress();
        else
            return BigInteger.valueOf(getMemoryBlock().getStartAddress());
    }

    /**
     * Displays the given message on the error page
     * @param message - the message to display
     */
    protected void showMessage(final String message) {
        fSwitchPageJob.setShowMessagePage(true);
        fSwitchPageJob.setMessage(message);
        fSwitchPageJob.schedule();
    }

    /**
     * Returns the number of bytes displayed in a single column cell.
     * 
     * @return the number of bytes displayed in a single column cell
     */
    public int getBytesPerColumn() {
        return fColumnSize;
    }

    /**
     * Returns the number of bytes displayed in a row.
     * 
     * @return the number of bytes displayed in a row
     */
    public int getBytesPerLine() {
        return fBytePerLine;
    }

    /**
     * Returns whether the error page is displayed.
     * 
     * @return whether the error page is displayed
     */
    public boolean isDisplayingError() {
        return fShowMessage;
    }

    /**
     * Displays the content of the table viewer.
     */
    public void showTable() {
        fSwitchPageJob.setShowMessagePage(false);
        fSwitchPageJob.schedule();
    }

    private BigInteger getTopVisibleAddress() {

        if (fTableViewer == null)
            return BigInteger.valueOf(0);

        Table table = fTableViewer.getTable();
        int topIndex = table.getTopIndex();

        if (topIndex < 0) {
            return null;
        }

        if (table.getItemCount() > topIndex) {
            MemorySegment topItem = (MemorySegment) table.getItem(topIndex).getData();
            if (topItem != null) {
                return topItem.getAddress();
            }
        }
        return null;
    }

    private synchronized void reloadTable(final BigInteger topAddress) {

        if (AsyncVirtualContentTableViewer.DEBUG_DYNAMIC_LOADING)
            System.out.println(this + " reload at: " + topAddress.toString(16)); //$NON-NLS-1$

        fContentDescriptor.setLoadAddress(topAddress);
        fContentDescriptor.setNumLines(getNumLinesToLoad());
        fTableViewer.setTopIndex(topAddress);
        fTableViewer.refresh();

    }

    private boolean isAtTopLimit() {
        BigInteger startAddress = fContentDescriptor.getStartAddress();
        startAddress = MemoryViewUtil.alignToBoundary(startAddress, getAddressableUnitPerLine());
        AbstractVirtualContentTableModel model = fTableViewer.getVirtualContentModel();

        if (model != null) {
            Object key = model.getKey(0);
            if (key instanceof BigInteger) {
                BigInteger startBufferAddress = (BigInteger) key;
                startBufferAddress = MemoryViewUtil.alignToBoundary(startBufferAddress,
                        getAddressableUnitPerLine());

                if (startAddress.compareTo(startBufferAddress) == 0)
                    return true;
            }
        }
        return false;
    }

    private boolean isAtBottomLimit() {
        BigInteger endAddress = fContentDescriptor.getEndAddress();
        endAddress = MemoryViewUtil.alignToBoundary(endAddress, getAddressableUnitPerLine());

        AbstractVirtualContentTableModel model = fTableViewer.getVirtualContentModel();
        if (model != null) {
            int numElements = model.getElements().length;
            Object key = model.getKey(numElements - 1);
            if (key instanceof BigInteger) {
                BigInteger endBufferAddress = (BigInteger) key;
                endBufferAddress = MemoryViewUtil.alignToBoundary(endBufferAddress, getAddressableUnitPerLine());

                if (endAddress.compareTo(endBufferAddress) == 0)
                    return true;
            }
        }

        return false;
    }

    private void formatViewer() {

        fTableViewer.disposeColumns();
        fTableViewer.disposeCellEditors();
        doFormatTable();
        fTableViewer.setColumnHeaders(getColumnProperties());
        fTableViewer.showColumnHeader(true);

        Table table = fTableViewer.getTable();
        int colCnt = table.getColumnCount();
        CellEditor[] editors = new CellEditor[fTableViewer.getTable().getColumnCount()];
        for (int i = 0; i < colCnt; i++) {
            editors[i] = createCellEditor(table, i);
        }

        fTableViewer.setCellEditors(editors);

        fTableViewer.formatViewer();

        // This resize needs to happen after the viewer has finished
        // getting the labels.
        // This fix is a hack to delay the resize until the viewer has a chance to get
        // the setData event from the UI thread.  Otherwise, the columns will be
        // squeezed together.
        UIJob job = new UIJob("resize to fit") { //$NON-NLS-1$
            public IStatus runInUIThread(IProgressMonitor monitor) {
                resizeColumnsToPreferredSize();
                return Status.OK_STATUS;
            }
        };

        job.setSystem(true);
        job.schedule();
    }

    private void doFormatTable() {
        int bytesPerLine = getBytesPerLine();
        int columnSize = getBytesPerColumn();
        int numColumns = bytesPerLine / columnSize;

        Table table = fTableViewer.getTable();
        TableColumn column0 = new TableColumn(table, SWT.LEFT, 0);
        column0.setText(DebugUIMessages.AbstractTableRendering_2);

        // create new byte columns
        TableColumn[] byteColumns = new TableColumn[numColumns];
        for (int i = 0; i < byteColumns.length; i++) {
            TableColumn column = new TableColumn(table, SWT.LEFT, i + 1);
            byteColumns[i] = column;
        }

        //Empty column for cursor navigation
        TableColumn emptyCol = new TableColumn(table, SWT.LEFT, byteColumns.length + 1);
        emptyCol.setText(" "); //$NON-NLS-1$
        emptyCol.setWidth(1);
        emptyCol.setResizable(false);
        table.setHeaderVisible(true);

        // allow clients to override column labels
        setColumnHeadings();

    }

    private String[] getColumnProperties() {
        int numColumns = getAddressableUnitPerLine() / getAddressableUnitPerColumn();
        // +2 to include properties for address and navigation column
        String[] columnProperties = new String[numColumns + 2];
        columnProperties[0] = TableRenderingLine.P_ADDRESS;

        int addressableUnit = getAddressableUnitPerColumn();

        // use column beginning offset to the row address as properties
        for (int i = 1; i < columnProperties.length - 1; i++) {
            // column properties are stored as number of addressable units from the
            // the line address
            columnProperties[i] = Integer.toHexString((i - 1) * addressableUnit);
        }

        // Empty column for cursor navigation
        columnProperties[columnProperties.length - 1] = " "; //$NON-NLS-1$
        return columnProperties;
    }

    /**
      * Create a cell editor from the specified composite and column.
     * @param composite parent composite that the cell editor is to be created from.
     * @param column the column where the cell editor is required
     * @return the cell editor for editing memory
     * 
     * @since 3.3
      *
     */
    protected CellEditor createCellEditor(Composite composite, int column) {

        return new TextCellEditor(composite);
    }

    private ICellModifier newInternalCellModifier() {
        return new AsyncTableRenderingCellModifier(this, createCellModifier());
    }

    /**
     * Create a custom cell modifier for this rendering.  Return null
     * if the default cell modifier is to be used.
     * @return the cell modifier for this rendering, or <code>null</code> if the 
     * default cell modifier is to be used.
     * 
     * @since 3.3
     * 
     */
    protected ICellModifier createCellModifier() {
        return null;
    }

    /* (non-Javadoc)
     * @see org.eclipse.debug.ui.memory.AbstractMemoryRendering#dispose()
     */
    public void dispose() {

        if (fIsDisposed)
            return;

        fIsDisposed = true;

        removeListeners();

        if (fMenuMgr != null) {
            fMenuMgr.removeAll();
            fMenuMgr.dispose();
            fMenuMgr = null;
        }

        if (fTableViewer != null) {
            if (fViewerListener != null)
                fTableViewer.removeVirtualContentListener(fViewerListener);

            if (fPresentationErrorListener != null)
                fTableViewer.removePresentationErrorListener(fPresentationErrorListener);

            fTableViewer.removeSelectionChangedListener(fViewerSelectionChangedListener);
            fTableViewer.getTable().getVerticalBar().removeSelectionListener(fScrollBarSelectionListener);

            fTableViewer.dispose();
        }

        if (fPresentationContext != null) {
            fPresentationContext.dispose();
        }

        if (!fToolTipShell.isDisposed()) {
            fToolTipShell.dispose();
            fToolTipShell = null;
        }

        fIsDisposed = true;

        super.dispose();
    }

    /**
     * Updates the label of this rendering, optionally displaying the
     * base address of this rendering's memory block.
     * 
     * @param showAddress whether to display the base address of this
     *  rendering's memory block in this rendering's label
     */
    protected void updateRenderingLabel(final boolean showAddress) {
        Job job = new Job("Update Rendering Label") { //$NON-NLS-1$
            protected IStatus run(IProgressMonitor monitor) {
                if (fIsDisposed)
                    return Status.OK_STATUS;
                fLabel = buildLabel(showAddress);
                firePropertyChangedEvent(new PropertyChangeEvent(AbstractAsyncTableRendering.this,
                        IBasicPropertyConstants.P_TEXT, null, fLabel));
                return Status.OK_STATUS;
            }
        };
        job.setSystem(true);
        job.setRule(serialByRenderingRule);
        job.schedule();
    }

    private String buildLabel(boolean showAddress) {
        String label = IInternalDebugCoreConstants.EMPTY_STRING;
        if (getMemoryBlock() instanceof IMemoryBlockExtension) {
            label = ((IMemoryBlockExtension) getMemoryBlock()).getExpression();

            if (label == null) {
                label = DebugUIMessages.AbstractTableRendering_8;
            }

            if (label.startsWith("&")) //$NON-NLS-1$
                label = "&" + label; //$NON-NLS-1$

            try {
                if (showAddress && ((IMemoryBlockExtension) getMemoryBlock()).getBigBaseAddress() != null) {
                    label += " : 0x"; //$NON-NLS-1$
                    label += ((IMemoryBlockExtension) getMemoryBlock()).getBigBaseAddress().toString(16)
                            .toUpperCase();
                }
            } catch (DebugException e) {
                // do nothing, the label will not show the address
            }
        } else {
            long address = getMemoryBlock().getStartAddress();
            label = Long.toHexString(address).toUpperCase();
        }

        String preName = DebugUITools.getMemoryRenderingManager().getRenderingType(getRenderingId()).getLabel();

        if (preName != null)
            label += " <" + preName + ">"; //$NON-NLS-1$ //$NON-NLS-2$

        return decorateLabel(label);
    }

    /* Returns the label of this rendering.
     * 
     * @return label of this rendering
     */
    public String getLabel() {

        if (fLabel == null) {
            fLabel = DebugUIMessages.AbstractAsyncTableRendering_1;
            updateRenderingLabel(isVisible());
        }

        return fLabel;
    }

    /* (non-Javadoc)
     * @see org.eclipse.core.runtime.PlatformObject#getAdapter(java.lang.Class)
     */
    public Object getAdapter(Class adapter) {

        if (adapter == IColorProvider.class)
            return getColorProviderAdapter();

        if (adapter == ILabelProvider.class)
            return getLabelProviderAdapter();

        if (adapter == IFontProvider.class)
            return getFontProviderAdapter();

        if (adapter == IModelChangedListener.class) {
            return fModelChangedListener;
        }

        if (adapter == IWorkbenchAdapter.class) {
            // needed workbench adapter to fill the title of property page
            if (fWorkbenchAdapter == null) {
                fWorkbenchAdapter = new IWorkbenchAdapter() {
                    public Object[] getChildren(Object o) {
                        return new Object[0];
                    }

                    public ImageDescriptor getImageDescriptor(Object object) {
                        return null;
                    }

                    public String getLabel(Object o) {
                        return AbstractAsyncTableRendering.this.getLabel();
                    }

                    public Object getParent(Object o) {
                        return null;
                    }
                };
            }
            return fWorkbenchAdapter;
        }

        if (adapter == TableRenderingContentDescriptor.class)
            return getContentDescriptor();

        return super.getAdapter(adapter);
    }

    /**
     * Returns the number of characters a byte will convert to
     * or -1 if unknown.
     * 
     * @return the number of characters a byte will convert to
     *  or -1 if unknown
     */
    public int getNumCharsPerByte() {
        return -1;
    }

    /**
     * Create actions for this rendering
     */
    protected void createActions() {

        fCopyToClipboardAction = new AsyncCopyTableRenderingAction(this, fTableViewer);
        fGoToAddressAction = new RenderingGoToAddressAction(getMemoryRenderingContainer(), this);
        fResetMemoryBlockAction = new ResetToBaseAddressAction(this);

        fPrintViewTabAction = new AsyncPrintTableRenderingAction(this, fTableViewer);

        fFormatRenderingAction = new FormatTableRenderingAction(this);
        fReformatAction = new ReformatAction(this);
        fToggleAddressColumnAction = new ToggleAddressColumnAction();

        IMemoryRenderingSite site = getMemoryRenderingContainer().getMemoryRenderingSite();
        if (site.getSite().getSelectionProvider() != null) {
            fPropertiesDialogAction = new PropertyDialogAction(site.getSite(),
                    site.getSite().getSelectionProvider());
        }

        fNextAction = new NextPageAction();
        fPrevAction = new PrevPageAction();
    }

    /**
     * Returns the currently selected address in this rendering.
     * 
     * @return the currently selected address in this rendering
     */
    public BigInteger getSelectedAddress() {
        if (!fIsCreated)
            return null;

        Object key = fTableViewer.getSelectionKey();

        if (key != null && key instanceof BigInteger)
            return (BigInteger) key;

        return null;
    }

    /**
     * Returns the currently selected content in this rendering as MemoryByte.
     * 
     * @return the currently selected content in array of MemoryByte.  
     * Returns an empty array if the selected address is out of buffered range.
     */
    public MemoryByte[] getSelectedAsBytes() {
        if (getSelectedAddress() == null)
            return new MemoryByte[0];

        Object key = fTableViewer.getSelectionKey();
        AbstractVirtualContentTableModel model = fTableViewer.getVirtualContentModel();

        if (model != null) {
            model = (AbstractVirtualContentTableModel) fTableViewer.getModel();
            int row = model.indexOfKey(key);
            Object element = model.getElement(row);
            int col = model.columnOf(element, key);

            // check precondition
            if (col <= 0 || col > getBytesPerLine() / getBytesPerColumn()) {
                return new MemoryByte[0];
            }

            if (!(element instanceof MemorySegment))
                return new MemoryByte[0];

            MemorySegment line = (MemorySegment) element;
            int offset = (col - 1) * (getAddressableUnitPerColumn() * getAddressableSize());

            // make a copy of the bytes to ensure that data cannot be changed
            // by caller
            MemoryByte[] bytes = line.getBytes(offset, getAddressableUnitPerColumn() * getAddressableSize());
            MemoryByte[] retBytes = new MemoryByte[bytes.length];

            System.arraycopy(bytes, 0, retBytes, 0, bytes.length);
            return retBytes;
        }
        return new MemoryByte[0];
    }

    /**
     * Returns the currently selected content in this rendering as a String.
     * 
     * @return the currently selected content in this rendering
     */
    public String getSelectedAsString() {

        if (getSelectedAddress() == null)
            return IInternalDebugCoreConstants.EMPTY_STRING;

        MemoryByte[] bytes = getSelectedAsBytes();
        if (bytes.length > 0) {
            return getString(this.getRenderingId(), getSelectedAddress(), bytes);
        } else
            return IInternalDebugCoreConstants.EMPTY_STRING;

    }

    /**
     * Moves the cursor to the specified address.
     * Will load more memory if the address is not currently visible.
     * 
     * @param address address to position cursor at
     * @throws DebugException if an exception occurs
     */
    public void goToAddress(final BigInteger address) throws DebugException {

        if (!fIsCreated || fTableViewer.getVirtualContentModel() == null)
            return;

        final int keyIndex = fTableViewer.getVirtualContentModel().indexOfKey(address);

        if (keyIndex < 0) {
            // if not extended memory block
            // do not allow user to go to an address that's out of range
            if (!(getMemoryBlock() instanceof IMemoryBlockExtension)) {
                Status stat = new Status(IStatus.ERROR, DebugUIPlugin.getUniqueIdentifier(),
                        DebugException.NOT_SUPPORTED, DebugUIMessages.AbstractTableRendering_11, null);
                DebugException e = new DebugException(stat);
                throw e;
            }

            BigInteger startAdd = fContentDescriptor.getStartAddress();
            BigInteger endAdd = fContentDescriptor.getEndAddress();

            if (address.compareTo(startAdd) < 0 || address.compareTo(endAdd) > 0) {
                Status stat = new Status(IStatus.ERROR, DebugUIPlugin.getUniqueIdentifier(),
                        DebugException.NOT_SUPPORTED, DebugUIMessages.AbstractTableRendering_11, null);
                DebugException e = new DebugException(stat);
                throw e;
            }
        }

        Runnable runnable = new Runnable() {
            public void run() {
                showTable();
                if (keyIndex >= 0) {
                    // address is within range, set cursor and reveal
                    fTableViewer.setSelection(address);
                    updateSyncTopAddress(getTopVisibleAddress());
                    updateSyncSelectedAddress(address);
                } else {
                    // load at the address
                    fTableViewer.setSelection(address);
                    reloadTable(address);

                    updateSyncSelectedAddress(address);
                    if (!isDynamicLoad()) {
                        updateSyncPageStartAddress(address);
                    }
                    updateSyncTopAddress(address);
                }
            }
        };
        runOnUIThread(runnable);
    }

    /**
     * Refresh the table viewer with the current top visible address.
     * Update labels in the memory rendering.
     */
    public void refresh() {
        if (!fIsCreated)
            return;

        fTableViewer.refresh();
    }

    /**
     * Resize column to the preferred size.
     */
    public void resizeColumnsToPreferredSize() {
        if (!fIsCreated)
            return;

        fTableViewer.resizeColumnsToPreferredSize();
        if (!fIsShowAddressColumn) {
            final TableColumn column = fTableViewer.getTable().getColumn(0);
            column.addControlListener(new ControlListener() {

                public void controlMoved(ControlEvent e) {
                }

                public void controlResized(ControlEvent e) {
                    column.removeControlListener(this);
                    column.setWidth(0);
                }
            });
        }
    }

    /**
     * Updates labels of this rendering.
     */
    public void updateLabels() {
        if (!fIsCreated)
            return;

        UIJob job = new UIJob("updateLabels") { //$NON-NLS-1$

            public IStatus runInUIThread(IProgressMonitor monitor) {

                // do not handle if the rendering is already disposed
                if (fPageBook.isDisposed())
                    return Status.OK_STATUS;

                // update tab labels
                updateRenderingLabel(true);

                if (fTableViewer != null) {
                    // update column labels
                    setColumnHeadings();

                    // rebuild cache and force labels to be refreshed
                    fTableViewer.formatViewer();
                }
                return Status.OK_STATUS;
            }
        };
        job.setSystem(true);
        job.schedule();
    }

    /**
     * Fills the context menu for this rendering
     * 
     * @param menu menu to fill
     */
    protected void fillContextMenu(IMenuManager menu) {

        menu.add(new Separator(EMPTY_MEMORY_GROUP));
        menu.add(new Separator());
        menu.add(fResetMemoryBlockAction);
        menu.add(fGoToAddressAction);
        menu.add(new Separator(EMPTY_NAVIGATION_GROUP));

        menu.add(new Separator());
        menu.add(fFormatRenderingAction);

        if (!isDynamicLoad() && getMemoryBlock() instanceof IMemoryBlockExtension) {
            menu.add(new Separator());
            menu.add(fPrevAction);
            menu.add(fNextAction);
            menu.add(new Separator(EMPTY_NON_AUTO_LOAD_GROUP));
        }

        menu.add(new Separator());
        menu.add(fReformatAction);
        menu.add(fToggleAddressColumnAction);
        menu.add(new Separator());
        menu.add(fCopyToClipboardAction);
        menu.add(fPrintViewTabAction);
        if (fPropertiesDialogAction != null) {
            menu.add(new Separator());
            menu.add(fPropertiesDialogAction);
            menu.add(new Separator(EMPTY_PROPERTY_GROUP));
        }

        menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    }

    private int getPageSizeInUnits() {
        return fPageSize * getAddressableUnitPerLine();
    }

    private void getPageSizeFromPreference() {
        fPageSize = DebugUIPlugin.getDefault().getPreferenceStore()
                .getInt(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE);
    }

    private void getPreBufferSizeFromPreference() {
        fPreBufferSize = DebugUIPlugin.getDefault().getPreferenceStore()
                .getInt(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PRE_BUFFER_SIZE);
    }

    private void getPostBufferSizeFromPreference() {
        fPostBufferSize = DebugUIPlugin.getDefault().getPreferenceStore()
                .getInt(IDebugPreferenceConstants.PREF_TABLE_RENDERING_POST_BUFFER_SIZE);
    }

    private void updateDynamicLoadProperty() {

        boolean value = DebugUIPlugin.getDefault().getPreferenceStore()
                .getBoolean(IDebugPreferenceConstants.PREF_DYNAMIC_LOAD_MEM);

        if (value != isDynamicLoad()) {
            setDynamicLoad(value);

            if (!fIsDisposed) {
                if (isDynamicLoad()) {
                    fContentDescriptor.setPostBuffer(getPostBufferSize());
                    fContentDescriptor.setPreBuffer(getPreBufferSize());
                    fContentDescriptor.setNumLines(getNumberOfVisibleLines());

                } else {
                    fContentDescriptor.setPostBuffer(0);
                    fContentDescriptor.setPreBuffer(0);
                    fContentDescriptor.setNumLines(fPageSize);
                }
            }
        }
    }

    private void getDynamicLoadFromPreference() {
        setDynamicLoad(DebugUIPlugin.getDefault().getPreferenceStore()
                .getBoolean(IDebugPreferenceConstants.PREF_DYNAMIC_LOAD_MEM));
    }

    private boolean isDynamicLoad() {
        return fContentDescriptor.isDynamicLoad();
    }

    private int getPageSize() {
        return fPageSize;
    }

    private int getNumLinesToLoad() {
        int numberOfLines = -1;

        if (isDynamicLoad())
            numberOfLines = getNumberOfVisibleLines();
        else
            numberOfLines = getPageSize();

        return numberOfLines;
    }

    private void setDynamicLoad(boolean load) {
        fContentDescriptor.setDynamicLoad(load);
    }

    private void handlePageStartAddressChanged(BigInteger address) {
        // do not handle if in dynamic mode
        if (isDynamicLoad())
            return;

        if (!(getMemoryBlock() instanceof IMemoryBlockExtension))
            return;

        // do not handle event if the base address of the memory
        // block has changed, wait for debug event to update to
        // new location
        if (isMemoryBlockBaseAddressChanged())
            return;

        Object key = fTableViewer.getKey(0);
        if (key != null) {
            if (key.equals(address))
                return;
        }

        BigInteger start = fContentDescriptor.getStartAddress();
        BigInteger end = fContentDescriptor.getEndAddress();

        // smaller than start address, load at start address
        if (address.compareTo(start) < 0) {
            if (isAtTopLimit())
                return;

            address = start;
        }

        // bigger than end address, no need to load, already at top
        if (address.compareTo(end) > 0) {
            if (isAtBottomLimit())
                return;

            address = end.subtract(BigInteger.valueOf(getPageSizeInUnits()));
        }

        fContentDescriptor.setLoadAddress(address);
        final BigInteger finaladdress = address;
        Runnable runnable = new Runnable() {
            public void run() {
                if (fTableViewer.getTable().isDisposed())
                    return;

                fTableViewer.setTopIndex(finaladdress);
                refresh();
            }
        };

        runOnUIThread(runnable);

        updateSyncPageStartAddress(address);
        updateSyncTopAddress(address);
    }

    private void handleDyanicLoadChanged() {

        // if currently in dynamic load mode, update page
        // start address
        BigInteger pageStart = getTopVisibleAddress();
        updateSyncPageStartAddress(pageStart);

        updateDynamicLoadProperty();
        if (isDynamicLoad()) {
            refresh();
            fTableViewer.setTopIndex(pageStart);
        } else {
            handlePageStartAddressChanged(pageStart);
        }
    }

    /* (non-Javadoc)
     * @see org.eclipse.debug.ui.memory.IMemoryRendering#becomesHidden()
     */
    public void becomesHidden() {
        // creates new object for storing potential changes in sync properties
        fPendingSyncProperties = new PendingPropertyChanges();
        super.becomesHidden();

        if (getMemoryBlock() instanceof IMemoryBlockExtension)
            updateRenderingLabel(false);
    }

    /* (non-Javadoc)
     * @see org.eclipse.debug.ui.memory.IMemoryRendering#becomesVisible()
     */
    public void becomesVisible() {

        if (!fIsCreated) {
            // label can still be constructed even though the rendering has not finished being
            // initialized
            updateRenderingLabel(true);
            super.becomesVisible();
            return;
        }

        // do not do anything if already visible
        if (isVisible() == true) {
            // super should always be called
            super.becomesVisible();
            return;
        }

        super.becomesVisible();

        if (fPendingSyncProperties != null) {
            // deal with format
            boolean format = false;
            int rowSize = getBytesPerLine();
            if (fPendingSyncProperties.getRowSize() > 0) {
                format = true;
                rowSize = fPendingSyncProperties.getRowSize();
            }

            int colSize = getBytesPerColumn();
            if (fPendingSyncProperties.getColumnSize() > 0) {
                format = true;
                colSize = fPendingSyncProperties.getColumnSize();
            }

            if (format)
                format(rowSize, colSize);

            BigInteger selectedAddress = fPendingSyncProperties.getSelectedAddress();
            if (selectedAddress != null)
                fTableViewer.setSelection(selectedAddress);

            updateDynamicLoadProperty();

            if (isDynamicLoad()) {
                BigInteger topVisibleAddress = fPendingSyncProperties.getTopVisibleAddress();
                if (topVisibleAddress != null) {
                    fContentDescriptor.setLoadAddress(topVisibleAddress);
                    fTableViewer.setTopIndex(topVisibleAddress);
                }
            } else if (!(getMemoryBlock() instanceof IMemoryBlockExtension)) {
                BigInteger topVisibleAddress = fPendingSyncProperties.getTopVisibleAddress();
                if (topVisibleAddress != null)
                    fTableViewer.setTopIndex(topVisibleAddress);
            } else {
                if (fPendingSyncProperties.getPageSize() > 0) {
                    fPageSize = fPendingSyncProperties.getPageSize();
                    fContentDescriptor.setNumLines(fPageSize);
                }

                BigInteger pageStartAddress = fPendingSyncProperties.getPageStartAddress();
                if (pageStartAddress != null)
                    fContentDescriptor.setLoadAddress(pageStartAddress);

                fTableViewer.setTopIndex(pageStartAddress);
            }

            showTable();
            refresh();
        }

        updateRenderingLabel(true);

        Job job = new Job("becomesVisible") //$NON-NLS-1$
        {
            protected IStatus run(IProgressMonitor monitor) {
                if (fIsDisposed)
                    return Status.OK_STATUS;
                try {
                    fContentDescriptor.updateContentBaseAddress();
                } catch (DebugException e) {
                    showMessage(e.getMessage());
                }
                return Status.OK_STATUS;
            }
        };
        job.setSystem(true);
        job.schedule();

        // discard these properties
        fPendingSyncProperties = null;
    }

    /**
     * Handle column size changed event from synchronizer
     * @param newColumnSize the new column size
     */
    private void columnSizeChanged(final int newColumnSize) {
        // ignore event if rendering is not visible
        if (!isVisible())
            return;

        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                int rowSize = getBytesPerLine();
                if (rowSize < newColumnSize)
                    rowSize = newColumnSize;

                format(rowSize, newColumnSize);
            }
        });
    }

    /**
     * @param newRowSize - new row size in number of bytes
     */
    private void rowSizeChanged(final int newRowSize) {
        // ignore event if rendering is not visible
        if (!isVisible())
            return;

        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                int colSize = getBytesPerColumn();
                if (newRowSize < colSize)
                    colSize = newRowSize;

                format(newRowSize, colSize);
            }
        });
    }

    /**
     * update selected address in synchronizer if update is true.
     * @param address the address to update
     */
    private void updateSyncSelectedAddress(BigInteger address) {

        if (!fIsCreated)
            return;
        PropertyChangeEvent event = new PropertyChangeEvent(this,
                AbstractAsyncTableRendering.PROPERTY_SELECTED_ADDRESS, null, address);
        firePropertyChangedEvent(event);
    }

    /**
     * update column size in synchronizer
     */
    private void updateSyncColSize() {

        if (!fIsCreated)
            return;

        PropertyChangeEvent event = new PropertyChangeEvent(this, AbstractAsyncTableRendering.PROPERTY_COL_SIZE,
                null, new Integer(fColumnSize));
        firePropertyChangedEvent(event);
    }

    /**
     * update column size in synchronizer
     */
    private void updateSyncRowSize() {

        if (!fIsCreated)
            return;

        PropertyChangeEvent event = new PropertyChangeEvent(this, AbstractAsyncTableRendering.PROPERTY_ROW_SIZE,
                null, new Integer(fBytePerLine));
        firePropertyChangedEvent(event);
    }

    /**
     * update top visible address in synchronizer
     * @param address the address to update
     */
    private void updateSyncTopAddress(BigInteger address) {

        if (!fIsCreated)
            return;

        PropertyChangeEvent event = new PropertyChangeEvent(this, AbstractAsyncTableRendering.PROPERTY_TOP_ADDRESS,
                null, address);
        firePropertyChangedEvent(event);
    }

    private void updateSyncPageStartAddress(BigInteger address) {

        if (!fIsCreated)
            return;

        if (isMemoryBlockBaseAddressChanged())
            return;

        PropertyChangeEvent event = new PropertyChangeEvent(this,
                IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS, null, address);
        firePropertyChangedEvent(event);
    }

    /**
     * Returns the color provider for this rendering's memory block or
     * <code>null</code> if none.
     * <p>
     * By default a color provider is obtained by asking this rendering's
     * memory block for its {@link IColorProvider} adapter. When the color
     * provider is queried for color information, it is provided with a
     * {@link MemoryRenderingElement} as an argument. 
     * </p>
     * @return the color provider for this rendering's memory block,
     *  or <code>null</code>
     */
    protected IColorProvider getColorProviderAdapter() {
        return (IColorProvider) getMemoryBlock().getAdapter(IColorProvider.class);
    }

    /**
     * Returns the label provider for this rendering's memory block or
     * <code>null</code> if none.
     * <p>
     * By default a label provider is obtained by asking this rendering's
     * memory block for its {@link ILabelProvider} adapter. When the label
     * provider is queried for label information, it is provided with a
     * {@link MemoryRenderingElement} as an argument. 
     * </p>
     * @return the label provider for this rendering's memory block,
     *  or <code>null</code>
     */
    protected ILabelProvider getLabelProviderAdapter() {
        return (ILabelProvider) getMemoryBlock().getAdapter(ILabelProvider.class);
    }

    /**
     * Returns the font provider for this rendering's memory block or
     * <code>null</code> if none.
     * <p>
     * By default a font provider is obtained by asking this rendering's
     * memory block for its {@link IFontProvider} adapter. When the font
     * provider is queried for font information, it is provided with a
     * {@link MemoryRenderingElement} as an argument. 
     * </p>
     * @return the font provider for this rendering's memory block,
     *  or <code>null</code>
     */
    protected IFontProvider getFontProviderAdapter() {
        return (IFontProvider) getMemoryBlock().getAdapter(IFontProvider.class);
    }

    /**
     * Returns the table presentation for this rendering's memory block or
     * <code>null</code> if none.
     * <p>
     * By default a table presentation is obtained by asking this rendering's
     * memory block for its {@link IMemoryBlockTablePresentation} adapter.
     * </p>
     * @return the table presentation for this rendering's memory block,
     *  or <code>null</code>
     */
    protected IMemoryBlockTablePresentation getTablePresentationAdapter() {
        return (IMemoryBlockTablePresentation) getMemoryBlock().getAdapter(IMemoryBlockTablePresentation.class);
    }

    /**
     * Setup the viewer so it supports hovers to show the offset of each field
     */
    private void createToolTip() {

        fToolTipShell = new Shell(DebugUIPlugin.getShell(), SWT.ON_TOP | SWT.RESIZE);
        GridLayout gridLayout = new GridLayout();
        gridLayout.numColumns = 1;
        gridLayout.marginWidth = 2;
        gridLayout.marginHeight = 0;
        fToolTipShell.setLayout(gridLayout);
        fToolTipShell.setBackground(fTableViewer.getTable().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));

        final Control toolTipControl = createToolTipControl(fToolTipShell);

        if (toolTipControl == null) {
            // if client decide not to use tooltip support
            fToolTipShell.dispose();
            return;
        }

        MouseTrackAdapter listener = new MouseTrackAdapter() {

            private TableItem fTooltipItem = null;
            private int fCol = -1;

            public void mouseExit(MouseEvent e) {

                if (!fToolTipShell.isDisposed())
                    fToolTipShell.setVisible(false);
                fTooltipItem = null;
            }

            public void mouseHover(MouseEvent e) {

                Point hoverPoint = new Point(e.x, e.y);
                Control control = null;

                if (e.widget instanceof Control)
                    control = (Control) e.widget;

                if (control == null)
                    return;

                hoverPoint = control.toDisplay(hoverPoint);
                TableItem item = getItem(hoverPoint);
                int column = getColumn(hoverPoint);

                //Only if there is a change in hover
                if (this.fTooltipItem != item || fCol != column) {

                    //Keep Track of the latest hover
                    fTooltipItem = item;
                    fCol = column;

                    if (item != null) {
                        toolTipAboutToShow(toolTipControl, fTooltipItem, column);

                        //Setting location of the tooltip
                        Rectangle shellBounds = fToolTipShell.getBounds();
                        shellBounds.x = hoverPoint.x;
                        shellBounds.y = hoverPoint.y + item.getBounds(0).height;

                        fToolTipShell.setBounds(shellBounds);
                        fToolTipShell.pack();

                        fToolTipShell.setVisible(true);
                    } else {
                        fToolTipShell.setVisible(false);
                    }
                }
            }
        };

        fTableViewer.getTable().addMouseTrackListener(listener);
        fTableViewer.getCursor().addMouseTrackListener(listener);
    }

    /**
     * Creates the control used to display tool tips for cells in this table. By default
     * a label is used to display the address of the cell. Clients may override this
     * method to create custom tooltip controls.
     * <p>
     * Also see the methods <code>getToolTipText(...)</code> and 
     * <code>toolTipAboutToShow(...)</code>.
     * </p>
     * @param composite parent for the tooltip control
     * @return the tooltip control to be displayed
     * @since 3.2
     */
    protected Control createToolTipControl(Composite composite) {
        Control fToolTipLabel = new Label(composite, SWT.NONE);
        fToolTipLabel.setForeground(fTableViewer.getTable().getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
        fToolTipLabel.setBackground(fTableViewer.getTable().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
        fToolTipLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_CENTER));
        return fToolTipLabel;
    }

    /**
     * Bug with table widget,BUG 113015, the widget is not able to return the correct
     * table item if SWT.FULL_SELECTION is not on when the table is created.
     * Created the following function to work around the problem.
     * We can remove this method when the bug is fixed.
     * @param point the given {@link Point} to find the {@link TableItem} for
     * @return the table item where the point is located, return null if the item cannot be located.
     */
    private TableItem getItem(Point point) {
        TableItem[] items = fTableViewer.getTable().getItems();
        for (int i = 0; i < items.length; i++) {
            TableItem item = items[i];
            if (item.getData() != null) {
                Point start = new Point(item.getBounds(0).x, item.getBounds(0).y);
                start = fTableViewer.getTable().toDisplay(start);
                Point end = new Point(start.x + item.getBounds(0).width, start.y + item.getBounds(0).height);

                if (start.y < point.y && point.y < end.y)
                    return item;
            }
        }
        return null;
    }

    /**
     * Method for figuring out which column the point is located.
     * @param point the {@link Point} the get the column number for
     * @return the column index where the point is located, return -1 if column is not found.
     */
    private int getColumn(Point point) {
        int colCnt = fTableViewer.getTable().getColumnCount();

        TableItem item = null;
        for (int i = 0; i < fTableViewer.getTable().getItemCount(); i++) {
            item = fTableViewer.getTable().getItem(i);
            if (item.getData() != null)
                break;
        }

        if (item != null) {
            for (int i = 0; i < colCnt; i++) {
                Point start = new Point(item.getBounds(i).x, item.getBounds(i).y);
                start = fTableViewer.getTable().toDisplay(start);
                Point end = new Point(start.x + item.getBounds(i).width, start.y + item.getBounds(i).height);

                if (start.x < point.x && end.x > point.x)
                    return i;
            }
        }
        return -1;
    }

    /**
     * Called when the tool tip is about to show in this rendering.
     * Clients who overrides <code>createTooltipControl</code> may need to
     * also override this method to ensure that the tooltip shows up properly
     * in their customized control.
     * <p>
     * By default a text tooltip is displayed, and the contents for the tooltip
     * are generated by the <code>getToolTipText(...)</code> method.
     * </p>
     * @param toolTipControl - the control for displaying the tooltip
     * @param item - the table item where the mouse is pointing.
     * @param col - the column at which the mouse is pointing.
     * @since 3.2
     */
    protected void toolTipAboutToShow(Control toolTipControl, TableItem item, int col) {
        if (toolTipControl instanceof Label) {
            Object address = fTableViewer.getKey(fTableViewer.getTable().indexOf(item), col);
            if (address != null && address instanceof BigInteger) {
                Object data = item.getData();
                if (data instanceof MemorySegment) {
                    MemorySegment line = (MemorySegment) data;

                    if (col > 0) {
                        int start = (col - 1) * getBytesPerColumn();
                        int end = start + getBytesPerColumn();
                        MemoryByte[] bytes = line.getBytes(start, end);

                        String str = getToolTipText((BigInteger) address, bytes);

                        if (str != null)
                            ((Label) toolTipControl).setText(str);
                    } else {
                        String str = getToolTipText((BigInteger) address, new MemoryByte[] {});

                        if (str != null)
                            ((Label) toolTipControl).setText(str);
                    }
                }
            }
        }
    }

    /**
     * Returns the text to display in a tool tip at the specified address
     * for the specified bytes. By default the address of the bytes is displayed.
     * Subclasses may override.
     * 
     * @param address address of cell that tool tip is displayed for 
     * @param bytes the bytes in the cell
     * @return the tooltip text for the memory bytes located at the specified
     *         address
     * @since 3.2
     */
    protected String getToolTipText(BigInteger address, MemoryByte[] bytes) {
        StringBuffer buf = new StringBuffer("0x"); //$NON-NLS-1$
        buf.append(address.toString(16).toUpperCase());

        return buf.toString();
    }

    private void setColumnHeadings() {
        String[] columnLabels = new String[0];

        IMemoryBlockTablePresentation presentation = getTablePresentationAdapter();
        if (presentation != null) {
            columnLabels = presentation.getColumnLabels(getMemoryBlock(), getBytesPerLine(),
                    getBytesPerLine() / getBytesPerColumn());
        }

        // check that column labels returned are not null
        if (columnLabels == null)
            columnLabels = new String[0];

        int numByteColumns = fBytePerLine / fColumnSize;

        TableColumn[] columns = fTableViewer.getTable().getColumns();

        int j = 0;
        for (int i = 1; i < columns.length - 1; i++) {
            // if the number of column labels returned is correct
            // use supplied column labels
            if (columnLabels.length == numByteColumns) {
                columns[i].setText(columnLabels[j]);
                j++;
            } else {
                // otherwise, use default
                int addressableUnit = getAddressableUnitPerColumn();
                if (addressableUnit >= 4) {
                    columns[i].setText(Integer.toHexString(j * addressableUnit).toUpperCase() + " - " //$NON-NLS-1$
                            + Integer.toHexString(j * addressableUnit + addressableUnit - 1).toUpperCase());
                } else {
                    columns[i].setText(Integer.toHexString(j * addressableUnit).toUpperCase());
                }
                j++;
            }
        }
    }

    /**
     * 
     * Return this rendering's viewer
     * @return this rendering's viewer
     */
    public StructuredViewer getViewer() {
        return fTableViewer;
    }

    private boolean isMemoryBlockBaseAddressChanged() {
        try {
            BigInteger address = getMemoryBlockBaseAddress();
            BigInteger oldBaseAddress = fContentDescriptor.getContentBaseAddress();
            if (!oldBaseAddress.equals(address))
                return true;
        } catch (DebugException e) {
            // fail silently
        }
        return false;
    }

    /**
     * @param topVisibleAddress the address to get the description for
     */
    private void createContentDescriptor(final BigInteger topVisibleAddress) {
        fContentDescriptor = new TableRenderingContentDescriptor(AbstractAsyncTableRendering.this);
        fContentDescriptor.setPostBuffer(getPostBufferSize());
        fContentDescriptor.setPreBuffer(getPreBufferSize());
        fContentDescriptor.setLoadAddress(topVisibleAddress);
        try {
            fContentDescriptor.updateContentBaseAddress();

        } catch (DebugException e) {
            fError = true;
            showMessage(e.getMessage());
        }

        fContentDescriptor.setAddressableSize(getAddressableSize());

        try {
            int addressSize = 4;
            if (getMemoryBlock() instanceof IMemoryBlockExtension) {
                IMemoryBlockExtension extMb = (IMemoryBlockExtension) getMemoryBlock();
                addressSize = extMb.getAddressSize();

                if (addressSize <= 0) {
                    DebugUIPlugin.logErrorMessage("Invalid address Size: " + addressSize); //$NON-NLS-1$
                    addressSize = 4;
                }
                fContentDescriptor.setAddressSize(addressSize);
            }
            fContentDescriptor.setAddressSize(addressSize);
        } catch (DebugException e) {
            fError = true;
            showMessage(e.getMessage());
        } finally {
            if (fContentDescriptor.getAddressSize() <= 0)
                fContentDescriptor.setAddressSize(4);
        }
    }

    /**
     * Return the number of lines to be bufferred before the top visible line of the memory rendering
     * @return number of lines to be buffered before the top visible line in the memory rendering
     */
    private int getPreBufferSize() {
        if (fPreBufferSize < 0)
            getPreBufferSizeFromPreference();

        return fPreBufferSize;
    }

    /**
     * Returns the number of lines to be bufferred after the last visible line in the memory rendering
     * @return the number of lines to be bufferred after the last visible line in the memory rendering
     */
    private int getPostBufferSize() {
        if (fPostBufferSize < 0)
            getPostBufferSizeFromPreference();

        return fPostBufferSize;
    }

    private TableRenderingContentDescriptor getContentDescriptor() {
        return fContentDescriptor;
    }

    private void createGoToAddressComposite(Composite parent) {
        fGoToAddressComposite = new GoToAddressComposite();
        fGoToAddressComposite.createControl(parent);
        Button button = fGoToAddressComposite.getButton(IDialogConstants.OK_ID);
        if (button != null) {
            button.addSelectionListener(new SelectionAdapter() {

                public void widgetSelected(SelectionEvent e) {
                    doGoToAddress();
                }
            });

            button = fGoToAddressComposite.getButton(IDialogConstants.CANCEL_ID);
            if (button != null) {
                button.addSelectionListener(new SelectionAdapter() {
                    public void widgetSelected(SelectionEvent e) {
                        hideGotoAddressComposite();
                    }
                });
            }
        }

        fGoToAddressComposite.getExpressionWidget().addSelectionListener(new SelectionAdapter() {
            public void widgetDefaultSelected(SelectionEvent e) {
                doGoToAddress();
            }
        });

        fGoToAddressComposite.getExpressionWidget().addKeyListener(new KeyAdapter() {

            public void keyPressed(KeyEvent e) {
                if (e.keyCode == SWT.ESC)
                    hideGotoAddressComposite();
                super.keyPressed(e);
            }
        });
    }

    private void showGoToAddressComposite() {

        String selectedStr = getSelectedAsString();
        Text text = fGoToAddressComposite.getExpressionWidget();
        text.setText(selectedStr);
        text.setSelection(0, text.getCharCount());

        double height = fGoToAddressComposite.getHeight();
        double canvasHeight = fSashForm.getParent().getClientArea().height;
        double tableHeight = canvasHeight - height;

        double tableWeight = (tableHeight / canvasHeight) * 100;
        double textWeight = (height / canvasHeight) * 100;
        fSashForm.setWeights(new int[] { (int) tableWeight, (int) textWeight });
        fSashForm.setMaximizedControl(null);

        fGoToAddressComposite.getExpressionWidget().setFocus();
    }

    private void hideGotoAddressComposite() {
        fSashForm.setMaximizedControl(fTableViewer.getControl());
        if (isActivated())
            fTableViewer.getControl().setFocus();
    }

    /**
     * 
     */
    private void doGoToAddress() {
        try {
            BigInteger address = fGoToAddressComposite.getGoToAddress(fContentDescriptor.getContentBaseAddress(),
                    getSelectedAddress());
            fGoToAddressAction.doGoToAddress(address.toString(16));
            hideGotoAddressComposite();
        } catch (DebugException e1) {
            MemoryViewUtil.openError(DebugUIMessages.GoToAddressAction_Go_to_address_failed,
                    DebugUIMessages.GoToAddressAction_Go_to_address_failed, e1);
        } catch (NumberFormatException e1) {
            MemoryViewUtil.openError(DebugUIMessages.GoToAddressAction_Go_to_address_failed,
                    DebugUIMessages.GoToAddressAction_Address_is_invalid, e1);
        }
    }

    public void activated() {
        super.activated();

        fActivated = true;
        IWorkbench workbench = PlatformUI.getWorkbench();
        ICommandService commandSupport = (ICommandService) workbench.getAdapter(ICommandService.class);
        IContextService contextSupport = (IContextService) workbench.getAdapter(IContextService.class);

        if (commandSupport != null && contextSupport != null) {
            fContext.add(contextSupport.activateContext(ID_ASYNC_TABLE_RENDERING_CONTEXT));
            Command gotoCommand = commandSupport.getCommand(ID_GO_TO_ADDRESS_COMMAND);

            if (fGoToAddressHandler == null) {
                fGoToAddressHandler = new AbstractHandler() {
                    public Object execute(ExecutionEvent event) throws ExecutionException {
                        if (fSashForm.getMaximizedControl() != null)
                            fGoToAddressAction.run();
                        else
                            hideGotoAddressComposite();
                        return null;
                    }
                };
            }
            gotoCommand.setHandler(fGoToAddressHandler);

            // The page up and page down actions can only be activated if the rendering
            // is in manual scrolling mode.  We are unable to determine the scrolling mode
            // until the content descriptor is created.  When the rendering is activated, the content
            // descriptor may not be created yet.  In that case, we cannot activate the shortcuts here.
            // We will activate the shortcut after the rendering is created.
            if (fContentDescriptor != null && !isDynamicLoad()) {
                activatePageActions();
            }
        }
    }

    private void activatePageActions() {
        IWorkbench workbench = PlatformUI.getWorkbench();
        ICommandService commandSupport = (ICommandService) workbench.getAdapter(ICommandService.class);
        if (commandSupport != null) {
            Command nextPage = commandSupport.getCommand(ID_NEXT_PAGE_COMMAND);
            if (fNextPageHandler == null) {
                fNextPageHandler = new AbstractHandler() {

                    public Object execute(ExecutionEvent arg0) throws ExecutionException {
                        fNextAction.run();
                        return null;
                    }
                };
            }
            nextPage.setHandler(fNextPageHandler);

            Command prevPage = commandSupport.getCommand(ID_PREV_PAGE_COMMAND);
            if (fPrevPageHandler == null) {
                fPrevPageHandler = new AbstractHandler() {

                    public Object execute(ExecutionEvent arg0) throws ExecutionException {
                        fPrevAction.run();
                        return null;
                    }
                };
            }
            prevPage.setHandler(fPrevPageHandler);
        }
    }

    public void deactivated() {

        fActivated = false;
        IWorkbench workbench = PlatformUI.getWorkbench();
        ICommandService commandSupport = (ICommandService) workbench.getAdapter(ICommandService.class);
        IContextService contextSupport = (IContextService) workbench.getAdapter(IContextService.class);

        if (commandSupport != null && contextSupport != null) {
            //    remove handler
            Command command = commandSupport.getCommand(ID_GO_TO_ADDRESS_COMMAND);
            command.setHandler(null);

            command = commandSupport.getCommand(ID_NEXT_PAGE_COMMAND);
            command.setHandler(null);

            command = commandSupport.getCommand(ID_PREV_PAGE_COMMAND);
            command.setHandler(null);

            if (fContext != null)
                contextSupport.deactivateContexts(fContext);
        }
        super.deactivated();
    }

    private boolean isActivated() {
        return fActivated;
    }

    /**
     * Returns text for the given memory bytes at the specified address for the specified
     * rendering type. This is called by the label provider for.
     * Subclasses must override.
     * 
     * @param renderingTypeId rendering type identifier
     * @param address address where the bytes belong to
     * @param data the bytes
     * @return a string to represent the memory. Cannot not return <code>null</code>.
     *    Returns a string to pad the cell if the memory cannot be converted
     *  successfully.
     */
    abstract public String getString(String renderingTypeId, BigInteger address, MemoryByte[] data);

    /**
     * Returns bytes for the given text corresponding to bytes at the given
     * address for the specified rendering type. This is called by the cell modifier
     * when modifying bytes in a memory block.
     * Subclasses must convert the string value to an array of bytes.  The bytes will
     * be passed to the debug adapter for memory block modification.
     * Returns <code>null</code> if the bytes cannot be formatted properly.
     * 
     * @param renderingTypeId rendering type identifier
     * @param address address the bytes begin at
     * @param currentValues current values of the data in bytes format
     * @param newValue the string to be converted to bytes
     * @return the bytes converted from a string
     */
    abstract public byte[] getBytes(String renderingTypeId, BigInteger address, MemoryByte[] currentValues,
            String newValue);
}