/***************************************************************************** * Copyright (c) 2010 CEA LIST. * * * 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: * * Yann Tanguy (CEA LIST) yann.tanguy@cea.fr - Initial API and implementation * *****************************************************************************/ package org.eclipse.papyrus.uml.service.types.helper; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.eclipse.emf.ecore.EObject; import org.eclipse.gmf.runtime.common.core.command.ICommand; import org.eclipse.gmf.runtime.emf.type.core.ElementTypeRegistry; import org.eclipse.gmf.runtime.emf.type.core.IElementType; import org.eclipse.gmf.runtime.emf.type.core.edithelper.AbstractEditHelper; import org.eclipse.gmf.runtime.emf.type.core.edithelper.IEditHelperAdvice; import org.eclipse.gmf.runtime.emf.type.core.requests.DestroyDependentsRequest; import org.eclipse.gmf.runtime.emf.type.core.requests.DestroyElementRequest; import org.eclipse.gmf.runtime.emf.type.core.requests.IEditCommandRequest; import org.eclipse.papyrus.commands.DestroyElementPapyrusCommand; /** * <pre> * This is a default Helper for UML element. * * The only reason to override getDestroyElementWithDependentsCommand and getDestroyElementCommand * method here is to propagate the shared IClientContext used by Papyrus during the request creation. * Without this changes, the command to destroy dependent element won't be correctly created, * in EditHelper(s) the getDestroyDependentsCommand will only be called with default element type * (null command) and in AdviceHelper the getBeforeDestroyDependentsCommand will work but will * not retrieve command to destroy elements that themselves depend on dependent element to destroy. * * The changes are replacing: * ElementTypeRegistry.getInstance().getElementType(req.getElementToDestroy()); * by * ElementTypeRegistry.getInstance().getElementType(req.getElementToDestroy(), req.getClientContext()); * * See: * - Bug328232 (https://bugs.eclipse.org/bugs/show_bug.cgi?id=328232) * - Bug328506 (https://bugs.eclipse.org/bugs/show_bug.cgi?id=328506) * * </pre> */ public class DefaultEditHelper extends AbstractEditHelper { /** Defined in org.eclipse.gmf.runtime.emf.type.core.internal.requests.RequestCacheEntries (internal) */ public static final String Cache_Maps = "Cache_Maps";//$NON-NLS-1$ /** Defined in org.eclipse.gmf.runtime.emf.type.core.internal.requests.RequestCacheEntries (internal) */ public static final String Element_Type = "Element_Type";//$NON-NLS-1$ /** Defined in org.eclipse.gmf.runtime.emf.type.core.internal.requests.RequestCacheEntries (internal) */ public static final String Checked_Elements = "Checked_Elements";//$NON-NLS-1$ /** Defined in org.eclipse.gmf.runtime.emf.type.core.internal.requests.RequestCacheEntries (internal) */ public static final String EditHelper_Advice = "EditHelper_Advice";//$NON-NLS-1$ /** * Gets the command to destroy a single child of an element of my kind along * with its dependents (not related by containment). By default, returns a * composite that destroys the elements and zero or more dependents. * * @param req * the destroy request * @return a command that destroys the element specified as the request's {@linkplain DestroyElementRequest#getElementToDestroy() element to * destroy} and its non-containment dependents */ protected ICommand getDestroyElementWithDependentsCommand(DestroyElementRequest req) { ICommand result = getBasicDestroyElementCommand(req); EObject initial = (EObject)req.getParameter(DestroyElementRequest.INITIAL_ELEMENT_TO_DESTROY_PARAMETER); if(initial == null) { // set the parameter to keep track of the initial element to destroy req.setParameter(DestroyElementRequest.INITIAL_ELEMENT_TO_DESTROY_PARAMETER, req.getElementToDestroy()); } // get elements dependent on the element we are destroying, that // must also be destroyed DestroyDependentsRequest ddr = (DestroyDependentsRequest)req.getParameter(DestroyElementRequest.DESTROY_DEPENDENTS_REQUEST_PARAMETER); if(ddr == null) { // create the destroy-dependents request that will be propagated to // destroy requests for all elements destroyed in this operation ddr = new DestroyDependentsRequest(req.getEditingDomain(), req.getElementToDestroy(), req.isConfirmationRequired()); // propagate the parameters, including the initial element to // destroy parameter ddr.addParameters(req.getParameters()); ddr.setClientContext(req.getClientContext()); req.setParameter(DestroyElementRequest.DESTROY_DEPENDENTS_REQUEST_PARAMETER, ddr); } else { ddr.setElementToDestroy(req.getElementToDestroy()); } IElementType typeToDestroy = null; Map cacheMaps = (Map)req.getParameter(Cache_Maps); if(cacheMaps != null) { Map map = (Map)cacheMaps.get(req.getElementToDestroy()); if(map != null) { typeToDestroy = (IElementType)map.get(Element_Type); } } if(typeToDestroy == null) { typeToDestroy = ElementTypeRegistry.getInstance().getElementType(req.getElementToDestroy(), req.getClientContext()); } if(typeToDestroy != null) { ICommand command = typeToDestroy.getEditCommand(ddr); if(command != null) { result = result.compose(command); } } return result; } /** * Gets the command to destroy a child of an element of my kind. By * default, returns a composite command that destroys the element specified * by the request and all of its contents. * * @param req * the destroy request * @return a command that destroys the element specified as the request's {@link DestroyElementRequest#getElementToDestroy() element to destroy} * along with its contents and other dependents */ protected ICommand getDestroyElementCommand(DestroyElementRequest req) { ICommand result = null; EObject parent = req.getElementToDestroy(); if(req.getParameter(DestroyElementRequest.INITIAL_ELEMENT_TO_DESTROY_PARAMETER) == null) { req.setParameter(DestroyElementRequest.INITIAL_ELEMENT_TO_DESTROY_PARAMETER, parent); } IElementType parentType = null; Map cacheMaps = (Map)req.getParameter(Cache_Maps); Set checkedElement = null; if(cacheMaps != null) { checkedElement = (Set)cacheMaps.get(Checked_Elements); checkedElement.add(parent); Map parentMap = (Map)cacheMaps.get(parent); if(parentMap != null) { parentType = (IElementType)parentMap.get(Element_Type); } else { parentType = ElementTypeRegistry.getInstance().getElementType(parent, req.getClientContext()); } } else { parentType = ElementTypeRegistry.getInstance().getElementType(parent, req.getClientContext()); } if(parentType != null) { for(Iterator iter = parent.eContents().iterator(); iter.hasNext();) { EObject next = (EObject)iter.next(); DestroyDependentsRequest ddr = (DestroyDependentsRequest)req.getParameter(DestroyElementRequest.DESTROY_DEPENDENTS_REQUEST_PARAMETER); // if another object is already destroying this one because it // is (transitively) a dependent, then don't destroy it again . if((ddr == null) || ((checkedElement != null) && checkedElement.add(next)) || (!ddr.getDependentElementsToDestroy().contains(next))) { // set the element to be destroyed req.setElementToDestroy(next); ICommand command = parentType.getEditCommand(req); if(command != null) { if(result == null) { result = command; } else { result = result.compose(command); } // Under normal circumstances the command is executable. // Checking canExecute here slows down large scenarios and it is therefore // better to skip this check. // if (!command.canExecute()) { // // no point in continuing if we're abandoning the works // break; // } } } } } // restore the elementToDestroy in the original request req.setElementToDestroy(parent); ICommand destroyParent = getDestroyElementWithDependentsCommand(req); //bottom-up destruction: destroy children before parent if(result == null) { result = destroyParent; } else { result = result.compose(destroyParent); } return result; } /** * Gets the array of edit helper advice for this request. * * @param req * the edit request * @return the edit helper advice, or <code>null</code> if there is none */ protected IEditHelperAdvice[] getEditHelperAdvice(IEditCommandRequest req) { IEditHelperAdvice[] advices = null; Object editHelperContext = req.getEditHelperContext(); Map cacheMaps = (Map)req.getParameter(Cache_Maps); if(cacheMaps != null) { Map contextMap = (Map)cacheMaps.get(editHelperContext); if(contextMap != null) { advices = (IEditHelperAdvice[])contextMap.get(EditHelper_Advice); } } if(advices == null) { if(editHelperContext instanceof EObject) { advices = ElementTypeRegistry.getInstance().getEditHelperAdvice((EObject)editHelperContext, req.getClientContext()); } else if(editHelperContext instanceof IElementType) { advices = ElementTypeRegistry.getInstance().getEditHelperAdvice((IElementType)editHelperContext, req.getClientContext()); } else { advices = ElementTypeRegistry.getInstance().getEditHelperAdvice(editHelperContext); } } return advices; } @Override protected ICommand getBasicDestroyElementCommand(DestroyElementRequest req) { ICommand result = req.getBasicDestroyCommand(); if (result == null) { result = new DestroyElementPapyrusCommand(req); } else { // ensure that re-use of this request will not accidentally // propagate this command, which would destroy the wrong object req.setBasicDestroyCommand(null); } return result; } }