/*******************************************************************************
 * Copyright (c) 2009, 2018 Red Hat, Inc.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Red Hat - initial API and implementation
 *******************************************************************************/

package org.eclipse.linuxtools.internal.callgraph.launch;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IIndexFile;
import org.eclipse.cdt.core.index.IIndexManager;
import org.eclipse.cdt.core.index.IIndexName;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.IBinary;
import org.eclipse.cdt.core.model.ICContainer;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.jface.window.Window;
import org.eclipse.linuxtools.internal.callgraph.core.LaunchConfigurationConstants;
import org.eclipse.linuxtools.internal.callgraph.core.PluginConstants;
import org.eclipse.linuxtools.internal.callgraph.core.SystemTapUIErrorMessages;
import org.eclipse.linuxtools.profiling.launch.ProfileLaunchShortcut;
import org.eclipse.swt.widgets.Button;
import org.eclipse.ui.model.WorkbenchLabelProvider;

/**
 * Launch method for a generated script that executes on a binary <br>
 * MUST specify (String) scriptPath and call config = createConfiguration(bin)! <br>
 * Noteworthy defaults:
 * <ul>
 * <li>'name' defaults to ""</li>
 * <li>'overwrite' defaults to true</li>
 * </ul>
 * To create new launches: <br>
 * <ul>
 * <li>
 * Extend the shortcut extension in Eclipse (recommend copying code from the
 * existing launch in the plugin.xml)</li>
 * <li>
 * Create a class that extends SystemTapLaunchShortcut with a function public
 * void launch(IBinary bin, String mode) that calls super.Init().</li>
 * <li>
 * Set name</li>
 * <li>
 * If the script is to be launched with a binary, call binName = getName(bin)</li>
 * <li>
 * Call config=createConfiguration(bin, name) or
 * config=createConfiguration(name)</li>
 * <li>
 * Specify whichever of the optional parameters you need</li>
 * <li>
 * Extend the parser extension defined in org.eclipse.linuxtools.callgraph
 * .core. Build a basic parsing class.</li>
 * <li>
 * Set parserID to the id of your extension</li>
 * <li>
 * Set scriptPath</li>
 * <li>
 * If you wish to generate a script on-the-fly, override generateScript() and
 * set needToGenerate to true</li>
 * <li>
 * Call finishLaunch or finishLaunchWithoutBinary</li>
 * </ul>
 *<br>
 *<br>
     * The following protected parameters are provided by
     * SystemTapLaunchShortcut:
     *<br> <br>
     * Optional customization parameters: <code> protected String name; protected
     * String binaryPath; protected String arguments; protected String
     * outputPath; protected String dirPath; protected String generatedScript;
     * protected boolean needToGenerate; protected boolean overwrite;</code>
     * <br> <br>
     * Mandatory: <code> protected String scriptPath; protected ILaunchConfiguration
     * config;</code>

 */
public abstract class SystemTapLaunchShortcut extends ProfileLaunchShortcut {

    private static final String USER_SELECTED_ALL = "ALL"; //$NON-NLS-1$
    private static final String MAIN_FUNC_NAME = "main"; //$NON-NLS-1$

    protected String name;
    protected String binaryPath;
    protected String scriptPath;
    protected String arguments;
    protected String outputPath;
    protected String binName;
    protected String generatedScript;
    protected String parserID;
    protected String viewID;
    protected boolean needToGenerate;
    protected boolean overwrite;
    protected boolean useColours;
    protected String resourceToSearchFor;
    protected boolean searchForResource;
    protected IBinary bin;

    private boolean testMode = false;

    /**
     * Initialize variables. Highly recommend calling this function within the
     * launch methods. Will call setScriptPath, setParserID and setViewID.
     */
    public void initialize() {
        name = ""; //$NON-NLS-1$
        binaryPath = LaunchConfigurationConstants.DEFAULT_BINARY_PATH;
        arguments = LaunchConfigurationConstants.DEFAULT_ARGUMENTS;
        outputPath = PluginConstants.getDefaultIOPath();
        overwrite = true;
        generatedScript = LaunchConfigurationConstants.DEFAULT_GENERATED_SCRIPT;
        needToGenerate = false;
        useColours = false;
    }

