/**********************************************************************
 * Copyright (c) 2013, 2019 Ericsson, École Polytechnique de Montréal
 *
 * All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License 2.0 which
 * accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *   Matthew Khouzam - Initial API and implementation
 *   Patrick Tasse - Update open trace and add open experiment
 *   Geneviève Bastien - Merge methods to open trace and experiments
 *   Bernd Hufmann - Updated handling of directory traces
 **********************************************************************/

package org.eclipse.tracecompass.tmf.ui.project.model;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.URIUtil;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.util.OpenStrategy;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tracecompass.common.core.log.TraceCompassLog;
import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils.FlowScopeLog;
import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils.FlowScopeLogBuilder;
import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils.ScopeLog;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.internal.tmf.ui.project.model.TmfImportHelper;
import org.eclipse.tracecompass.tmf.core.TmfCommonConstants;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceImportException;
import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceType;
import org.eclipse.tracecompass.tmf.core.project.model.TraceTypeHelper;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;
import org.eclipse.tracecompass.tmf.ui.editors.TmfEditorInput;
import org.eclipse.tracecompass.tmf.ui.editors.TmfEventsEditor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IReusableEditor;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.FileEditorInput;

/**
 * Open trace helper
 *
 * Helper class for opening trace resources and loading them to a tracing
 * project.
 *
 * @author Matthew Khouzam
 */
public class TmfOpenTraceHelper {

    private static final @NonNull Logger LOGGER = TraceCompassLog.getLogger(TmfOpenTraceHelper.class);
    private static final @NonNull String LOCAL_CATEGORY = "TmfOpenTraceHelper"; //$NON-NLS-1$
    private static final String ENDL = System.getProperty("line.separator"); //$NON-NLS-1$
    private static final Set<TmfCommonProjectElement> fOpening = Collections.synchronizedSet(new HashSet<>());

    private TmfOpenTraceHelper() {
        // do nothing
    }


    /**
     * Opens a trace from a path while importing it to the destination folder. The
     * trace is linked as a resource.
     *
     * @param destinationFolder
     *            The destination trace folder
     * @param path
     *            the file to import
     * @param shell
     *            the shell to use for dialogs
     * @return IStatus OK if successful. In addition to the OK status, a code OK
     *         means the trace will be opened correctly, otherwise, a code of
     *         {@link IStatus#INFO} means the operation completely successfully,
     *         but the path won't be opened as a trace.
     * @throws CoreException
     *             core exceptions if something is not well set up in the back end
     */
    public static IStatus openTraceFromPath(TmfTraceFolder destinationFolder, String path, Shell shell) throws CoreException {
        return openTraceFromPath(destinationFolder, path, shell, null);
    }

    /**
     * Opens a trace from a path while importing it to the destination folder.
     * The trace is linked as a resource.
     *
     * @param destinationFolder
     *            The destination trace folder
     * @param path
     *            the file to import
     * @param shell
     *            the shell to use for dialogs
     * @param tracetypeHint
     *            The trace type id, can be null
     * @return IStatus OK if successful. In addition to the OK status, a code OK
     *         means the trace will be opened correctly, otherwise, a code of
     *         {@link IStatus#INFO} means the operation completely successfully,
     *         but the path won't be opened as a trace.
     * @throws CoreException
     *             core exceptions if something is not well set up in the back
     *             end
     */
    public static IStatus openTraceFromPath(TmfTraceFolder destinationFolder, String path, Shell shell, String tracetypeHint) throws CoreException {
        final String pathToUse = checkTracePath(path);
        TraceTypeHelper traceTypeToSet = null;
        try (ScopeLog scopeLog = new ScopeLog(LOGGER, Level.FINE, "TmfOpenTraceHelper#openTraceFromPath", "Get trace type")) { //$NON-NLS-1$//$NON-NLS-2$
            traceTypeToSet = TmfTraceTypeUIUtils.selectTraceType(pathToUse, null, tracetypeHint);
        } catch (TmfTraceImportException e) {
            TraceUtils.displayErrorMsg(e);
            return new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage());
        }

