/* * 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.text.DecimalFormat; import java.util.Comparator; import java.util.HashMap; import java.util.Vector; import net.rim.ejde.internal.core.IConstants; import net.rim.ejde.internal.ui.CompositeFactory; import net.rim.ejde.internal.ui.views.AbstractTreeOwnerDrawLabelProvider; import net.rim.ejde.internal.ui.views.IDebugLazyContentProvider; import net.rim.ejde.internal.util.DebugUtils; import net.rim.ejde.internal.util.Messages; import net.rim.ide.RIA; import net.rim.ide.RIA.ProfileType; 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.ProfileModule; import org.eclipse.jface.viewers.OwnerDrawLabelProvider; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; /** * This is the super class of all customized tab classes which are used to display summary, method, and source information of a * ProfileData instance. * */ abstract public class ProfileTab implements IConstants { // Display levels final static int PROFILE_DATA_LEVEL = 5; final static int PROFILE_MODULE_LEVEL = 4; final static int PROFILE_METHOD_LEVEL = 3; final static int PROFILE_LINE_LEVEL = 2; final static int PROFILE_BYTECODE_LEVEL = 1; final static int UNREGOINZED_LEVEL = 0; // Column index final static int COLUM_DETAIL = 0; final static int COLUM_PERCENT = 1; final static int COLUM_TICKS = 2; final static int COLUM_COUNT = 3; final static private DecimalFormat _percentFormat = new DecimalFormat( Messages.ProfileTab_0 ); private TabItem _tabItem; ProfilerView _profilerView; TabFolder _tabFolder; TableViewer _tableViewer; TableColumn[] _columns = new TableColumn[ 4 ]; long _total; int _displayLevelofTab = -1; Comparator _comparator = _ticksComparator; protected AbstractTreeOwnerDrawLabelProvider _labelProvider; protected IDebugLazyContentProvider _contentProvider; // map which stores the objects which are expanded on the view protected HashMap< Object, Object > _expansions = new HashMap< Object, Object >(); // data which will be set to the content provider of a tab to be displayed protected Object[] _data; // history information protected int _historyIndex; protected Vector< History > _history; /** * Constructs a ProfileTab instance. * * @param view */ public ProfileTab( ProfilerView view ) { _profilerView = view; _tabFolder = _profilerView.getTabFolder(); // create a TabItem instance _tabItem = new TabItem( _tabFolder, SWT.NONE ); initializeTableViewer(); } /** * Initializes the table viewer. */ void initializeTableViewer() { // create a TableViewer instance Composite composite = CompositeFactory.gridComposite( _tabFolder, 1 ); _tableViewer = new TableViewer( composite, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.VIRTUAL | SWT.SINGLE | SWT.FULL_SELECTION ); _tableViewer.getTable().setLayoutData( new GridData( GridData.FILL_BOTH ) ); _tableViewer.getTable().setHeaderVisible( true ); _tableViewer.getTable().setLinesVisible( true ); _tableViewer.getTable().addMouseListener( new MyMouseAdapter() ); _tableViewer.setUseHashlookup( true ); _tabItem.setControl( composite ); _contentProvider = new MyContentProvider( _tableViewer ); _tableViewer.setContentProvider( _contentProvider ); _labelProvider = createLabelProvider(); _tableViewer.setLabelProvider( _labelProvider ); OwnerDrawLabelProvider.setUpOwnerDraw( _tableViewer ); // create the table columns createTableColumns(); } private void createTableColumns() { _columns[ COLUM_DETAIL ] = new TableColumn( _tableViewer.getTable(), SWT.CENTER ); _columns[ COLUM_DETAIL ].setText( Messages.ProfileTab_DETAILS_COLUMN_TITLE ); _columns[ COLUM_DETAIL ].setWidth( 400 ); _columns[ COLUM_DETAIL ].setResizable( true ); _columns[ COLUM_PERCENT ] = new TableColumn( _tableViewer.getTable(), SWT.RIGHT ); _columns[ COLUM_PERCENT ].setText( Messages.ProfileTab_PERCENT_COLUMN_TITLE ); _columns[ COLUM_PERCENT ].setWidth( 150 ); _columns[ COLUM_PERCENT ].setResizable( true ); _columns[ COLUM_TICKS ] = new TableColumn( _tableViewer.getTable(), SWT.RIGHT ); _columns[ COLUM_TICKS ].setText( getNameOfType( _profilerView.getWhatToProfile() ) ); _columns[ COLUM_TICKS ].setWidth( 150 ); _columns[ COLUM_TICKS ].setResizable( true ); _columns[ COLUM_COUNT ] = new TableColumn( _tableViewer.getTable(), SWT.RIGHT ); _columns[ COLUM_COUNT ].setText( Messages.ProfileTab_COUNT_COLUMN_TITLE ); _columns[ COLUM_COUNT ].setWidth( 150 ); _columns[ COLUM_COUNT ].setResizable( true ); } /** * Removes <code>item</code> from expansion map. * * @param item */ protected void removeFromExpansion( Object item ) { if( item instanceof ProfileItem ) _expansions.remove( ( (ProfileItem) item ).getHandle() ); else if( item instanceof AllMethods ) _expansions.remove( ( (AllMethods) item ).getLabel() ); else if( item instanceof Callers ) _expansions.remove( ( (Callers) item ).getParent() ); else if( item instanceof SummaryItem ) _expansions.remove( ( (SummaryItem) item ).getDetailString() ); } /** * Adds <code>item</code> into expansion map. * * @param item */ protected void putIntoExpansion( Object item ) { if( item instanceof ProfileItem ) _expansions.put( ( (ProfileItem) item ).getHandle(), item ); else if( item instanceof AllMethods ) _expansions.put( ( (AllMethods) item ).getLabel(), item ); else if( item instanceof Callers ) _expansions.put( ( (Callers) item ).getParent(), item ); else if( item instanceof SummaryItem ) _expansions.put( ( (SummaryItem) item ).getDetailString(), item ); } /** * Clears expansion map. */ protected void clearExpansion() { _expansions.clear(); } /** * Clears history vector. */ protected void clearHistory() { _historyIndex = 0; if( _history != null ) _history.clear(); } public Object[] getData() { return _data; } /** * Checks if <code>item</code> was expanded. This method needs to be overridden in sub-classes. * * @param item * @return */ protected boolean isItemExpanded( Object item ) { return false; } /** * Updates the title of the type column (what to profile). */ public void updateTypeColumeTitle() { _columns[ COLUM_TICKS ].setText( getNameOfType( _profilerView.getWhatToProfile() ) ); } private String getNameOfType( int id ) { if( DebugUtils.isRIMDebuggerRunning() ) { RIA ria = RIA.getCurrentDebugger(); if( ria != null ) { // MKS 2486071 String debugAttachedTo = ria.getDebugAttachTo(); if( debugAttachedTo != null && !debugAttachedTo.isEmpty() ) { ProfileType[] types = ria.profileGetTypes(); for( int i = 0; i < types.length; i++ ) if( types[ i ].getId() == id ) return types[ i ].getDescription(); } } } return IConstants.EMPTY_STRING; } /** * Sets the comparator of this tab. * * @param comparator */ void setComparator( Comparator comparator ) { if( _comparator.equals( comparator ) ) return; _comparator = comparator; _profilerView.displayProfileData( new ProfileTab[] { this } ); } /** * Gets the TabItem control of this ProfileTab instance. * * @return The TabItem control of this ProfileTab instance. */ public TabItem getTabItem() { return _tabItem; } /** * Calculates the percentage of <code>num</code> against the total. * * @param num * @return */ public String getPercent( long num ) { if( _total == 0 ) return _percentFormat.format( (double) 0 ); return _percentFormat.format( (double) num / _total ); } /** * Set the total number of ticks of the ProfileData presented by this ProfileTab instance. * * @param total * The total number of the ticks. */ public void setTotal( long total ) { _total = total; } void expandItem( Object obj ) { if( _data == null || _data.length == 0 ) return; _data = expandItem( _data, obj ); displayData( _data ); putIntoExpansion( obj ); } Object[] expandItem( Object[] data, Object item ) { Object[] children = getChildren( item ); int childrenCount = children.length; Object[] newData = new Object[ data.length + childrenCount ]; int index = getItemIndex( data, item ); if( index < 0 ) return data; // copy the items up to the given item into new data System.arraycopy( data, 0, newData, 0, index + 1 ); // copy the children of the given item into new data System.arraycopy( children, 0, newData, index + 1, childrenCount ); // copy the items after the given item into new data System.arraycopy( data, index + 1, newData, index + childrenCount + 1, data.length - index - 1 ); return newData; } void collapseItem( Object obj ) { if( _data == null || _data.length == 0 ) return; _data = collapseItem( _data, obj ); displayData( _data ); removeFromExpansion( obj ); } private Object[] collapseItem( Object[] data, Object item ) { int childrenCount = 0; childrenCount = numChildrenToRemove( item ); Object[] newData = new Object[ data.length - childrenCount ]; int index = getItemIndex( data, item ); if( index < 0 ) return data; // copy the items up to the given item into new data System.arraycopy( data, 0, newData, 0, index + 1 ); // copy the items after the given item into new data (children are // skipped) System.arraycopy( data, index + childrenCount + 1, newData, index + 1, data.length - index - childrenCount - 1 ); return newData; } private int numChildrenToRemove( Object obj ) { Object[] children = getChildren( obj ); int childrenCount = children.length; for( int i = 0; i < children.length; i++ ) if( isItemExpanded( children[ i ] ) ) { childrenCount += numChildrenToRemove( children[ i ] ); removeFromExpansion( children[ i ] ); } return childrenCount; } int getItemIndex( Object[] data, Object obj ) { if( data == null ) return -1; for( int i = 0; i < data.length; i++ ) if( data[ i ].equals( obj ) ) return i; return -1; } void displayData( Object[] objects ) { if( objects == null ) return; _tableViewer.getTable().setRedraw( false ); _tableViewer.setInput( objects ); _tableViewer.getTable().setItemCount( objects.length ); _tableViewer.getTable().setRedraw( true ); } /** * Gets the number of children of given <code>obj</code>. This method needs to be override by sub-classes. * * @param obj * @return */ int getChildrenCount( Object obj ) { if( obj instanceof ProfileItem ) return ( (ProfileItem) obj ).getChildCount(); return 0; } /** * Gets children of given <code>obj</code> This method needs to be override by sub-classes. * * @param obj * @return */ Object[] getChildren( Object obj ) { return new Object[ 0 ]; } /** * Gets the display level of given <code>obj</code>. * * @param obj * @return Display level of given <code>obj</code>. */ static int getDisplayLevel( Object obj ) { if( obj instanceof ProfileData ) return PROFILE_DATA_LEVEL; if( obj instanceof ProfileModule ) return PROFILE_MODULE_LEVEL; else if( obj instanceof AllMethods ) return PROFILE_MODULE_LEVEL; else if( obj instanceof ProfileMethod ) return PROFILE_METHOD_LEVEL; else if( obj instanceof Callers ) return PROFILE_METHOD_LEVEL; else if( obj instanceof ProfileAddress ) return PROFILE_METHOD_LEVEL; else if( obj instanceof ProfileLine ) return PROFILE_LINE_LEVEL; else if( obj instanceof Caller ) return PROFILE_LINE_LEVEL; else if( obj instanceof ProfileByteCode ) return PROFILE_BYTECODE_LEVEL; else return UNREGOINZED_LEVEL; } /** * Sorts children ProfileItems of <code>source</code>. * * @param source * ProfileItemSource instance. * @param comparator * Comparator instance used to sort children items of <code>source</code>. * @return Array of sorted children ProfileItems of <code>source</code>. */ ProfileItem[] sortedElements( ProfileItemSource source, Comparator comparator ) { return ProfilerView.sortedElements( source, comparator ); } /** * Gets the TreeViewer of this tab. * * @return TreeViewer of this tab. */ TableViewer getViewer() { return _tableViewer; } /** * Clears the display, history, expansions of the tab. * */ public void clearTab( boolean clearPreferences ) { _tableViewer.getTable().removeAll(); if( clearPreferences ) { clearExpansion(); clearHistory(); } } /** * Adds children of expanded items to <code>data</code>. * * This method should be implemented in sub-classes. */ Object[] handleExpandedItems( Object[] data ) { return new Object[ 0 ]; } /** * Forward history. Need to be implemented by sub-classes. */ public void forward() { } /** * Backward history. Need to be implemented by sub-classes. */ public void backward() { } /** * Set history information. Need to be implemented by sub-classes. */ void setHistory( ProfileItemSource pis ) { } // ------ Abstract Methods ------ /** * Displays information of <code>pis</code> on a ProfileTab. * * @param pis * An instance of ProfileItemSource. */ abstract void displayData( ProfileItemSource pis ); abstract AbstractTreeOwnerDrawLabelProvider createLabelProvider(); abstract int getIndent( Object obj ); // ------ Comparators ------ static Comparator _countComparator = new Comparator() { public int compare( Object o1, Object o2 ) { return ( (ProfileItem) o2 ).getCount() - ( (ProfileItem) o1 ).getCount(); } }; static Comparator _ticksComparator = new Comparator() { public int compare( Object o1, Object o2 ) { long c1 = ( (ProfileItem) o1 ).getTicks(); long c2 = ( (ProfileItem) o2 ).getTicks(); if( c2 > c1 ) return 1; if( c2 < c1 ) return -1; return 0; } }; // ------ inner classes ------ /** * Content provider of objects tree view. */ class MyContentProvider implements IDebugLazyContentProvider { private TableViewer _myViewer; private Object[] _models; public MyContentProvider( TableViewer viewer ) { _myViewer = viewer; } public void updateElement( int index ) { _myViewer.replace( _models[ index ], index ); } public void dispose() { // TODO Auto-generated method stub } public void inputChanged( Viewer viewer, Object oldInput, Object newInput ) { if( newInput == null ) _models = new Object[ 0 ]; else _models = (Object[]) newInput; } public Object getData() { return _models; } } class SummaryItem { String _detail; String _percent; long _ticks; long _count; SummaryItem( String detail, ProfileItem item ) { _detail = detail; _ticks = item.getTicks(); _percent = getPercent( _ticks ); _count = item.getCount(); } SummaryItem( String detail, String percent, long ticks, long count ) { _detail = detail; _percent = percent; _ticks = ticks; _count = count; } String getDetailString() { return _detail; } String getPercentString() { return _percent; } long getTicks() { return _ticks; } long getCount() { return _count; } } class Callers { Object _parent; String _label = Messages.ProfileTab_CALLER_TITLE; Object[] _children; Callers( Object parent ) { _parent = parent; } void setChildren( Object[] children ) { _children = new Object[ children.length ]; for( int i = 0; i < children.length; i++ ) _children[ i ] = new Caller( (ProfileAddress) children[ i ] ); } Object[] getChildren() { return _children; } Object getParent() { return _parent; } String getLabel() { return _label; } public boolean equals( Object obj ) { if( !( obj instanceof Callers ) ) return false; if( !( (Callers) obj ).getParent().equals( this.getParent() ) ) return false; return true; } } class Caller { ProfileAddress _address; Caller( ProfileAddress address ) { _address = address; } ProfileAddress getProfileAddress() { return _address; } public boolean equals( Object obj ) { if( !( obj instanceof Caller ) ) return false; if( !( (Caller) obj ).getProfileAddress().equals( this.getProfileAddress() ) ) return false; return true; } } class AllMethods { String _label; ProfileData _pd; public AllMethods( ProfileData pd ) { _label = RIA.getString( "ProfileAll" ); _pd = pd; } Object[] getChildren() { if( _pd == null ) return new Object[ 0 ]; return sortedElements( _pd.getAllMethods(), _comparator ); } int getChildrenCount() { return _pd.getAllMethods().getChildCount(); } void setProfileDate( ProfileData pd ) { _pd = pd; } ProfileData getProfileData() { return _pd; } String getLabel() { return _label; } public boolean equals( Object obj ) { if( obj instanceof AllMethods ) return true; return false; } } class MyMouseAdapter extends MouseAdapter { public void mouseDown( MouseEvent e ) { if( _profilerView.getProfileData() == null ) return; TableItem selectedItem = _tableViewer.getTable().getItem( _tableViewer.getTable().getSelectionIndex() ); if( selectedItem == null ) return; // If user clicked on the [+] or [-], expand or collapse the item Object item = selectedItem.getData(); Rectangle rect = _labelProvider.getImageBounds( item, selectedItem.getBounds( 0 ) ); if( rect.contains( e.x, e.y ) && getChildrenCount( item ) != 0 && ( _displayLevelofTab < 0 || ProfileTab.getDisplayLevel( item ) > _displayLevelofTab ) ) { if( isItemExpanded( item ) ) collapseItem( item ); else expandItem( item ); } } } class History { Object _selection; HashMap< Object, Object > _expansionMap; ProfileItemSource _profileItemSource; void setSelectedItem( Object item ) { _selection = item; } Object getSelectedItem() { return _selection; } void setExpansionMap( HashMap< Object, Object > map ) { _expansionMap = map; } HashMap< Object, Object > getExpansionMap() { return _expansionMap; } void setProfileItemSource( ProfileItemSource pis ) { _profileItemSource = pis; } ProfileItemSource getProfileItemSource() { return _profileItemSource; } } }