    @Override
    protected ILaunchConfigurationType getLaunchConfigType() {
        return getLaunchManager().getLaunchConfigurationType(
                PluginConstants.CONFIGURATION_TYPE_ID);
    }

    @Override
    protected void setDefaultProfileAttributes(
            ILaunchConfigurationWorkingCopy wc) {
        SystemTapOptionsTab tab = new SystemTapOptionsTab();
        tab.setDefaults(wc);
    }

    /**
     * Helper function to complete launches. Uses protected parameters (Strings)
     * scriptPath, binaryPath, arguments, outputPath and (boolean) overwrite.
     * These must be set by the calling function (or else nonsensical results
     * will occur). <br>
     * If scriptPath has not been set, the setScriptPath() method will be
     * called.
     *
     * @param name : Used to generate the name of the new configuration
     * @param bin : Affiliated executable
     * @param mode : Mode setting
     * @param wc : A working copy of the launch configuration
     * @throws IOException
     */
    protected void finishLaunch(String name, String mode, ILaunchConfigurationWorkingCopy wc) throws IOException  {
        if (!finishLaunchHelper()) {
            return;
        }

        if (wc != null) {

            wc.setAttribute(LaunchConfigurationConstants.SCRIPT_PATH,scriptPath);

            if (!invalid(binaryPath)) {
                wc.setAttribute(LaunchConfigurationConstants.BINARY_PATH,binaryPath);
            }

            wc.setAttribute(LaunchConfigurationConstants.OUTPUT_PATH,outputPath);
            wc.setAttribute(LaunchConfigurationConstants.ARGUMENTS, arguments);
            wc.setAttribute(LaunchConfigurationConstants.GENERATED_SCRIPT,generatedScript);
            wc.setAttribute(LaunchConfigurationConstants.NEED_TO_GENERATE,needToGenerate);
            wc.setAttribute(LaunchConfigurationConstants.OVERWRITE, overwrite);
            wc.setAttribute(LaunchConfigurationConstants.USE_COLOUR,useColours);
            wc.setAttribute(LaunchConfigurationConstants.PARSER_CLASS,parserID);
            wc.setAttribute(LaunchConfigurationConstants.VIEW_CLASS, viewID);
            wc.setAttribute(LaunchConfigurationConstants.SECONDARY_VIEW_ID, ""); //$NON-NLS-1$

            if (!testMode) {
                DebugUITools.launch(wc, mode);
            }
        }

    }

    /**
     * returns true if str == null || str.length() < 1. Convenience method.
     *
     * @param str
     * @return
     */
    private static boolean invalid(String str) {
        return (str == null || str.length() < 1);
    }

