blob: cb4eafd564132f5e126bdde4a9b4357335698c67 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2009, 2019 Stephan Wahlbrink and others.
#
# 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, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.internal.r.objectbrowser;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.IElementComparer;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.jcommons.status.ProgressMonitor;
import org.eclipse.statet.jcommons.status.StatusException;
import org.eclipse.statet.jcommons.ts.core.Tool;
import org.eclipse.statet.ecommons.models.core.util.ElementPartition;
import org.eclipse.statet.ecommons.ts.ui.ToolRunnableDecorator;
import org.eclipse.statet.ecommons.ui.util.UIAccess;
import org.eclipse.statet.ltk.core.ElementName;
import org.eclipse.statet.nico.core.runtime.ToolProcess;
import org.eclipse.statet.nico.ui.util.ToolMessageDialog;
import org.eclipse.statet.r.console.core.AbstractRDataRunnable;
import org.eclipse.statet.r.console.core.IRDataAdapter;
import org.eclipse.statet.r.core.data.CombinedRElement;
import org.eclipse.statet.r.core.model.RElementName;
import org.eclipse.statet.r.ui.util.RElementInputContentProvider;
import org.eclipse.statet.rj.data.RObject;
class DeleteHandler extends AbstractHandler {
private static class DeleteRunnable extends AbstractRDataRunnable implements ToolRunnableDecorator {
private final List<String> names;
private final List<String> commands;
private final Set<ElementName> topEnvirs;
public DeleteRunnable(final List<String> names, final List<String> commands,
final Set<ElementName> topEnvirs) {
super("r/objectbrowser/delete", "Delete Elements"); //$NON-NLS-1$
this.names = names;
this.commands = commands;
this.topEnvirs = topEnvirs;
}
@Override
public Image getImage() {
return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_TOOL_DELETE);
}
@Override
public boolean changed(final int event, final Tool tool) {
if (event == MOVING_FROM) {
return false;
}
return true;
}
@Override
protected void run(final IRDataAdapter r,
final ProgressMonitor m) throws StatusException {
r.briefAboutToChange();
try {
for (int i = 0; i < this.names.size(); i++) {
r.evalVoid(this.commands.get(i), m);
}
}
finally {
r.briefChanged(this.topEnvirs, 0);
}
}
}
private final ObjectBrowserView view;
public DeleteHandler(final ObjectBrowserView view) {
this.view = view;
}
private boolean isValidSelection(final ITreeSelection selection) {
if (selection == null || selection.isEmpty()) {
return false;
}
for (final Iterator<?> iter = selection.iterator(); iter.hasNext(); ) {
final Object element = iter.next();
if (element instanceof ElementPartition) {
return false;
}
}
return true;
}
@Override
public void setEnabled(final @Nullable Object evaluationContext) {
final ToolProcess process = this.view.getTool();
setBaseEnabled(process != null && !process.isTerminated()
&& isValidSelection(this.view.getSelection()) );
}
@Override
public @Nullable Object execute(final ExecutionEvent event) throws ExecutionException {
if (!UIAccess.isOkToUse(this.view.getViewer())) {
return null;
}
final ITreeSelection selection = this.view.getSelection();
if (!isValidSelection(selection)) {
return null;
}
final TreePath[] treePaths = selection.getPaths();
Arrays.sort(treePaths, new Comparator<TreePath>() {
@Override
public int compare(final TreePath o1, final TreePath o2) {
return o1.getSegmentCount()-o2.getSegmentCount();
}
});
final IElementComparer comparer = new IElementComparer() {
@Override
public int hashCode(final Object e) {
return e.hashCode();
}
@Override
public boolean equals(final Object e1, final Object e2) {
return (e1 == e2);
}
};
final List<String> commands= new ArrayList<>(treePaths.length);
final List<String> names= new ArrayList<>(treePaths.length);
final Set<ElementName> topEnvirs= new HashSet<>(treePaths.length);
ITER_ELEMENTS: for (int i = 0; i < treePaths.length; i++) {
for (int j = 0; j < i; j++) {
if (treePaths[j] != null && treePaths[i].startsWith(treePaths[j], comparer)) {
treePaths[i] = null;
continue ITER_ELEMENTS;
}
}
final TreePath treePath = treePaths[i];
final CombinedRElement element= RElementInputContentProvider.getCombinedRElement(treePath.getLastSegment());
final CombinedRElement parent= element.getModelParent();
final RElementName elementName = this.view.getFQElementName(treePath);
if (parent != null && elementName != null) {
final RElementName topName;
switch (parent.getRObjectType()) {
case RObject.TYPE_ENVIRONMENT: {
final RElementName envirName = (treePath.getSegmentCount() > 1) ? this.view.getFQElementName(treePath.getParentPath()) : parent.getElementName();
final ElementName itemName = element.getElementName();
topName = elementName.getScope();
if (envirName != null) { // elementName ok => segmentName ok
commands.add("rm(`"+itemName.getSegmentName()+"`,"+ //$NON-NLS-1$ //$NON-NLS-2$
"pos="+RElementName.createDisplayName(envirName, RElementName.DISPLAY_FQN | RElementName.DISPLAY_EXACT)+ //$NON-NLS-1$
")"); //$NON-NLS-1$
names.add(elementName.getDisplayName());
topEnvirs.add(topName);
continue ITER_ELEMENTS;
}
break; }
case RObject.TYPE_LIST:
case RObject.TYPE_DATAFRAME:
case RObject.TYPE_S4OBJECT:
topName = elementName.getScope();
final String name = RElementName.createDisplayName(elementName, RElementName.DISPLAY_EXACT);
commands.add("with("+RElementName.createDisplayName(topName, RElementName.DISPLAY_FQN)+","+ //$NON-NLS-1$ //$NON-NLS-2$
name+"<-NULL"+ //$NON-NLS-1$
")"); //$NON-NLS-1$
names.add(elementName.getDisplayName());
topEnvirs.add(topName);
continue ITER_ELEMENTS;
}
}
final StringBuilder message = new StringBuilder("Selection contains unsupported object");
if (elementName != null) {
message.append("\n\t"); //$NON-NLS-1$
message.append(elementName.getDisplayName());
}
else {
message.append("."); //$NON-NLS-1$
}
MessageDialog.openError(this.view.getSite().getShell(), "Delete", message.toString());
return null;
}
final StringBuilder message = new StringBuilder(names.size() == 1 ?
"Delete this object?" :
NLS.bind("Delete these {0} objects?", names.size()));
final int show = (names.size() > 5) ? 3 : names.size();
for (int i = 0; i < show; i++) {
message.append("\n\t"); //$NON-NLS-1$
message.append(names.get(i));
}
if (show < names.size()) {
message.append("\n\t..."); //$NON-NLS-1$
}
final ToolProcess process = this.view.getTool();
if (ToolMessageDialog.openConfirm(process, this.view.getSite().getShell(), "Delete R Objects",
message.toString(), "Delete")) {
process.getQueue().add(new DeleteRunnable(names, commands, topEnvirs));
}
return null;
}
}