com.nokia.s60tools.crashanalyser.ui.viewers.CallStackTableViewer.java Source code

Java tutorial

Introduction

Here is the source code for com.nokia.s60tools.crashanalyser.ui.viewers.CallStackTableViewer.java

Source

/*
* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/

package com.nokia.s60tools.crashanalyser.ui.viewers;

import org.eclipse.jface.action.*;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.SWT;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.ui.*;
import org.eclipse.ui.editors.text.*;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.menus.*;
import sun.awt.windows.*;
import com.nokia.s60tools.crashanalyser.model.*;
import com.nokia.s60tools.crashanalyser.resources.*;
import com.nokia.s60tools.crashanalyser.containers.*;
import com.nokia.s60tools.crashanalyser.ui.console.CrashAnalyserEditorConsole;
import com.nokia.s60tools.util.sourcecode.*;
import java.util.*;
import java.net.URI;
import java.io.*;
import java.awt.datatransfer.*;
import java.awt.Toolkit;

/**
 * Table viewer for call stack table. Call stack table is used in 
 * CallStackPage.java (Crash Data page in Crash Visualiser editor) 
 *
 */
public class CallStackTableViewer extends CrashAnalyserTableViewer {
    Action actionCopySelectionToClipboardAsRichText;
    Action actionCopySelectionToClipboardAsPlainText;
    Action actionCopyStackToClipboardAsRichText;
    Action actionCopyStackToClipboardAsPlainText;
    Action actionOpenSourceFile;
    Action actionDoubleClick;
    Table tableControl;

    public final static int COLUMN_ADDRESS = 0;
    public final static int COLUMN_SYMBOL = 1;
    public final static int COLUMN_VALUE = 2;
    public final static int COLUMN_OFFSET = 3;
    public final static int COLUMN_OBJECT = 4;
    public final static int COLUMN_TEXT = 5;

    MenuManager subMenuSdk;

    /**
     * Constructor
     * @param table table where this viewer is used
     */
    public CallStackTableViewer(Table table) {
        super(table);

        tableControl = table;

        doCreateActions();
        doCreateContextMenu();

        TableViewerColumn column = new TableViewerColumn(this, SWT.NONE);
        column.setLabelProvider(labelProvider);
        column.getColumn().setText("");

        column = new TableViewerColumn(this, SWT.NONE);
        column.setLabelProvider(labelProvider);
        column.getColumn().setText("Symbol");

        column = new TableViewerColumn(this, SWT.NONE);
        column.setLabelProvider(labelProvider);
        column.getColumn().setText("Address");

        column = new TableViewerColumn(this, SWT.NONE);
        column.setLabelProvider(labelProvider);
        column.getColumn().setText("Offset");

        column = new TableViewerColumn(this, SWT.NONE);
        column.setLabelProvider(labelProvider);
        column.getColumn().setText("Object");

        column = new TableViewerColumn(this, SWT.NONE);
        column.setLabelProvider(labelProvider);
        column.getColumn().setText("Data");

        addDoubleClickListener(new IDoubleClickListener() {
            public void doubleClick(DoubleClickEvent event) {
                actionDoubleClick.run();
            }
        });
    }

    /**
     * Creates pop-up menu actions
     */
    private void doCreateActions() {
        actionCopySelectionToClipboardAsRichText = new Action("as Rich Text") {
            public void run() {
                copyToClipboard(tableControl, true, true);
            }
        };

        actionCopyStackToClipboardAsRichText = new Action("as Rich Text") {
            public void run() {
                copyToClipboard(tableControl, false, true);
            }
        };

        actionCopySelectionToClipboardAsPlainText = new Action("as Plain Text") {
            public void run() {
                copyToClipboard(tableControl, true, false);
            }
        };

        actionCopyStackToClipboardAsPlainText = new Action("as Plain Text") {
            public void run() {
                copyToClipboard(tableControl, false, false);
            }
        };

        actionOpenSourceFile = new Action("Open Source File") {
            public void run() {
                openSourceFile(tableControl);
            }
        };

        actionDoubleClick = new Action() {
            public void run() {
                openSourceFile(tableControl);
            }
        };
    }