        IFolder folder = destinationFolder.getResource();
        String traceName = getTraceName(pathToUse, folder);
        if (traceExists(pathToUse, folder)) {
            return openTraceFromFolder(destinationFolder, traceName);
        }
        final IPath pathString = Path.fromOSString(pathToUse);
        IResource linkedTrace = TmfImportHelper.createLink(folder, pathString, traceName);

        if (linkedTrace == null || !linkedTrace.exists()) {
            return new Status(IStatus.ERROR, Activator.PLUGIN_ID,
                    Messages.TmfOpenTraceHelper_LinkFailed);
        }

        String sourceLocation = URIUtil.toUnencodedString(pathString.toFile().toURI());
        linkedTrace.setPersistentProperty(TmfCommonConstants.SOURCE_LOCATION, sourceLocation);

        // No trace type was determined.
        if (traceTypeToSet == null) {
            return new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.INFO,
                                        Messages.TmfOpenTraceHelper_NoTraceType, null);
        }

        IStatus ret = TmfTraceTypeUIUtils.setTraceType(linkedTrace, traceTypeToSet);
        if (ret.isOK()) {
            ret = openTraceFromFolder(destinationFolder, traceName);
        }
        return ret;
    }

    /**
     * Checks whether the parent or grandparent of given path to a file is a valid
     * directory trace. If it is a directory trace then return the parent or
     * grandparent path.
     *
     * @param path
     *            the path to check
     * @return path to use for trace type validation.
     */
    private static String checkTracePath(String path) {
        File file = new File(path);
        if (file.exists() && !file.isDirectory()) {
            // First check parent
            File parent = file.getParentFile();
            String pathToUse = parent.getAbsolutePath();
            if (TmfTraceType.isDirectoryTrace(pathToUse)) {
                return pathToUse;
            }
            // Second check grandparent
            File grandParent = parent.getParentFile();
            if (grandParent != null) {
                pathToUse = grandParent.getAbsolutePath();
                if (TmfTraceType.isDirectoryTrace(pathToUse)) {
                    return pathToUse;
                }
            }
        }
        return path;
    }

    private static boolean traceExists(String path, IFolder folder) {
        String val = getTraceName(path, folder);
        return (folder.findMember(val) != null);
    }

    private static boolean isWrongMember(IFolder folder, String name, final File traceFile) {
        final IResource candidate = folder.findMember(name);
        if (candidate != null) {
            final IPath rawLocation = candidate.getRawLocation();
            File file = rawLocation.toFile();
            try {
                file = file.getCanonicalFile();
            } catch (IOException e) {
                /* just use original file path */
            }
            return !file.equals(traceFile);
        }
        return false;
    }

    /**
     * Gets the display name, either "filename" or "filename(n)" if there is already
     * a filename existing where n is the next unused integer starting from 2
     *
     * @param path
     *            the file path
     * @param folder
     *            the folder to import to
     * @return the filename
     */
    private static String getTraceName(String path, IFolder folder) {
        String name;
        File traceFile = new File(path);
        try {
            traceFile = traceFile.getCanonicalFile();
        } catch (IOException e) {
            /* just use original file path */
        }
        name = traceFile.getName();
        for (int i = 2; isWrongMember(folder, name, traceFile); i++) {
            name = traceFile.getName() + '(' + i + ')';
        }
        return name;
    }

    /**
     * Open a trace from a trace folder
     *
     * @param destinationFolder
     *            The destination trace folder
     * @param traceName
     *            the trace name
     * @return success or error
     */
    private static IStatus openTraceFromFolder(TmfTraceFolder destinationFolder, String traceName) {
        final List<ITmfProjectModelElement> elements = destinationFolder.getChildren();
        TmfTraceElement traceElement = null;
        for (ITmfProjectModelElement element : elements) {
            if (element instanceof TmfTraceElement && element.getName().equals(traceName)) {
                traceElement = (TmfTraceElement) element;
            }
        }
        if (traceElement == null) {
            return new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(Messages.TmfOpenTraceHelper_TraceNotFound, traceName));
        }
        IStatus status = openFromElement(traceElement);
        if (!status.isOK()) {
            Activator.getDefault().logError("Error opening trace from folder: " + status.getMessage()); //$NON-NLS-1$
        }
        return status;
    }

    private static ITmfTrace openTraceElement(final TmfTraceElement traceElement) {
        final ITmfTrace trace = traceElement.instantiateTrace();
        final ITmfEvent traceEvent = traceElement.instantiateEvent();
        if ((trace == null) || (traceEvent == null)) {
            TraceUtils.displayErrorMsg(NLS.bind(Messages.TmfOpenTraceHelper_OpenElement, traceElement.getTypeName()),
                    Messages.TmfOpenTraceHelper_NoTraceType);
            if (trace != null) {
                trace.dispose();
            }
            return null;
        }

        try {
            trace.initTrace(traceElement.getResource(), traceElement.getResource().getLocation().toOSString(), traceEvent.getClass(), traceElement.getElementPath(), traceElement.getTraceType());
        } catch (final TmfTraceException e) {
            TraceUtils.displayErrorMsg(NLS.bind(Messages.TmfOpenTraceHelper_OpenElement, traceElement.getTypeName()),
                    Messages.TmfOpenTraceHelper_InitError + ENDL + ENDL + e, e.getCause());
            trace.dispose();
            return null;
        }
        return trace;
    }

    private static ITmfTrace openExperimentElement(final TmfExperimentElement experimentElement) {
        /* Experiment element now has an experiment type associated with it */
        final TmfExperiment experiment = experimentElement.instantiateTrace();
        if (experiment == null) {
            TraceUtils.displayErrorMsg(NLS.bind(Messages.TmfOpenTraceHelper_OpenElement, experimentElement.getTypeName()),
                    NLS.bind(Messages.TmfOpenTraceHelper_NoTraceOrExperimentType, experimentElement.getTypeName()));
            return null;
        }

        // Instantiate the experiment's traces
        final List<TmfTraceElement> traceEntries = experimentElement.getTraces();
        int cacheSize = Integer.MAX_VALUE;
        final ITmfTrace[] traces = new ITmfTrace[traceEntries.size()];
        for (int i = 0; i < traceEntries.size(); i++) {
            TmfTraceElement element = traceEntries.get(i);

            // Since trace is under an experiment, use the original trace from
            // the traces folder
            element = element.getElementUnderTraceFolder();

            ITmfTrace trace = null;
            if (element.getParent() instanceof TmfExperimentElement) {
                TraceUtils.displayErrorMsg(NLS.bind(Messages.TmfOpenTraceHelper_OpenElement, element.getTypeName()),
                        NLS.bind(Messages.TmfOpenTraceHelper_TraceNotFound, element.getElementPath()));
            } else {
                trace = openTraceElement(element);
            }

            if (trace == null) {
                for (int j = 0; j < i; j++) {
                    traces[j].dispose();
                }
                experiment.dispose();
                return null;
            }
            cacheSize = Math.min(cacheSize, trace.getCacheSize());

            traces[i] = trace;
        }

        // Create the experiment
        experiment.initExperiment(ITmfEvent.class, experimentElement.getName(), traces, cacheSize, experimentElement.getResource(), experimentElement.getTraceType());

        return experiment;
    }

    private static ITmfTrace openProjectElement(final TmfCommonProjectElement element) {
        ITmfTrace trace = null;
        if (element instanceof TmfTraceElement) {
            trace = openTraceElement((TmfTraceElement) element);
        } else if (element instanceof TmfExperimentElement) {
            trace = openExperimentElement((TmfExperimentElement) element);
        }
        return trace;
    }

    /**
     * Open a trace (or experiment) from a project element. If the trace is
     * already opened, its editor is activated and brought to top.
     *
     * @param traceElement
     *            the {@link TmfTraceElement} to open
     * @return The status of the opening. If the status is {@link IStatus#OK},
     *         then the element will be opened. In addition to the OK status, a
     *         code OK means that a trace trace will be opened correctly,
     *         otherwise, a code of {@link IStatus#INFO} means the operation
     *         completely successfully, but the path won't be opened as a trace.
     * @since 5.2
     */
    public static IStatus openFromElement(final TmfCommonProjectElement traceElement) {
        try (FlowScopeLog flow = new FlowScopeLogBuilder(LOGGER, Level.FINE, "openTraceFromElement").setCategory(LOCAL_CATEGORY).build()) { //$NON-NLS-1$
            AtomicReference<IFile> bookmarksFile = new AtomicReference<>();
            try (FlowScopeLog bmFlow = new FlowScopeLogBuilder(LOGGER, Level.FINE, "createBookmarkFile").setParentScope(flow).build()) { //$NON-NLS-1$
                PlatformUI.getWorkbench().getProgressService().busyCursorWhile(monitor -> {
                    try {
                        traceElement.refreshSupplementaryFolder(monitor);
                        bookmarksFile.set(traceElement.createBookmarksFile(monitor));
                    } catch (OperationCanceledException e) {
                    } catch (CoreException e) {
                        throw new InvocationTargetException(e);
                    }
                });
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(Messages.TmfOpenTraceHelper_ErrorOpeningElement, traceElement.getElementPath()) + ENDL + ENDL + e.getMessage(), e);
            } catch (InvocationTargetException e) {
                Activator.getDefault().logError(NLS.bind(Messages.TmfOpenTraceHelper_ErrorOpeningElement, traceElement.getTypeName()) + ' ' + traceElement.getName());
                TraceUtils.displayErrorMsg(NLS.bind(Messages.TmfOpenTraceHelper_OpenElement, traceElement.getTypeName()),
                        NLS.bind(Messages.TmfOpenTraceHelper_ErrorElement, traceElement.getTypeName()) + ENDL + ENDL + e.getTargetException().getMessage(), e.getTargetException());
                return new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(Messages.TmfOpenTraceHelper_ErrorOpeningElement, traceElement.getElementPath()) + ENDL + ENDL + e.getMessage(), e);
            }
            IFile file = bookmarksFile.get();
            if (file == null) {
                return new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(Messages.TmfOpenTraceHelper_ErrorOpeningElement, traceElement.getElementPath()) + ENDL + ENDL);
            }
            final IWorkbench wb = PlatformUI.getWorkbench();
            IWorkbenchWindow window = wb.getActiveWorkbenchWindow();
            if (window == null) {
                return new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(Messages.TmfOpenTraceHelper_ErrorOpeningElement, traceElement.getElementPath()) + ENDL + ENDL);
            }
            final IWorkbenchPage activePage = window.getActivePage();
            final IEditorPart editor = findEditor(new FileEditorInput(file), true);
            if (editor != null) {
                activePage.activate(editor);
                return editor instanceof TmfEventsEditor ? Status.OK_STATUS :
                    new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.INFO, NLS.bind(Messages.TmfOpenTraceHelper_NotATrace, traceElement.getElementPath()), null);
            }

            // If a trace type is not set then delegate it to the eclipse platform
            if ((traceElement instanceof TmfTraceElement) && (traceElement.getResource() instanceof IFile) && (traceElement.getTraceType() == null)) {
                try (FlowScopeLog bmFlow = new FlowScopeLogBuilder(LOGGER, Level.FINE, "OpenEditor").setParentScope(flow).build()) { //$NON-NLS-1$
                    boolean activate = OpenStrategy.activateOnOpen();
                    // only local open is supported
                    IEditorPart openedEditor = IDE.openEditor(activePage, file, activate);
                    return openedEditor instanceof TmfEventsEditor ? Status.OK_STATUS :
                        new Status(IStatus.OK, Activator.PLUGIN_ID, IStatus.INFO, NLS.bind(Messages.TmfOpenTraceHelper_NotATrace, traceElement.getElementPath()), null);
                } catch (PartInitException e) {
                    TraceUtils.displayErrorMsg(NLS.bind(Messages.TmfOpenTraceHelper_OpenElement, traceElement.getTypeName()),
                            NLS.bind(Messages.TmfOpenTraceHelper_ErrorOpeningElement, traceElement.getElementPath()) + ENDL + ENDL + e.getMessage(), e);
                    return new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(Messages.TmfOpenTraceHelper_ErrorOpeningElement, traceElement.getElementPath()) + ENDL + ENDL + e.getMessage(), e);
                }
            }
            if (!fOpening.add(traceElement)) {
                /* element is already opening */
                return Status.OK_STATUS;
            }
            OpenProjectElementJob job = new OpenProjectElementJob(traceElement, file);
            job.fParentScope = flow;
            job.addJobChangeListener(new JobChangeAdapter() {
                @Override
                public void done(IJobChangeEvent event) {
                    fOpening.remove(traceElement);
                }
            });
            job.schedule();
            return Status.OK_STATUS;
        }
    }

    /**
     * Job that creates a new trace instance for the specified project element and
     * opens its associated editor.
     *
     * @since 3.2
     */
    public static class OpenProjectElementJob extends Job {

        private final TmfCommonProjectElement fTraceElement;
        private final IFile fFile;
        private ITmfTrace fTrace = null;
        private @Nullable FlowScopeLog fParentScope;

        /**
         * Constructor
         *
         * @param traceElement
         *            the trace element
         * @param file
         *            the editor file
         */
        public OpenProjectElementJob(TmfCommonProjectElement traceElement, IFile file) {
            super("Opening " + traceElement.getName()); //$NON-NLS-1$
            setSystem(true);
            this.fTraceElement = traceElement;
            this.fFile = file;
        }

        @Override
        public IStatus run(IProgressMonitor monitor) {

            FlowScopeLog parentScope = fParentScope;
            FlowScopeLogBuilder flowScopeLogBuilder = new FlowScopeLogBuilder(LOGGER, Level.FINE, "OpenProjectElementJob"); //$NON-NLS-1$
            try (FlowScopeLog log = parentScope == null ? flowScopeLogBuilder.setCategory(LOCAL_CATEGORY).build()
                    : flowScopeLogBuilder.setParentScope(parentScope).build();) {
                fTrace = openProjectElement(fTraceElement);
                if (fTrace == null) {
                    return Status.OK_STATUS;
                }

                // Get the editor id from the extension point
                String traceEditorId = fTraceElement.getEditorId();
                final String editorId = (traceEditorId != null) ? traceEditorId : TmfEventsEditor.ID;
                final IEditorInput editorInput = new TmfEditorInput(fFile, fTrace);

                Display.getDefault().syncExec(() -> {
                    try (FlowScopeLog displayLog = new FlowScopeLogBuilder(LOGGER, Level.FINE, "OpenEditor").setParentScope(log).build();) { //$NON-NLS-1$

                        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
                        if (window == null) {
                            return;
                        }
                        final IWorkbenchPage activePage = window.getActivePage();
                        if (activePage == null) {
                            return;
                        }
                        try {
                            activePage.openEditor(editorInput, editorId);
                            IDE.setDefaultEditor(fFile, editorId);
                            // editor should dispose the trace on close
                        } catch (final PartInitException e) {
                            TraceUtils.displayErrorMsg(NLS.bind(Messages.TmfOpenTraceHelper_OpenElement, fTraceElement.getTypeName()),
                                    NLS.bind(Messages.TmfOpenTraceHelper_ErrorOpeningElement, fTraceElement.getTypeName()) + ENDL + ENDL + e.getMessage(), e);
                            Activator.getDefault().logError(NLS.bind(Messages.TmfOpenTraceHelper_ErrorOpeningElement, fTraceElement.getTypeName()) + ' ' + fTraceElement.getName());
                            fTrace.dispose();
                        }
                    }
                });
                return Status.OK_STATUS;
            }
        }

        /**
         * Get the new trace instance.
         *
         * @return the new trace instance
         */
        public ITmfTrace getTrace() {
            return fTrace;
        }
    }

    /**
     * Returns the editor with the specified input. Returns null if there is no
     * opened editor with that input. If restore is requested, the method finds and
     * returns the editor even if it is not restored yet after a restart.
     *
     * @param input
     *            the editor input
     * @param restore
     *            true if the editor should be restored
     * @return an editor with input equals to <code>input</code>
     */
    private static IEditorPart findEditor(IEditorInput input, boolean restore) {
        final IWorkbench wb = PlatformUI.getWorkbench();
        final IWorkbenchPage activePage = wb.getActiveWorkbenchWindow().getActivePage();
        for (IEditorReference editorReference : activePage.getEditorReferences()) {
            try {
                IEditorInput editorInput = editorReference.getEditorInput();
                if (editorInput.equals(input)) {
                    return editorReference.getEditor(restore);
                }
            } catch (PartInitException e) {
                // do nothing
            }
        }
        return null;
    }

    /**
     * Reopen a trace or experiment from a project element in the provided editor
     *
     * @param traceElement
     *            the {@link TmfTraceElement} to open
     * @param editor
     *            the reusable editor
     */
    public static void reopenTraceFromElement(final TmfCommonProjectElement traceElement, final IReusableEditor editor) {

        try (FlowScopeLog flow = new FlowScopeLogBuilder(LOGGER, Level.FINE, "reopenTraceFromElement").setCategory(LOCAL_CATEGORY).build()) { //$NON-NLS-1$
            AtomicReference<IFile> bookmarksFile = new AtomicReference<>();
            try (FlowScopeLog scopeLog = new FlowScopeLogBuilder(LOGGER, Level.FINE, "createBookmarks").setParentScope(flow).build()) { //$NON-NLS-1$
                PlatformUI.getWorkbench().getProgressService().busyCursorWhile(monitor -> {
                    try {
                        traceElement.refreshSupplementaryFolder(monitor);
                        bookmarksFile.set(traceElement.createBookmarksFile(monitor));
                    } catch (OperationCanceledException e) {
                    } catch (CoreException e) {
                        throw new InvocationTargetException(e);
                    }
                });
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return;
            } catch (final InvocationTargetException e) {
                Activator.getDefault().logError(NLS.bind(Messages.TmfOpenTraceHelper_ErrorOpeningElement, traceElement.getTypeName()) + ' ' + traceElement.getName());
                TraceUtils.displayErrorMsg(NLS.bind(Messages.TmfOpenTraceHelper_OpenElement, traceElement.getTypeName()),
                        NLS.bind(Messages.TmfOpenTraceHelper_ErrorElement, traceElement.getTypeName()) + ENDL + ENDL + e.getTargetException().getMessage(), e.getTargetException());
                return;
            }
            IFile file = bookmarksFile.get();
            if (file == null) {
                return;
            }
            Thread thread = new Thread() {
                @Override
                public void run() {
                    try (FlowScopeLog scopeLog = new FlowScopeLogBuilder(LOGGER, Level.FINE, "createThread").setParentScope(flow).build()) { //$NON-NLS-1$
                        final ITmfTrace trace = openProjectElement(traceElement);
                        if (trace == null) {
                            return;
                        }

                        final IEditorInput editorInput = new TmfEditorInput(file, trace);

                        Display.getDefault().asyncExec(() -> {
                            try (FlowScopeLog innerScopeLog = new FlowScopeLogBuilder(LOGGER, Level.FINE, "OpenEditor").setParentScope(flow).build()) { //$NON-NLS-1$
                                final IWorkbench wb = PlatformUI.getWorkbench();
                                IWorkbenchWindow activeWorkbenchWindow = wb.getActiveWorkbenchWindow();
                                if (activeWorkbenchWindow == null) {
                                    return;
                                }

                                final IWorkbenchPage activePage = activeWorkbenchWindow.getActivePage();
                                activePage.reuseEditor(editor, editorInput);
                                activePage.activate(editor);
                            }
                        });
                    }
                }
            };
            thread.start();
        }
    }
}
