/*
* Copyright (c) 2010-2012 Research In Motion Limited. All rights reserved.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License, Version 1.0,
* which accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*
*/
package net.rim.ejde.internal.ui.views.profiler;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Vector;
import net.rim.ejde.internal.core.RimIDEUtil;
import net.rim.ejde.internal.ui.views.AbstractTreeOwnerDrawLabelProvider;
import net.rim.ejde.internal.ui.views.BasicDebugView;
import net.rim.ejde.internal.util.Messages;
import net.rim.ide.core.ProfileAddress;
import net.rim.ide.core.ProfileByteCode;
import net.rim.ide.core.ProfileData;
import net.rim.ide.core.ProfileItem;
import net.rim.ide.core.ProfileItemSource;
import net.rim.ide.core.ProfileLine;
import net.rim.ide.core.ProfileMethod;
import net.rim.ide.core.ProfileSourceLine;
import org.apache.log4j.Logger;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.IFileSystem;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.widgets.Event;
/**
* An instance of this class is used to display information (source code and callers) of the ProfileMethod instance selected on
* the MethodProfileTab.
*/
public class SourceProfileTab extends ProfileTab {
private static final Logger log = Logger.getLogger( SourceProfileTab.class );
private static final int PROFILE_METHOD = 0;
private static final int PROFILE_SOURCE_LINE = 1;
private static final int PROFILE_BYTE_CODE = 2;
private static final int UNRECOGINZED_TYPE = -1;
private ProfileData _pd;
// The ProfileMethod instance represented by this tab
private ProfileMethod _profileItem;
private Object _methodHandle;
// IPath of the source file of the ProfileMethod represented by this tab
private IPath _sourcePath;
// IFileStore of the source file of the ProfileMethod represented by this
// tab
private IFileStore _fileStore;
/**
* Constructs an instance of SourceProfileTab.
*
* @param view
* An instance of ProfileView on which this tab will be displayed.
*/
public SourceProfileTab( ProfilerView view ) {
super( view );
_historyIndex = 0;
_history = new Vector< History >();
getTabItem().setText( Messages.SourceProfileTab_SOURCE_TAB_TITLE );
_displayLevelofTab = PROFILE_BYTECODE_LEVEL;
}
/**
* Initializes the tab.
*/
void initializeTableViewer() {
super.initializeTableViewer();
_tableViewer.getTable().addMouseListener( new MyMouseAdapter() );
}
/**
* Gets the path of source file of .
*
* @return path of source file of <code>pi</code>, or <code>null</code> will be returned if no path information is found.
*/
IPath setSourcePath() {
Enumeration enumeration = _profileItem.getChildrenKeys();
if( enumeration == null )
return null;
ProfileItem childPI = _profileItem.getChild( enumeration.nextElement() );
ProfileSourceLine sourceLine = childPI.getLineHandle();
if( sourceLine == null )
return null;
return new Path( childPI.getLineHandle().getFileName() );
}
public int getIndent( Object obj ) {
if( ProfileTab.getDisplayLevel( obj ) < _displayLevelofTab )
return UNRECOGINZED_TYPE;
if( obj instanceof ProfileMethod )
return PROFILE_METHOD;
if( obj instanceof Callers )
return PROFILE_METHOD;
else if( obj instanceof Caller )
return PROFILE_SOURCE_LINE;
else if( obj instanceof ProfileLine )
return PROFILE_SOURCE_LINE;
else if( obj instanceof ProfileAddress )
return PROFILE_BYTE_CODE;
else if( obj instanceof ProfileByteCode )
return PROFILE_BYTE_CODE;
else
return UNRECOGINZED_TYPE;
}
/**
* Checks if <code>item</code> was expanded.
*
* @param item
* @return
*/
protected boolean isItemExpanded( Object item ) {
if( item instanceof ProfileItem )
return _expansions.get( ( (ProfileItem) item ).getHandle() ) != null;
else if( item instanceof Callers )
return _expansions.get( ( (Callers) item ).getParent() ) != null;
return false;
}
Object[] getChildren( Object obj ) {
if( obj instanceof ProfileItem ) {
if( obj instanceof ProfileMethod )
return sortedElements( (ProfileItemSource) obj, null );
return ( (ProfileItem) obj ).sortedChildren( _comparator );
} else if( obj instanceof Callers )
return ( (Callers) obj ).getChildren();
return new Object[ 0 ];
}
int getChildrenCount( Object obj ) {
if( obj instanceof ProfileItem )
return ( (ProfileItem) obj ).getChildCount();
else if( obj instanceof Callers )
return ( (Callers) obj ).getChildren().length;
return 0;
}
Object[] handleExpandedItems( Object[] data ) {
if( data == null )
return new Object[ 0 ];
Object[] newData = data;
for( int i = PROFILE_METHOD; i <= PROFILE_BYTE_CODE; i++ ) {
for( Object object : _expansions.entrySet() ) {
Object value = ( (Entry) object ).getValue();
if( getIndent( value ) == i )
newData = expandItem( newData, value );
}
}
return newData;
}
/**
* @see ProfileTab#displayData(ProfileItem pi).
*
* @param pi
* An instance of ProfileItem.
*/
/**
* Displays information of <code>pis</code> on a ProfileTab.
*
* @param pis
* An instance of ProfileItemSource.
*/
void displayData( ProfileItemSource pis ) {
if( pis == null && _methodHandle == null )
return;
if( pis instanceof ProfileMethod ) {
Object handle = ( (ProfileMethod) pis ).getHandle();
if( !handle.equals( _methodHandle ) )
_methodHandle = handle;
_profileItem = (ProfileMethod) pis;
displayData( _profileItem );
} else if( pis instanceof ProfileData )
displayData( (ProfileData) pis );
}
private void displayData( ProfileMethod pm ) {
clearDisplay();
if( pm == null )
return;
_sourcePath = setSourcePath();
if( _sourcePath != null ) {
IFileSystem fileSystem = EFS.getLocalFileSystem();
_fileStore = fileSystem.getStore( _sourcePath );
} else
_fileStore = null;
_data = new Object[ 2 ];
Callers callers = new Callers( pm );
callers.setChildren( sortedElements( pm.getCallers(), _comparator ) );
_data[ 0 ] = callers;
_data[ 1 ] = pm;
_data = handleExpandedItems( _data );
displayData( _data );
refreshForwardButton();
refreshBackwardButton();
}
private void displayData( ProfileData pd ) {
if( _methodHandle == null )
return;
_pd = pd;
_profileItem = pd.getProfileMethod( _methodHandle );
displayData( _profileItem );
}
public void setProfildData( ProfileData pd ) {
_pd = pd;
}
private void clearDisplay() {
_tableViewer.getTable().removeAll();
}
void displayHistory( History history ) {
// reset expansion information
HashMap< Object, Object > map = history.getExpansionMap();
if( map != null )
_expansions = history.getExpansionMap();
// display data
displayData( history.getProfileItemSource() );
// reset selection information
Object selection = history.getSelectedItem();
if( selection != null ) {
_tableViewer.setSelection( (ISelection) selection );
}
}
void setHistory( ProfileItemSource pis ) {
for( int i = _historyIndex + 1; i < _history.size(); i++ )
_history.removeElementAt( i );
// set the selection and expansion information of current ProfileItem
History history;
if( _history.size() != 0 ) {
history = _history.get( _historyIndex );
history.setExpansionMap( (HashMap< Object, Object >) _expansions.clone() );
int selectionIndex = _tableViewer.getTable().getSelectionIndex();
if( selectionIndex >= 0 )
// history.setSelectedItem(_tableViewer.getTable().getItem(
// selectionIndex).getData());
history.setSelectedItem( _tableViewer.getSelection() );
}
// create a new history instance for the ProfileItem which is going to
// be shown
history = new History();
history.setProfileItemSource( pis );
_history.add( history );
_historyIndex = _history.size() - 1;
refreshForwardButton();
refreshBackwardButton();
}
public void forward() {
if( _tabFolder.getSelectionIndex() == ProfilerView.INDEX_OF_TAB_SOURCE )
if( _history.size() <= 1 )
System.out.println( "History size is not bigger than 1." );
if( _history.size() != 0 && _history.size() > _historyIndex ) {
History history = _history.get( _historyIndex );
history.setExpansionMap( (HashMap< Object, Object >) _expansions.clone() );
int selectionIndex = _tableViewer.getTable().getSelectionIndex();
if( selectionIndex >= 0 )
// history.setSelectedItem(_tableViewer.getTable().getItem(
// selectionIndex).getData());
history.setSelectedItem( _tableViewer.getSelection() );
}
_historyIndex++;
if( _historyIndex == _history.size() )
System.out.println( "No more forward data." );
displayHistory( _history.get( _historyIndex ) );
}
public void backward() {
if( _history.size() != 0 && _history.size() > _historyIndex ) {
History history = _history.get( _historyIndex );
history.setExpansionMap( (HashMap< Object, Object >) _expansions.clone() );
int selectionIndex = _tableViewer.getTable().getSelectionIndex();
if( selectionIndex >= 0 )
// history.setSelectedItem(_tableViewer.getTable().getItem(
// selectionIndex).getData());
history.setSelectedItem( _tableViewer.getSelection() );
}
_historyIndex--;
if( _historyIndex < 0 )
System.out.println( "No more backward data." );
displayHistory( _history.get( _historyIndex ) );
}
/**
* Overrides the ProfileTab#sortedElement(ProfileItemSource source). Added function that fetches string source code for each
* ProfileLine.
*
* @param source
* ProfileItemSource instance.
* @param comparator
* Comparator instance used to sort children items of <code>source</code>.
* @return An array of ProfileItem.
*/
ProfileItem[] sortedElements( ProfileItemSource source, Comparator comparator ) {
// sort the children ProfileItems of source
ProfileItem[] profileItems = super.sortedElements( source, null );
if( source instanceof ProfileMethod )
// fetch source line string for each ProfileLine
setSourceLineString( profileItems );
return profileItems;
}
/**
* Fetches corresponding source line string for each ProfileItem in <code>profileItems</code>. The ProfileItems in
* <code>profileItems</code> are supposed to be sorted by line number (ascent).
*
* @param profileItems
* Array of ProfileItem.
*/
void setSourceLineString( ProfileItem[] profileItems ) {
if( profileItems == null || profileItems.length == 0 )
return;
if( _sourcePath == null )
return;
BufferedReader reader = null;
try {
// create the BufferedReader for the source code file
reader = new BufferedReader( new FileReader( _sourcePath.toFile() ) );
} catch( FileNotFoundException e ) {
log.error( e.getMessage() );
}
ProfileSourceLine psl = null;
try {
int currentLineIndex = 0;
for( int i = 0; i < profileItems.length; i++ ) {
psl = profileItems[ i ].getLineHandle();
if( reader == null )
psl.setLine( Messages.SourceProfileTab_NO_SOURCE_MESSAGE );
else {
int lineNumber = psl.getLineNumber();
String stringLine = null;
// read the corresponding source line string
for( ; currentLineIndex < lineNumber; currentLineIndex++ )
try {
stringLine = reader.readLine();
} catch( IOException e ) {
log.error( e.getMessage() );
try {
reader.close();
} catch( IOException e1 ) {
log.error( e1.getMessage() );
}
}
psl.setLine( stringLine == null ? Messages.SourceProfileTab_SOURCE_NOT_FOUND_MESSAGE : stringLine );
}
}
if( reader != null )
reader.close();
} catch( IOException e ) {
log.error( e.getMessage() );
}
}
void refreshForwardButton() {
if( _history.size() > 1 && _historyIndex < ( _history.size() - 1 ) )
_profilerView.enableActions( BasicDebugView.FORWARD_BUTTON, true );
else
_profilerView.enableActions( BasicDebugView.FORWARD_BUTTON, false );
}
void refreshBackwardButton() {
if( _history.size() > 1 && _historyIndex > 0 )
_profilerView.enableActions( BasicDebugView.BACKWARD_BUTTON, true );
else
_profilerView.enableActions( BasicDebugView.BACKWARD_BUTTON, false );
}
class MyMouseAdapter extends MouseAdapter {
public void mouseDoubleClick( MouseEvent e ) {
// Get selected TableItem
int index = _tableViewer.getTable().getSelectionIndex();
if( index < 0 )
// if nothing selected, return
return;
// its a single-select tree
Object obj = _tableViewer.getTable().getItem( index ).getData();
if( obj instanceof ProfileLine && _fileStore != null )
// if selected TableItem present a ProfileLine instance
// open the corresponding source file in an editor and
// highlight the line
RimIDEUtil.openSourceFile( _fileStore, ( (ProfileLine) obj ).getLineHandle().getLineNumber() );
else if( obj instanceof ProfileItem ) {
// if selected TableItem present a ProfileItem instance
// get the ProfileMethod of it and display the source code
// of the ProfileMethod.
if( _profilerView.getProfileData() == null ) {
MessageDialog.openError( _profilerView.getSite().getShell(), Messages.SourceProfileTab_PROFILE_VIEW_TITLE,
NLS.bind( Messages.SourceProfileTab_PROFILE_DATA_IS_NULL_MSG, obj.toString() ) );
return;
}
ProfileMethod pm = _profilerView.getProfileData().getProfileMethod( (ProfileItem) obj );
if( pm != null )
_profilerView.displaySourceData( pm );
} else if( obj instanceof Caller ) {
ProfileAddress address = ( (Caller) obj ).getProfileAddress();
if( _profilerView.getProfileData() == null ) {
MessageDialog.openError( _profilerView.getSite().getShell(), Messages.SourceProfileTab_PROFILE_VIEW_TITLE,
NLS.bind( Messages.SourceProfileTab_PROFILE_DATA_IS_NULL_MSG, address.toString() ) );
return;
}
_profilerView.displaySourceData( _profilerView.getProfileData().getProfileMethod( address ) );
}
}
}
/**
* @see ProfileTab#createLabelProvider()
*/
AbstractTreeOwnerDrawLabelProvider createLabelProvider() {
AbstractTreeOwnerDrawLabelProvider provider = new MyLabelProvider( this );
provider.setDiaplsyLevel( PROFILE_BYTECODE_LEVEL );
return provider;
}
class MyLabelProvider extends AbstractProfileLabelProvider {
public MyLabelProvider( ProfileTab tab ) {
super( tab );
}
@Override
public int getIndent( Object obj ) {
return this._tab.getIndent( obj );
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.OwnerDrawLabelProvider#paint(org.eclipse .swt.widgets.Event, java.lang.Object)
*/
protected void paint( Event event, Object element ) {
// call super method to initiate variables
super.paint( event, element );
String text = EMPTY_STRING;
boolean isCaller = element instanceof Caller;
if( element instanceof ProfileItem || isCaller ) {
// get value of each column
ProfileItem pi;
if( isCaller )
pi = ( (Caller) element ).getProfileAddress();
else
pi = (ProfileItem) element;
long ticks = pi.getTicks();
switch( event.index ) {
case COLUM_DETAIL: {
if( pi instanceof ProfileLine ) {
ProfileSourceLine sourceLine = ( (ProfileLine) pi ).getLineHandle();
if( sourceLine != null )
// display source line
text = (String) ( (ProfileLine) pi ).getLineHandle().getLine();
else
text = Messages.SourceProfileTab_NO_SOURCE_MESSAGE;
} else
text = pi.toString();
break;
}
case COLUM_PERCENT: {
text = getPercent( ticks );
break;
}
case COLUM_TICKS: {
text = String.valueOf( ticks );
break;
}
case COLUM_COUNT: {
text = String.valueOf( pi.getCount() );
break;
}
default:
text = EMPTY_STRING;
}
} else if( element instanceof Callers )
text = ( (Callers) element ).getLabel();
if( event.index == 0 )
drawFirstColumn( event, element, text, false );
else
drawText( event, text, event.x, event.y, false );
}
}
}