//------------------------------------------------------------------------------ // Copyright (c) 2005, 2006 IBM Corporation and others. // 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: // IBM Corporation - initial implementation //------------------------------------------------------------------------------ package org.eclipse.epf.library.edit.command; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.EventObject; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; import org.eclipse.emf.common.command.Command; import org.eclipse.emf.common.command.CommandWrapper; import org.eclipse.emf.common.command.CompoundCommand; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.common.util.AbstractTreeIterator; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.common.util.WrappedException; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EContentsEList; import org.eclipse.emf.edit.command.RemoveCommand; import org.eclipse.emf.edit.provider.ItemProviderAdapter; import org.eclipse.epf.common.utils.StrUtil; import org.eclipse.epf.library.edit.ICommandListener; import org.eclipse.epf.library.edit.IReferencer; import org.eclipse.epf.library.edit.IStatefulItemProvider; import org.eclipse.epf.library.edit.LibraryEditPlugin; import org.eclipse.epf.library.edit.LibraryEditResources; import org.eclipse.epf.library.edit.Providers; import org.eclipse.epf.library.edit.ui.UserInteractionHelper; import org.eclipse.epf.library.edit.util.ExtensionManager; import org.eclipse.epf.library.edit.util.IRunnableWithProgress; import org.eclipse.epf.library.edit.util.LibraryEditUtil; import org.eclipse.epf.library.edit.util.Messenger; import org.eclipse.epf.library.edit.util.ProcessUtil; import org.eclipse.epf.library.edit.util.TngUtil; import org.eclipse.epf.services.ILibraryPersister; import org.eclipse.epf.services.Services; import org.eclipse.epf.services.ILibraryPersister.FailSafeMethodLibraryPersister; import org.eclipse.epf.uma.Activity; import org.eclipse.epf.uma.CustomCategory; import org.eclipse.epf.uma.DescribableElement; import org.eclipse.epf.uma.Descriptor; import org.eclipse.epf.uma.MethodElement; import org.eclipse.epf.uma.MethodPlugin; import org.eclipse.epf.uma.UmaPackage; import org.eclipse.epf.uma.VariabilityType; import org.eclipse.epf.uma.ecore.impl.MultiResourceEObject; import org.eclipse.epf.uma.util.AssociationHelper; import org.eclipse.epf.uma.util.UmaUtil; import org.eclipse.osgi.util.NLS; /** * This command is used to delete a method element permanently. This involves * deleting the storage content and removing all references to this element. * * @author Phong Nguyen Le * @since 1.0 */ public class DeleteMethodElementCommand extends CommandWrapper { protected Collection elements; private boolean refRemoved; public boolean executed = false; private Collection commandListeners; private FailSafeMethodLibraryPersister persister; protected ArrayList elementsToDeleteContent; protected Set<Resource> modifiedResources; // Map of element to Map of its referencer to features list // protected Map<EObject, Map<EObject, Collection<EStructuralFeature>>> elementToRemovedRefsMap; private BatchCommand batchCommand = new BatchCommand(true); /** * List of Reference objects * * @see Reference */ protected ArrayList removedReferences; public boolean failed; private boolean confirmRemoveReferences; private ArrayList<Command> nestedCommands; private HashSet<Descriptor> descriptors; private Set<MethodPlugin> plugins; /** * @param command * @param elements * MethodElement objects to be permanently deleted. */ public DeleteMethodElementCommand(Command command, Collection elements) { this(command, elements, true); } public DeleteMethodElementCommand(Command command, Collection elements, boolean confirmRemoveRefs) { super(command); this.elements = elements == null ? null : new ArrayList(elements); commandListeners = new ArrayList(); confirmRemoveReferences = confirmRemoveRefs; } /* * (non-Javadoc) * * @see org.eclipse.emf.common.command.CommandWrapper#dispose() */ public void dispose() { batchCommand.dispose(); if (commandListeners != null) { commandListeners.clear(); } if (elements != null) { elements.clear(); } if (elementsToDeleteContent != null) { elementsToDeleteContent.clear(); } if (elementToRemovedRefsMap != null) { elementToRemovedRefsMap.clear(); } if (modifiedResources != null) { modifiedResources.clear(); } if (removedReferences != null) { removedReferences.clear(); } // Dispose nested commands. if(nestedCommands != null){ if(!nestedCommands.isEmpty()){ for (Command command : nestedCommands) { command.dispose(); } } } if(descriptors != null) { descriptors.clear(); } if (plugins != null) { plugins.clear(); } super.dispose(); } protected void superExecute() { super.execute(); } private void notifyPreExecute() { List commandListeners = Providers .getCommandListeners(DeleteMethodElementCommand.class); if (commandListeners != null && !commandListeners.isEmpty()) { for (Iterator iter = commandListeners.iterator(); iter.hasNext();) { ICommandListener cmdListener = (ICommandListener) iter.next(); try { cmdListener.preExecute(this); } catch (Exception e) { LibraryEditPlugin.getDefault().getLogger().logError(e); } } } } private void notifyPreUndo() { List commandListeners = Providers .getCommandListeners(DeleteMethodElementCommand.class); if (commandListeners != null && !commandListeners.isEmpty()) { for (Iterator iter = commandListeners.iterator(); iter.hasNext();) { ICommandListener cmdListener = (ICommandListener) iter.next(); try { cmdListener.preUndo(this); } catch (Exception e) { LibraryEditPlugin.getDefault().getLogger().logError(e); } } } } protected void prepareElements() { plugins = new HashSet<MethodPlugin>(); ArrayList newElements = new ArrayList(); Collection<CustomCategory> customCategoriesToDelete = new HashSet<CustomCategory>(); RemoveCommand cmd = null; for (Iterator iter = elements.iterator(); iter.hasNext();) { Object element = iter.next(); if (element instanceof CustomCategory) { cmd = getRemoveCommand(element); if (cmd.getFeature() instanceof EReference && ((EReference) cmd.getFeature()).isContainment() && cmd.getOwnerList().contains(element)) { customCategoriesToDelete.add((CustomCategory) element); } MethodPlugin plugin = UmaUtil.getMethodPlugin((CustomCategory) element); if (plugin != null) { plugins.add(plugin); } } } boolean newDeleteRule = true; if (newDeleteRule) { return; } //The following code is no longer in used, keep it for future reference if (!customCategoriesToDelete.isEmpty()) { ArrayList<CustomCategory> topCustomCategoriesToDelete = new ArrayList<CustomCategory>( customCategoriesToDelete); // find all subcategories in the same plug-in that are not // referenced by // any other custom category to delete them as well // Iterator<CustomCategory> iter = new AbstractTreeIterator<CustomCategory>( new ArrayList<CustomCategory>( topCustomCategoriesToDelete), false) { private static final long serialVersionUID = 1L; @Override protected Iterator<? extends CustomCategory> getChildren( Object object) { if (object instanceof Collection) { return ((Collection) object).iterator(); } ArrayList<CustomCategory> children = new ArrayList<CustomCategory>(); CustomCategory cc = ((CustomCategory) object); MethodPlugin plugin = UmaUtil.getMethodPlugin(cc); for (DescribableElement element : cc .getCategorizedElements()) { if (element instanceof CustomCategory && UmaUtil.getMethodPlugin(element) == plugin) { children.add((CustomCategory) element); } } return children.iterator(); } }; HashSet<CustomCategory> allCustomCategories = new HashSet<CustomCategory>(); while(iter.hasNext()) { allCustomCategories.add(iter.next()); } int size; do { size = customCategoriesToDelete.size(); check_cc: for (CustomCategory cc : allCustomCategories) { if (!customCategoriesToDelete.contains(cc)) { List parents = AssociationHelper .getCustomCategories(cc); for (Object parent : parents) { if (!customCategoriesToDelete.contains(parent)) { // parent is not in the collection of custom // categories to be deleted // cannot delete the sub custom category for now // continue check_cc; } } customCategoriesToDelete.add(cc); } } } while (size != customCategoriesToDelete.size()); customCategoriesToDelete.removeAll(topCustomCategoriesToDelete); elements.addAll(customCategoriesToDelete); Collection collection = cmd.getCollection(); collection.addAll(customCategoriesToDelete); } } /** * Gets the RemoveCommand for the given element * * @param e * @return */ protected RemoveCommand getRemoveCommand(Object e) { if (command instanceof RemoveCommand) { if (((RemoveCommand) command).getCollection().contains(e)) { return (RemoveCommand) command; } } else if (command instanceof CompoundCommand) { for (Iterator iter = ((CompoundCommand) command).getCommandList() .iterator(); iter.hasNext();) { Object cmd = (Object) iter.next(); if (cmd instanceof RemoveCommand) { RemoveCommand removeCommand = (RemoveCommand) cmd; if (removeCommand.getCollection().contains(e)) { return removeCommand; } } } } return null; } public void execute() { prepareElements(); notifyPreExecute(); elementsToDeleteContent = new ArrayList(); descriptors = new HashSet<Descriptor>(); for (Iterator iter = elements.iterator(); iter.hasNext();) { Object element = TngUtil.unwrap(iter.next()); if (element instanceof MethodElement) { collectObjectsToDeleteContent(elementsToDeleteContent, (MethodElement) element); } } IRunnableWithProgress runnable = new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { prepareRemovingReferences(); } }; UserInteractionHelper.runWithProgress(runnable, LibraryEditResources.processingReferencesTask_name); if(!confirmRemovingReferences()) { descriptors.clear(); return; } modifiedResources = new HashSet<Resource>(); Set unmodifiedResources = new HashSet(); // get the owner resources before the elements got removed from // container in superExecute() // collectOwnerResources(modifiedResources, unmodifiedResources); superExecute(); executeNestedCommands(); // Collect the nested command modified resources for saving. collectNestedCommandResources(modifiedResources); // get resources of the objects that have been affected by this command // until now // collectResources(modifiedResources, super.getAffectedObjects()); modifiedResources.removeAll(unmodifiedResources); final Exception[] exceptions = new Exception[1]; UserInteractionHelper.getUIHelper().runWithBusyIndicator(new Runnable() { public void run() { try { removeReferences(); } catch (Exception e) { exceptions[0] = e; } } }); if (exceptions[0] != null) { Messenger.INSTANCE.showError( LibraryEditResources.deleteDialog_title, LibraryEditResources.deleteElementError_msg, LibraryEditResources.deleteReferencesError_reason, exceptions[0]); undo(); return; } modifiedResources.addAll(getReferencingResources()); for (Iterator iter = elementsToDeleteContent.iterator(); iter.hasNext();) { EObject element = (EObject) iter.next(); if (element.eContainer() != null) { iter.remove(); } } elementsToDeleteContent.addAll(elements); HashSet<Resource> deletedResources = new HashSet<Resource>(); for (Iterator iter = elementsToDeleteContent.iterator(); iter.hasNext();) { Object obj = iter.next(); if (! (obj instanceof EObject)) { obj = TngUtil.unwrap(obj); } if (obj instanceof EObject) { EObject e = (EObject) obj; UmaUtil.getResources(e, deletedResources); } } // exclude deleted resources from the set of modified resources // modifiedResources.removeAll(deletedResources); // exclude any resource that is contained by the deleted resources // for (Iterator iter = modifiedResources.iterator(); iter.hasNext();) { Resource resource = (Resource) iter.next(); for (Iterator iterator = deletedResources.iterator(); iterator .hasNext();) { Resource deletedResource = (Resource) iterator.next(); if(getPersister().isContainedBy(resource, deletedResource)) { iter.remove(); } } } // check affected resources for unmodifiable // IStatus status = UserInteractionHelper.checkModify(modifiedResources, LibraryEditPlugin.getDefault().getContext()); if (!status.isOK()) { Messenger.INSTANCE.showError( LibraryEditResources.deleteDialog_title, LibraryEditResources.deleteElementError_msg, status); undo(); return; } runnable = new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { monitor.beginTask("", 3); //$NON-NLS-1$ getPersister(); // save resources that had been changed after references to the // deleted elements // had been removed // try { monitor .subTask(LibraryEditResources.deletingElementsTask_name); monitor.worked(1); deleteContent(); // save modified resources // monitor.subTask(LibraryEditResources.savingResources_msg); monitor.worked(1); for (Iterator iter = modifiedResources.iterator(); iter .hasNext();) { Resource resource = (Resource) iter.next(); if (resource.isLoaded()) { persister.save(resource); } } persister.commit(); executed = true; removeAdapters(); } catch (Exception e) { LibraryEditPlugin.INSTANCE.log(e); try { persister.rollback(); } catch (Exception ex) { failed = true; } if (e instanceof RuntimeException) { throw (RuntimeException) e; } else { throw new WrappedException(e); } } } }; // if (!UserInteractionHelper.runWithProgress(runnable, // LibraryEditResources.deletingElementsTask_name)) { //$NON-NLS-1$ // if (failed) { // notifyFailure(); // } else { // undo(); // } // return; // } // UserInteractionHelper.runInUI(runnable, (Shell) null); UserInteractionHelper.runWithProgress(runnable, LibraryEditResources.deletingElementsTask_name); if (executed) { try { List warnings = persister.getWarnings(); if (!warnings.isEmpty()) { String title = LibraryEditResources.deleteDialog_title; String msg = LibraryEditResources.DeleteMethodElementCommand_warningMsg; StringBuffer reason = new StringBuffer(); for (Iterator iter = warnings.iterator(); iter.hasNext();) { Exception e = (Exception) iter.next(); String str = e.getMessage(); if (!StrUtil.isBlank(str)) { reason.append(str).append('\n'); } } Messenger.INSTANCE.showWarning(title, msg, reason .toString()); } } catch (Exception e) { LibraryEditPlugin.getDefault().getLogger().logError(e); } notifyExecuted(); if (plugins != null && !plugins.isEmpty()) { for (MethodPlugin p : plugins) { LibraryEditUtil.getInstance().fixUpDanglingCustomCategories(p); } } } else { if (failed) { notifyFailure(); } else { undo(); } } } private boolean confirmRemovingReferences() { if (!elementToRemovedRefsMap.isEmpty()) { // collect set of referencers HashSet<EObject> referencers = new HashSet<EObject>(); for (Map<EObject, Collection<EStructuralFeature>> referencerToFeaturesMap : elementToRemovedRefsMap.values()) { // collect only referencer with an unidirectional or containment // relationship // for (Map.Entry<EObject, Collection<EStructuralFeature>> entry : referencerToFeaturesMap.entrySet()) { Collection<EStructuralFeature> features = entry.getValue(); boolean canCollect = false; boolean hasDescriptor = false; check_ref: for (EStructuralFeature f : features) { if (!canCollect && f instanceof EReference) { EReference ref = (EReference) f; if (ref.isContainment() || ref.getEOpposite() == null) { canCollect = true; } } if(!hasDescriptor && (f == UmaPackage.eINSTANCE.getTaskDescriptor_Task() || f == UmaPackage.eINSTANCE.getRoleDescriptor_Role() || f == UmaPackage.eINSTANCE.getWorkProductDescriptor_WorkProduct())) { hasDescriptor = true; } if(canCollect && hasDescriptor) { break check_ref; } } if (canCollect) { referencers.add(entry.getKey()); } if(hasDescriptor && entry.getKey() instanceof Descriptor) { descriptors.add((Descriptor) entry.getKey()); } } } // confirm with user before removing illegal references // if (confirmRemoveReferences) { MultiStatus multiStatus = new MultiStatus( LibraryEditPlugin.INSTANCE.getSymbolicName(), 0, "", null); //$NON-NLS-1$ if(!descriptors.isEmpty()) { // ask user to delete the descriptors that are associated // with the content element to be deleted // for (EObject e : descriptors) { String msg = NLS.bind( LibraryEditResources.elementType_text, TngUtil.getTypeText(e), TngUtil .getLabelWithPath(e)); IStatus status = new Status(IStatus.INFO, LibraryEditPlugin.INSTANCE.getSymbolicName(), 0, msg, null); multiStatus.add(status); } IUserInteractionHandler uiHandler = ExtensionManager .getDefaultUserInteractionHandler(); if (uiHandler != null) { switch (uiHandler .selectOne( new int[] { IUserInteractionHandler.ACTION_YES, IUserInteractionHandler.ACTION_NO, IUserInteractionHandler.ACTION_CANCEL }, LibraryEditResources.confirmDescriptorsDeletion_title, LibraryEditResources.confirmDescriptorsDeletion_msg, multiStatus)) { case IUserInteractionHandler.ACTION_CANCEL: return false; case IUserInteractionHandler.ACTION_NO: descriptors.clear(); } } } if(!descriptors.isEmpty()) { // remove the descriptors that user wanted to delete from the referencers // referencers.removeAll(descriptors); } multiStatus = new MultiStatus( LibraryEditPlugin.INSTANCE.getSymbolicName(), 0, "", null); //$NON-NLS-1$ for (EObject e : referencers) { // don't show predefined element // if (!(e instanceof MethodElement && TngUtil.isPredefined((MethodElement) e))) { String msg = NLS.bind( LibraryEditResources.elementType_text, TngUtil.getTypeText(e), TngUtil .getLabelWithPath(e)); IStatus status = new Status(IStatus.INFO, LibraryEditPlugin.INSTANCE.getSymbolicName(), 0, msg, null); multiStatus.add(status); } } if (multiStatus.getChildren().length > 0) { IUserInteractionHandler uiHandler = ExtensionManager .getDefaultUserInteractionHandler(); if (uiHandler != null) { if (uiHandler .selectOne( new int[] { IUserInteractionHandler.ACTION_OK, IUserInteractionHandler.ACTION_CANCEL }, LibraryEditResources.deleteReferencesDialog_title, LibraryEditResources.deleteReferencesDialog_text, multiStatus) == IUserInteractionHandler.ACTION_CANCEL) { return false; } } } } // check if the referencers can be changed // for (EObject e : referencers) { IStatus status = UserInteractionHelper.checkModify(e, LibraryEditPlugin.getDefault().getContext()); if (!status.isOK()) { Messenger.INSTANCE .showError( LibraryEditResources.deleteDialog_title, LibraryEditResources.deleteElementError_msg, status); return false; } } } return true; } /** * Disposes all stateful adapters, then removes all adapters that are * attached to the given element */ private static void removeAdapters(EObject element) { for (Iterator iterator = new ArrayList(element.eAdapters()).iterator(); iterator .hasNext();) { Object adapter = iterator.next(); if (adapter instanceof IStatefulItemProvider) { ((IStatefulItemProvider) adapter).dispose(); if (adapter instanceof ItemProviderAdapter) { AdapterFactory adapterFactory = ((ItemProviderAdapter) adapter) .getAdapterFactory(); if (adapterFactory instanceof IReferencer) { ((IReferencer) adapterFactory).remove(adapter); } } } } element.eAdapters().clear(); } /** * Disposes all stateful adapters, then removes all adapters that are * attached to the deleted elements */ protected void removeAdapters() { for (Iterator iter = elements.iterator(); iter.hasNext();) { Object obj = TngUtil.unwrap(iter.next()); if (obj instanceof EObject) { EObject element = (EObject) obj; if (element.eContainer() == null) { for (Iterator iterator = element.eAllContents(); iterator .hasNext();) { EObject e = (EObject) iterator.next(); removeAdapters(e); } removeAdapters(element); } } } } /** * */ private void notifyFailure() { EventObject eventObject = new EventObject(this); for (Iterator iter = commandListeners.iterator(); iter.hasNext();) { CommandListener listener = (CommandListener) iter.next(); listener.notifyFailure(eventObject); } } /** * @return * */ protected FailSafeMethodLibraryPersister getPersister() { if (persister == null) { persister = Services.getDefaultLibraryPersister() .getFailSafePersister(); } return persister; } private void notifyExecuted() { EventObject eventObject = new EventObject(this); for (Iterator iter = this.commandListeners.iterator(); iter.hasNext();) { CommandListener listener = (CommandListener) iter.next(); listener.notifyExecuted(eventObject); } List commandListeners = Providers .getCommandListeners(DeleteMethodElementCommand.class); if (commandListeners != null && !commandListeners.isEmpty()) { for (Iterator iter = commandListeners.iterator(); iter.hasNext();) { ICommandListener cmdListener = (ICommandListener) iter.next(); try { cmdListener.notifyExecuted(this); } catch (Exception e) { LibraryEditPlugin.getDefault().getLogger().logError(e); } } } } private Collection getReferencingResources() { HashSet referrers = new HashSet(); // for (Iterator iter = removedReferencesMap.values().iterator(); // iter.hasNext();) { // Map referrerToFeaturesMap = (Map) iter.next(); // referrers.addAll(referrerToFeaturesMap.keySet()); // } for (Iterator iter = removedReferences.iterator(); iter.hasNext();) { Reference ref = (Reference) iter.next(); referrers.add(ref.owner); } HashSet resources = new HashSet(); for (Iterator iter = referrers.iterator(); iter.hasNext();) { MethodElement element = (MethodElement) iter.next(); if (element.eResource() != null) { resources.add(element.eResource()); } } return resources; } /** * Collects owner resources of the elements to be deleted to save later. * * @param resources */ private void collectOwnerResources(Set resources, Set unmodifiedResources) { ILibraryPersister persister = Services.getDefaultLibraryPersister(); for (Iterator iter = elements.iterator(); iter.hasNext();) { Object element = TngUtil.unwrap(iter.next()); if (element instanceof MethodElement) { EObject container = ((MethodElement) element).eContainer(); Resource resource = container.eResource(); if (resource != null) { if (persister.hasOwnResourceWithoutReferrer(element)) { unmodifiedResources.add(resource); } else { resources.add(resource); } } } } } private static void collectResources(Set resources, Collection objects) { for (Iterator iter = objects.iterator(); iter.hasNext();) { Object element = TngUtil.unwrap(iter.next()); if (element instanceof EObject) { EObject eObj = (MethodElement) element; if (eObj.eResource() != null) { resources.add(eObj.eResource()); } } } } public void redo() { super.redo(); removeReferences(); } protected void deleteContent() throws Exception { List<MethodElement> toDeleteList = new ArrayList<MethodElement>(); for (Iterator iter = elementsToDeleteContent.iterator(); iter.hasNext();) { Object element = TngUtil.unwrap(iter.next()); if (element instanceof MethodElement) { MethodElement e = (MethodElement) element; if (e.eContainer() == null) { toDeleteList.add(e); } } } persister.delete(toDeleteList); } public Collection getElementsToRemoveReferences() { Collection list = new ArrayList(); for (Iterator iter = elements.iterator(); iter.hasNext();) { Object element = TngUtil.unwrap(iter.next()); if (element instanceof EObject && willRemoveElementFromContainer((EObject) element)) { list.add(element); } } return list; } protected boolean canRemoveReferences(MethodElement e) { // if e is one of the deleted elements, make sure that it actually got // deleted by checking its container // for (Iterator iter = elements.iterator(); iter.hasNext();) { Object obj = TngUtil.unwrap(iter.next()); if (e == obj) { return e.eContainer() == null; } } return true; } /** * Loads all opposite features of the elements to be deleted * * @return list of elements whose opposite features are loaded */ public List loadOppositeFeatures() { HashSet oppositeFeatures = new HashSet(); HashSet deletedGUIDs = new HashSet(); ArrayList elements = new ArrayList(); for (Iterator iter = getElementsToRemoveReferences().iterator(); iter .hasNext();) { Object obj = iter.next(); if (obj instanceof MethodElement) { MethodElement e = (MethodElement) obj; for (Iterator iterator = e.eAllContents(); iterator.hasNext();) { Object element = iterator.next(); if (element instanceof MethodElement) { elements.add(element); Map oppositeFeatureMap = ((MultiResourceEObject) element) .getOppositeFeatureMap(); if (oppositeFeatureMap != null && !oppositeFeatureMap.isEmpty()) { oppositeFeatures .addAll(oppositeFeatureMap.keySet()); deletedGUIDs.add(((MethodElement) element) .getGuid()); } } } elements.add(e); Map oppositeFeatureMap = ((MultiResourceEObject) e) .getOppositeFeatureMap(); if (oppositeFeatureMap != null && !oppositeFeatureMap.isEmpty()) { oppositeFeatures.addAll(oppositeFeatureMap.keySet()); deletedGUIDs.add(((MethodElement) e).getGuid()); } } } loadOppositeFeatures(new ArrayList(oppositeFeatures), deletedGUIDs); return elements; } private boolean isContainedByDeletedElement(EObject e) { for (Iterator iter = elements.iterator(); iter.hasNext();) { Object deleted = TngUtil.unwrap(iter.next()); if (deleted instanceof EObject && UmaUtil.isContainedBy(e, deleted)) { return true; } } return false; } /** * Collects objects that should be removed if <code>elementToDelete</code> * will be removed from <code>references</code> of <code>referencer</code>. * * @param objectsToRemove * output * @param referencer * element that references to elementToDelete * @param references * collection of {@link EReference} that contains elementToDelete * @return true if one of the collected objects is the * <code>referencer</code> or the container of * <code>referencer</code> */ protected boolean collectObjectsToRemove(Collection objectsToRemove, EObject elementToDelete, EObject referencer, Collection references) { boolean ret = false; List commandListeners = Providers .getCommandListeners(DeleteMethodElementCommand.class); if (commandListeners != null && !commandListeners.isEmpty()) { for (Iterator iter = commandListeners.iterator(); iter.hasNext();) { Object cmdListener = iter.next(); if (cmdListener instanceof IDeleteMethodElementCommandListener) { try { boolean b = ((IDeleteMethodElementCommandListener) cmdListener) .collectObjectsToRemove(objectsToRemove, elementToDelete, referencer, references); if (b) { ret = true; } } catch (Exception e) { LibraryEditPlugin.getDefault().getLogger().logError(e); } } } } return ret; } protected boolean willRemoveElementFromContainer(EObject element) { if (willRemoveElementFromContainer(command, element)) { return true; } else if (command instanceof CompoundCommand) { for (Iterator iter = ((CompoundCommand) command).getCommandList() .iterator(); iter.hasNext();) { Command cmd = (Command) iter.next(); if (willRemoveElementFromContainer(cmd, element)) { return true; } } } return false; } private static boolean willRemoveElementFromContainer(Command cmd, EObject element) { if (cmd instanceof RemoveCommand) { RemoveCommand removeCommand = ((RemoveCommand) cmd); return removeCommand.getCollection().contains(element) && removeCommand.getOwner() == element.eContainer(); } return false; } protected void prepareRemovingReferences() { List elements = loadOppositeFeatures(); elementToRemovedRefsMap = new HashMap<EObject, Map<EObject, Collection<EStructuralFeature>>>(); HashSet objectsToRemove = new HashSet(); for (Iterator iter = elements.iterator(); iter.hasNext();) { MethodElement element = (MethodElement) iter.next(); Map<EObject, Collection<EStructuralFeature>> refMap = AssociationHelper.getReferenceMap(element); if (!refMap.isEmpty()) { for (Iterator iterator = refMap.entrySet().iterator(); iterator .hasNext();) { Map.Entry entry = (Entry) iterator.next(); EObject referencer = (EObject) entry.getKey(); Collection references = (Collection) entry.getValue(); boolean b = collectObjectsToRemove(objectsToRemove, element, referencer, references); if (b || elements.contains(referencer) || isContainedByDeletedElement(referencer)) { iterator.remove(); } } if (!refMap.isEmpty()) elementToRemovedRefsMap.put(element, refMap); } } // add entries for objectsToRemove to elementToRemovedRefsMap // for (Iterator iter = objectsToRemove.iterator(); iter.hasNext();) { EObject obj = (EObject) iter.next(); Map<EObject, Collection<EStructuralFeature>> map = elementToRemovedRefsMap.get(obj); if (map == null) { map = new HashMap(); elementToRemovedRefsMap.put(obj, map); } EObject container = obj.eContainer(); EReference containmentFeature = obj.eContainmentFeature(); Collection refs = (Collection) map.get(container); if (refs == null) { refs = new ArrayList(); refs.add(containmentFeature); map.put(container, refs); } else { if (!refs.contains(containmentFeature)) { refs.add(containmentFeature); } } } // remove all bi-directional relationships // for (Iterator iter = elements.iterator(); iter.hasNext();) { Object element = iter.next(); if (element instanceof EObject) { EObject eObject = (EObject) element; Map objToRefsMap = new HashMap(); for (EContentsEList.FeatureIterator featureIterator = (EContentsEList.FeatureIterator) eObject .eCrossReferences().iterator(); featureIterator .hasNext();) { EObject eObj = (EObject) featureIterator.next(); EReference eReference = (EReference) featureIterator .feature(); if (eReference.getEOpposite() != null) { List refs = (List) objToRefsMap.get(eObj); if (refs == null) { refs = new ArrayList(); objToRefsMap.put(eObj, refs); } refs.add(eReference.getEOpposite()); } } Map map = (Map) elementToRemovedRefsMap.get(eObject); if (map == null) { elementToRemovedRefsMap.put(eObject, objToRefsMap); } else { // merge objToRefsMap to map // for (Iterator iterator = objToRefsMap.entrySet().iterator(); iterator .hasNext();) { Map.Entry entry = (Map.Entry) iterator.next(); Object obj = entry.getKey(); Collection refs = (Collection) entry.getValue(); Collection existingRefs = (Collection) map.get(obj); if (existingRefs != null) { for (Iterator itor = refs.iterator(); itor .hasNext();) { Object ref = (Object) itor.next(); if (!existingRefs.contains(ref)) { existingRefs.add(ref); } } } else { map.put(obj, refs); } } } } } } /** * @param collectedObjects * @param element * the element that will be deleted by this command */ protected void collectObjectsToDeleteContent(Collection collectedObjects, MethodElement element) { // this special handling is no longer needed since affected // subcategories are added // to the list of elements to be deleted // // if (element instanceof CustomCategory) { // // have to handle CustomCategory specially since deleting a // // CustomCategory might triger deleting // // its subcategories even the relationship between a CustomCategory // and // // its subcategories is // // non-containment reference // // // Iterator iter1 = new AbstractTreeIterator(element, false) { // // private static final long serialVersionUID = -6285969923138781437L; // protected Iterator getChildren(Object object) { // ArrayList children = new ArrayList(); // Collection catElements = ((CustomCategory) object) // .getCategorizedElements(); // for (Iterator iterator = catElements.iterator(); iterator // .hasNext();) { // Object e = iterator.next(); // if (e instanceof CustomCategory) { // children.add(e); // } // } // return children.iterator(); // } // // }; // while (iter1.hasNext()) { // collectedObjects.add(iter1.next()); // } // } List commandListeners = Providers .getCommandListeners(DeleteMethodElementCommand.class); if (commandListeners != null && !commandListeners.isEmpty()) { for (Iterator iter = commandListeners.iterator(); iter.hasNext();) { Object cmdListener = iter.next(); if (cmdListener instanceof IDeleteMethodElementCommandListener) { try { ((IDeleteMethodElementCommandListener) cmdListener) .collectObjectsToDeleteContent( collectedObjects, element); } catch (Exception e) { LibraryEditPlugin.getDefault().getLogger().logError(e); } } } } } protected void removeReferences() { if (refRemoved) return; if (removedReferences == null) { removedReferences = new ArrayList(); } else { removedReferences.clear(); } for (Iterator iter = elementToRemovedRefsMap.entrySet().iterator(); iter .hasNext();) { Map.Entry entry = (Map.Entry) iter.next(); MethodElement referenced = (MethodElement) entry.getKey(); if (canRemoveReferences(referenced)) { Map removedRefMap = (Map) entry.getValue(); for (Iterator iterator = removedRefMap.entrySet().iterator(); iterator .hasNext();) { Map.Entry ent = (Map.Entry) iterator.next(); EObject referencer = (EObject) ent.getKey(); Collection features = (Collection) ent.getValue(); for (Iterator iter1 = features.iterator(); iter1.hasNext();) { EStructuralFeature feature = (EStructuralFeature) iter1 .next(); if (feature.isMany()) { List list = ((List) referencer.eGet(feature)); int index = list.indexOf(referenced); // list.remove(index); if (index != -1) { list.remove(index); removedReferences.add(new Reference(referencer, feature, referenced, index)); removeReferenceFollowUp(referencer, referenced, feature); } else { if (TngUtil.DEBUG) { System.out .println("DeleteMethodElementCommand.removeReferences(): index=" + index + ", size=" + list.size() + ", referencer=" + referencer + ", referenced=" + referenced + ", feature=" + feature); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ } // work-around: try to find the proxy and remove // it. // TODO: kind of hack, needs revisit // String guid = ((MethodElement) referenced) .getGuid(); find_proxy: for (int i = 0; i < list.size(); i++) { InternalEObject ref = (InternalEObject) list .get(i); URI uri = ref.eProxyURI(); if (uri != null && guid.equals(uri.fragment())) { list.remove(i); removedReferences.add(new Reference( referencer, feature, referenced, i)); break find_proxy; } } } } else { referencer.eSet(feature, null); removedReferences.add(new Reference(referencer, feature, referenced, -1)); } } } } } if (TngUtil.DEBUG) { System.out .println("removedReferences: size=" + removedReferences.size()); //$NON-NLS-1$ } batchCommand.getObjectToNewFeatureValuesMap().clear(); for (Iterator iter = removedReferences.iterator(); iter.hasNext();) { Reference ref = (Reference) iter.next(); if (ref.getFeature() == UmaPackage.Literals.VARIABILITY_ELEMENT__VARIABILITY_BASED_ON_ELEMENT) { batchCommand .addFeatureValue( ref.getOwner(), UmaPackage.Literals.VARIABILITY_ELEMENT__VARIABILITY_TYPE, UmaPackage.Literals.VARIABILITY_ELEMENT__VARIABILITY_TYPE .getDefaultValue()); if (ref.getOwner() instanceof Activity) { // Fill blank presentation name of extended/locally // contributing activity with presentation name // from base activity when base is deleted // Activity act = (Activity) ref.getOwner(); VariabilityType vType = act.getVariabilityType(); if ((vType == VariabilityType.EXTENDS || vType == VariabilityType.LOCAL_CONTRIBUTION) && StrUtil.isNull(act.getPresentationName())) { Activity base = (Activity) ref.getValue(); batchCommand .addFeatureValue( act, UmaPackage.Literals.METHOD_ELEMENT__PRESENTATION_NAME, ProcessUtil.getPresentationName(base)); } } } } if (batchCommand.canExecute()) { batchCommand.execute(); } refRemoved = true; } /** * Subclass should override this method to resolve all target features so * opposite features of the given MethodElement are fully loaded. * * @param deletedGUIDs * * @param e */ protected void loadOppositeFeatures(List oppositeFeatures, Set deletedGUIDs) { // } protected static void restoreReferences(List removedReferences) { for (int i = removedReferences.size() - 1; i > -1; i--) { Reference ref = (Reference) removedReferences.get(i); if (ref.feature.isMany()) { List list = (List) ref.owner.eGet(ref.feature); if (ref.index != -1) { // TODO: need revisits // if (!list.contains(ref.value)) { if (ref.index < list.size()) { list.add(ref.index, ref.value); } else { if (TngUtil.DEBUG) { System.out .println("DeleteMethodElementCommand.removeReferences(): index=" + ref.index + ", size=" + list.size() + ", referencer=" + ref.owner + ", referenced=" + ref.value + ", feature=" + ref.feature); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ } list.add(ref.value); } } else { if (TngUtil.DEBUG) { System.out .println("DeleteMethodElementCommand.removeReferences(): reference already exists: referencer=" + ref.owner + ", referenced=" + ref.value + ", feature=" + ref.feature); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } } } else { list.add(ref.value); } } else { ref.owner.eSet(ref.feature, ref.value); } } } protected void restoreReferences() { if (!refRemoved) return; batchCommand.undo(); restoreReferences(removedReferences); refRemoved = false; } public void undo() { notifyPreUndo(); try { super.undo(); restoreReferences(); } catch (Exception e) { LibraryEditPlugin.INSTANCE.log(e); notifyFailure(); } } public Collection getAffectedObjects() { if (executed) { return super.getAffectedObjects(); } return elements; } public void addCommandListener(CommandListener listener) { if (!commandListeners.contains(listener)) { commandListeners.add(listener); } } public void removeCommandListener(CommandListener listener) { commandListeners.remove(listener); } public static interface CommandListener { void notifyExecuted(EventObject eventObject); void notifyFailure(EventObject eventObject); } /** * */ protected void executeNestedCommands() { List nestedCommandProviders = ExtensionManager .getNestedCommandProviders(); if (!nestedCommandProviders.isEmpty()) { if (!elements.isEmpty()) { nestedCommands = new ArrayList(); for (Iterator iter = nestedCommandProviders.iterator(); iter .hasNext();) { INestedCommandProvider cmdProvider = (INestedCommandProvider) iter .next(); try { Command cmd = cmdProvider.removeRelatedObjects(elements, this); if (cmd != null && cmd.canExecute()) { cmd.execute(); nestedCommands.add(cmd); } } catch (Exception e) { LibraryEditPlugin.getDefault().getLogger().logError(e); } } } } } /** * * @param modifiedResources2 */ private void collectNestedCommandResources(Set<Resource> modifiedResources) { if (nestedCommands != null && !nestedCommands.isEmpty()) { for (int i = nestedCommands.size() - 1; i > -1; i--) { Command cmd = (Command) nestedCommands.get(i); try { if (cmd instanceof IResourceAwareCommand) { Collection resources = ((IResourceAwareCommand) cmd) .getModifiedResources(); if (resources != null) { modifiedResources.addAll(resources); } } } catch (Exception e) { LibraryEditPlugin.getDefault().getLogger().logError(e); } finally { } } } } public Collection<Descriptor> getDescriptorsToDelete() { return descriptors; } public boolean isExecuted() { return executed; } protected void removeReferenceFollowUp(EObject referencer, EObject referenced, EStructuralFeature feature) { } }