blob: d6bb6fd5f834a37ab1646979f37e7c3dd450704e [file] [log] [blame]
/*
*(c) Copyright QNX Software Systems Ltd. 2002.
* All Rights Reserved.
*
*/
package org.eclipse.cdt.debug.internal.ui.views.memory;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.cdt.debug.core.CDebugUtils;
import org.eclipse.cdt.debug.core.ICMemoryManager;
import org.eclipse.cdt.debug.core.model.IFormattedMemoryBlock;
import org.eclipse.cdt.debug.core.model.IFormattedMemoryBlockRow;
import org.eclipse.cdt.debug.internal.ui.CDebugUIUtils;
import org.eclipse.debug.core.DebugException;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
/**
*
* Provides rendering methods to the MemoryText widget.
*
* @since Jul 25, 2002
*/
public class MemoryPresentation
{
private static final int INTERVAL_BETWEEN_ADDRESS_AND_DATA = 2;
private static final int INTERVAL_BETWEEN_DATA_ITEMS = 1;
private static final int INTERVAL_BETWEEN_DATA_AND_ASCII = 1;
private IFormattedMemoryBlock fBlock;
private List fAddressZones;
private List fChangedZones;
private boolean fDisplayAscii = true;
/**
* Constructor for MemoryPresentation.
*/
public MemoryPresentation()
{
fAddressZones = new LinkedList();
fChangedZones = new LinkedList();
}
public IFormattedMemoryBlock getMemoryBlock()
{
return fBlock;
}
public void setMemoryBlock( IFormattedMemoryBlock block )
{
fBlock = block;
}
/**
* Returns the string that contains the textual representation of
* the memory according to this presentation.
*
* @return the string that contains the textual representation of the memory
*/
public String getText()
{
fAddressZones.clear();
IFormattedMemoryBlockRow[] rows = ( getMemoryBlock() != null ) ? getMemoryBlock().getRows() : new IFormattedMemoryBlockRow[0];
StringBuffer sb = new StringBuffer();
for ( int i = 0; i < rows.length; ++i )
{
int offset = sb.length();
sb.append( getRowText( rows[i] ) );
fAddressZones.add( new Point( offset, offset + getAddressLength() ) );
}
return sb.toString();
}
public String[] getText( Point[] zones )
{
return new String[0];
}
public boolean isAcceptable( char ch, int offset )
{
if ( isInAsciiArea( offset ) )
return true;
if ( isInDataArea( offset ) )
return isValidValue( ch );
return false;
}
public Point[] getAddressZones()
{
return (Point[])fAddressZones.toArray( new Point[fAddressZones.size()] );
}
public Point[] getChangedZones()
{
fChangedZones.clear();
Long[] changedAddresses = getChangedAddresses();
for ( int i = 0; i < changedAddresses.length; ++i )
{
int dataOffset = getDataItemOffsetByAddress( changedAddresses[i] );
if ( dataOffset != -1 )
{
fChangedZones.add( new Point( dataOffset, dataOffset + getDataItemLength() - 1 ) );
}
if ( displayASCII() )
{
int asciiOffset = getAsciiOffsetByAddress( changedAddresses[i] );
if ( asciiOffset != -1 )
{
fChangedZones.add( new Point( asciiOffset, asciiOffset ) );
}
}
}
return (Point[])fChangedZones.toArray( new Point[fChangedZones.size()] );
}
public String getStartAddress()
{
return ( fBlock != null ) ? getAddressString( fBlock.getStartAddress() ) : "";
}
public String getAddressExpression()
{
return ( fBlock != null ) ? fBlock.getAddressExpression() : "";
}
private String getInterval( int length )
{
char[] chars = new char[length];
Arrays.fill( chars, ' ' );
return new String( chars );
}
private String getAddressString( long address )
{
return CDebugUIUtils.toHexAddressString( address );
}
private String getRowText( IFormattedMemoryBlockRow row )
{
StringBuffer result = new StringBuffer( getRowLength() );
result.append( getAddressString( row.getAddress() ) );
result.append( getInterval( INTERVAL_BETWEEN_ADDRESS_AND_DATA ) );
String[] items = row.getData();
for ( int i = 0; i < items.length; ++i )
{
result.append( getDataItemPresentation( items[i] ) );
result.append( getInterval( INTERVAL_BETWEEN_DATA_ITEMS ) );
}
if ( displayASCII() )
{
result.append( getInterval( INTERVAL_BETWEEN_DATA_AND_ASCII ) );
result.append( row.getASCII() );
}
result.append( '\n' );
return result.toString();
}
private int getRowLength()
{
return getAddressLength() +
INTERVAL_BETWEEN_ADDRESS_AND_DATA +
(getDataItemLength() + INTERVAL_BETWEEN_DATA_ITEMS) * getNumberOfDataItemsInRow() +
( ( displayASCII() ) ? INTERVAL_BETWEEN_DATA_AND_ASCII +
getDataBytesPerRow() : 0 ) + 1;
}
private int getAddressLength()
{
return 10;
}
private boolean isInAsciiArea( int offset )
{
if ( displayASCII() && getRowLength() != 0 )
{
int pos = offset % getRowLength();
int asciiColumn = getAddressLength() +
INTERVAL_BETWEEN_ADDRESS_AND_DATA +
(getDataItemLength() + INTERVAL_BETWEEN_DATA_ITEMS ) * getNumberOfDataItemsInRow() +
INTERVAL_BETWEEN_DATA_AND_ASCII;
return ( pos >= asciiColumn && pos < getRowLength() - 1 );
}
return false;
}
private boolean isInDataArea( int offset )
{
if ( getRowLength() != 0 )
{
int pos = offset % getRowLength();
int dataBegin = getAddressLength() + INTERVAL_BETWEEN_ADDRESS_AND_DATA;
int dataEnd = dataBegin + ((getDataItemLength() + INTERVAL_BETWEEN_DATA_ITEMS ) * getNumberOfDataItemsInRow());
if ( pos >= dataBegin && pos < dataEnd )
return isInDataItem( pos - dataBegin );
}
return false;
}
private boolean isInDataItem( int pos )
{
for ( int i = 0; i < getNumberOfDataItemsInRow(); ++i )
{
if ( pos < i * (getDataItemLength() + INTERVAL_BETWEEN_DATA_ITEMS) )
return false;
if ( pos >= i * (getDataItemLength() + INTERVAL_BETWEEN_DATA_ITEMS) &&
pos < (i * (getDataItemLength() + INTERVAL_BETWEEN_DATA_ITEMS)) + getDataItemLength() )
return true;
}
return false;
}
private int getDataItemLength()
{
if ( getMemoryBlock() != null )
{
switch( getDataFormat() )
{
case IFormattedMemoryBlock.MEMORY_FORMAT_HEX:
return getMemoryBlock().getWordSize() * 2;
case IFormattedMemoryBlock.MEMORY_FORMAT_SIGNED_DECIMAL:
return getDecimalDataItemLength( getMemoryBlock().getWordSize(), true );
case IFormattedMemoryBlock.MEMORY_FORMAT_UNSIGNED_DECIMAL:
return getDecimalDataItemLength( getMemoryBlock().getWordSize(), false );
}
}
return 0;
}
private int getNumberOfDataItemsInRow()
{
if ( getMemoryBlock() != null )
return getMemoryBlock().getNumberOfColumns();
return 0;
}
protected boolean displayASCII()
{
if ( canDisplayAscii() )
return fDisplayAscii;
return false;
}
protected void setDisplayAscii( boolean displayAscii )
{
fDisplayAscii = displayAscii;
}
private int getDataBytesPerRow()
{
if ( getMemoryBlock() != null )
return getMemoryBlock().getNumberOfColumns() * getMemoryBlock().getWordSize();
return 0;
}
private boolean isValidValue( char ch )
{
switch( getDataFormat() )
{
case ICMemoryManager.MEMORY_FORMAT_HEX:
return isHexadecimal( ch );
case ICMemoryManager.MEMORY_FORMAT_BINARY:
case ICMemoryManager.MEMORY_FORMAT_OCTAL:
case ICMemoryManager.MEMORY_FORMAT_SIGNED_DECIMAL:
case ICMemoryManager.MEMORY_FORMAT_UNSIGNED_DECIMAL:
case -1:
default:
return false;
}
}
private boolean isHexadecimal( char ch )
{
return ( Character.isDigit( ch ) ||
( ch >= 'a' && ch <= 'f' ) ||
( ch >= 'A' && ch <= 'F' ) );
}
private int getDataFormat()
{
if ( getMemoryBlock() != null )
return getMemoryBlock().getFormat();
return IFormattedMemoryBlock.MEMORY_FORMAT_HEX;
}
private Long[] getChangedAddresses()
{
return ( getMemoryBlock() != null ) ? getMemoryBlock().getChangedAddresses() : new Long[0];
}
private int getDataItemOffsetByAddress( Long address )
{
if ( getMemoryBlock() != null )
{
IFormattedMemoryBlockRow[] rows = getMemoryBlock().getRows();
for ( int i = 0; i < rows.length; ++i )
{
int wordSize = getMemoryBlock().getWordSize();
int numberOfColumns = getMemoryBlock().getNumberOfColumns();
if ( address.longValue() >= rows[i].getAddress() &&
address.longValue() < rows[i].getAddress() + (wordSize * numberOfColumns) )
{
for ( int j = 1; j <= numberOfColumns; ++j )
{
if ( address.longValue() >= rows[i].getAddress() + ((j - 1) * wordSize) &&
address.longValue() < rows[i].getAddress() + (j * wordSize) )
{
return (i * getRowLength()) + ((j - 1) * (getDataItemLength() + INTERVAL_BETWEEN_DATA_ITEMS)) + getAddressLength() + INTERVAL_BETWEEN_ADDRESS_AND_DATA;
}
}
}
}
}
return -1;
}
private int getAsciiOffsetByAddress( Long address )
{
if ( getMemoryBlock() != null )
{
IFormattedMemoryBlockRow[] rows = getMemoryBlock().getRows();
if ( rows.length > 0 )
{
IFormattedMemoryBlockRow firstRow = rows[0];
IFormattedMemoryBlockRow lastRow = rows[rows.length - 1];
if ( address.longValue() >= firstRow.getAddress() && address.longValue() <= lastRow.getAddress() )
{
int asciiOffset = (int)(address.longValue() - firstRow.getAddress());
int asciiRowlength = getMemoryBlock().getWordSize() * getMemoryBlock().getNumberOfColumns();
int numberOfRows = asciiOffset / asciiRowlength;
int offsetInRow = asciiOffset % asciiRowlength;
return (numberOfRows * getRowLength()) +
getAddressLength() + INTERVAL_BETWEEN_ADDRESS_AND_DATA +
(getDataItemLength() + INTERVAL_BETWEEN_DATA_ITEMS) * getMemoryBlock().getNumberOfColumns() +
INTERVAL_BETWEEN_DATA_AND_ASCII + offsetInRow;
}
}
}
return -1;
}
protected boolean canDisplayAscii()
{
if ( getMemoryBlock() != null )
return getMemoryBlock().displayASCII();
return false;
}
protected int getDataItemIndex( int offset )
{
int row = offset / getRowLength();
int pos = offset % getRowLength() - getAddressLength() - INTERVAL_BETWEEN_ADDRESS_AND_DATA;
for ( int i = 0; i < getNumberOfDataItemsInRow(); ++i )
{
if ( pos < i * (getDataItemLength() + INTERVAL_BETWEEN_DATA_ITEMS) )
return -1;
if ( pos >= i * (getDataItemLength() + INTERVAL_BETWEEN_DATA_ITEMS) &&
pos < (i * (getDataItemLength() + INTERVAL_BETWEEN_DATA_ITEMS)) + getDataItemLength() )
return i + (row * getNumberOfDataItemsInRow());
}
if ( displayASCII() && pos >= getNumberOfDataItemsInRow() * (getDataItemLength() + INTERVAL_BETWEEN_DATA_ITEMS) + INTERVAL_BETWEEN_DATA_AND_ASCII )
{
return ((pos - ((getNumberOfDataItemsInRow() * (getDataItemLength() + INTERVAL_BETWEEN_DATA_ITEMS) + INTERVAL_BETWEEN_DATA_AND_ASCII))) * 2 / getDataItemLength()) + row * getNumberOfDataItemsInRow();
}
return -1;
}
private int getDataItemOffset( int index )
{
int row = index / getNumberOfDataItemsInRow();
int pos = index % getNumberOfDataItemsInRow();
return row * getRowLength() +
getAddressLength() + INTERVAL_BETWEEN_ADDRESS_AND_DATA +
pos * (getDataItemLength() + INTERVAL_BETWEEN_DATA_ITEMS);
}
private char[] getDataItemChars( int index )
{
if ( getMemoryBlock() != null )
{
int rowNumber = index / getMemoryBlock().getNumberOfColumns();
int pos = index % getMemoryBlock().getNumberOfColumns();
IFormattedMemoryBlockRow[] rows = getMemoryBlock().getRows();
if ( rowNumber < rows.length )
{
String[] data = rows[rowNumber].getData();
if ( pos < data.length )
{
return data[pos].toCharArray();
}
}
}
return new char[0];
}
private int getOffsetInDataItem( int offset, int index )
{
if ( isInDataArea( offset ) )
{
return offset - getDataItemOffset( index );
}
return -1;
}
public void dispose()
{
if ( fAddressZones != null )
{
fAddressZones.clear();
}
if ( fChangedZones != null )
{
fChangedZones.clear();
}
}
private String getDataItemPresentation( String item )
{
switch( getDataFormat() )
{
case IFormattedMemoryBlock.MEMORY_FORMAT_HEX:
return item;
case IFormattedMemoryBlock.MEMORY_FORMAT_SIGNED_DECIMAL:
return convertToDecimal( getWordSize(), item, true );
case IFormattedMemoryBlock.MEMORY_FORMAT_UNSIGNED_DECIMAL:
return convertToDecimal( getWordSize(), item, false );
}
return "";
}
private int getDecimalDataItemLength( int wordSize, boolean signed )
{
switch( wordSize )
{
case IFormattedMemoryBlock.MEMORY_SIZE_BYTE:
return ( signed ) ? 4 : 3;
case IFormattedMemoryBlock.MEMORY_SIZE_HALF_WORD:
return ( signed ) ? 6 : 5;
case IFormattedMemoryBlock.MEMORY_SIZE_WORD:
return ( signed ) ? 11 : 10;
}
return 0;
}
private int getWordSize()
{
if ( getMemoryBlock() != null )
{
return getMemoryBlock().getWordSize();
}
return 0;
}
private String convertToDecimal( int wordSize, String item, boolean signed )
{
String result = "";
boolean le = getMemoryBlock().isLittleEndian();
switch( wordSize )
{
case IFormattedMemoryBlock.MEMORY_SIZE_BYTE:
result = Long.toString( ( signed ) ? CDebugUtils.toByte( item.toCharArray(), le ) : CDebugUtils.toUnsignedByte( item.toCharArray(), le ) );
break;
case IFormattedMemoryBlock.MEMORY_SIZE_HALF_WORD:
result = Long.toString( ( signed ) ? CDebugUtils.toShort( item.toCharArray(), le ) : CDebugUtils.toUnsignedShort( item.toCharArray(), le ) );
break;
case IFormattedMemoryBlock.MEMORY_SIZE_WORD:
result = Long.toString( ( signed ) ? CDebugUtils.toInt( item.toCharArray(), le ) : CDebugUtils.toUnsignedInt( item.toCharArray(), le ) );
break;
}
return CDebugUtils.prependString( result, getDataItemLength(), ' ' );
}
protected boolean isStartAddressChanged()
{
if ( getMemoryBlock() != null )
{
return getMemoryBlock().isStartAddressChanged();
}
return false;
}
protected String getNewItemValue( int offset, char newChar )
{
if ( getMemoryBlock() != null )
{
int index = getDataItemIndex( offset );
if ( index != -1 )
{
char[] chars = getDataItemChars( index );
if ( isInDataArea( offset ) )
{
int charIndex = getOffsetInDataItem( offset, index );
chars[charIndex] = newChar;
}
if ( isInAsciiArea( offset ) )
{
chars = CDebugUtils.getByteText( (byte)newChar );
}
return new String( chars );
}
}
return null;
}
protected void setItemValue( int offset, char ch )
{
if ( getMemoryBlock() != null )
{
int index = getDataItemIndex( offset );
if ( index != -1 )
{
String newValue = getNewItemValue( offset, ch );
if ( newValue != null )
{
try
{
getMemoryBlock().setItemValue( index, newValue );
return;
}
catch( DebugException e )
{
Display.getDefault().beep();
}
}
}
}
Display.getDefault().beep();
}
}