/**
* Copyright (c) 2007 Borland Software Corporation
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* bblajer - initial API and implementation
*/
package org.eclipse.gmf.runtime.lite.commands;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.edit.command.CommandParameter;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.gmf.runtime.notation.NotationPackage;
/**
* Command that should be used to destroy an underlying model element for which
* a notational element may be registered. It behaves effectively the same way
* as the {@link DeleteCommand}, but allows filtering of some references, so that they are not removed.
*/
public class DestroyElementCommand extends CompoundCommand {
/**
* This is the editing doman in which this command operates.
*/
protected EditingDomain domain;
/**
* This is the collection of objects to be deleted.
*/
protected Collection<?> collection;
/**
* This creates a command that deletes the given object.
*/
public static Command create(EditingDomain domain, Object object) {
return create(domain, Collections.singleton(object));
}
/**
* This creates a command that deletes the objects in the given collection.
*/
public static Command create(EditingDomain domain, Collection<?> collection) {
return domain.createCommand(DestroyElementCommand.class, new CommandParameter(null, null, collection));
}
protected static final String LABEL = "Delete";
protected static final String DESCRIPTION = "Remove the objects and clean up references to them from within the editing domain";
/**
* This constructs a command that deletes the objects in the given
* collection.
*/
public DestroyElementCommand(EditingDomain domain, Collection<?> collection) {
super(0, LABEL, DESCRIPTION);
this.domain = domain;
this.collection = collection;
}
/**
* This constructs a command that deletes the objects in the collection
* specified by the given command parameter. This constructor is called by
* default implementations of editing domains.
*/
public DestroyElementCommand(EditingDomain domain, CommandParameter commandParameter) {
this(domain, commandParameter.getCollection());
}
/**
* Returns whether value should be removed from the reference identified by
* the given setting.
*/
protected boolean shouldRemoveReference(EStructuralFeature.Setting setting) {
return NotationPackage.eINSTANCE.getView_Element() != setting.getEStructuralFeature();
}
protected boolean prepare() {
prepareCommand();
return super.prepare();
}
protected void prepareCommand() {
HashMap<Resource, Collection<Object>> uncontainedObjects = null;
Collection<Object> otherObjects = new BasicEList<Object>();
for (Object next : collection) {
if (next instanceof EObject) {
EObject nextEObject = (EObject) next;
if (nextEObject.eContainer() == null && nextEObject.eResource() != null) {
//Object directly contained within a resource should be removed differently
if (uncontainedObjects == null) {
uncontainedObjects = new HashMap<Resource, Collection<Object>>();
}
Collection<Object> uncontainedObjectsForResource = uncontainedObjects.get(nextEObject.eResource());
if (uncontainedObjectsForResource == null) {
uncontainedObjectsForResource = new BasicEList<Object>();
uncontainedObjects.put(nextEObject.eResource(), uncontainedObjectsForResource);
}
uncontainedObjectsForResource.add(nextEObject);
continue;
}
}
otherObjects.add(next);
}
if (!otherObjects.isEmpty()) {
append(RemoveCommand.create(domain, otherObjects));
}
if (uncontainedObjects != null) {
for (Map.Entry<Resource, Collection<Object>> nextEntry : uncontainedObjects.entrySet()) {
append(new RemoveCommand(domain, nextEntry.getKey().getContents(), nextEntry.getValue()));
}
}
}
@Override
public void execute() {
Collection<Object> eObjects = new UniqueEList<Object>();
for (Iterator<?> i = collection.iterator(); i.hasNext(); ) {
Object object = AdapterFactoryEditingDomain.unwrap(i.next());
if (object instanceof EObject) {
eObjects.add(object);
for (Iterator<EObject> j = ((EObject) object).eAllContents(); j.hasNext(); ) {
eObjects.add(j.next());
}
} else if (object instanceof Resource) {
for (Iterator<EObject> j = ((Resource) object).getAllContents(); j.hasNext(); ) {
eObjects.add(j.next());
}
}
}
Map<EObject, Collection<EStructuralFeature.Setting>> usages = EcoreUtil.UsageCrossReferencer.findAll(eObjects, domain.getResourceSet());
super.execute();
for (Iterator<Map.Entry<EObject, Collection<EStructuralFeature.Setting>>> i = usages.entrySet().iterator(); i.hasNext(); ) {
Map.Entry<EObject, Collection<EStructuralFeature.Setting>> entry = i.next();
EObject eObject = entry.getKey();
Collection<EStructuralFeature.Setting> settings = entry.getValue();
for (Iterator<EStructuralFeature.Setting> j = settings.iterator(); j.hasNext(); ) {
EStructuralFeature.Setting setting = j.next();
if (!shouldRemoveReference(setting)) {
continue;
}
EObject referencingEObject = setting.getEObject();
if (!eObjects.contains(referencingEObject)) {
EStructuralFeature eStructuralFeature = setting.getEStructuralFeature();
if (eStructuralFeature.isChangeable()) {
if (eStructuralFeature.isMany()) {
appendAndExecute(RemoveCommand.create(domain, referencingEObject, eStructuralFeature, eObject));
} else {
appendAndExecute(SetCommand.create(domain, referencingEObject, eStructuralFeature, null));
}
}
}
}
}
}
}