blob: d7c5a40962769a03727d0384813a56ce24a43cc9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 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.internal.ui.views.memory.renderings;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
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.ui.memory.provisional.AbstractAsyncTableRendering;
import org.eclipse.debug.internal.ui.memory.provisional.MemoryViewPresentationContext;
import org.eclipse.debug.internal.ui.viewers.AsynchronousTableViewer;
import org.eclipse.debug.internal.ui.viewers.ModelNode;
import org.eclipse.debug.internal.ui.views.memory.MemoryViewUtil;
import org.eclipse.debug.ui.memory.IMemoryRendering;
public class TableRenderingModel extends AbstractVirtualContentTableModel
implements IContentChangeComputer {
private Hashtable fCache;
private Vector fOrderedCache; // needed to re-organize cache
private boolean fMBSupportsChangeManagement;
private IMemoryBlock fMemoryBlock;
class SupportsChangeMgmtJob extends Job {
SupportsChangeMgmtJob()
{
super("Support Change Management"); //$NON-NLS-1$
setSystem(true);
}
protected IStatus run(IProgressMonitor monitor) {
IMemoryBlock mb = getMemoryBlock();
if (mb instanceof IMemoryBlockExtension)
{
IMemoryBlockExtension mbExt = (IMemoryBlockExtension)mb;
fMBSupportsChangeManagement = mbExt.supportsChangeManagement();
}
return Status.OK_STATUS;
}
}
public TableRenderingModel(AsynchronousTableViewer viewer) {
super(viewer);
fCache = new Hashtable();
fOrderedCache = new Vector();
}
public int indexOfKey(Object key)
{
if (key instanceof BigInteger)
{
BigInteger address = (BigInteger)key;
Object items[] = getElements();
for (int i=0; i<items.length; i++){
if (items[i] != null && items[i] instanceof MemorySegment)
{
MemorySegment line = (MemorySegment)items[i];
if (line.containsAddress(address))
return i;
}
}
}
return -1;
}
public int columnOf(Object element, Object key)
{
if (element instanceof MemorySegment && key instanceof BigInteger)
{
BigInteger address = (BigInteger)key;
MemorySegment line = (MemorySegment)element;
if (line.containsAddress(address))
{
if (getAddressableUnitsPerColumn() > 0)
{
BigInteger offset = address.subtract(line.getAddress());
// locate column
int colAddressableUnit = getAddressableUnitsPerColumn();
int col = ((offset.intValue()/colAddressableUnit)+1);
if (col == 0)
col = 1;
return col;
}
}
}
return -1;
}
public Object getKey(int idx)
{
Object elmt = getElement(idx);
if (elmt instanceof MemorySegment)
{
return ((MemorySegment)elmt).getAddress();
}
return null;
}
public Object getKey(Object element) {
int idx = indexOfElement(element);
if (idx >= 0)
{
return getKey(idx);
}
return null;
}
public Object getKey(int idx, int col) {
Object element = getElement(idx);
if (element != null && element instanceof MemorySegment)
{
MemorySegment segment = (MemorySegment)element;
BigInteger rowAddress = segment.getAddress();
int offset;
if (col > 0)
{
// get address offset
int addressableUnit = getAddressableUnitsPerColumn();
offset = (col-1) * addressableUnit;
}
else
{
offset = 0;
}
return rowAddress.add(BigInteger.valueOf(offset));
}
return null;
}
private int getAddressableUnitsPerColumn()
{
AsynchronousTableViewer viewer = getTableViewer();
if (viewer.getPresentationContext() instanceof MemoryViewPresentationContext)
{
MemoryViewPresentationContext context = (MemoryViewPresentationContext)viewer.getPresentationContext();
if (getTableRendering(context)!= null)
{
return getTableRendering(context).getAddressableUnitPerColumn();
}
}
return -1;
}
public void cache(Object[] elements) {
for (int i=0; i<elements.length; i++)
{
Object obj = elements[i];
if (obj instanceof MemorySegment)
{
cache(((MemorySegment)obj).getAddress(), obj);
}
}
}
private void cache(Object key, Object element)
{
fCache.put(key, element);
fOrderedCache.add(element);
}
public Object[] compare(Object[] newElements) {
if (fCache.isEmpty())
return newElements;
for (int j=0; j<newElements.length; j++)
{
Object obj = newElements[j];
if (obj instanceof MemorySegment)
{
MemorySegment newSegment = (MemorySegment)obj;
MemorySegment oldSegment = (MemorySegment)fCache.get(newSegment.getAddress());
if (oldSegment != null)
{
if (oldSegment.getNumAddressableUnits() == newSegment.getNumAddressableUnits())
{
MemoryByte[] newBytes = newSegment.getBytes();
MemoryByte[] oldBytes = oldSegment.getBytes();
for (int i=0; i<newBytes.length; i++)
{
newBytes[i].setHistoryKnown(true);
if (newBytes[i].isReadable() != oldBytes[i].isReadable())
{
newBytes[i].setChanged(true);
continue;
}
if (newBytes[i].isReadable() && oldBytes[i].isReadable() &&
(newBytes[i].getValue() != oldBytes[i].getValue()))
newBytes[i].setChanged(true);
}
}
}
}
}
return newElements;
}
public void clearCache()
{
fCache.clear();
fOrderedCache.clear();
}
public boolean isEmpty() {
return fCache.isEmpty();
}
public void handleViewerChanged() {
// viewer has changed, content manager needs to re-organize the cache
rebuildCache();
rebuildContent();
}
private void rebuildCache()
{
if (isEmpty())
return;
MemoryViewPresentationContext context = (MemoryViewPresentationContext)getTableViewer().getPresentationContext();
AbstractAsyncTableRendering rendering = getTableRendering(context);
if (rendering == null)
return;
ArrayList segments = new ArrayList();
Enumeration enumeration = fOrderedCache.elements();
BigInteger address = ((MemorySegment)fOrderedCache.get(0)).getAddress();
while (enumeration.hasMoreElements())
{
Object element = enumeration.nextElement();
if (element instanceof MemorySegment)
{
segments.add(element);
}
}
MemoryByte[] bytes = convertSegmentsToBytes((MemorySegment[])segments.toArray(new MemorySegment[0]));
int bytesPerLine = rendering.getBytesPerLine();
int numAddressableUnitPerLine = rendering.getAddressableUnitPerLine();
int addressableSize = rendering.getAddressableSize();
clearCache();
TableRenderingContentDescriptor descriptor = (TableRenderingContentDescriptor)rendering.getAdapter(TableRenderingContentDescriptor.class);
boolean alignAddress = true;
if (descriptor != null && !descriptor.isAlignAddressToBoundary())
{
alignAddress = descriptor.isAlignAddressToBoundary();
}
MemorySegment[] newSegments = convertMemoryBytesToSegments(address, bytes, bytesPerLine, numAddressableUnitPerLine, addressableSize, alignAddress);
for (int i=0; i<newSegments.length; i++)
{
cache(newSegments[i].getAddress(), newSegments[i]);
}
}
private void rebuildContent()
{
MemoryViewPresentationContext context = (MemoryViewPresentationContext)getTableViewer().getPresentationContext();
AbstractAsyncTableRendering rendering = getTableRendering(context);
if (rendering == null)
return;
ArrayList segments = new ArrayList();
Object[] elements = getElements();
for (int i=0; i<elements.length; i++)
{
Object element = elements[i];
if (element instanceof MemorySegment)
{
segments.add(element);
}
}
MemoryByte[] bytes = convertSegmentsToBytes((MemorySegment[])segments.toArray(new MemorySegment[segments.size()]));
int bytesPerLine = rendering.getBytesPerLine();
int numAddressableUnitPerLine = rendering.getAddressableUnitPerLine();
BigInteger address = (BigInteger)getKey(0);
int addressableSize = rendering.getAddressableSize();
TableRenderingContentDescriptor descriptor = (TableRenderingContentDescriptor)rendering.getAdapter(TableRenderingContentDescriptor.class);
boolean alignAddress = true;
if (descriptor != null && !descriptor.isAlignAddressToBoundary())
{
alignAddress = descriptor.isAlignAddressToBoundary();
}
MemorySegment[] newSegments = convertMemoryBytesToSegments(address, bytes, bytesPerLine, numAddressableUnitPerLine, addressableSize, alignAddress);
remove(getElements());
add(newSegments);
}
private MemoryByte[] convertSegmentsToBytes(MemorySegment[] segments)
{
ArrayList toReturn = new ArrayList();
for (int i=0; i<segments.length; i++)
{
MemoryByte[] temp = segments[i].getBytes();
for (int j=0; j<temp.length; j++)
{
toReturn.add(temp[j]);
}
}
return (MemoryByte[])toReturn.toArray(new MemoryByte[0]);
}
private MemorySegment[] convertMemoryBytesToSegments(BigInteger address, MemoryByte[] bytes, int bytesPerLine, int numAddressableUnitPerLine, int addressableSize, boolean alignAddress) {
Assert.isTrue(bytesPerLine > 0);
Assert.isTrue(numAddressableUnitPerLine > 0);
ArrayList segments = new ArrayList();
MemoryByte[] temp = bytes;
if (alignAddress)
{
BigInteger alignedAddress = MemoryViewUtil.alignToBoundary(address, numAddressableUnitPerLine);
// also check that the address is properly aligned and prepend bytes if need to
if (!address.subtract(alignedAddress).equals(BigInteger.ZERO))
{
BigInteger unitsToSetBack = address.subtract(alignedAddress);
BigInteger tempAddress = address.subtract(unitsToSetBack);
// only do this if the resulted address >= 0
// do not want to have negative addresses
if (tempAddress.compareTo(BigInteger.ZERO) >= 0)
{
address = alignedAddress;
int numBytesNeeded = unitsToSetBack.intValue() * addressableSize;
temp = new MemoryByte[bytes.length + numBytesNeeded];
for (int i=0; i<numBytesNeeded; i++)
{
temp[i] = new MemoryByte();
temp[i].setReadable(false);
temp[i].setWritable(false);
temp[i].setEndianessKnown(false);
}
System.arraycopy(bytes, 0, temp, numBytesNeeded, bytes.length);
bytes = temp;
}
}
}
if (bytes.length % bytesPerLine != 0)
{
int numBytesNeeded = bytesPerLine - (bytes.length % bytesPerLine);
temp = new MemoryByte[bytes.length + numBytesNeeded];
System.arraycopy(bytes, 0, temp, 0, bytes.length);
for (int i=bytes.length; i<temp.length; i++)
{
temp[i] = new MemoryByte();
temp[i].setReadable(false);
temp[i].setWritable(false);
temp[i].setEndianessKnown(false);
}
bytes = temp;
}
int idx = 0;
while (idx < bytes.length && (idx + bytesPerLine)<= bytes.length)
{
MemoryByte[] newBytes = new MemoryByte[bytesPerLine];
System.arraycopy(bytes, idx, newBytes, 0, bytesPerLine);
MemorySegment segment = new MemorySegment(address, newBytes, numAddressableUnitPerLine);
segments.add(segment);
address = address.add(BigInteger.valueOf(numAddressableUnitPerLine));
idx += bytesPerLine;
}
return (MemorySegment[])segments.toArray(new MemorySegment[segments.size()]);
}
private AsynchronousTableViewer getTableViewer()
{
return (AsynchronousTableViewer)getViewer();
}
protected void setChildren(ModelNode parentNode, List kids) {
if (computeChanges())
{
Object[] newContent = compare(kids.toArray());
ArrayList newList = new ArrayList();
for (int i=0; i<newContent.length; i++)
{
newList.add(newContent[i]);
}
super.setChildren(parentNode, newList);
}
else
super.setChildren(parentNode, kids);
}
private boolean computeChanges()
{
if (isEmpty())
return false;
if (fMBSupportsChangeManagement)
{
return false;
}
return true;
}
private IMemoryBlock getMemoryBlock()
{
return fMemoryBlock;
}
public void init(Object root) {
if (root instanceof IMemoryBlock)
{
fMemoryBlock = (IMemoryBlock)root;
new SupportsChangeMgmtJob().schedule();
}
super.init(root);
}
private AbstractAsyncTableRendering getTableRendering(MemoryViewPresentationContext context)
{
IMemoryRendering memRendering = context.getRendering();
if (memRendering != null && memRendering instanceof AbstractAsyncTableRendering)
{
return (AbstractAsyncTableRendering)memRendering;
}
return null;
}
}