Skip to content
Snippets Groups Projects
Commit bcdfcda7 authored by Simon Barner's avatar Simon Barner
Browse files

Delete resources that directly or indirectly reference an AF3 project to be deleted:

- Scan the directory in which the AF3 project resides for external models that directly or indirectly reference the AF3 project resource.
- Note that this may be long-running operation which is why the work of the DeleteAction has been wrapped into an Job that provides an IProgressMonitor.

refs 2691
parent a1ad0612
No related branches found
No related tags found
No related merge requests found
......@@ -17,11 +17,13 @@ $Id$
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.kernel.ui.internal.actions;
import static org.eclipse.emf.ecore.util.EcoreUtil.resolveAll;
import static org.eclipse.ui.PlatformUI.getWorkbench;
import static org.fortiss.tooling.kernel.utils.LoggingUtils.error;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
......@@ -29,7 +31,16 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IContainer;
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.jobs.Job;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
......@@ -43,9 +54,11 @@ import org.fortiss.tooling.kernel.service.ICommandStackService;
import org.fortiss.tooling.kernel.service.IConnectionCompositorService;
import org.fortiss.tooling.kernel.service.IElementCompositorService;
import org.fortiss.tooling.kernel.service.IPersistencyService;
import org.fortiss.tooling.kernel.ui.ToolingKernelUIActivator;
import org.fortiss.tooling.kernel.ui.extension.base.EObjectActionBase;
import org.fortiss.tooling.kernel.ui.internal.editor.ExtendableMultiPageEditor;
import org.fortiss.tooling.kernel.ui.service.IModelEditorBindingService;
import org.fortiss.tooling.kernel.utils.ResourceUtils;
/**
* The {@link DeleteAction} uses the {@link IElementCompositorService} and the
......@@ -55,10 +68,104 @@ import org.fortiss.tooling.kernel.ui.service.IModelEditorBindingService;
* @author hoelzl
* @author $Author$
* @version $Rev$
* @ConQAT.Rating YELLOW Hash: 4E5ECAA76A7542E5C8E7E028FC4CFF68
* @ConQAT.Rating YELLOW Hash: A2D2F354AE79A3BB465C4B93E9A8E3E8
*/
public class DeleteAction extends EObjectActionBase<EObject> {
/**
*
* @author barner
* @author $Author$
* @version $Rev$
* @ConQAT.Rating RED Hash:
*/
private static final class DeleteTopLevelElementJob extends Job {
/**
* {@link ITopLevelElement} which (together with its directly or indirectly referencing
* {@link Resource}s) should be deleted.
*/
private final ITopLevelElement topElement;
/**
* {@link URI}s of {@link Resource}s that directly or indirectly reference
* {@link #topElement}.
*/
private Collection<URI> uris = Collections.emptySet();
/** {@link Shell} used to display dialog. */
private Shell sh;
/**
* Creates a {@link DeleteTopLevelElementJob} that deletes the given
* {@link ITopLevelElement} (together with its directly or indirectly referencing
* {@link Resource}s).
*/
public DeleteTopLevelElementJob(ITopLevelElement topElement) {
super("Deleting \'" + topElement.getSaveableName() + "\'");
this.topElement = topElement;
sh = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
}
/** {@inheritDoc} */
@Override
protected IStatus run(IProgressMonitor progressMonitor) {
// Check if there are further resources that reference the resource to be deleted. If
// so, extend confirmation dialog accordingly.
Resource resource = topElement.getRootModelElement().eResource();
uris = ResourceUtils.getReferencingResourceURIs(resource, false, progressMonitor);
String message =
"Are you sure you want to delete '" + topElement.getSaveableName() + "'?";
if(!uris.isEmpty()) {
message +=
"\n\nNote that this model also references the following external resources:\n\n";
for(URI uri : uris) {
message += " - " + URI.decode(uri.lastSegment()) + "\n";
}
}
final MessageDialog dialog =
new MessageDialog(sh, "Confirm Delete", null, message, MessageDialog.QUESTION,
new String[] {IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL}, 0) {
@Override
protected int getShellStyle() {
return super.getShellStyle() | SWT.SHEET;
}
};
sh.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
dialog.open();
}
});
if(dialog.getReturnCode() == 0) {
IContainer parent = ResourceUtils.getIFile(resource).getParent();
// Close all opened views belonging to the deleted project
IModelEditorBindingService.getInstance().closeEditors(
topElement.getRootModelElement());
for(URI externalModelURIs : uris) {
topElement.getResourceSet().createResource(externalModelURIs);
}
// Deletes the entire resource set, including its external models
topElement.delete();
try {
parent.refreshLocal(IResource.DEPTH_INFINITE, progressMonitor);
} catch(CoreException e) {
error(ToolingKernelUIActivator.getDefault(), e.getMessage(), e);
}
}
return Status.OK_STATUS;
}
}
/** Constructor. */
public DeleteAction() {
super("Delete", PlatformUI.getWorkbench().getSharedImages()
......@@ -167,39 +274,9 @@ public class DeleteAction extends EObjectActionBase<EObject> {
private static void doTopLevelElementDelete(EObject target) {
ITopLevelElement topElement =
IPersistencyService.getInstance().getTopLevelElementFor(target);
Shell sh = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
String message = "Are you sure you want to delete '" + topElement.getSaveableName() + "'?";
// Check if there are further resources that reference the resource to be deleted. If so,
// extend confirmation dialog accordingly.
resolveAll(topElement.getResourceSet());
if(topElement.getResourceSet().getResources().size() > 1) {
message +=
"\nThis will also include all models that reference '" +
topElement.getSaveableName() + "'.";
}
final MessageDialog dialog =
new MessageDialog(sh, "Confirm Delete", null, message, MessageDialog.QUESTION,
new String[] {IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL}, 0) {
@Override
protected int getShellStyle() {
return super.getShellStyle() | SWT.SHEET;
}
};
sh.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
dialog.open();
}
});
if(dialog.getReturnCode() == 0) {
topElement.delete();
// close also all opened views belonging to the deleted project
IModelEditorBindingService.getInstance().closeEditors(topElement.getRootModelElement());
}
DeleteTopLevelElementJob job = new DeleteTopLevelElementJob(topElement);
job.schedule();
}
/**
......
......@@ -17,15 +17,28 @@ $Id$
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.kernel.utils;
import static org.fortiss.tooling.kernel.utils.LoggingUtils.error;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EObject;
......@@ -33,9 +46,12 @@ import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceFactoryImpl;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
import org.eclipse.emf.ecore.xmi.impl.XMLResourceFactoryImpl;
import org.eclipse.emf.ecore.xml.type.AnyType;
import org.fortiss.tooling.kernel.ToolingKernelActivator;
import org.fortiss.tooling.kernel.extension.data.ITopLevelElement;
import org.fortiss.tooling.kernel.model.IIdLabeled;
import org.fortiss.tooling.kernel.service.IPersistencyService;
......@@ -46,7 +62,7 @@ import org.fortiss.tooling.kernel.service.IPersistencyService;
* @author mou
* @author $Author$
* @version $Rev$
* @ConQAT.Rating GREEN Hash: 7BDB9BF8F67C10263CEAB1343925DCDD
* @ConQAT.Rating YELLOW Hash: 537CB459443A8D92ECF19DBA605B1D9C
*/
public final class ResourceUtils {
......@@ -307,4 +323,120 @@ public final class ResourceUtils {
return null;
}
/**
* Determines the {@link IFile} in the Eclipse workspace that corresponds to the given
* {@link Resource}.
*
* @param resource
* Resource for which {@link IFile} should be determined.
* @return {@link IFile} in the Eclipse workspace that corresponds to {@code resource}.
*/
public static IFile getIFile(Resource resource) {
final URI uri = resource.getURI();
String resourcePath = uri.toPlatformString(true);
final IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
final IResource member = workspaceRoot.findMember(resourcePath);
// EMF resources always correspond to IFile IResources
return (IFile)member;
}
/**
* For the given {@link Resource}, returns the absolute (file) {@link URI}.
*
* @param resource
* {@link Resource} for which absolute (file) {@link URI} should be determined.
* @return Absolute (file) {@link URI} of given {@link Resource}.
*/
public static URI getAbsoluteResourceURI(Resource resource) {
IFile file = getIFile(resource);
final IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
String absoluteResourcePath =
workspaceRoot.getLocation().append(file.getFullPath()).toFile().toURI().toString();
final URI resourceUri = URI.createURI(absoluteResourcePath, true);
return resourceUri;
}
/**
* Determines {@link URI}s of the resources that directly or indirectly the given
* {@link Resource}. It investigates all files that reside in the same directory as
* {@code resource}.
*
* @param resource
* {@link Resource} for which {@link URI}s of directly or indirectly referencing
* resources should be determined.
* @param includeSelf
* Flag if the {@link URI} of the given {@code resource} should be contained in the
* result.
* @param progressMonitor
* {@link IProgressMonitor} (may be {@code null}). Loading all resources in a
* directory may be a long-running operation.
* @return {@link URI}s of directly or indirectly referencing resources, possibly including the
* {@link URI} of the given {@code resource} itself (see {@code includeSelf}).
*/
public static Collection<URI> getReferencingResourceURIs(Resource resource,
boolean includeSelf, IProgressMonitor progressMonitor) {
IFile file = getIFile(resource);
final URI resourceUri = getAbsoluteResourceURI(resource);
Set<URI> uris = new HashSet<URI>();
uris.add(resourceUri);
IContainer parent = file.getParent();
try {
final IResource[] members = parent.members();
if(progressMonitor != null) {
progressMonitor.beginTask("Determining referencing resources.", members.length);
}
for(IResource member : members) {
if(member.equals(file)) {
continue;
}
if(member.getFileExtension().equals("af3_23")) {
continue;
}
ResourceSet resourceSet = new ResourceSetImpl();
try {
URI siblingUri = URI.createFileURI(member.getLocation().toString());
Resource siblingResource = resourceSet.createResource(siblingUri);
siblingResource.load(EMFResourceUtils.buildOptionsMap());
EcoreUtil.resolveAll(resourceSet);
for(Resource currentResource : resourceSet.getResources()) {
final URI currentUri = currentResource.getURI();
if(uris.contains(currentUri)) {
uris.addAll(resourceSet.getResources().stream().map(r -> r.getURI())
.collect(Collectors.toSet()));
break;
}
}
siblingResource.unload();
} catch(IOException e) {
error(ToolingKernelActivator.getDefault(),
"Failed to load \"" + member.getName(), e);
}
if(progressMonitor != null) {
progressMonitor.worked(1);
}
}
} catch(CoreException e) {
error(ToolingKernelActivator.getDefault(),
"Failed to determine members of \"" + parent.getName(), e);
}
if(!includeSelf) {
uris.remove(resourceUri);
}
return uris;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment