/***************************************************************************** * Copyright (c) 2011 Atos. * * * 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: * Arthur Daussy (Atos) - Initial API and implementation * Arthur Daussy - 371712 : 372745: [ActivityDiagram] Major refactoring group framework * *****************************************************************************/ package org.eclipse.papyrus.uml.diagram.activity.activitygroup.editpolicy; import java.util.Iterator; import java.util.List; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.gef.EditPart; import org.eclipse.gef.Request; import org.eclipse.gef.commands.Command; import org.eclipse.gef.commands.UnexecutableCommand; import org.eclipse.gef.requests.ChangeBoundsRequest; import org.eclipse.gmf.runtime.common.core.command.CompositeCommand; import org.eclipse.gmf.runtime.diagram.core.edithelpers.CreateElementRequestAdapter; import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil; import org.eclipse.gmf.runtime.diagram.ui.commands.CommandProxy; import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; import org.eclipse.gmf.runtime.diagram.ui.commands.SemanticCreateCommand; import org.eclipse.gmf.runtime.diagram.ui.editparts.GroupEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart; import org.eclipse.gmf.runtime.diagram.ui.editpolicies.CreationEditPolicy; import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramUIMessages; import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewAndElementRequest; import org.eclipse.gmf.runtime.diagram.ui.requests.EditCommandRequestWrapper; import org.eclipse.gmf.runtime.diagram.ui.requests.RefreshConnectionsRequest; import org.eclipse.gmf.runtime.emf.core.util.PackageUtil; import org.eclipse.gmf.runtime.emf.type.core.requests.CreateElementRequest; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.ContainerNodeDescriptorRegistry; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.GroupRequestAdvisor; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.IContainerNodeDescriptor; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.request.AbstractGroupRequest; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.request.IGroupRequest; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.utils.Utils; public class GroupCreationEditPolicy extends CreationEditPolicy { /** * Node descriptor */ private IContainerNodeDescriptor groupDescriptor; /** * @param groupDescriptor */ public GroupCreationEditPolicy(IContainerNodeDescriptor groupDescriptor) { super(); this.groupDescriptor = groupDescriptor; } /** * Return the {@link IContainerNodeDescriptor} of the group owning this edit policy * * @return */ protected IContainerNodeDescriptor getGroupDescriptor() { return groupDescriptor; } /* * (non-Javadoc) * * @see org.eclipse.gmf.runtime.diagram.ui.editpolicies.CreationEditPolicy#getReparentCommand(org.eclipse.gef.requests.ChangeBoundsRequest) */ @Override protected Command getReparentCommand(ChangeBoundsRequest request) { Iterator editParts = request.getEditParts().iterator(); View container = (View)getHost().getAdapter(View.class); EObject context = container == null ? null : ViewUtil.resolveSemanticElement(container); CompositeCommand cc = new CompositeCommand(DiagramUIMessages.AddCommand_Label); while(editParts.hasNext()) { EditPart ep = (EditPart)editParts.next(); if(ep instanceof LabelEditPart) { continue; } if(ep instanceof GroupEditPart) { cc.compose(getReparentGroupCommand((GroupEditPart)ep)); } View view = (View)ep.getAdapter(View.class); if(view == null) { continue; } EObject semantic = ViewUtil.resolveSemanticElement(view); if(semantic == null) { cc.compose(getReparentViewCommand((IGraphicalEditPart)ep)); } else if(context != null) { if(shouldReparentModel(semantic, context)) { cc.compose(getReparentCommand((IGraphicalEditPart)ep)); } else if(shouldGraphicalyReparent(semantic, context)) { cc.compose(getReparentViewCommand((IGraphicalEditPart)ep)); } } } return cc.isEmpty() ? null : new ICommandProxy(cc.reduce()); } /** * Return true if the node is visually contained into the host * * @param ep * @return */ protected boolean isVisuallyContained(IGraphicalEditPart ep, ChangeBoundsRequest request) { Rectangle hostBounds = Utils.getAbsoluteBounds((IGraphicalEditPart)getHost()); Rectangle ndeBounds = Utils.getAbslotueRequestBounds(request, ep); return hostBounds.contains(ndeBounds); } /** * Return true if this element should be a model child of this element * * @param semantic * @param context * @return */ protected boolean shouldReparentModel(EObject element, EObject newContext) { EReference feature = PackageUtil.findFeature(newContext.eClass(), element.eClass()); return feature != null && feature.isContainment() && shouldReparent(element, newContext) && !isSameContainer(element, newContext); } /** * Return true if the element should be a graphical child to this element * * @param semantic * @param context * @return */ protected boolean shouldGraphicalyReparent(EObject element, EObject newContext) { EReference feature = findFeatureWhichReference(newContext.eClass(), element.eClass()); return feature != null && !feature.isContainment() && shouldReparent(element, newContext); } /** * Finds a feature that can contain an object of type eClass. * * @param container * The container <code>EClass</code>. * @param eClass * The contained <code>EClass</code>. * @return The found feature. */ protected static EReference findFeatureWhichReference(EClass container, EClass eClass) { Iterator i = container.getEAllReferences().iterator(); while(i.hasNext()) { EReference reference = (EReference)i.next(); if(PackageUtil.canReference(container, reference, eClass)) return reference; } return null; } /** * Checks if a container <code>EClass</code> can reference another <code>EClass</code>. * * @param class1 * The referencer <code>EClass</code>. * @param class2 * The referenced <code>EClass</code>. * @return True if an object can contain other objects of a given type. */ protected static boolean canReference(EClass class1, EClass class2) { Iterator i = class1.getEAllReferences().iterator(); while(i.hasNext()) { EReference reference = (EReference)i.next(); if(!reference.isContainment()) { EClass eType = (EClass)reference.getEType(); if((eType.equals(class2)) || (eType.isSuperTypeOf(class2))) { return true; } } } return false; } @Override protected boolean shouldReparent(EObject element, EObject newContext) { return !(element == null || element == newContext || isContainedIn(element, newContext)); } /** * Return true if the the container of the element if the newContext * * @param element * @param newContext * @return */ protected boolean isSameContainer(EObject element, EObject newContext) { return element.eContainer() == newContext; } /* * This method checks to see element's containment tree already includes * itself. This is necessary to prevent cyclic graphs in the model that * cause StackOverflowExceptions. */ private boolean isContainedIn(EObject element, EObject newContext) { EObject container = newContext.eContainer(); while(container != null) { if(container.equals(element)) return true; container = container.eContainer(); } return false; } @Override public EditPart getTargetEditPart(Request request) { if(request instanceof ChangeBoundsRequest) { ChangeBoundsRequest chRequest = (ChangeBoundsRequest)request; List editParts = chRequest.getEditParts(); if(editParts != null) { for(Object p : editParts) { if(p instanceof IGraphicalEditPart) { IGraphicalEditPart graphEditPart = (IGraphicalEditPart)p; if(isVisuallyContained(graphEditPart, chRequest)) { return getHost(); } return ((IGraphicalEditPart)getHost()).getTopGraphicEditPart().getParent(); } } } } return super.getTargetEditPart(request); } @Override protected Command getCreateElementAndViewCommand(CreateViewAndElementRequest request) { // get the element descriptor CreateElementRequestAdapter requestAdapter = request.getViewAndElementDescriptor().getCreateElementRequestAdapter(); // get the semantic request CreateElementRequest createElementRequest = (CreateElementRequest)requestAdapter.getAdapter(CreateElementRequest.class); tryToFindModelParentFromGroupFramework(createElementRequest, new AbstractGroupRequest((IGraphicalEditPart)getHost(), request, request.getViewAndElementDescriptor().getElementAdapter(), ContainerNodeDescriptorRegistry.getInstance().getContainerNodeDescriptor(createElementRequest.getElementType().getEClass())) { public GroupRequestType getGroupRequestType() { return GroupRequestType.CREATION; } }); if(createElementRequest.getContainer() == null) { // complete the semantic request by filling in the host's semantic // element as the context View view = (View)getHost().getModel(); EObject hostElement = ViewUtil.resolveSemanticElement(view); if(hostElement == null && view.getElement() == null) { hostElement = view; } // Returns null if host is unresolvable so that trying to create a // new element in an unresolved shape will not be allowed. if(hostElement == null) { return null; } createElementRequest.setContainer(hostElement); } // get the create element command based on the elementdescriptor's // request Command createElementCommand = getHost().getCommand(new EditCommandRequestWrapper((CreateElementRequest)requestAdapter.getAdapter(CreateElementRequest.class), request.getExtendedData())); if(createElementCommand == null) { return UnexecutableCommand.INSTANCE; } if(!createElementCommand.canExecute()) { return createElementCommand; } // create the semantic create wrapper command SemanticCreateCommand semanticCommand = new SemanticCreateCommand(requestAdapter, createElementCommand); Command viewCommand = getCreateCommand(request); Command refreshConnectionCommand = getHost().getCommand(new RefreshConnectionsRequest(((List)request.getNewObject()))); // form the compound command and return CompositeCommand cc = new CompositeCommand(semanticCommand.getLabel()); cc.compose(semanticCommand); cc.compose(new CommandProxy(viewCommand)); if(refreshConnectionCommand != null) { cc.compose(new CommandProxy(refreshConnectionCommand)); } return new ICommandProxy(cc); } /** * Try to set the container of the request thanks to the group framework * * @param createElementRequest */ protected void tryToFindModelParentFromGroupFramework(CreateElementRequest createElementRequest, IGroupRequest request) { EObject parent = GroupRequestAdvisor.getInstance().getPossibleModelParent(request); if(parent != null) { createElementRequest.setContainer(parent); } } }