    /**
     * Setting the focus in opened file there where the method name occurs,
     * must call after file is opened and only if opening was successful
     * @param location
     * @throws CoreException
     */
    private static void setFocusToLineWhereMethodIs(final SourceFileLocation location) throws CoreException {
        //Runnable to open new file
        final IWorkspaceRunnable runSetFocus = new IWorkspaceRunnable() {
            public void run(IProgressMonitor monitor) throws CoreException {
                // do the actual work in here

                try {
                    //Setting focus to correct line
                    IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow()
                            .getActivePage();
                    IEditorPart activeEditor = activePage.getActiveEditor();

                    if (activeEditor != null && activeEditor instanceof TextEditor) {

                        // This is actually an instance of 'org.eclipse.cdt.internal.ui.editor.CEditor' 
                        // that extends org.eclipse.ui.editors.text.TextEditor
                        TextEditor editor = (TextEditor) activeEditor;
                        IDocument doc = getDocument(editor);
                        if (doc != null) {

                            String text = doc.get();
                            int methodOffset = location.getMethodOffset();
                            if (methodOffset == SourceFileLocation.OFFSET_NOT_FOUND) {
                                // Removing parameters and getting new offset.
                                String methodNameWithOutParams = location.getMethodName();
                                methodNameWithOutParams = methodNameWithOutParams.substring(0,
                                        methodNameWithOutParams.indexOf("(")); //$NON-NLS-1$
                                methodOffset = text.indexOf(methodNameWithOutParams);

                                if (methodOffset == SourceFileLocation.OFFSET_NOT_FOUND) {
                                    // Removing possible namespace and getting new offset.
                                    String separator = "::"; //$NON-NLS-1$
                                    int separatorLocation = methodNameWithOutParams.lastIndexOf(separator);
                                    if (separatorLocation > 0) {
                                        methodNameWithOutParams = methodNameWithOutParams
                                                .substring(separatorLocation + separator.length());
                                        methodOffset = text.indexOf(methodNameWithOutParams);
                                    }
                                }
                            }

                            editor.setHighlightRange(methodOffset, 0, true);
                        }
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                    Status status = new Status(IStatus.ERROR, "com.nokia.s60tools.crashanalyser", 0, e //$NON-NLS-1$
                            .getMessage(), e);

                    throw new CoreException(status);
                }

            }
        };

        ResourcesPlugin.getWorkspace().run(runSetFocus, null, IWorkspace.AVOID_UPDATE, null);
    }

    /**
     * Returns the document interface for the currently active document 
     * in the given editor.
     * @param editor Editor to ask currently active document from. 
     * @return Document interface if found, otherwise <code>null</code>.
     */
    private static IDocument getDocument(TextEditor editor) {

        TextFileDocumentProvider documentProvider = (TextFileDocumentProvider) editor.getDocumentProvider();
        if (documentProvider != null) {
            return documentProvider.getDocument(editor.getEditorInput());
        }
        return null;
    }

    /**
     * Shows a message box with given message
     * @param message
     */
    private static void showMessage(String message, Shell shell) {
        MessageDialog.openInformation(shell, "Crash Analyser", message);
    }

    /**
     * Tries to open source file for the selected line in the call stack table
     * @param tableControl
     */
    static void openSourceFile(Table tableControl) {
        try {
            if (tableControl.getSelection() != null && tableControl.getSelection().length == 1) {
                StackEntry stackEntry = (StackEntry) tableControl.getSelection()[0].getData();

                // we need code segment in order to be able to open source code,
                // if we don't find code segment, notify user
                if (stackEntry == null || "".equals(stackEntry.getCodeSegmentName())) {
                    showMessage(
                            "CODE SEGMENT NOT FOUND\n\nThe selected row does not contain information from which Code Segment (e.g. dll, exe) its address can be found. Source file cannot be opened.",
                            tableControl.getShell());
                    return;
                }

                String currentSdkName = SourceSdkManager.getCurrentSkdName();
                // user must first choose an active SDK. If it has not been chosen, notify user
                if (currentSdkName == null || "".equals(currentSdkName)) {
                    showMessage(
                            "ACTIVE SDK NOT SELECTED.\n\n Please select Active SDK first by right-clicking in the Call Stack table.",
                            tableControl.getShell());
                    return;
                }

                String epocroot = SourceSdkManager.getEpocroot(currentSdkName);
                // user might have e.g. selected long ago an sdk which is not available anymore
                if (epocroot == null) {
                    showMessage(
                            "INVALID ACTIVE SDK.\n\n Please select a valid Active SDK by right-clicking in the Call Stack table.",
                            tableControl.getShell());
                    return;
                }
                File epoc = new File(epocroot);

                // epocroot for the selected SDK if not valid, notify user
                if (!epoc.isDirectory() || !epoc.exists()) {
                    String message = "INVALID EPOCROOT.\n\nThe EPOCROOT (" + epocroot + ") of selected Active SDK ("
                            + currentSdkName
                            + ") is not valid. Please check your SDK settings from Window > Preferences > Carbide.c++ > SDK Preferences, or select a different Active SDK by right-clicking in the Call Stack table.";
                    showMessage(message, tableControl.getShell());
                    return;
                }
                // currentSdkName is e.g. 'SDK_Name armv5 urel', we need to get out, build and variant
                int lastSpace = currentSdkName.lastIndexOf(" ");
                int secondToLastSpace = currentSdkName.lastIndexOf(" ", lastSpace - 1);

                String build = currentSdkName.substring(lastSpace + 1, currentSdkName.length()); //e.g. urel
                String variant = currentSdkName.substring(secondToLastSpace + 1, lastSpace); // e.g. armv5

                String methodNameAsItsInMapFile = tableControl.getSelection()[0].getText(COLUMN_SYMBOL);
                String dllName = stackEntry.getCodeSegmentName();

                ISourceFinder finder = SourceFinderFactory
                        .createMapSourceFinder(CrashAnalyserEditorConsole.getInstance());

                SourceFileLocation location = finder.findSourceFileByMethodName(methodNameAsItsInMapFile, dllName,
                        variant, build, epocroot);

                File file = new File(location.getSourceFileLocation());
                // we could not find the source file, notify user
                if (file == null || !file.exists()) {
                    showMessage("SOURCE FILE CANNOT BE FOUND\n\n" + file.getName() + " was not found from "
                            + FileOperations.getFolder(location.getSourceFileLocation())
                            + ". Please make sure you have selected a correct Active SDK by right-clicking in the Call Stack table. It is also possible that you don't have all source files for your Active SDK present. Source file cannot be opened.",
                            tableControl.getShell());
                    return;
                }

                //Create URI to open file
                String uriStr = location.getSourceFileLocation().replace("\\", "/"); //$NON-NLS-1$ //$NON-NLS-2$
                uriStr = "file://" + uriStr; //$NON-NLS-1$
                final URI srcURI = new URI(uriStr);

                //Find default editor for that file
                IEditorRegistry reg = PlatformUI.getWorkbench().getEditorRegistry();

                IEditorDescriptor editor = reg.getDefaultEditor(file.getName());
                //We open editor by it's ID
                final String editorId = editor.getId();

                IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
                try {
                    IEditorPart part = IDE.openEditor(page, srcURI, editorId, true);
                    if (part != null) {
                        //Set focus to correct line
                        setFocusToLineWhereMethodIs(location);
                    }
                } catch (PartInitException e) {
                    e.printStackTrace();
                }
            }
        } catch (CannotFoundFileException ex) {
            String newLine = System.getProperty("line.separator");
            showMessage(ex.getMessage() + newLine + newLine
                    + "Please check that you have selected correct Active SDK, by right-clicking in the Call Stack table.",
                    tableControl.getShell());
        } catch (Exception e) {
            e.printStackTrace();
            showMessage("Unable to find source file. Please check your SDK properties.", tableControl.getShell());
        }
    }

    /**
     * Copies data from table into clipboard 
     * @param table table from which data is copied from
     * @param selection if true, only the selected rows are copied, if false, all rows are copied
     * @param richText if true, data is copied in rich-text format, if false, data is copied in plain text format
     */
    static void copyToClipboard(Table table, boolean selection, boolean richText) {
        TableItem[] items = null;
        // copy only selected rows
        if (selection)
            items = table.getSelection();
        // copy all rows
        else
            items = table.getItems();

        if (items != null && items.length > 0) {
            // rich-text format
            if (richText) {
                try {
                    String data = HtmlFormatter.formatStackForClipboard(items, table.getColumnCount());
                    byte[] bytes = convertToHTMLFormat(data);
                    PEBClip clip = new PEBClip();
                    clip.setData(WDataTransferer.CF_HTML, bytes);
                } catch (Exception e) {
                }
                // plain text format
            } else {
                String separator = System.getProperty("line.separator");
                String data = "";
                for (int i = 0; i < items.length; i++) {
                    TableItem item = items[i];
                    data += String.format("%-10s  %-10s  %-6s  %-6s  %s    %s", item.getText(COLUMN_ADDRESS),
                            item.getText(COLUMN_VALUE), item.getText(COLUMN_TEXT), item.getText(COLUMN_OFFSET),
                            item.getText(COLUMN_OBJECT), item.getText(COLUMN_SYMBOL)) + separator;
                }
                StringSelection stringSelection = new StringSelection(data);
                Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
                clipboard.setContents(stringSelection, null);
            }
        }
    }

    /**
     * surrounds the html with this envelope, ready for
     * windows clipboard (jdk support can be made better)
     * <pre>
     * Version:1.0
     * StartHTML:00000000000
     * EndHTML:00000000000
     * StartFragment:00000000000
     * EndFragment:00000000000
     * &lt;!--StartFragment--&gt;
     * ...
     * &lt;!-- EndFragment-- &gt;
     * </pre>
     * We have to return a byte array 'cause in Windows the html needs to be utf-8
     * encoded. And because we have to calculate char-offsets, we encode it here.
     * @param htmlText
     * @return byte[]
     */
    static byte[] convertToHTMLFormat(String htmlText) {
        try {
            String sep = "\r\n";
            String header = "Version:1.0" + sep + "StartHTML:00000000000" + sep + "EndHTML:00000000000" + sep
                    + "StartFragment:00000000000" + sep + "EndFragment:00000000000" + sep;

            String html = "<!--StartFragment-->\r\n" + htmlText + "<!--EndFragment-->\r\n";

            byte[] bHtml = html.getBytes("UTF-8");// encode first 'cause it may grow

            int headerLen = header.length();
            int htmlLen = bHtml.length;

            StringBuffer buf = new StringBuffer(header);
            setValue(buf, "StartHTML", headerLen - 1);
            setValue(buf, "EndHTML", headerLen + htmlLen - 1);
            setValue(buf, "StartFragment", headerLen - 1);
            setValue(buf, "EndFragment", headerLen + htmlLen - 1);
            byte[] bHeader = buf.toString().getBytes("UTF-8");// should stay the same (no nonASCII chars in header)

            byte result[] = new byte[headerLen + htmlLen];
            System.arraycopy(bHeader, 0, result, 0, bHeader.length);
            System.arraycopy(bHtml, 0, result, bHeader.length, bHtml.length);

            return result;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Replaces name+":00000000000" with name+":xxxxxxxxxxx" where xxx... is the '0' padded value.
     * Value can't be to long, since maxint can be displayed with 11 digits. If value is below zero
     * there is enough place (10 for the digits 1 for sign).<br>
     * If the search is not found nothing is done.
     * @param src
     * @param name
     * @param value
     */
    private static void setValue(StringBuffer src, String name, int value) {
        int val = value;
        String search = name + ":00000000000";
        int pos = src.indexOf(search);
        if (pos == -1)
            return;// not found, do nothing

        boolean belowZero = val < 0;
        if (belowZero)
            val = -val;

        src.replace(pos + search.length() - (val + "").length(), pos + search.length(), val + "");
        if (belowZero)
            src.setCharAt(pos + name.length() + 1, '-'); // +1 'cause of ':' in "SearchMe:"
    }

    /**
     * creates context menu accordingly to what is selected in the table.
     */
    private void doCreateContextMenu() {

        // Active SDK menu
        subMenuSdk = new MenuManager("Active SDK");
        subMenuSdk.setRemoveAllWhenShown(true);
        subMenuSdk.addMenuListener(new IMenuListener() {
            public void menuAboutToShow(IMenuManager manager) {
                Map<String, String> sdks = SourceSdkManager.getAllSdks();
                String currentSdk = SourceSdkManager.getCurrentSkdName();
                // if there are sdks
                if (sdks != null && !sdks.isEmpty()) {
                    String[] sdkNames = sdks.keySet().toArray(new String[sdks.size()]);
                    java.util.Arrays.sort(sdkNames);
                    // go through all found sdks and and them to pop-up menu
                    for (int i = 0; i < sdkNames.length; i++) {
                        String sdk = sdkNames[i];
                        CommandContributionItemParameter p = new CommandContributionItemParameter(
                                PlatformUI.getWorkbench().getActiveWorkbenchWindow(), null,
                                "com.nokia.s60tools.crashanalyser.commands.SdkSelection",
                                CommandContributionItem.STYLE_PUSH);
                        p.label = sdk;
                        // if this sdk is selected as Active sdk, draw an circular image to 
                        // this menu item to indicate that this is the active sdk
                        if (sdk.equalsIgnoreCase(currentSdk)) {
                            p.icon = ImageResourceManager.getImageDescriptor((ImageKeys.SELECTED_SDK));
                        }
                        CommandContributionItem item = new CommandContributionItem(p);
                        subMenuSdk.add(item);
                    }
                }
            }
        });

        final MenuManager subMenuSelection = new MenuManager("Copy Selection to Clipboard");
        subMenuSelection.add(actionCopySelectionToClipboardAsPlainText);
        subMenuSelection.add(actionCopySelectionToClipboardAsRichText);

        final MenuManager subMenuFull = new MenuManager("Copy Whole Stack to Clipboard");
        subMenuFull.add(actionCopyStackToClipboardAsPlainText);
        subMenuFull.add(actionCopyStackToClipboardAsRichText);

        MenuManager manager = new MenuManager("#PopupMenu");
        Menu menu = manager.createContextMenu(getControl());
        getControl().setMenu(menu);

        manager.setRemoveAllWhenShown(true);
        manager.addMenuListener(new IMenuListener() {
            public void menuAboutToShow(IMenuManager manager) {
                Table table = (Table) getControl();
                if (table.getSelection() != null) {
                    // add 'Open source file' and 'Active SDK' menu items to pop-up menu
                    // only if exactly one row is selected in table and if that
                    // row's symbol column is not empty
                    if (table.getSelection().length == 1
                            && !"".equals(table.getSelection()[0].getText(COLUMN_SYMBOL))) {
                        manager.add(actionOpenSourceFile);
                        manager.add(subMenuSdk);
                        manager.add(new Separator());
                    }
                    manager.add(subMenuSelection);
                    manager.add(subMenuFull);
                }
            }
        });
    }
}