    /**
     * Helper function for methods common to both types of finishLaunch.
     * @throws IOException
     *
     */
    private boolean finishLaunchHelper() throws IOException {
        if (invalid(scriptPath)) {
            scriptPath = setScriptPath();
        }
        if (invalid(scriptPath)) {
            // Setting the variable didn't work, do not launch.

            SystemTapUIErrorMessages mess = new SystemTapUIErrorMessages(
                    Messages
                            .getString("SystemTapLaunchShortcut.ErrorMessageName"), //$NON-NLS-1$
                    Messages
                            .getString("SystemTapLaunchShortcut.ErrorMessageTitle"), Messages.getString("SystemTapLaunchShortcut.ErrorMessage") + name); //$NON-NLS-1$ //$NON-NLS-2$
            mess.schedule();
            return false;
        }

        if (invalid(parserID)) {
            parserID = setParserID();
        }
        if (invalid(parserID)) {
            SystemTapUIErrorMessages mess = new SystemTapUIErrorMessages(
                    Messages.getString("SystemTapLaunchShortcut.InvalidParser1"), //$NON-NLS-1$
                    Messages.getString("SystemTapLaunchShortcut.InvalidParser2"), Messages.getString("SystemTapLaunchShortcut.InvalidParser3")); //$NON-NLS-1$ //$NON-NLS-2$
            mess.schedule();
            return false;
        }

        if (invalid(viewID)) {
            viewID = setViewID();
        }
        if (invalid(viewID)) {
            // Setting the variable didn't work, do not launch.
            SystemTapUIErrorMessages mess = new SystemTapUIErrorMessages(
                    Messages.getString("SystemTapLaunchShortcut.InvalidView1"), //$NON-NLS-1$
                    Messages.getString("SystemTapLaunchShortcut.InvalidView2"), Messages.getString("SystemTapLaunchShortcut.InvalidView3")); //$NON-NLS-1$ //$NON-NLS-2$
            mess.schedule();
            return false;
        }

        if (needToGenerate) {
            if (invalid(generatedScript)) {
                generatedScript = generateScript();
            }
            if (invalid(generatedScript)) {
                // Setting the variable didn't work, do not launch.
                SystemTapUIErrorMessages mess = new SystemTapUIErrorMessages(
                        Messages
                                .getString("SystemTapLaunchShortcut.InvalidGeneration1"), //$NON-NLS-1$
                        Messages
                                .getString("SystemTapLaunchShortcut.InvalidGeneration2"), Messages.getString("SystemTapLaunchShortcut.InvalidGeneration3"));//$NON-NLS-1$ //$NON-NLS-2$
                mess.schedule();
                return false;
            }
        }
        return true;
    }

    /**
     * Returns bin.getPath().toString()
     *
     * @param bin
     * @return
     */
    public String getName(IBinary bin) {
        if (bin != null) {
            binName = bin.getPath().toString();
        } else {
            binName = ""; //$NON-NLS-1$
        }
        return binName;
    }

    /**
     * Creates a configuration for the given IBinary
     *
     */
    @Override
    protected ILaunchConfiguration createConfiguration(IBinary bin) {
        if (bin != null) {
            return super.createConfiguration(bin);
        } else {
            try {
                return getLaunchConfigType()
                        .newInstance(
                                null,
                                getLaunchManager()
                                        .generateLaunchConfigurationName(
                                                Messages.getString("SystemTapLaunchShortcut.Invalid"))); //$NON-NLS-1$
            } catch (CoreException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * Allows null configurations to be launched. Any launch that uses a binary
     * should never call this configuration with a null parameter, and any
     * launch that does not use a binary should never call this function. The
     * null handling is included for ease of testing.
     *
     * @param bin
     * @param name
     *            - Customize the name based on the shortcut being launched
     * @return A launch configuration, or null
     */
    protected ILaunchConfigurationWorkingCopy createConfiguration(IBinary bin,
            String name) {
        ILaunchConfigurationWorkingCopy wc = null;
        if (bin != null) {
            try {
                String projectName = bin.getResource().getProjectRelativePath().toString();
                ILaunchConfigurationType configType = getLaunchConfigType();
                wc = configType.newInstance(
                        null,
                        getLaunchManager().generateLaunchConfigurationName(
                                name + " - " + bin.getElementName())); //$NON-NLS-1$

                wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME,projectName);
                wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, bin.getCProject().getElementName());
                wc.setMappedResources(new IResource[] { bin.getResource(),bin.getResource().getProject() });
                wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY,(String) null);

                setDefaultProfileAttributes(wc);

            } catch (CoreException e) {
                e.printStackTrace();
            }
        } else {

            try {
                wc = getLaunchConfigType().newInstance(
                        null,
                        getLaunchManager()
                                .generateLaunchConfigurationName(name));
                setDefaultProfileAttributes(wc);
            } catch (CoreException e) {
                e.printStackTrace();
                return null;
            }
        }
        return wc;
    }

    /*
     * The following are convenience methods for test programs, etc. to check
     * the value of certain protected parameters.
     */

    public ILaunchConfigurationType outsideGetLaunchConfigType() {
        return getLaunchConfigType();
    }

    public String getArguments() {
        return arguments;
    }

