blob: 54541383717c068790b0cdf0384e9c005dd20849 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2018 Ericsson
*
* 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:
* Bernd Hufmann - Initial API and implementation
* Simon Delisle - Add time range selection support
*******************************************************************************/
package org.eclipse.tracecompass.internal.tmf.ui.project.operations;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.eclipse.core.resources.IFolder;
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.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.operation.ModalContext;
import org.eclipse.swt.widgets.Display;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfExperimentElement;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfExperimentFolder;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceElement;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceFolder;
import org.eclipse.tracecompass.tmf.ui.project.model.UpdateTraceBoundsJob;
import com.google.common.collect.Iterables;
/**
* Operation to add traces to an experiment.
*
* @author Bernd Hufmann
*/
public class SelectTracesOperation implements IRunnableWithProgress {
private final @Nullable TmfExperimentElement fExperimentElement;
private final @Nullable TmfTraceFolder fParentTraceFolder;
private final @Nullable List<TmfTraceElement> fTraceElements;
private final @Nullable List<IResource> fResources;
private final @Nullable Map<String, TmfTraceElement> fPreviousTraces;
private @NonNull IStatus fStatus = Status.OK_STATUS;
private final ITmfTimestamp fStartTimeRange;
private final ITmfTimestamp fEndTimeRange;
/**
* Constructor
*
* @param experimentFolderElement
* workspace experiment folder containing the experiment
* @param experiment
* experiment folder where to add traces
* @param parentTraceFolder
* the parent trace folder containing the trace resources
* @param resources
* the trace resources to add to the experiment
*/
public SelectTracesOperation(@NonNull TmfExperimentFolder experimentFolderElement, @NonNull IFolder experiment, @NonNull TmfTraceFolder parentTraceFolder, @NonNull List<IResource> resources) {
this(experimentFolderElement.getExperiment(experiment), parentTraceFolder, null, resources, null, null, null);
}
/**
* Constructor. It will add traces to given experiment and remove traces
* that don't exist anymore.
*
* @param experimentElement
* the experiment element to add the traces
* @param traces
* the trace elements
* @param previousTraces
* map of traces currently available in the experiment
*/
public SelectTracesOperation(@NonNull TmfExperimentElement experimentElement, @NonNull TmfTraceElement[] traces, @NonNull Map<String, TmfTraceElement> previousTraces) {
this(experimentElement, null, traces, null, previousTraces, null, null);
}
/**
* Constructor.
*
* @param experimentElement
* the experiment element to add the traces
* @param traces
* the trace elements
* @param previousTraces
* map of traces currently available in the experiment
* @param startTimeRange
* The start timestamp
* @param endTimeRange
* The end timestamp
*/
public SelectTracesOperation(@NonNull TmfExperimentElement experimentElement, @NonNull TmfTraceElement[] traces, @NonNull Map<String, TmfTraceElement> previousTraces, ITmfTimestamp startTimeRange, ITmfTimestamp endTimeRange) {
this(experimentElement, null, traces, null, previousTraces, startTimeRange, endTimeRange);
}
// Full constructor for internal use only
private SelectTracesOperation(TmfExperimentElement experimentElement, TmfTraceFolder parentTraceFolder, TmfTraceElement[] traces, List<IResource> resources, Map<String, TmfTraceElement> previousTraces, ITmfTimestamp startTimeRange,
ITmfTimestamp endTimeRange) {
fExperimentElement = experimentElement;
fParentTraceFolder = parentTraceFolder;
if (traces == null) {
fTraceElements = null;
} else {
fTraceElements = new ArrayList<>(Arrays.asList(traces));
}
fResources = resources;
fPreviousTraces = previousTraces;
if (startTimeRange != null && endTimeRange != null && startTimeRange.compareTo(endTimeRange) > 0) {
fStartTimeRange = endTimeRange;
fEndTimeRange = startTimeRange;
} else {
fStartTimeRange = startTimeRange;
fEndTimeRange = endTimeRange;
}
}
@Override
public void run(IProgressMonitor progressMonitor) {
TmfExperimentElement experimentElement = fExperimentElement;
if (experimentElement == null) {
return;
}
// Check if operation was cancelled.
boolean changed = false;
Map<String, TmfTraceElement> previousTraces = new HashMap<>();
if (fPreviousTraces != null) {
previousTraces = fPreviousTraces;
}
List<TmfTraceElement> elements = fTraceElements;
if (elements == null) {
if ((fParentTraceFolder != null) && (fResources != null)) {
elements = fParentTraceFolder.getTraceElements(fResources);
} else {
return;
}
}
Set<String> keys = previousTraces.keySet();
int workLoad = elements.size() + keys.size();
SubMonitor subMonitor = SubMonitor.convert(progressMonitor, workLoad);
if (fStartTimeRange != null && fEndTimeRange != null) {
workLoad += elements.size();
subMonitor.setWorkRemaining(workLoad);
elements = filterTraceElementByTimeRange(elements, subMonitor.newChild(elements.size()));
}
// We might have less elements now
subMonitor.setWorkRemaining(elements.size() + keys.size());
// Add the selected traces to the experiment
try {
for (TmfTraceElement trace : elements) {
ModalContext.checkCanceled(progressMonitor);
String name = trace.getElementPath();
if (keys.contains(name)) {
subMonitor.setTaskName(Messages.SelectTracesWizardPage_TraceRemovalTask + " " + trace.getElementPath()); //$NON-NLS-1$
keys.remove(name);
} else {
subMonitor.setTaskName(Messages.SelectTracesWizardPage_TraceSelectionTask + " " + trace.getElementPath()); //$NON-NLS-1$
experimentElement.addTrace(trace, false);
changed = true;
}
subMonitor.worked(1);
}
// Remove traces that were unchecked (thus left in fPreviousTraces)
for (Map.Entry<String, TmfTraceElement> entry : previousTraces.entrySet()) {
ModalContext.checkCanceled(progressMonitor);
TmfTraceElement trace = entry.getValue();
subMonitor.setTaskName(Messages.SelectTracesWizardPage_TraceRemovalTask + " " + trace.getElementPath()); //$NON-NLS-1$
try {
experimentElement.removeTrace(trace);
} catch (CoreException e) {
Activator.getDefault().logError(Messages.SelectTracesWizardPage_SelectionError + " " + experimentElement.getName(), e); //$NON-NLS-1$
}
changed = true;
subMonitor.worked(1);
}
if (changed) {
Display.getDefault().asyncExec(() -> {
experimentElement.closeEditors();
experimentElement.deleteSupplementaryResources();
});
}
setStatus(Status.OK_STATUS);
} catch (InterruptedException e) {
setStatus(Status.CANCEL_STATUS);
Thread.currentThread().interrupt();
} catch (Exception e) {
Activator.getDefault().logError(Messages.SelectTracesWizardPage_SelectionError, e);
setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.SelectTracesWizardPage_SelectionError, e));
}
}
/**
* Filter the given list according to the time range.
*
* @param elements
* List of TmfTraceElement to filter
* @param progressMonitor
* Progress monitor for the job
* @return A filtered list that contain traces that are in or overlap the
* time range
*/
private List<TmfTraceElement> filterTraceElementByTimeRange(List<TmfTraceElement> elements, IProgressMonitor progressMonitor) {
SubMonitor subMonitor = SubMonitor.convert(progressMonitor, elements.size());
List<TmfTraceElement> filteredElements = new ArrayList<>();
List<TmfTraceElement> knownBounds = new ArrayList<>();
Queue<TmfTraceElement> unknownBounds = new ConcurrentLinkedQueue<>();
Iterable<TmfTraceElement> traceElements = Iterables.concat(knownBounds, unknownBounds);
for (TmfTraceElement tmfTraceElement : elements) {
if (tmfTraceElement.getStartTime() == null || tmfTraceElement.getEndTime() == null) {
unknownBounds.add(tmfTraceElement);
} else {
knownBounds.add(tmfTraceElement);
}
}
if (!unknownBounds.isEmpty()) {
UpdateTraceBoundsJob job = new UpdateTraceBoundsJob(Messages.SelectTracesWizardPage_UpdateTraceBoundsJobName, new ConcurrentLinkedQueue<>(unknownBounds));
job.run(subMonitor.newChild(1));
}
for (TmfTraceElement element : traceElements) {
if (isInTimeRange(element.getStartTime(), element.getEndTime())) {
filteredElements.add(element);
}
}
return filteredElements;
}
/**
* Check if the time range overlap or is completely included in this
* SelectTracesOperation's time range.
*
* @param traceStartTime
* Start timestamp of the trace
* @param traceEndTime
* End timestamp of the trace
* @return True if the the trace is in or overlap the time range
*/
private boolean isInTimeRange(ITmfTimestamp traceStartTime, ITmfTimestamp traceEndTime) {
return traceStartTime.compareTo(fEndTimeRange) <= 0 && traceEndTime.compareTo(fStartTimeRange) >= 0;
}
/**
* Set the status for this operation
*
* @param status
* the status
*/
protected void setStatus(@NonNull IStatus status) {
fStatus = status;
}
/**
* Returns the status of the operation execution.
*
* @return status
*/
public @NonNull IStatus getStatus() {
return fStatus;
}
}