blob: 27c075e6a3241bad2ed89d318a8fc9a7469db0c2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 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:
* Francois Chouinard - Initial API and implementation
* Patrick Tasse - Close editors to release resources
* Geneviève Bastien - Moved the delete code to element model's classes
* Marc-Andre Laperle - Merged DeleteTraceHandler and DeleteFolderHandler
*******************************************************************************/
package org.eclipse.tracecompass.internal.tmf.ui.project.handlers;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map.Entry;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tracecompass.tmf.core.io.ResourceUtil;
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.TmfTracesFolder;
import org.eclipse.tracecompass.tmf.ui.project.model.TraceUtils;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.handlers.HandlerUtil;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
/**
* An handler for deletion of both traces and trace folders. It allows mixing
* both types of elements.
*/
public class DeleteTraceFolderElementHandler extends AbstractHandler {
private IStructuredSelection fSelection = null;
private enum DeleteType {
/**
* Only trace folders are selected.
*/
DELETE_TRACE_FOLDERS,
/**
* Only traces are selected.
*/
DELETE_TRACES,
/**
* A mix of different elements are selected.
*/
DELETE_GENERIC,
/**
* Only Traces (top trace folders) are selected.
*/
CLEAR_TRACES_FOLDER
}
// ------------------------------------------------------------------------
// Validation
// ------------------------------------------------------------------------
@Override
public boolean isEnabled() {
// Check if we are closing down
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window == null) {
return false;
}
// Get the selection
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
IWorkbenchPart part = page.getActivePart();
if (part == null) {
return false;
}
ISelectionProvider selectionProvider = part.getSite().getSelectionProvider();
if (selectionProvider == null) {
return false;
}
ISelection selection = selectionProvider.getSelection();
// Make sure selection contains only traces and trace folders
fSelection = null;
if (selection instanceof IStructuredSelection) {
fSelection = (IStructuredSelection) selection;
Iterator<Object> iterator = fSelection.iterator();
while (iterator.hasNext()) {
Object element = iterator.next();
if (!(element instanceof TmfTraceElement) && !(element instanceof TmfTraceFolder)) {
return false;
}
}
}
// If we get here, either nothing is selected or everything is a trace or folder
return !selection.isEmpty();
}
// ------------------------------------------------------------------------
// Execution
// ------------------------------------------------------------------------
private static DeleteType getDeleteType(IStructuredSelection selection) {
DeleteType deleteType = DeleteType.DELETE_GENERIC;
Iterator<Object> iterator = selection.iterator();
while (iterator.hasNext()) {
Object element = iterator.next();
if ((element instanceof TmfTracesFolder)) {
if (deleteType == DeleteType.DELETE_GENERIC) {
deleteType = DeleteType.CLEAR_TRACES_FOLDER;
} else if (deleteType != DeleteType.CLEAR_TRACES_FOLDER) {
return DeleteType.DELETE_GENERIC;
}
} else if (element instanceof TmfTraceFolder) {
if (deleteType == DeleteType.DELETE_GENERIC) {
deleteType = DeleteType.DELETE_TRACE_FOLDERS;
} else if (deleteType != DeleteType.DELETE_TRACE_FOLDERS) {
return DeleteType.DELETE_GENERIC;
}
} else if (element instanceof TmfTraceElement) {
if (deleteType == DeleteType.DELETE_GENERIC) {
deleteType = DeleteType.DELETE_TRACES;
} else if (deleteType != DeleteType.DELETE_TRACES) {
return DeleteType.DELETE_GENERIC;
}
}
}
return deleteType;
}
private static String getTitle(final DeleteType deleteType) {
switch (deleteType)
{
case DELETE_GENERIC:
case DELETE_TRACES:
case DELETE_TRACE_FOLDERS:
return Messages.DeleteDialog_Title;
case CLEAR_TRACES_FOLDER:
return Messages.ClearDialog_Title;
default:
throw new IllegalArgumentException();
}
}
private static String getMessage(DeleteType deleteType) {
switch (deleteType)
{
case DELETE_GENERIC:
return Messages.DeleteTraceHandlerGeneric_Message;
case DELETE_TRACES:
return Messages.DeleteTraceHandler_Message;
case CLEAR_TRACES_FOLDER:
return Messages.DeleteFolderHandlerClear_Message;
case DELETE_TRACE_FOLDERS:
return Messages.DeleteFolderHandler_Message;
default:
throw new IllegalArgumentException();
}
}
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
// Check if we are closing down
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window == null) {
return null;
}
// Get the selection
ISelection selection = HandlerUtil.getCurrentSelection(event);
if (!(selection instanceof IStructuredSelection)) {
return null;
}
fSelection = (IStructuredSelection) selection;
final DeleteType deleteType = getDeleteType(fSelection);
// Confirm the operation
Shell shell = window.getShell();
MessageDialog dialog = new MessageDialog(
shell,
getTitle(deleteType),
null,
getMessage(deleteType),
MessageDialog.QUESTION,
new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL },
1);
if (dialog.open() != 0) {
return null;
}
IRunnableWithProgress operation = monitor -> {
Multimap<IProject, TmfTraceElement> tracesToDelete = LinkedHashMultimap.create();
Multimap<IProject, IResource> resourcesToDelete = LinkedHashMultimap.create();
Multimap<IProject, IFolder> foldersToCreate = LinkedHashMultimap.create();
for (Object element : fSelection.toList()) {
if (element instanceof TmfTraceElement) {
TmfTraceElement traceElement = ((TmfTraceElement) element).getElementUnderTraceFolder();
tracesToDelete.put(traceElement.getProject().getResource(), traceElement);
IResource resource = traceElement.getResource();
resourcesToDelete.put(resource.getProject(), resource);
} else if (element instanceof TmfTracesFolder) {
/*
* It is much faster to delete the Traces and Experiments folders then recreate
* them than to delete each of their child members individually
*/
TmfTracesFolder tracesFolder = (TmfTracesFolder) element;
tracesToDelete.putAll(tracesFolder.getProject().getResource(), tracesFolder.getTraces());
IFolder resource = tracesFolder.getResource();
IProject project = resource.getProject();
resourcesToDelete.put(project, resource);
foldersToCreate.put(project, resource);
TmfExperimentFolder experimentsFolder = tracesFolder.getProject().getExperimentsFolder();
if (experimentsFolder != null) {
resource = experimentsFolder.getResource();
resourcesToDelete.put(project, resource);
foldersToCreate.put(project, resource);
}
} else if (element instanceof TmfTraceFolder) {
TmfTraceFolder traceFolder = (TmfTraceFolder) element;
tracesToDelete.putAll(traceFolder.getProject().getResource(), traceFolder.getTraces());
IFolder resource = traceFolder.getResource();
resourcesToDelete.put(resource.getProject(), resource);
}
}
Iterator<IResource> iterator = resourcesToDelete.values().iterator();
while (iterator.hasNext()) {
IResource resource = iterator.next();
for (IResource parent : resourcesToDelete.get(resource.getProject())) {
// remove resource if any of its parent folders is included
if (parent.getFullPath().isPrefixOf(resource.getFullPath()) && !parent.equals(resource)) {
iterator.remove();
break;
}
}
}
// Close the traces to be deleted
monitor.setTaskName(Messages.CloseTraces_TaskName);
tracesToDelete.values().forEach(traceElement -> {
Display.getDefault().syncExec(traceElement::closeEditors);
});
SubMonitor subMon = SubMonitor.convert(monitor, tracesToDelete.size() + resourcesToDelete.size() + foldersToCreate.size());
subMon.setTaskName(Messages.DeleteTraceHandlerGeneric_TaskName);
for (Entry<IProject, Collection<IResource>> entry : resourcesToDelete.asMap().entrySet()) {
IProject project = entry.getKey();
try {
ResourcesPlugin.getWorkspace().run(mon -> {
for (IResource resource : entry.getValue()) {
ResourceUtil.deleteResource(resource, subMon.split(1));
}
for (TmfTraceElement traceElement : tracesToDelete.get(project)) {
traceElement.delete(subMon.split(1), false, false);
}
for (IFolder folder : foldersToCreate.get(project)) {
TraceUtils.createFolder(folder, subMon.split(1));
}
subMon.subTask(""); //$NON-NLS-1$
}, project, IWorkspace.AVOID_UPDATE, null);
} catch (CoreException e) {
throw new InvocationTargetException(e);
}
}
};
try {
PlatformUI.getWorkbench().getProgressService().run(true, true, operation);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (InvocationTargetException e) {
TraceUtils.displayErrorMsg(Messages.DeleteTraceHandlerGeneric_Error, e.getTargetException().toString(), e.getTargetException());
}
return null;
}
}