    /**
     * Retrieves the names of all functions referenced by the binary. If
     * searchForResource is true, this function will return all function names
     * belonging to an element with name matching the String held by
     * resourceToSearchFor. Otherwise it will create a dialog prompting the user
     * to select from a list of files to profile, or select the only available
     * file if only one file is available.
     *
     *
     * @param bin
     * @return
     */
    protected String getFunctionsFromBinary(IBinary bin, String targetResource) {
        String funcs = ""; //$NON-NLS-1$
        if (bin == null) {
            return funcs;
        }
        try {
            ArrayList<ICContainer> list = new ArrayList<>();
            TranslationUnitVisitor v = new TranslationUnitVisitor();

            for (ICElement b : bin.getCProject().getChildrenOfType(
                    ICElement.C_CCONTAINER)) {
                ICContainer c = (ICContainer) b;

                for (ITranslationUnit tu : c.getTranslationUnits()) {
                    if (searchForResource
                            && tu.getElementName().contains(targetResource)) {
                        tu.accept(v);
                        funcs += v.getFunctions();
                        return funcs;
                    } else {
                        if (!list.contains(c)) {
                            list.add(c);
                        }
                    }
                }

                // Iterate down to all children, checking for more C_Containers
                while (c.getChildrenOfType(ICElement.C_CCONTAINER).size() > 0) {
                    ICContainer e = null;
                    for (ICElement d : c
                            .getChildrenOfType(ICElement.C_CCONTAINER)) {
                        e = (ICContainer) d;
                        for (ITranslationUnit tu : e.getTranslationUnits()) {
                            if (searchForResource
                                    && tu.getElementName().contains(
                                            targetResource)) {
                                tu.accept(v);
                                funcs += (v.getFunctions());
                                return funcs;
                            } else {
                                if (!list.contains(c)) {
                                    list.add(c);
                                }
                            }
                        }
                    }
                    c = e;
                }
            }

            int numberOfFiles = numberOfValidFiles(list.toArray());
            if (numberOfFiles == 1) {
                for (ICContainer c : list) {
                    for (ITranslationUnit e : c.getTranslationUnits()) {
                        if (validElement(e)) {
                            e.accept(v);
                            funcs += v.getFunctions();
                        }
                    }
                }
            } else {

                Object[] unitList = chooseUnit(list, numberOfFiles);
                if (unitList == null || unitList.length == 0) {
                    return null;
                } else if (unitList.length == 1
                        && unitList[0].toString().equals(USER_SELECTED_ALL)) {
                    funcs = "*"; //$NON-NLS-1$
                    return funcs;
                }

                StringBuilder tmpFunc = new StringBuilder();
                for (String item : getAllFunctions(bin.getCProject(), unitList)) {
                    tmpFunc.append(item);
                    tmpFunc.append(" "); //$NON-NLS-1$
                }
                funcs = tmpFunc.toString();

            }

            return funcs;

        } catch (CoreException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * Creates a dialog that prompts the user to select from the given list of
     * ICElements
     *
     * @param list
     *            : list of ICElements
     * @return
     */
    private Object[] chooseUnit(List<ICContainer> list, int numberOfValidFiles) {
        ListTreeContentProvider prov = new ListTreeContentProvider();

        RuledTreeSelectionDialog dialog = new RuledTreeSelectionDialog(
                getActiveWorkbenchShell(), new WorkbenchLabelProvider(), prov);

        dialog.setTitle(Messages.getString("SystemTapLaunchShortcut.SelectFiles")); //$NON-NLS-1$
        dialog.setMessage(Messages.getString("SystemTapLaunchShortcut.SelectFilesMsg")); //$NON-NLS-1$
        dialog.setInput(list);
        dialog.setHelpAvailable(false);
        dialog.setStatusLineAboveButtons(false);
        dialog.setEmptyListMessage(Messages
                .getString("SystemTapLaunchShortcut.NoFiles")); //$NON-NLS-1$
        dialog.setContainerMode(true);

        Object[] topLevel = prov.findElements(list);
        dialog.setInitialSelections(topLevel);
        dialog.setSize(cap(topLevel.length * 10, 30, 55), cap(
                (int) (topLevel.length * 1.5), 3, 13));

        dialog.create();
        Button okButton = dialog.getOkButton();

        Object[] result = null;

        if (testMode) {
            okButton.setSelection(true);
            result = list.toArray();
            ArrayList<Object> output = new ArrayList<>();
            try {
                for (Object obj : result) {
                    if (obj instanceof ICContainer) {
                        ICElement[] array = ((ICContainer) obj).getChildren();
                        for (ICElement c : array) {
                            if (!(validElement(c))) {
                                continue;
                            }
                            if (c.getElementName().contains(MAIN_FUNC_NAME) && !output.contains(c)) {
                                output.add(c);
                            }
                        }
                    }
                }

                if (output.size() >= numberOfValidFiles) {
                    output.clear();
                    output.add(USER_SELECTED_ALL);
                }
            } catch (CModelException e) {
                e.printStackTrace();
            }

            result = output.toArray();
        } else {
            if (dialog.open() == Window.CANCEL) {
                return null;
            }
            result = dialog.getResult();
        }

        if (result == null) {
            return null;
        }

        ArrayList<Object> output = new ArrayList<>();
        try {
            for (Object obj : result) {
                if (obj instanceof ICContainer) {
                    ICElement[] array = ((ICContainer) obj).getChildren();
                    for (ICElement c : array) {
                        if (!(validElement(c))) {
                            continue;
                        }
                        if (!output.contains(c)) {
                            output.add(c);
                        }
                    }
                } else if ((obj instanceof ICElement)
                        && validElement((ICElement) obj)
                        && !output.contains(obj)) {
                    output.add(obj);
                }
            }

            if (output.size() >= numberOfValidFiles) {
                output.clear();
                output.add(USER_SELECTED_ALL);
            }
        } catch (CModelException e) {
            e.printStackTrace();
        }

        return output.toArray();
    }

    private int numberOfValidFiles(Object[] list) throws CModelException {
        int output = 0;
        for (Object parent : list) {
            if (parent instanceof ICContainer) {
                ICContainer cont = (ICContainer) parent;
                for (ICElement ele : cont.getChildren()) {
                    if (ele instanceof ICContainer) {
                        output += numberOfValidFiles(((ICContainer) ele)
                                .getChildren());
                    }
                    if (validElement(ele)) {
                        output++;
                    }
                }
            } else if ((parent instanceof ICElement)
                    && validElement((ICElement) parent)) {
                output++;
            }
        }
        return output;
    }

    /**
     * @param project
     *            : C Project Type
     * @return A String list of all functions contained within the specified C
     *         Project
     */
    private static List<String> getAllFunctions(ICProject project,
            Object[] listOfFiles) {
        try {
            GetFunctionsJob j = new GetFunctionsJob(project
                    .getHandleIdentifier(), project, listOfFiles);
            j.schedule();
            j.join();
            ArrayList<String> functionList = j.getFunctionList();

            return functionList;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Returns all ICElements in val that contains the given path. WARNING: Uses
     * .contains, so be careful with the String path or you'll get too many
     * hits.
     *
     * @param list
     * @param path
     * @return
     */
    private static boolean specialContains(Object[] list, String path) {
        for (Object val : list) {
            if (val instanceof ICElement) {
                ICElement el = (ICElement) val;
                if (el.getPath().toString().contains(path)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Returns a number clipped into the range [low,high].
     *
     * @param number
     * @param low
     * @param high
     * @return number if number is in [low,high], low, or high.
     */
    private int cap(int number, int low, int high) {
        if (number > high) {
            return high;
        }
        if (number < low) {
            return low;
        }
        return number;
    }

    /**
     * Function for generating scripts. Should be overridden by interested
     * classes
     *
     * @throws IOException
     */
    @SuppressWarnings("unused")
    public String generateScript() throws IOException {
        return null;
    }

    private static class GetFunctionsJob extends Job {
        private ArrayList<String> functionList;
        private ICProject project;
        private Object[] listOfFiles;

        public GetFunctionsJob(String name, ICProject p, Object[] o) {
            super(name);
            functionList = new ArrayList<>();
            listOfFiles = Arrays.copyOf(o, o.length);
            project = p;
        }

        @Override
        protected IStatus run(IProgressMonitor monitor) {
            IIndexManager manager = CCorePlugin.getIndexManager();
            IIndex index = null;
            IProgressMonitor m = monitor;

            if (m == null) {
                m = new NullProgressMonitor();
            }
            m.worked(1);

            try {
                index = manager.getIndex(project);
                index.acquireReadLock();

                IIndexFile[] blah = index.getAllFiles();
                for (IIndexFile file : blah) {
                    String fullFilePath = file.getLocation().getFullPath();
                    if (fullFilePath == null
                            || !specialContains(listOfFiles, fullFilePath)) {
                        continue;
                    }

                    IIndexName[] indexNamesArray = file.findNames(0,
                            Integer.MAX_VALUE);
                    for (IIndexName name : indexNamesArray) {
                        if (name.isDefinition()
                                && specialContains(listOfFiles, name.getFile()
                                        .getLocation().getFullPath())) {
                            IIndexBinding binder = index.findBinding(name);
                            if (binder instanceof IFunction
                                    && !functionList.contains(binder.getName())) {
                                functionList.add(binder.getName());
                            }
                        }
                    }

                    m.worked(1);
                }
                index.releaseReadLock();

            } catch (CoreException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return Status.OK_STATUS;
        }

        public ArrayList<String> getFunctionList() {
            return functionList;
        }
    }


    /**
     * Set the parserID variable. ParserID should point to the ID of an
     * extension extending the org.eclipse.linuxtools.callgraph.core.parser
     * extension point. This function must return the parserID to be set.
     *
     * If not declared, the parserID will be set to the default SystemTap
     * Text parser with colour support
     *
     * @return a valid parserID
     */
    public String setParserID() {
        return PluginConstants.DEFAULT_PARSER_ID;
    }

    public String getScript() {
        return generatedScript;
    }

    public void setTestMode(boolean val) {
        testMode = val;
    }

    /**
     * Set the viewID variable. ViewID should point to the ID of an extension
     * extending the org.eclipse.ui.views extension point. This function must
     * return the viewID to be set. Defaults to the SystemTap Text View, if
     * not overridden.
     *
     * @return a valid viewID
     */
    public String setViewID() {
        return PluginConstants.DEFAULT_VIEW_ID;
    }

    public static boolean validElement(ICElement e) {
        return e.getElementName().endsWith(".c") || //$NON-NLS-1$
        e.getElementName().endsWith(".cpp") || //$NON-NLS-1$
        e.getElementName().endsWith(".h"); //$NON-NLS-1$
    }


    /**
     * Default implementation of launch. It will run stap with the selected binary
     * as an argument and set the output path to <code>PluginConstants.getDefaultIOPath()</code>.
     * <br>
     * The name of the created launch will be 'DefaultSystemTapLaunch'
     */
    @Override
    public void launch(IBinary bin, String mode) {
        initialize();
        this.bin = bin;
        binName = getName(bin);
        name = "DefaultSystemTapLaunch";  //$NON-NLS-1$

        try {

            ILaunchConfigurationWorkingCopy wc = createConfiguration(bin, name);
            binaryPath = bin.getResource().getLocation().toString();
            arguments = binaryPath;
            outputPath = PluginConstants.getDefaultIOPath();
            finishLaunch(name, mode, wc);

        } catch (IOException e) {
            SystemTapUIErrorMessages mess = new SystemTapUIErrorMessages(
                    "LaunchShortcutScriptGen",  //$NON-NLS-1$
                    Messages.getString("LaunchStapGraph.ScriptGenErr"),   //$NON-NLS-1$
                    Messages.getString("LaunchStapGraph.ScriptGenErrMsg"));  //$NON-NLS-1$
            mess.schedule();
            e.printStackTrace();
        } finally {
            resourceToSearchFor = ""; //$NON-NLS-1$
            searchForResource = false;
        }


    }


    /**
     * Each launch class should define its own script path. Must return the
     * correct script path or launch will fail.
     */
    public abstract String setScriptPath();
}
