org.eclipse.debug.ui.memory.AbstractTableRendering.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.debug.ui.memory.AbstractTableRendering.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 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
 *******************************************************************************/

package org.eclipse.debug.ui.memory;

import java.math.BigInteger;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
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.IMemoryBlockConnection;
import org.eclipse.debug.internal.ui.memory.IPersistableDebugElement;
import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants;
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.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.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.TableRenderingCellModifier;
import org.eclipse.debug.internal.ui.views.memory.renderings.TableRenderingContentInput;
import org.eclipse.debug.internal.ui.views.memory.renderings.TableRenderingContentProvider;
import org.eclipse.debug.internal.ui.views.memory.renderings.TableRenderingLabelProvider;
import org.eclipse.debug.internal.ui.views.memory.renderings.TableRenderingLabelProviderEx;
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.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
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.TableViewer;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.TableCursor;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseAdapter;
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.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
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.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ScrollBar;
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.IWorkbenchActionConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PropertyDialogAction;
import org.eclipse.ui.model.IWorkbenchAdapter;
import org.eclipse.ui.part.PageBook;

/**
 * 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.1
 */
public abstract class AbstractTableRendering 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 = "selectedAddress"; //$NON-NLS-1$

    /**
     * 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 = "columnSize"; //$NON-NLS-1$

    /**
     * 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 = "topAddress"; //$NON-NLS-1$

    /**
     * Property identifier for the row size in a table rendering
     * This property is used for synchronization between renderings.
     * @since 3.2
     */
    public static final String PROPERTY_ROW_SIZE = "rowSize"; //$NON-NLS-1$

    private static final int BUFFER_THRESHOLD = 1; // threshold value
    private static final int BUFFER_START = 0; // flag to indicate asking for threshold at buffer start
    private static final int BUFFER_END = 1; // flat to indicate asking for threshold at buffer end

    private PageBook fPageBook;
    private TableViewer fTableViewer;
    private TextViewer fTextViewer;

    private int fBytePerLine; // number of bytes per line: 16
    private int fColumnSize; // number of bytes per column:  1,2,4,8
    private int fAddressableSize;

    private boolean fIsShowingErrorPage;

    private TableRenderingContentProvider fContentProvider;
    private BigInteger fSelectedAddress;
    private TableRenderingContentInput fContentInput;
    private TableRenderingCellModifier fCellModifier;
    private boolean fIsCreated;
    private CellEditor[] fEditors;
    private String fLabel;
    private TableCursor fTableCursor;
    private boolean fIsDisposed;
    private TraverseListener fCursorTraverseListener;
    private KeyAdapter fCursorKeyAdapter;
    private BigInteger fTopRowAddress;

    private CopyTableRenderingToClipboardAction fCopyToClipboardAction;
    private GoToAddressAction fGoToAddressAction;
    private ResetToBaseAddressAction fResetMemoryBlockAction;
    private PrintTableRenderingAction fPrintViewTabAction;
    private ReformatAction fReformatAction;
    private ToggleAddressColumnAction fToggleAddressColumnAction;
    private EventHandleLock fEvtHandleLock = new EventHandleLock();
    private TableEditor fCursorEditor;
    private FocusAdapter fEditorFocusListener;
    private MouseAdapter fCursorMouseListener;
    private KeyAdapter fEditorKeyListener;
    private SelectionAdapter fCursorSelectionListener;
    private IWorkbenchAdapter fWorkbenchAdapter;
    private IMemoryBlockConnection fConnection;

    private boolean fIsShowAddressColumn = true;
    private SelectionAdapter fScrollbarSelectionListener;

    private PropertyDialogAction fPropertiesAction;

    private int fPageSize;
    private NextPageAction fNextAction;
    private PrevPageAction fPrevAction;

    private Shell fToolTipShell;
    private FormatTableRenderingAction fFormatRenderingAction;

    private IMenuListener fMenuListener;

    private int fPreBuffer;
    private int fPostBuffer;

    private class EventHandleLock {
        Object fOwner;

        public boolean acquireLock(Object client) {
            if (fOwner == null) {
                fOwner = client;
                return true;
            }
            return false;
        }

        public boolean releaseLock(Object client) {
            if (fOwner == client) {
                fOwner = null;
                return true;
            }
            return false;
        }

    }

    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;
            resizeColumnsToPreferredSize();
            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 = fContentInput.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 = fContentInput.getLoadAddress();
            address = address.subtract(BigInteger.valueOf(getPageSizeInUnits()));
            handlePageStartAddressChanged(address);
        }
    }

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

    /* (non-Javadoc)
     * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
     */
    public void propertyChange(PropertyChangeEvent event) {
        // 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;
        }

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

        Object evtSrc = event.getSource();

        if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE)
                || event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PRE_BUFFER_SIZE)
                || event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_POST_BUFFER_SIZE)) {
            // always update page size, only refresh if the table is visible
            getPageSizeFromPreference();
        }

        // do not handle event if the rendering is displaying an error
        if (isDisplayingError())
            return;

        // do not handle property change event if the rendering is not visible
        if (!isVisible())
            return;

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

        if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PAGE_SIZE)) {
            if (!isDynamicLoad()) {
                // only refresh if in non-autoload mode
                refresh();
            }
            return;
        }

        if (event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_PRE_BUFFER_SIZE)
                || event.getProperty().equals(IDebugPreferenceConstants.PREF_TABLE_RENDERING_POST_BUFFER_SIZE)) {
            if (isDynamicLoad()) {
                // only refresh if in non-autoload mode
                refresh();
            }
            return;
        }

        if (evtSrc == this)
            return;

        if (!(evtSrc instanceof IMemoryRendering))
            return;

        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(AbstractTableRendering.PROPERTY_SELECTED_ADDRESS) && value instanceof BigInteger) {
            selectedAddressChanged((BigInteger) value);
        } else if (propertyName.equals(AbstractTableRendering.PROPERTY_COL_SIZE) && value instanceof Integer) {
            columnSizeChanged(((Integer) value).intValue());
        } else if (propertyName.equals(AbstractTableRendering.PROPERTY_ROW_SIZE) && value instanceof Integer) {
            rowSizeChanged(((Integer) value).intValue());
        } else if (propertyName.equals(AbstractTableRendering.PROPERTY_TOP_ADDRESS)
                && value instanceof BigInteger) {
            if (needMoreLines()) {
                if (isDynamicLoad())
                    reloadTable(getTopVisibleAddress(), false);
            }
            topVisibleAddressChanged((BigInteger) value, false);
        } else if (propertyName.equals(IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS)
                && value instanceof BigInteger) {
            handlePageStartAddressChanged((BigInteger) value);
        }
    }

    private void handleDyanicLoadChanged() {

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

        updateDynamicLoadProperty();
        if (isDynamicLoad()) {
            refresh();
        } else {
            BigInteger pageStart = (BigInteger) getSynchronizedProperty(
                    IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS);
            if (pageStart == null)
                pageStart = fTopRowAddress;
            handlePageStartAddressChanged(pageStart);
        }
    }

    private void updateDynamicLoadProperty() {

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

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

            if (!fIsDisposed) {
                if (isDynamicLoad()) {
                    fContentInput.setPostBuffer(20);
                    fContentInput.setPreBuffer(20);
                    fContentInput.setNumLines(getNumberOfVisibleLines());

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

    /**
     * Handle top visible address change event from synchronizer
     * @param address the address
     * @param force if the notification should be forced
     */
    private void topVisibleAddressChanged(final BigInteger address, boolean force) {
        // do not handle event if rendering is not visible
        // continue to handle event if caller decides to force the rendering
        // to move to the top visible address even when the rendering
        // is not visible
        if (!isVisible() && !force)
            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 (isBaseAddressChanged())
            return;

        if (!address.equals(fTopRowAddress)) {
            fTopRowAddress = address;
            updateSyncTopAddress();
            if (getMemoryBlock() instanceof IMemoryBlockExtension) {
                handleTopAddressChangedforExtended(address);
            } else {
                handleTopAddressChangedForSimple(address);
            }
        }
    }

    /**
     * @param address the address
     */
    private void handleTopAddressChangedForSimple(final BigInteger address) {
        // IMemoryBlock support
        int index = findAddressIndex(address);
        Table table = fTableViewer.getTable();
        if (index >= 0) {
            setTopIndex(table, index);
        }

        if (isAddressVisible(fSelectedAddress))
            fTableCursor.setVisible(true);
        else
            fTableCursor.setVisible(false);

    }

    /**
     * @param address the address
     */
    private void handleTopAddressChangedforExtended(final BigInteger address) {

        Object evtLockClient = new Object();
        try {
            if (!fEvtHandleLock.acquireLock(evtLockClient))
                return;

            if (!isAddressOutOfRange(address)) {
                Table table = fTableViewer.getTable();
                int index = findAddressIndex(address);
                int startThreshold = getBufferThreshold(BUFFER_START);
                int endThrreshold = getBufferThreshold(BUFFER_END);
                if (index >= startThreshold
                        && table.getItemCount() - (index + getNumberOfVisibleLines()) >= endThrreshold) {
                    // update cursor position
                    setTopIndex(table, index);
                } else {
                    int numInBuffer = table.getItemCount();
                    if (index < getBufferThreshold(BUFFER_START)) {
                        if (isAtTopLimit()) {
                            setTopIndex(table, index);
                        } else {
                            if (isDynamicLoad() && getBufferThreshold(BUFFER_START) > 0)
                                reloadTable(address, false);
                            else
                                setTopIndex(table, index);
                        }
                    } else if ((numInBuffer - (index + getNumberOfVisibleLines())) <= getBufferThreshold(
                            BUFFER_END)) {
                        if (!isAtBottomLimit() && isDynamicLoad() && getBufferThreshold(BUFFER_END) > 0)
                            reloadTable(address, false);
                        else
                            setTopIndex(table, index);
                    }
                }
            } else {
                // approaching limit, reload table
                reloadTable(address, false);
            }

            if (isAddressVisible(fSelectedAddress))
                fTableCursor.setVisible(true);
            else
                fTableCursor.setVisible(false);
        } finally {
            fEvtHandleLock.releaseLock(evtLockClient);
        }
    }

    /**
     * @param value the new value
     */
    private void selectedAddressChanged(BigInteger value) {

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

        try {
            // do not handle event if the event is out of range and the 
            // rendering is in non-dynamic-load mode, otherwise, will
            // cause rendering to continue to scroll when it shouldn't
            if (isDynamicLoad())
                goToAddress(value);
            else if (!isAddressOutOfRange(value))
                goToAddress(value);
        } catch (DebugException e) {
            // do nothing
        }
    }

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

        if (fContentInput == null)
            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 (isBaseAddressChanged())
            return;

        if (fContentProvider.getBufferTopAddress().equals(address))
            return;

        BigInteger start = fContentInput.getStartAddress();
        BigInteger end = fContentInput.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()));
        }

        fContentInput.setLoadAddress(address);
        refresh();
        updateSyncPageStartAddress();
        setTopIndex(fTableViewer.getTable(), 0);
        fTopRowAddress = address;
        updateSyncTopAddress();

        BigInteger selectedAddress = (BigInteger) getSynchronizedProperty(
                AbstractTableRendering.PROPERTY_SELECTED_ADDRESS);
        if (selectedAddress != null) {
            fSelectedAddress = selectedAddress;
            if (!isAddressOutOfRange(fSelectedAddress)) {
                setCursorAtAddress(fSelectedAddress);
                fTableCursor.setVisible(true);
            } else {
                fTableCursor.setVisible(false);
            }
        }
    }

    /* (non-Javadoc)
     * @see org.eclipse.debug.ui.memory.IMemoryRendering#createControl(org.eclipse.swt.widgets.Composite)
     */
    public Control createControl(Composite parent) {

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

        fTableViewer.getTable().redraw();
        createToolTip();

        return fPageBook;
    }

    /**
     * Create the table viewer and other support controls
     * for this rendering.
     * 
     * @param parent parent composite
     */
    private void createTableViewer(Composite parent) {

        fTableViewer = new TableViewer(parent,
                SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.HIDE_SELECTION | SWT.BORDER);

        TableRenderingLabelProvider labelProvider;
        if (hasCustomizedDecorations())
            labelProvider = new TableRenderingLabelProviderEx(this);
        else
            labelProvider = new TableRenderingLabelProvider(this);

        fTableViewer.setLabelProvider(labelProvider);

        fContentProvider = new TableRenderingContentProvider();
        fContentProvider.setDynamicLoad(DebugUIPlugin.getDefault().getPreferenceStore()
                .getBoolean(IDebugPreferenceConstants.PREF_DYNAMIC_LOAD_MEM));

        fTableViewer.setContentProvider(fContentProvider);
        fContentProvider.setViewer(fTableViewer);

        ScrollBar scroll = ((Table) fTableViewer.getControl()).getVerticalBar();
        scroll.setMinimum(-100);
        scroll.setMaximum(200);

        fTableViewer.getTable().setHeaderVisible(true);
        fTableViewer.getTable().setLinesVisible(true);

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

        }
        if (getAddressableSize() < 1)
            fAddressableSize = 1;

        // set up initial format
        setupInitialFormat();

        // set up selected address      
        setupSelectedAddress();

        // figure out top visible address
        BigInteger topVisibleAddress = getInitialTopVisibleAddress();

        getPageSizeFromPreference();

        if (isDynamicLoad()) {
            int numLines = getNumberOfVisibleLines();
            if (numLines <= 0) {
                // add listener to reload when we know the number of lines to load
                fTableViewer.getTable().addPaintListener(new PaintListener() {
                    public void paintControl(PaintEvent e) {
                        fTableViewer.getTable().removePaintListener(this);
                        fContentInput.setNumLines(getNumberOfVisibleLines());
                        reloadTable(fContentInput.getLoadAddress(), false);
                        resizeColumnsToPreferredSize();
                        setCursorAtAddress(fSelectedAddress);
                        fTableCursor.setVisible(true);
                    }
                });
            }
            fContentInput = new TableRenderingContentInput(this, fPreBuffer, fPostBuffer, topVisibleAddress,
                    numLines, false, null);
        } else {
            BigInteger addressToLoad = topVisibleAddress;

            // check synchronization service to see if we need to sync with another rendering
            Object obj = getSynchronizedProperty(IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS);
            if (obj != null && obj instanceof BigInteger) {
                addressToLoad = (BigInteger) obj;
            }
            fContentInput = new TableRenderingContentInput(this, 0, 0, addressToLoad, fPageSize, false, null);
        }

        fTableViewer.setInput(fContentInput);

        // set up cell modifier
        fCellModifier = new TableRenderingCellModifier(this);
        fTableViewer.setCellModifier(fCellModifier);

        // SET UP FONT      
        // set to a non-proportional font
        fTableViewer.getTable().setFont(JFaceResources.getFont(IInternalDebugUIConstants.FONT_NAME));
        if (!(getMemoryBlock() instanceof IMemoryBlockExtension)) {
            // If not extended memory block, do not create any buffer
            // no scrolling
            fContentInput.setPreBuffer(0);
            fContentInput.setPostBuffer(0);
        }

        // set up table cursor
        createCursor(fTableViewer.getTable(), fSelectedAddress);
        fTableViewer.getTable().addMouseListener(new MouseAdapter() {
            public void mouseDown(MouseEvent e) {
                handleTableMouseEvent(e);
            }
        });

        // create pop up menu for the rendering
        createActions();
        createPopupMenu(fTableViewer.getControl());
        createPopupMenu(fTableCursor);

        fMenuListener = new IMenuListener() {
            public void menuAboutToShow(IMenuManager manager) {
                fillContextMenu(manager);
                manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
            }
        };
        getPopupMenuManager().addMenuListener(fMenuListener);

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

        //synchronize
        addRenderingToSyncService();
        synchronize();

        fTopRowAddress = getTopVisibleAddress();
        // Need to resize column after content is filled in
        // Pack function does not work unless content is not filled in
        // since the table is not able to compute the preferred size.
        resizeColumnsToPreferredSize();
        try {
            if (getMemoryBlock() instanceof IMemoryBlockExtension) {
                if (((IMemoryBlockExtension) getMemoryBlock()).getBigBaseAddress() == null) {
                    DebugException e = new DebugException(
                            DebugUIPlugin.newErrorStatus(DebugUIMessages.AbstractTableRendering_1, null));
                    displayError(e);
                }
            }
        } catch (DebugException e1) {
            displayError(e1);
        }

        // add font change listener and update font when the font has been changed
        JFaceResources.getFontRegistry().addListener(this);
        fScrollbarSelectionListener = new SelectionAdapter() {

            public void widgetSelected(SelectionEvent event) {
                handleScrollBarSelection();

            }
        };
        scroll.addSelectionListener(fScrollbarSelectionListener);
        DebugUIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this);
    }

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

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

    private BigInteger getInitialTopVisibleAddress() {
        BigInteger topVisibleAddress = (BigInteger) getSynchronizedProperty(
                AbstractTableRendering.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 setupSelectedAddress() {
        // figure out selected address 
        BigInteger selectedAddress = (BigInteger) getSynchronizedProperty(
                AbstractTableRendering.PROPERTY_SELECTED_ADDRESS);
        if (selectedAddress == null) {
            if (getMemoryBlock() instanceof IMemoryBlockExtension) {
                try {
                    selectedAddress = ((IMemoryBlockExtension) getMemoryBlock()).getBigBaseAddress();
                } catch (DebugException e1) {
                    selectedAddress = new BigInteger("0"); //$NON-NLS-1$
                }
                if (selectedAddress == null) {
                    selectedAddress = new BigInteger("0"); //$NON-NLS-1$
                }

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

    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 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 column size
        Integer colSize = (Integer) getSynchronizedProperty(AbstractTableRendering.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(AbstractTableRendering.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;
    }

    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;
    }

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

    private void createCursor(Table table, BigInteger address) {
        fTableCursor = new TableCursor(table, SWT.NONE);
        Display display = fTableCursor.getDisplay();

        // set up cursor color
        fTableCursor.setBackground(display.getSystemColor(SWT.COLOR_LIST_SELECTION));
        fTableCursor.setForeground(display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT));

        fTableCursor.setFont(JFaceResources.getFont(IInternalDebugUIConstants.FONT_NAME));
        fTableCursor.setVisible(true);

        fCursorKeyAdapter = new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                handleCursorKeyPressed(e);
            }
        };
        fTableCursor.addKeyListener(fCursorKeyAdapter);

        fCursorTraverseListener = new TraverseListener() {
            public void keyTraversed(TraverseEvent e) {
                handleCursorTraverseEvt(e);
            }
        };

        fTableCursor.addTraverseListener(fCursorTraverseListener);

        fCursorMouseListener = new MouseAdapter() {
            public void mouseDown(MouseEvent e) {
                handleCursorMouseEvent(e);
            }
        };
        fTableCursor.addMouseListener(fCursorMouseListener);

        // cursor may be disposed before disposed is called
        // remove listeners whenever the cursor is disposed
        fTableCursor.addDisposeListener(new DisposeListener() {
            public void widgetDisposed(DisposeEvent e) {
                if (fTableCursor == null)
                    return;
                fTableCursor.removeTraverseListener(fCursorTraverseListener);
                fTableCursor.removeKeyListener(fCursorKeyAdapter);
                fTableCursor.removeMouseListener(fCursorMouseListener);
                fTableCursor.removeSelectionListener(fCursorSelectionListener);
            }
        });

        fCursorSelectionListener = new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {

                if (!fEvtHandleLock.acquireLock(this))
                    return;

                handleCursorMoved();

                fEvtHandleLock.releaseLock(this);

            }
        };
        fTableCursor.addSelectionListener(fCursorSelectionListener);

        setCursorAtAddress(address);

        fCursorEditor = new TableEditor(fTableViewer.getTable());
    }

    private void handleCursorTraverseEvt(TraverseEvent e) {

        if (fTableCursor.getRow() == null)
            return;

        Table table = (Table) fTableCursor.getParent();
        int row = table.indexOf(fTableCursor.getRow());
        int col = fTableCursor.getColumn();
        if (col == getNumCol() && e.keyCode == SWT.ARROW_RIGHT) {
            if (row + 1 >= table.getItemCount()) {
                return;
            }

            row = row + 1;
            col = 0;
            fTableCursor.setSelection(row, col);
        }
        if (col <= 1 && e.keyCode == SWT.ARROW_LEFT) {
            if (row - 1 < 0) {
                return;
            }

            row = row - 1;
            col = getNumCol() + 1;
            fTableCursor.setSelection(row, col);
        }

        Object evtLockClient = new Object();
        if (!fEvtHandleLock.acquireLock(evtLockClient))
            return;

        handleCursorMoved();

        fEvtHandleLock.releaseLock(evtLockClient);

    }

    /**
     * Update selected address.
     * Load more memory if required.
     */
    private void handleCursorMoved() {
        if (fIsDisposed)
            return;

        BigInteger selectedAddress = getSelectedAddressFromCursor(fTableCursor);

        // when the cursor is moved, the selected address is changed
        if (selectedAddress != null && !selectedAddress.equals(fSelectedAddress)) {
            setSelectedAddress(selectedAddress);
            updateSyncSelectedAddress();
        }

        // now check to see if the cursor is approaching buffer limit
        TableItem item = fTableCursor.getRow();
        if (item == null)
            return;

        if (getMemoryBlock() instanceof IMemoryBlockExtension) {
            int row = fTableViewer.getTable().indexOf(item);

            if (row < getBufferThreshold(BUFFER_START)) {
                if (!isAtTopLimit() && getBufferThreshold(BUFFER_START) > 0) {
                    if (isDynamicLoad()) {
                        refresh();
                        setCursorAtAddress(fSelectedAddress);
                    }
                }
            } else if (row >= fTableViewer.getTable().getItemCount() - getBufferThreshold(BUFFER_END)) {
                if (!isAtBottomLimit() && getBufferThreshold(BUFFER_END) > 0) {
                    if (isDynamicLoad()) {
                        refresh();
                        setCursorAtAddress(fSelectedAddress);
                    }
                }
            }
        }

        // if the cursor has moved, the top index of the table may change
        // just update the synchronization service
        BigInteger address = getTopVisibleAddress();
        if (!address.equals(fTopRowAddress)) {
            fTopRowAddress = address;
            updateSyncTopAddress();
        }
    }

    private void handleCursorKeyPressed(KeyEvent event) {
        // allow edit if user hits return
        if (event.character == '\r' && event.getSource() instanceof TableCursor) {
            activateCellEditor(null);
            return;
        }

        if (MemoryViewUtil.isValidEditEvent(event.keyCode)) {
            // activate edit as soon as user types something at the cursor
            if (event.getSource() instanceof TableCursor) {
                String initialValue = String.valueOf(event.character);
                activateCellEditor(initialValue);
                return;
            }
        }
    }

    /**
     * Calculate selected address based on cursor's current position
     * @param cursor the cursor
     * @return the selected address
     */
    private BigInteger getSelectedAddressFromCursor(TableCursor cursor) {
        TableItem row = cursor.getRow();
        int col = cursor.getColumn();

        return getAddressFromTableItem(row, col);
    }

    private BigInteger getAddressFromTableItem(TableItem row, int col) {
        if (row == null)
            return null;

        // get row address
        String temp = ((TableRenderingLine) row.getData()).getAddress();
        BigInteger rowAddress = new BigInteger(temp, 16);

        int offset;
        if (col > 0) {
            //    get address offset
            int addressableUnit = getAddressableUnitPerColumn();
            offset = (col - 1) * addressableUnit;
        } else {
            offset = 0;
        }

        return rowAddress.add(BigInteger.valueOf(offset));
    }

    /**
     * Sets the cursor at the specified address
     * @param address the address
     * @return true if successful, false otherwise
     */
    private boolean setCursorAtAddress(BigInteger address) {
        if (fContentProvider.getBufferTopAddress() == null)
            return false;

        // selected address is out of range, simply return false
        if (address.compareTo(fContentProvider.getBufferTopAddress()) < 0)
            return false;

        // calculate selected row address
        int addressableUnit = getAddressableUnitPerLine();
        int numOfRows = address.subtract(fContentProvider.getBufferTopAddress()).intValue() / addressableUnit;
        BigInteger rowAddress = fContentProvider.getBufferTopAddress()
                .add(BigInteger.valueOf(numOfRows * addressableUnit));

        // try to find the row of the selected address
        int row = findAddressIndex(address);

        if (row == -1) {
            return false;
        }

        // calculate offset to the row address
        BigInteger offset = address.subtract(rowAddress);

        // locate column
        int colAddressableUnit = getAddressableUnitPerColumn();
        int col = ((offset.intValue() / colAddressableUnit) + 1);

        if (col == 0)
            col = 1;

        fTableCursor.setSelection(row, col);

        return true;
    }

    /**
     * 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) {

        // selected address gets changed as the cursor is moved
        // during the reformat.
        // Back up the address and restore it later.
        BigInteger selectedAddress = fSelectedAddress;

        // 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;

        Object evtLockClient = new Object();
        if (!fEvtHandleLock.acquireLock(evtLockClient))
            return false;

        // if the tab is already created and is being reformatted
        if (fIsCreated) {
            if (fTableViewer == null)
                return false;

            if (fTableViewer.getTable() == null)
                return false;

            // clean up old columns
            TableColumn[] oldColumns = fTableViewer.getTable().getColumns();

            for (int i = 0; i < oldColumns.length; i++) {
                oldColumns[i].dispose();
            }

            // clean up old cell editors
            CellEditor[] oldCellEditors = fTableViewer.getCellEditors();

            for (int i = 0; i < oldCellEditors.length; i++) {
                oldCellEditors[i].dispose();
            }
        }

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

        // create new byte columns
        TableColumn[] byteColumns = new TableColumn[bytesPerLine / columnSize];

        String[] columnLabels = new String[0];
        IMemoryBlockTablePresentation presentation = getTablePresentationAdapter();
        if (presentation != null) {
            columnLabels = presentation.getColumnLabels(getMemoryBlock(), bytesPerLine, getNumCol());
        }

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

        for (int i = 0; i < byteColumns.length; i++) {
            TableColumn column = new TableColumn(fTableViewer.getTable(), SWT.LEFT, i + 1);

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

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

        // +2 to include properties for address and navigation column
        String[] columnProperties = new String[byteColumns.length + 2];
        columnProperties[0] = TableRenderingLine.P_ADDRESS;

        int addressableUnit = columnSize / getAddressableSize();

        // 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$

        fTableViewer.setColumnProperties(columnProperties);

        Table table = fTableViewer.getTable();
        fEditors = new CellEditor[table.getColumnCount()];
        for (int i = 0; i < fEditors.length; i++) {
            fEditors[i] = new TextCellEditor(table);
        }

        // create and set cell editors
        fTableViewer.setCellEditors(fEditors);

        if (fIsCreated) {
            fTableViewer.refresh();
        }

        resizeColumnsToPreferredSize();
        updateSyncRowSize();
        updateSyncColSize();

        if (fIsCreated) {
            // for Linux GTK, this must happen after table viewer is refreshed
            int i = findAddressIndex(fTopRowAddress);

            if (i >= 0)
                setTopIndex(fTableViewer.getTable(), i);

            if (isAddressVisible(selectedAddress))
                // after refresh, make sure the cursor is at the correct position
                setCursorAtAddress(selectedAddress);
        }

        fEvtHandleLock.releaseLock(evtLockClient);

        return true;
    }

    /**
     * Create the error page for this rendering.
     * The error page is used to report any error resulted from
     * getting memory from a memory block.
     * @param parent the parent composite
     */
    private void createErrorPage(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);
        }
    }

    /**
     * Displays the content of the table viewer.
     */
    public void displayTable() {
        fIsShowingErrorPage = false;
        fPageBook.showPage(fTableViewer.getControl());
    }

    /**
     * Displays an error message for the given exception.
     * 
     * @param e exception to display 
     */
    public void displayError(DebugException e) {
        StyledText styleText = null;
        fIsShowingErrorPage = true;

        styleText = fTextViewer.getTextWidget();

        if (styleText != null)
            styleText.setText(DebugUIMessages.AbstractTableRendering_3 + e.getMessage());
        fPageBook.showPage(fTextViewer.getControl());

        // clear content cache if we need to display error
        fContentProvider.clearContentCache();
    }

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

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

    /**
     * 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);
    }

    /**
     * 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;
        }

        int numberOfLines = doGetNumberOfVisibleLines(table, height);

        if (numberOfLines <= 0) {
            return 0;
        }

        return numberOfLines;
    }

    /**
     * @param table the table
     * @param height the current height of the table
     * @return the number of visible lines in the table
     */
    private int doGetNumberOfVisibleLines(Table table, int height) {
        // 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;
        return numberOfLines;
    }

    private static void setTopIndex(Table table, int index) {
        table.setTopIndex(index);
    }

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

        if (syncService == null)
            return;

        syncService.addPropertyChangeListener(this, null);

        // we could be in a format error even though the error is not yet displayed
        // do not update sync property in this case
        if (!isDisplayingError()) {
            if (syncService.getSynchronizationProvider() == null)
                syncService.setSynchronizationProvider(this);

            // check if there is already synchronization info available
            Object selectedAddress = getSynchronizedProperty(AbstractTableRendering.PROPERTY_SELECTED_ADDRESS);
            Object rowSize = getSynchronizedProperty(AbstractTableRendering.PROPERTY_ROW_SIZE);
            Object colSize = getSynchronizedProperty(AbstractTableRendering.PROPERTY_COL_SIZE);
            Object topAddress = getSynchronizedProperty(AbstractTableRendering.PROPERTY_TOP_ADDRESS);

            if (!isDynamicLoad()) {
                Object pageStartAddress = getSynchronizedProperty(
                        IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS);
                if (pageStartAddress == null)
                    updateSyncPageStartAddress();
            }

            // if info is available, some other view tab has already been
            // created
            // do not overwrite info in the synchronizer if that's the case
            if (selectedAddress == null) {
                updateSyncSelectedAddress();
            }

            if (rowSize == null) {
                updateSyncRowSize();
            }

            if (colSize == null) {
                updateSyncColSize();
            }
            if (topAddress == null) {
                updateSyncTopAddress();
            }
        }
    }

    /**
     * Get properties from synchronizer and synchronize settings
     */
    private void synchronize() {
        if (!isDynamicLoad()) {
            BigInteger pageStart = (BigInteger) getSynchronizedProperty(
                    IInternalDebugUIConstants.PROPERTY_PAGE_START_ADDRESS);
            if (pageStart != null && fContentInput != null && fContentInput.getLoadAddress() != null) {
                if (!fContentInput.getLoadAddress().equals(pageStart))
                    handlePageStartAddressChanged(pageStart);
            } else if (pageStart != null) {
                handlePageStartAddressChanged(pageStart);
            }
        }

        Integer rowSize = (Integer) getSynchronizedProperty(AbstractTableRendering.PROPERTY_ROW_SIZE);
        Integer columnSize = (Integer) getSynchronizedProperty(AbstractTableRendering.PROPERTY_COL_SIZE);
        BigInteger selectedAddress = (BigInteger) getSynchronizedProperty(
                AbstractTableRendering.PROPERTY_SELECTED_ADDRESS);
        BigInteger topAddress = (BigInteger) getSynchronizedProperty(AbstractTableRendering.PROPERTY_TOP_ADDRESS);

        if (rowSize != null) {
            int rSize = rowSize.intValue();
            if (rSize > 0 && rSize != fBytePerLine) {
                rowSizeChanged(rSize);
            }
        }

        if (columnSize != null) {
            int colSize = columnSize.intValue();
            if (colSize > 0 && colSize != fColumnSize) {
                columnSizeChanged(colSize);
            }
        }
        if (topAddress != null) {
            if (!topAddress.equals(getTopVisibleAddress())) {
                if (selectedAddress != null) {
                    if (!fSelectedAddress.equals(selectedAddress)) {
                        selectedAddressChanged(selectedAddress);
                    }
                }
                topVisibleAddressChanged(topAddress, false);
            }
        }
        if (selectedAddress != null) {
            if (selectedAddress.compareTo(fSelectedAddress) != 0) {
                selectedAddressChanged(selectedAddress);
            }
        }
    }

    /**
     * Resize column to the preferred size.
     */
    public void resizeColumnsToPreferredSize() {
        // pack columns
        Table table = fTableViewer.getTable();
        TableColumn[] columns = table.getColumns();

        for (int i = 0; i < columns.length - 1; i++) {
            columns[i].pack();
        }

        if (!fIsShowAddressColumn) {
            columns[0].setWidth(0);
        }
    }

    /**
     * update selected address in synchronizer if update is true.
     */
    private void updateSyncSelectedAddress() {

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

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

        if (!fIsCreated)
            return;

        PropertyChangeEvent event = new PropertyChangeEvent(this, AbstractTableRendering.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, AbstractTableRendering.PROPERTY_ROW_SIZE, null,
                new Integer(fBytePerLine));
        firePropertyChangedEvent(event);
    }

    /**
     * update top visible address in synchronizer
     */
    private void updateSyncTopAddress() {

        if (!fIsCreated)
            return;

        PropertyChangeEvent event = new PropertyChangeEvent(this, AbstractTableRendering.PROPERTY_TOP_ADDRESS, null,
                fTopRowAddress);
        firePropertyChangedEvent(event);
    }

    private void updateSyncPageStartAddress() {

        if (!fIsCreated)
            return;

        if (isBaseAddressChanged())
            return;

        BigInteger pageStart;
        if (isDynamicLoad()) {
            // if dynamic loading, the page address should be the top
            // row address
            pageStart = fTopRowAddress;
        } else {
            // otherwise, the address is the buffer's start address
            pageStart = fContentProvider.getBufferTopAddress();
        }

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

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

        menu.add(new Separator("topMenu")); //$NON-NLS-1$
        menu.add(fResetMemoryBlockAction);
        menu.add(fGoToAddressAction);

        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());
        menu.add(fReformatAction);
        menu.add(fToggleAddressColumnAction);
        menu.add(new Separator());
        menu.add(fCopyToClipboardAction);
        menu.add(fPrintViewTabAction);
        if (fPropertiesAction != null) {
            menu.add(new Separator());
            menu.add(fPropertiesAction);
        }

    }

    /**
     * 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();
    }

    /**
     * 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;
    }

    /**
     * Updates labels of this rendering.
     */
    public void updateLabels() {
        // update tab labels
        updateRenderingLabel(true);

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

    /* Returns the label of this rendering.
     * 
     * @return label of this rendering
     */
    public String getLabel() {
        if (fLabel == null)
            fLabel = buildLabel(true);

        return fLabel;
    }

    /**
     * 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(boolean showAddress) {
        fLabel = buildLabel(showAddress);
        firePropertyChangedEvent(new PropertyChangeEvent(this, IBasicPropertyConstants.P_TEXT, null, fLabel));
    }

    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);
    }

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

        IMemoryBlockTablePresentation presentation = getTablePresentationAdapter();
        if (presentation != null) {
            columnLabels = presentation.getColumnLabels(getMemoryBlock(), fBytePerLine, getNumCol());
        }

        // 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
                if (fColumnSize >= 4) {
                    columns[i].setText(Integer.toHexString(j * fColumnSize).toUpperCase() + " - " //$NON-NLS-1$
                            + Integer.toHexString(j * fColumnSize + fColumnSize - 1).toUpperCase());
                } else {
                    columns[i].setText(Integer.toHexString(j * fColumnSize).toUpperCase());
                }
                j++;
            }
        }
    }

    /**
     * Refresh the table viewer with the current top visible address.
     * Update labels in the memory rendering.
     */
    public void refresh() {
        // refresh at start address of this memory block
        // address may change if expression is evaluated to a different value
        IMemoryBlock mem = getMemoryBlock();
        BigInteger address;

        if (mem instanceof IMemoryBlockExtension) {
            try {
                address = ((IMemoryBlockExtension) mem).getBigBaseAddress();
                if (address == null) {
                    DebugException e = new DebugException(
                            DebugUIPlugin.newErrorStatus(DebugUIMessages.AbstractTableRendering_10, null));
                    displayError(e);
                    return;
                }
                updateRenderingLabel(true);
                // base address has changed
                if (address.compareTo(fContentProvider.getContentBaseAddress()) != 0) {
                    // get to new address
                    setSelectedAddress(address);
                    updateSyncSelectedAddress();

                    reloadTable(address, true);

                    if (!isDynamicLoad()) {
                        updateSyncPageStartAddress();
                        setTopIndex(fTableViewer.getTable(), 0);
                    }

                    fTopRowAddress = getTopVisibleAddress();
                    updateSyncTopAddress();

                    fContentInput.updateContentBaseAddress();
                } else {
                    // reload at top of table
                    if (isDynamicLoad())
                        address = getTopVisibleAddress();
                    else
                        address = fContentInput.getLoadAddress();
                    reloadTable(address, true);
                }
            } catch (DebugException e) {
                displayError(e);
                return;
            }
        } else {
            address = BigInteger.valueOf(mem.getStartAddress());
            reloadTable(address, true);
        }
    }

    synchronized private void reloadTable(BigInteger topAddress, boolean updateDelta) {

        if (fTableViewer == null)
            return;

        try {
            Table table = (Table) fTableViewer.getControl();

            TableRenderingContentInput input;
            if (isDynamicLoad())
                input = new TableRenderingContentInput(this, fPreBuffer, fPostBuffer, topAddress,
                        getNumberOfVisibleLines(), updateDelta, null);
            else
                input = new TableRenderingContentInput(this, fContentInput.getPreBuffer(),
                        fContentInput.getPostBuffer(), topAddress, fPageSize, updateDelta, null);

            fContentInput = input;
            fTableViewer.setInput(fContentInput);

            if (isDynamicLoad()) {
                if (getMemoryBlock() instanceof IMemoryBlockExtension) {
                    int topIdx = findAddressIndex(topAddress);

                    if (topIdx != -1) {
                        setTopIndex(table, topIdx);
                    }
                }

                // cursor needs to be refreshed after reload
                if (isAddressVisible(fSelectedAddress))
                    setCursorAtAddress(fSelectedAddress);
            } else {
                if (!isAddressOutOfRange(fSelectedAddress)) {
                    setCursorAtAddress(fSelectedAddress);
                    fTableCursor.setVisible(true);
                } else {
                    fTableCursor.setVisible(false);
                }
            }
        } finally {
        }
    }

    private BigInteger getTopVisibleAddress() {

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

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

        if (topIndex < 1) {
            topIndex = 0;
        }

        if (table.getItemCount() > topIndex) {
            TableRenderingLine topItem = (TableRenderingLine) table.getItem(topIndex).getData();

            String calculatedAddress = null;
            if (topItem == null) {
                calculatedAddress = table.getItem(topIndex).getText();
            } else {
                calculatedAddress = topItem.getAddress();
            }

            BigInteger bigInt = new BigInteger(calculatedAddress, 16);
            return bigInt;
        }
        return BigInteger.valueOf(0);
    }

    private int findAddressIndex(BigInteger address) {
        TableItem items[] = fTableViewer.getTable().getItems();

        for (int i = 0; i < items.length; i++) {

            // Again, when the table resizes, the table may have a null item
            // at then end.  This is to handle that.
            if (items[i] != null) {
                TableRenderingLine line = (TableRenderingLine) items[i].getData();
                BigInteger lineAddress = new BigInteger(line.getAddress(), 16);
                int addressableUnit = getAddressableUnitPerLine();
                BigInteger endLineAddress = lineAddress.add(BigInteger.valueOf(addressableUnit));

                if (lineAddress.compareTo(address) <= 0 && endLineAddress.compareTo(address) > 0) {
                    return i;
                }
            }
        }

        return -1;
    }

    private static int getTopVisibleIndex(Table table) {
        int index = table.getTopIndex();

        TableItem item;
        try {
            item = table.getItem(index);
        } catch (IllegalArgumentException e) {
            return 0;
        }
        int cnt = table.getItemCount();

        while (item.getBounds(0).y < 0) {
            index++;
            if (index >= cnt) {
                index--;
                break;
            }
            item = table.getItem(index);
        }

        return index;
    }

    /**
     * Returns this rendering's table viewer.
     * 
     * @return the {@link TableViewer}
     */
    public TableViewer getTableViewer() {
        return fTableViewer;
    }

    /* (non-Javadoc)
     * @see org.eclipse.debug.ui.memory.IMemoryRendering#dispose()
     */
    public void dispose() {
        try {
            // prevent rendering from being disposed again
            if (fIsDisposed)
                return;

            fIsDisposed = true;

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

            ScrollBar scroll = ((Table) fTableViewer.getControl()).getVerticalBar();
            if (scroll != null && !scroll.isDisposed())
                scroll.removeSelectionListener(fScrollbarSelectionListener);

            if (!fTableCursor.isDisposed()) {
                fTableCursor.removeTraverseListener(fCursorTraverseListener);
                fTableCursor.removeKeyListener(fCursorKeyAdapter);
                fTableCursor.removeMouseListener(fCursorMouseListener);
            }

            fCursorEditor.dispose();

            fTextViewer = null;
            fTableViewer = null;
            fTableCursor = null;

            // clean up cell editors
            for (int i = 0; i < fEditors.length; i++) {
                fEditors[i].dispose();
            }

            // remove font change listener when the view tab is disposed
            JFaceResources.getFontRegistry().removeListener(this);

            // remove the view tab from the synchronizer
            IMemoryRenderingSynchronizationService syncService = getMemoryRenderingContainer()
                    .getMemoryRenderingSite().getSynchronizationService();
            if (syncService != null)
                syncService.removePropertyChangeListener(this);

            DebugUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this);

            fToolTipShell.dispose();

            if (getPopupMenuManager() != null) {
                getPopupMenuManager().removeMenuListener(fMenuListener);
            }

            super.dispose();

        } catch (Exception e) {
        }
    }

    private int getNumCol() {

        int bytesPerLine = getBytesPerLine();
        int columnSize = getBytesPerColumn();

        return bytesPerLine / columnSize;
    }

    /* (non-Javadoc)
     * @see org.eclipse.debug.ui.IMemoryViewTab#setFont(org.eclipse.swt.graphics.Font)
     */
    private void setFont(Font font) {
        int oldIdx = getTopVisibleIndex(fTableViewer.getTable());

        // BUG in table, if font is changed when table is not starting
        // from the top, causes table grid-line to be misaligned.
        setTopIndex(fTableViewer.getTable(), 0);

        // set font
        fTableViewer.getTable().setFont(font);
        fTableCursor.setFont(font);

        setTopIndex(fTableViewer.getTable(), oldIdx);

        resizeColumnsToPreferredSize();

        // update table cursor and force redraw
        setCursorAtAddress(fSelectedAddress);
    }

    /**
     * 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(BigInteger address) throws DebugException {
        Object evtLockClient = new Object();
        try {
            if (!fEvtHandleLock.acquireLock(evtLockClient))
                return;

            // if address is within the range, highlight         
            if (!isAddressOutOfRange(address)) {
                setSelectedAddress(address);
                updateSyncSelectedAddress();
                setCursorAtAddress(fSelectedAddress);

                // force the cursor to be shown
                if (!isAddressVisible(fSelectedAddress)) {
                    int i = findAddressIndex(fSelectedAddress);
                    fTableViewer.getTable().showItem(fTableViewer.getTable().getItem(i));
                }
            } else {
                // 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 = fContentInput.getStartAddress();
                BigInteger endAdd = fContentInput.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;
                }

                setSelectedAddress(address);
                updateSyncSelectedAddress();

                reloadTable(address, false);

                if (!isDynamicLoad()) {
                    updateSyncPageStartAddress();
                }

                // if the table is reloaded, the top address is changed in this case
                fTopRowAddress = address;
                updateSyncTopAddress();

                // set the cursor at the selected address after reload
                setCursorAtAddress(address);
            }
            fTableCursor.setVisible(true);
        } catch (DebugException e) {
            throw e;
        } finally {
            fEvtHandleLock.releaseLock(evtLockClient);
        }
    }

    /**
     * Check if address provided is out of buffered range
     * @param address the address
     * @return if address is out of buffered range
     */
    private boolean isAddressOutOfRange(BigInteger address) {
        return fContentProvider.isAddressOutOfRange(address);
    }

    /**
     * Check if address is visible
     * @param address the address
     * @return if the given address is visible
     */
    private boolean isAddressVisible(BigInteger address) {
        // if view tab is not yet created 
        // cursor should always be visible
        if (!fIsCreated)
            return true;

        BigInteger topVisible = getTopVisibleAddress();
        int addressableUnit = getAddressableUnitPerLine();
        BigInteger lastVisible = getTopVisibleAddress()
                .add(BigInteger.valueOf((getNumberOfVisibleLines() * addressableUnit) + addressableUnit));

        if (topVisible.compareTo(address) <= 0 && lastVisible.compareTo(address) > 0) {
            return true;
        }
        return false;
    }

    /**
     * Create actions for this rendering
     */
    protected void createActions() {
        fCopyToClipboardAction = new CopyTableRenderingToClipboardAction(this, fTableViewer);
        fGoToAddressAction = new GoToAddressAction(getMemoryRenderingContainer(), this);
        fResetMemoryBlockAction = new ResetToBaseAddressAction(this);
        fPrintViewTabAction = new PrintTableRenderingAction(this, fTableViewer);

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

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

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

    /**
     * Handle scrolling and reload table if necessary
     */
    private synchronized void handleScrollBarSelection() {
        Object evtLockClient = new Object();
        try {
            if (fIsDisposed)
                return;

            BigInteger address = getTopVisibleAddress();

            if (!fTopRowAddress.equals(address)) {
                fTopRowAddress = address;
                updateSyncTopAddress();
            }

            if (!fEvtHandleLock.acquireLock(evtLockClient))
                return;

            if (getMemoryBlock() instanceof IMemoryBlockExtension) {

                if (isDynamicLoad()) {
                    if (!isAddressOutOfRange(address)) {
                        Table table = fTableViewer.getTable();
                        int numInBuffer = table.getItemCount();
                        int index = findAddressIndex(address);
                        if (index < getBufferThreshold(BUFFER_START)) {
                            if (isAtTopLimit()) {
                                setTopIndex(table, index);
                            } else if (getBufferThreshold(BUFFER_START) > 0) {
                                reloadTable(address, false);
                            }
                        } else if (getBufferThreshold(BUFFER_END) != 0
                                && (numInBuffer - (index + getNumberOfVisibleLines())) <= getBufferThreshold(
                                        BUFFER_END)) {
                            if (!isAtBottomLimit() && getBufferThreshold(BUFFER_END) > 0)
                                reloadTable(address, false);
                        }
                    } else {
                        // approaching limit, reload table
                        reloadTable(address, false);
                    }
                }

                if (isAddressVisible(fSelectedAddress))
                    fTableCursor.setVisible(true);
                else
                    fTableCursor.setVisible(false);
            }
        } finally {
            fEvtHandleLock.releaseLock(evtLockClient);
        }
    }

    private boolean isAtTopLimit() {
        BigInteger startAddress = fContentInput.getStartAddress();
        startAddress = MemoryViewUtil.alignToBoundary(startAddress, getAddressableUnitPerLine());

        BigInteger startBufferAddress = fContentProvider.getBufferTopAddress();
        startBufferAddress = MemoryViewUtil.alignToBoundary(startBufferAddress, getAddressableUnitPerLine());

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

        return false;
    }

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

        BigInteger endBufferAddress = fContentProvider.getBufferEndAddress();
        endBufferAddress = MemoryViewUtil.alignToBoundary(endBufferAddress, getAddressableUnitPerLine());

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

        return false;
    }

    private boolean needMoreLines() {
        if (getMemoryBlock() instanceof IMemoryBlockExtension) {
            Table table = fTableViewer.getTable();
            TableItem firstItem = table.getItem(0);
            TableItem lastItem = table.getItem(table.getItemCount() - 1);

            if (firstItem == null || lastItem == null)
                return true;

            TableRenderingLine first = (TableRenderingLine) firstItem.getData();
            TableRenderingLine last = (TableRenderingLine) lastItem.getData();

            if (first == null || last == null) {
                // For some reason, the table does not return the correct number
                // of table items in table.getItemCount(), causing last to be null.
                // This check is to ensure that we don't get a null pointer exception.
                return true;
            }

            BigInteger startAddress = new BigInteger(first.getAddress(), 16);
            BigInteger lastAddress = new BigInteger(last.getAddress(), 16);
            int addressableUnit = getAddressableUnitPerLine();
            lastAddress = lastAddress.add(BigInteger.valueOf(addressableUnit));

            BigInteger topVisibleAddress = getTopVisibleAddress();
            long numVisibleLines = getNumberOfVisibleLines();
            long numOfBytes = numVisibleLines * addressableUnit;

            BigInteger lastVisibleAddrss = topVisibleAddress.add(BigInteger.valueOf(numOfBytes));

            // if there are only 3 lines left at the top, refresh
            BigInteger numTopLine = topVisibleAddress.subtract(startAddress)
                    .divide(BigInteger.valueOf(addressableUnit));
            if (numTopLine.compareTo(BigInteger.valueOf(getBufferThreshold(BUFFER_START))) <= 0
                    && (startAddress.compareTo(BigInteger.valueOf(0)) != 0)) {
                if (!isAtTopLimit() && getBufferThreshold(BUFFER_START) > 0)
                    return true;
            }

            // if there are only 3 lines left at the bottom, refresh
            BigInteger numBottomLine = lastAddress.subtract(lastVisibleAddrss)
                    .divide(BigInteger.valueOf(addressableUnit));
            if (numBottomLine.compareTo(BigInteger.valueOf(getBufferThreshold(BUFFER_END))) <= 0) {
                if (!isAtBottomLimit() && getBufferThreshold(BUFFER_END) > 0)
                    return true;
            }

            return false;
        }

        return false;
    }

    private void handleTableMouseEvent(MouseEvent e) {
        // figure out new cursor position based on here the mouse is pointing
        TableItem[] tableItems = fTableViewer.getTable().getItems();
        TableItem selectedRow = null;
        int colNum = -1;
        int numCol = fTableViewer.getColumnProperties().length;

        for (int j = 0; j < tableItems.length; j++) {
            TableItem item = tableItems[j];
            for (int i = 0; i < numCol; i++) {
                Rectangle bound = item.getBounds(i);
                if (bound.contains(e.x, e.y)) {
                    colNum = i;
                    selectedRow = item;
                    break;
                }
            }
        }

        // if column position cannot be determined, return
        if (colNum < 1)
            return;

        // handle user mouse click onto table
        // move cursor to new position
        if (selectedRow != null) {
            int row = fTableViewer.getTable().indexOf(selectedRow);
            fTableCursor.setVisible(true);
            fTableCursor.setSelection(row, colNum);

            // manually call this since we don't get an event when
            // the table cursor changes selection.
            handleCursorMoved();

            fTableCursor.setFocus();
        }
    }

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

        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                format(getBytesPerLine(), newColumnSize);
            }
        });
    }

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

        int bytesPerLine = newRowSize;
        int col = getBytesPerColumn();
        if (bytesPerLine < getBytesPerColumn())
            col = bytesPerLine;

        final int columnSize = col;
        final int rowSize = bytesPerLine;
        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                format(rowSize, columnSize);
            }
        });
    }

    private void handleCursorMouseEvent(MouseEvent e) {
        if (e.button == 1) {
            int col = fTableCursor.getColumn();
            if (col > 0 && col <= (getNumCol()))
                activateCellEditor(null);
        }
    }

    /**
     * Activate cell editor and pre-fill it with initial value.
     * If initialValue is null, use cell content as initial value
     * @param initialValue the initial value to edit 
     */
    private void activateCellEditor(String initialValue) {

        int col = fTableCursor.getColumn();
        int row = findAddressIndex(fSelectedAddress);

        if (row < 0)
            return;
        // do not allow user to edit address column
        if (col == 0 || col > getNumCol()) {
            return;
        }

        ICellModifier cellModifier = null;

        if (fTableViewer == null) {
            return;
        }
        cellModifier = fTableViewer.getCellModifier();

        TableItem tableItem = fTableViewer.getTable().getItem(row);

        Object element = tableItem.getData();
        Object property = fTableViewer.getColumnProperties()[col];
        Object value = cellModifier.getValue(element, (String) property);

        // The cell modifier canModify function always returns false if the edit action 
        // is not invoked from here.  This is to prevent data to be modified when
        // the table cursor loses focus from a cell.  By default, data will
        // be changed in a table when the cell loses focus.  This is to workaround
        // this default behavior and only change data when the cell editor
        // is activated.
        ((TableRenderingCellModifier) cellModifier).setEditActionInvoked(true);
        boolean canEdit = cellModifier.canModify(element, (String) property);
        ((TableRenderingCellModifier) cellModifier).setEditActionInvoked(false);

        if (!canEdit)
            return;

        // activate based on current cursor position
        TextCellEditor selectedEditor = (TextCellEditor) fTableViewer.getCellEditors()[col];

        if (fTableViewer != null && selectedEditor != null) {
            // The control that will be the editor must be a child of the Table
            Text text = (Text) selectedEditor.getControl();

            String cellValue = null;

            if (initialValue != null) {
                cellValue = initialValue;
            } else {
                cellValue = ((String) value);
            }

            text.setText(cellValue);

            fCursorEditor.horizontalAlignment = SWT.LEFT;
            fCursorEditor.grabHorizontal = true;

            // Open the text editor in selected column of the selected row.
            fCursorEditor.setEditor(text, tableItem, col);

            // Assign focus to the text control
            selectedEditor.setFocus();

            if (initialValue != null) {
                text.clearSelection();
            }

            text.setFont(JFaceResources.getFont(IInternalDebugUIConstants.FONT_NAME));

            // add listeners for the text control
            addListeners(text);

            // move cursor below text control
            fTableCursor.moveBelow(text);
        }
    }

    /**
     * @param text the {@link Text} widget to add the listeners to
     */
    private void addListeners(Text text) {
        fEditorFocusListener = new FocusAdapter() {
            public void focusLost(FocusEvent e) {
                handleTableEditorFocusLost(e);
            }
        };
        text.addFocusListener(fEditorFocusListener);

        fEditorKeyListener = new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                handleKeyEventInEditor(e);
            }
        };

        text.addKeyListener(fEditorKeyListener);
    }

    /**
     * @param text the {@link Text} widget to remove the listeners from
     */
    private void removeListeners(Text text) {

        text.removeFocusListener(fEditorFocusListener);
        text.removeKeyListener(fEditorKeyListener);
    }

    private void handleTableEditorFocusLost(FocusEvent event) {
        final FocusEvent e = event;

        Display.getDefault().syncExec(new Runnable() {

            public void run() {
                try {
                    int row = findAddressIndex(fSelectedAddress);
                    int col = fTableCursor.getColumn();

                    Text text = (Text) e.getSource();
                    removeListeners(text);

                    // get new value
                    String newValue = text.getText();

                    // modify memory at fRow and fCol
                    modifyValue(row, col, newValue);

                    // show cursor after modification is completed
                    setCursorAtAddress(fSelectedAddress);
                    fTableCursor.moveAbove(text);
                    fTableCursor.setVisible(false);
                    fTableCursor.setVisible(true);
                } catch (NumberFormatException e1) {
                    MemoryViewUtil.openError(DebugUIMessages.MemoryViewCellModifier_failure_title,
                            DebugUIMessages.MemoryViewCellModifier_data_is_invalid, null);
                }
            }
        });
    }

    /**
     * @param event the {@link KeyEvent}
     */
    private void handleKeyEventInEditor(KeyEvent event) {
        final KeyEvent e = event;
        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                Text text = (Text) e.getSource();
                int row = findAddressIndex(fSelectedAddress);
                int col = fTableCursor.getColumn();

                try {
                    switch (e.keyCode) {
                    case SWT.ARROW_UP:

                        // move text editor box up one row      
                        if (row - 1 < 0)
                            return;

                        // modify value for current cell
                        modifyValue(row, col, text.getText());

                        row--;

                        //   update cursor location and selection in table   
                        fTableCursor.setSelection(row, col);
                        handleCursorMoved();

                        // remove listeners when focus is lost
                        removeListeners(text);
                        activateCellEditor(null);
                        break;
                    case SWT.ARROW_DOWN:

                        // move text editor box down one row

                        if (row + 1 >= fTableViewer.getTable().getItemCount())
                            return;

                        // modify value for current cell
                        modifyValue(row, col, text.getText());

                        row++;

                        //   update cursor location and selection in table                        
                        fTableCursor.setSelection(row, col);
                        handleCursorMoved();

                        // remove traverse listener when focus is lost
                        removeListeners(text);
                        activateCellEditor(null);
                        break;
                    case 0:

                        // if user has entered the max number of characters allowed in a cell, move to next cell
                        // Extra changes will be used as initial value for the next cell
                        int numCharsPerByte = getNumCharsPerByte();
                        if (numCharsPerByte > 0) {
                            if (text.getText().length() > getBytesPerColumn() * numCharsPerByte) {
                                String newValue = text.getText();
                                text.setText(newValue.substring(0, getBytesPerColumn() * numCharsPerByte));

                                modifyValue(row, col, text.getText());

                                // if cursor is at the end of a line, move to next line
                                if (col >= getNumCol()) {
                                    col = 1;
                                    row++;
                                } else {
                                    // move to next column
                                    row++;
                                }

                                // update cursor position and selected address
                                fTableCursor.setSelection(row, col);
                                handleCursorMoved();

                                removeListeners(text);

                                // activate text editor at next cell
                                activateCellEditor(newValue.substring(getBytesPerColumn() * numCharsPerByte));
                            }
                        }
                        break;
                    case SWT.ESC:

                        // if user has pressed escape, do not commit the changes
                        // that's why "modifyValue" is not called
                        fTableCursor.setSelection(row, col);
                        handleCursorMoved();

                        removeListeners(text);

                        // cursor needs to have focus to remove focus from cell editor
                        fTableCursor.setFocus();
                        break;
                    default:
                        numCharsPerByte = getNumCharsPerByte();
                        if (numCharsPerByte > 0) {
                            if (text.getText().length() > getBytesPerColumn() * numCharsPerByte) {
                                String newValue = text.getText();
                                text.setText(newValue.substring(0, getBytesPerColumn() * numCharsPerByte));
                                modifyValue(row, col, text.getText());
                                // if cursor is at the end of a line, move to next line
                                if (col >= getNumCol()) {
                                    col = 1;
                                    row++;
                                } else {
                                    col++;
                                }

                                fTableCursor.setSelection(row, col);
                                handleCursorMoved();

                                removeListeners(text);

                                activateCellEditor(newValue.substring(getBytesPerColumn() * numCharsPerByte));
                            }
                        }
                        break;
                    }
                } catch (NumberFormatException e1) {
                    MemoryViewUtil.openError(DebugUIMessages.MemoryViewCellModifier_failure_title,
                            DebugUIMessages.MemoryViewCellModifier_data_is_invalid, null);

                    fTableCursor.setSelection(row, col);
                    handleCursorMoved();

                    removeListeners(text);
                }
            }
        });
    }

    /**
     * Modify value and send new value to debug adapter
     * @param row the row
     * @param col the column
     * @param newValue the new value
     * @throws NumberFormatException if the {@link ICellModifier} cannot convert the new value to a string - in cases where it needs to do so
     */
    private void modifyValue(int row, int col, String newValue) throws NumberFormatException {
        if (newValue.length() == 0) {
            // do not do anything if user has not entered anything
            return;
        }

        TableItem tableItem = fTableViewer.getTable().getItem(row);

        Object property = fTableViewer.getColumnProperties()[col];
        fTableViewer.getCellModifier().modify(tableItem, (String) property, newValue);
    }

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

        if (isVisible() == false) {
            // super should always be called
            super.becomesHidden();
            return;
        }

        super.becomesHidden();

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

        // once the view tab is disabled, all deltas information becomes invalid.
        // reset changed information and recompute if data has really changed when
        // user revisits the same tab.   
        fContentProvider.resetDeltas();

    }

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

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

        super.becomesVisible();

        boolean value = DebugUIPlugin.getDefault().getPreferenceStore()
                .getBoolean(IDebugPreferenceConstants.PREF_DYNAMIC_LOAD_MEM);
        if (value != isDynamicLoad())
            // this call will cause a reload
            handleDyanicLoadChanged();
        else
            refresh();

        synchronize();
        updateRenderingLabel(true);
    }

    /**
     * Resets this memory rendering.
     * The cursor will be moved to the base address of the memory block.
     * The table will be positioned to have the base address
     * at the top.
     * 
     * @deprecated use <code>resetRendering</code> to reset this rendering.
     */
    public void reset() {
        try {
            resetToBaseAddress();
        } catch (DebugException e) {
            MemoryViewUtil.openError(DebugUIMessages.AbstractTableRendering_12,
                    DebugUIMessages.AbstractTableRendering_13, e); //
        }
    }

    /**
     * Reset this rendering to the base address.  
     * The cursor will be moved to the base address of the memory block.
     * The table will be positioned to have the base address
     * at the top.
     * @throws DebugException is an exception occurs
     */
    private void resetToBaseAddress() throws DebugException {
        BigInteger baseAddress;

        if (getMemoryBlock() instanceof IMemoryBlockExtension) {
            baseAddress = ((IMemoryBlockExtension) getMemoryBlock()).getBigBaseAddress();
        } else {
            baseAddress = BigInteger.valueOf(getMemoryBlock().getStartAddress());
        }

        goToAddress(baseAddress);
        topVisibleAddressChanged(baseAddress, true);
    }

    /**
     * Returns the currently selected address in this rendering.
     * 
     * @return the currently selected address in this rendering
     */
    public BigInteger getSelectedAddress() {
        return fSelectedAddress;
    }

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

        if (isAddressOutOfRange(fSelectedAddress))
            return IInternalDebugCoreConstants.EMPTY_STRING;

        int col = fTableCursor.getColumn();
        TableItem rowItem = fTableCursor.getRow();
        int row = fTableViewer.getTable().indexOf(rowItem);

        if (col == 0) {
            return rowItem.getText(0);
        }

        // check precondition
        if (col > getBytesPerLine() / getBytesPerColumn()) {
            return IInternalDebugCoreConstants.EMPTY_STRING;
        }

        TableItem tableItem = getTableViewer().getTable().getItem(row);

        return tableItem.getText(col);
    }

    /**
     * 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 (isAddressOutOfRange(fSelectedAddress))
            return new MemoryByte[0];

        int col = fTableCursor.getColumn();
        TableItem rowItem = fTableCursor.getRow();

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

        Object data = rowItem.getData();
        if (data == null || !(data instanceof TableRenderingLine))
            return new MemoryByte[0];

        TableRenderingLine line = (TableRenderingLine) data;
        int offset = (col - 1) * (getAddressableUnitPerColumn() * getAddressableSize());
        int end = offset + (getAddressableUnitPerColumn() * getAddressableSize());

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

        System.arraycopy(bytes, 0, retBytes, 0, bytes.length);

        return retBytes;
    }

    /**
     * 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;
    }

    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++) {
                minHeight = Math.min(items[i].getBounds(0).height, minHeight);
            }

            return minHeight;

        }
        return table.getItemHeight();
    }

    /* (non-Javadoc)
     * @see org.eclipse.core.runtime.IAdaptable#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 == IMemoryBlockTablePresentation.class)
            return getTablePresentationAdapter();

        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 AbstractTableRendering.this.getLabel();
                    }

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

        if (adapter == IMemoryBlockConnection.class) {
            if (fConnection == null) {
                fConnection = new IMemoryBlockConnection() {
                    public void update() {
                        try {
                            fContentProvider.takeContentSnapshot();
                            if (getMemoryBlock() instanceof IMemoryBlockExtension) {
                                BigInteger address = ((IMemoryBlockExtension) getMemoryBlock()).getBigBaseAddress();
                                if (address.compareTo(fContentProvider.getContentBaseAddress()) != 0) {
                                    // get to new address
                                    setSelectedAddress(address);
                                    updateSyncSelectedAddress();
                                    fTopRowAddress = address;
                                    fContentInput.updateContentBaseAddress();
                                    fContentInput.setLoadAddress(address);
                                }
                                fContentProvider.loadContentForExtendedMemoryBlock();
                            } else
                                fContentProvider.loadContentForSimpleMemoryBlock();

                            // update UI asynchronously
                            Display display = DebugUIPlugin.getDefault().getWorkbench().getDisplay();
                            display.asyncExec(new Runnable() {
                                public void run() {
                                    updateLabels();

                                    if (getMemoryBlock() instanceof IMemoryBlockExtension) {
                                        int topIdx = findAddressIndex(fTopRowAddress);
                                        if (topIdx != -1) {
                                            setTopIndex(fTableViewer.getTable(), topIdx);
                                        }
                                    }

                                    // cursor needs to be refreshed after reload
                                    if (isAddressVisible(fSelectedAddress)) {
                                        setCursorAtAddress(fSelectedAddress);
                                        fTableCursor.setVisible(true);
                                        fTableCursor.redraw();
                                    } else {
                                        fTableCursor.setVisible(false);
                                    }

                                    if (!isDynamicLoad())
                                        updateSyncPageStartAddress();

                                    updateSyncTopAddress();
                                }
                            });
                        } catch (DebugException e) {
                            displayError(e);
                        }
                    }
                };
            }
            return fConnection;
        }

        return super.getAdapter(adapter);
    }

    private boolean hasCustomizedDecorations() {
        if (getFontProviderAdapter() == null && getColorProviderAdapter() == null
                && getLabelProviderAdapter() == null)
            return false;
        return true;
    }

    private boolean isBaseAddressChanged() {
        try {
            IMemoryBlock mb = getMemoryBlock();
            if (mb instanceof IMemoryBlockExtension) {
                BigInteger baseAddress = ((IMemoryBlockExtension) mb).getBigBaseAddress();
                if (baseAddress != null) {
                    if (!baseAddress.equals(fContentInput.getContentBaseAddress()))
                        return true;
                }
            }
        } catch (DebugException e1) {
            return false;
        }
        return false;
    }

    /**
     * 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);
    }

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

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

    private void setSelectedAddress(BigInteger address) {
        fSelectedAddress = address;
    }

    /**
     * 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);
        fTableCursor.addMouseTrackListener(listener);
    }

    /**
     * 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 {@link Point} to get the {@link TableItem} from
     * @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++) {
            Point start = new Point(items[i].getBounds(0).x, items[i].getBounds(0).y);
            start = fTableViewer.getTable().toDisplay(start);
            Point end = new Point(start.x + items[i].getBounds(0).width, start.y + items[i].getBounds(0).height);

            if (start.y < point.y && point.y < end.y)
                return items[i];
        }
        return null;
    }

    /**
     * Method for figuring out which column the point is located.
     * @param point the {@link Point} to et 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();
        if (fTableViewer.getTable().getItemCount() > 0) {
            TableItem item = fTableViewer.getTable().getItem(0);
            Point start, end;
            for (int i = 0; i < colCnt; i++) {
                start = new Point(item.getBounds(i).x, item.getBounds(i).y);
                start = fTableViewer.getTable().toDisplay(start);
                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;
    }

    /**
     * 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;
    }

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

    /**
     * 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) {
            BigInteger address = getAddressFromTableItem(item, col);
            if (address != null) {
                Object data = item.getData();
                if (data instanceof TableRenderingLine) {
                    TableRenderingLine line = (TableRenderingLine) data;

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

                        String str = getToolTipText(address, bytes);

                        if (str != null)
                            ((Label) toolTipControl).setText(str);
                    } else {
                        String str = getToolTipText(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 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;
    }

    /**
     * @param modelId the debug model identifier
     * @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 debug model identifier
     * @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 int getBufferThreshold(int startOrEnd) {
        if (startOrEnd == BUFFER_START) {
            if (BUFFER_THRESHOLD > fPreBuffer)
                return fPreBuffer;
            return BUFFER_THRESHOLD;
        }

        if (BUFFER_THRESHOLD > fPostBuffer)
            return fPostBuffer;

        return BUFFER_THRESHOLD;
    }

    /**
     * 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);

}