/***************************************************************************** * 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; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.naming.InitialContext; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.Assert; 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.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.workspace.WorkspaceEditingDomainFactory; import org.eclipse.emf.workspace.util.WorkspaceSynchronizer; import org.eclipse.gef.EditPolicy; import org.eclipse.gef.Request; import org.eclipse.gef.RequestConstants; import org.eclipse.gef.commands.Command; import org.eclipse.gef.requests.ChangeBoundsRequest; import org.eclipse.gef.requests.GroupRequest; import org.eclipse.gmf.runtime.common.core.command.CompositeCommand; import org.eclipse.gmf.runtime.common.core.command.ICommand; import org.eclipse.gmf.runtime.diagram.ui.commands.CommandProxy; import org.eclipse.gmf.runtime.diagram.ui.commands.SetBoundsCommand; import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest; import org.eclipse.gmf.runtime.emf.commands.core.command.CompositeTransactionalCommand; import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter; import org.eclipse.gmf.runtime.emf.type.core.commands.MoveElementsCommand; import org.eclipse.gmf.runtime.emf.type.core.requests.MoveRequest; import org.eclipse.gmf.runtime.notation.Diagram; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.papyrus.commands.wrappers.GMFtoEMFCommandWrapper; import org.eclipse.papyrus.infra.core.log.LogHelper; import org.eclipse.papyrus.infra.widgets.toolbox.notification.ICompositeCreator; import org.eclipse.papyrus.infra.widgets.toolbox.notification.INotification; import org.eclipse.papyrus.infra.widgets.toolbox.notification.NotificationRunnable; import org.eclipse.papyrus.infra.widgets.toolbox.notification.Type; import org.eclipse.papyrus.infra.widgets.toolbox.notification.builders.IContext; import org.eclipse.papyrus.infra.widgets.toolbox.notification.builders.NotificationBuilder; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.editpolicy.notifiers.GroupNotifyingOnMoveEditPolicy; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.editpolicy.notifiers.IGroupNotifier; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.predicates.AncestorFilter; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.predicates.DescendantsFilter; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.predicates.DescendantsFilterIGroupNotifier; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.predicates.SameContainerFilter; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.request.IGroupRequest; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.request.SetDeferredRequest; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.ui.IntegrateViewToConfigureComposite; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.utils.DebugUtils; import org.eclipse.papyrus.uml.diagram.activity.activitygroup.utils.Utils; import org.eclipse.papyrus.uml.diagram.activity.commands.DeferredSetValueCommand; import org.eclipse.papyrus.uml.diagram.activity.commands.RunNotificationCommand; import org.eclipse.papyrus.uml.diagram.activity.edit.part.ActivityGroup.IGroupCompartmentEditPart; import org.eclipse.papyrus.uml.diagram.activity.part.UMLDiagramEditorPlugin; import org.eclipse.papyrus.uml.diagram.common.commands.RemoveValueCommand; import org.eclipse.papyrus.uml.diagram.common.commands.RemoveValueRequest; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.uml2.uml.ActivityGroup; import org.eclipse.uml2.uml.Element; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Collections2; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; public class GroupRequestAdvisor implements IGroupRequestAdvisor { /** * Exception message */ private static final String UNABLE_TO_GET_THE_INTIAL_TARGET_REQUEST_BOUNDS = "Unable to get the intial target request bounds";////$NON-NLS-1$ /** * Debug message */ private static final String CHILDREN_REFERENCES_ARE = " ---- Children references are : ---- "; /** * Debug message */ private static final String ALL_PARENT_REFERENCES_ARE = " ---- All parent references are : ---- "; /** * Runnable to run command to change parent of selected element * * @author arthur daussy * */ private final class ChangeGraphicalParentRunnable implements NotificationRunnable { /** * New graphical host */ private final IGraphicalEditPart host; /** * Initial request */ private final IGroupRequest request; /** * List of all modified view * Used to compute list of modfied files */ private List<View> viewTomodify; /** * Constructor * * @param host * New graphical host * @param request * Initial request */ private ChangeGraphicalParentRunnable(IGraphicalEditPart host, IGroupRequest request) { this.host = host; this.request = request; } /* * (non-Javadoc) * * @see org.eclipse.papyrus.ui.toolbox.notification.NotificationRunnable#run(org.eclipse.papyrus.ui.toolbox.notification.builders.IContext) */ public void run(IContext context) { IntegrateViewToConfigureComposite composite = (IntegrateViewToConfigureComposite)context.get(IContext.COMPOSITE_CREATED); final INotification notif = (INotification)context.get(IContext.NOTIFICATION_OBJECT); CompositeTransactionalCommand ccMovingElement = new CompositeTransactionalCommand(request.getHostRequest().getEditingDomain(), "Move graphical elements into a new host");////$NON-NLS-1$ /* * Use to calculate impacted file */ viewTomodify = Lists.newArrayList(); IGraphicalEditPart hostEditPart = request.getHostRequest(); viewTomodify.add(host.getNotationView()); for(IGroupNotifier notifier : composite.getSelectedNotifier()) { IGraphicalEditPart notifierEditPart = notifier.getHostEditPart(); IGraphicalEditPart hostCompartmentEditPart = request.getNodeDescpitor().getCompartmentPartFromView(hostEditPart); /* * debugging label */ if(DebugUtils.isDebugging()) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("Move "); stringBuilder.append(Utils.getCorrectLabel(notifierEditPart)); stringBuilder.append(" to "); stringBuilder.append(Utils.getCorrectLabel(hostEditPart)); DebugUtils.getLog().info(stringBuilder.toString()); } if(notifierEditPart != null) { EObject semanticElementToDrop = notifierEditPart.resolveSemanticElement(); if(semanticElementToDrop != null && hostCompartmentEditPart != null) { /* * Request to change graphical parent */ ChangeBoundsRequest chdBoundsRequest = new ChangeBoundsRequest(RequestConstants.REQ_ADD); chdBoundsRequest.setEditParts(notifierEditPart); viewTomodify.add(notifierEditPart.getNotationView()); Command dropcommand = hostCompartmentEditPart.getCommand(chdBoundsRequest); if(dropcommand != null) { ccMovingElement.compose(new CommandProxy(dropcommand)); } } else { //log error } } else { //log error } } /* * Execute resulting command */ if(ccMovingElement != null && !ccMovingElement.isEmpty()) { host.getEditingDomain().getCommandStack().execute(new GMFtoEMFCommandWrapper(ccMovingElement)); } notif.delete(); } /* * (non-Javadoc) * * @see org.eclipse.papyrus.ui.toolbox.notification.NotificationRunnable#getLabel() */ public String getLabel() { return "OK"; } /** * @return the list of all EObject modifed */ public List getModifiedObject() { return viewTomodify == null ? Collections.emptyList() : viewTomodify; } } /** * SingletonHolder is loaded on the first execution of Singleton.getInstance() * or the first access to SingletonHolder.INSTANCE, not before. */ private static class SingletonHolder { public static final GroupRequestAdvisor instance = new GroupRequestAdvisor(); } /** * Get the instance of {@link GroupRequestAdvisor} * * @return */ public static GroupRequestAdvisor getInstance() { return SingletonHolder.instance; } /** * Store all the listener */ private Multimap<EObject, IGroupNotifier> listenners; /** * Logger */ private static LogHelper log = new LogHelper(UMLDiagramEditorPlugin.getInstance()); /*** * Private constructor */ private GroupRequestAdvisor() { listenners = ArrayListMultimap.create(); } /** * Add a {@link IGroupNotifier} * * @param editpolicy */ public void addListenner(EObject eObject, IGroupNotifier editpolicy) { getListenerRegistry().put(eObject, editpolicy); } /** * Remove a {@link IGroupNotifier} * * @param editpolicy */ public void removeListenner(IGroupNotifier editpolicy) { if(editpolicy == null) { return; } Iterator<Entry<EObject, IGroupNotifier>> iterator = getListenerRegistry().entries().iterator(); while(iterator.hasNext()) { Entry<EObject, IGroupNotifier> entry = iterator.next(); if(editpolicy.equals(entry.getValue())) { iterator.remove(); } } } public ICommand notifyGroupFramework(IGroupRequest request) { if(DebugUtils.isDebugging()) { log.debug("***********************BEGIN : Group Request Advisor***********************************"); } /****************** * Handling parent ******************/ CompositeCommand cc = new CompositeCommand("Global command from GroupRequestAdvisor"); Request initialRequest = request.getInitialRequest(); if(initialRequest instanceof ChangeBoundsRequest) { handleMove(request, cc); } else if(initialRequest instanceof GroupRequest && RequestConstants.REQ_DELETE.equals(initialRequest.getType())) { handleDelete(request, cc); } if(cc != null && !cc.isEmpty()) { return cc; } return null; } protected void handleDelete(IGroupRequest request, CompositeCommand cc) { // request.getNodeDescpitor().getChildrenReferences() } /** * Handle move * * @param request * @param cc */ protected void handleMove(IGroupRequest request, CompositeCommand cc) { /* * All parent */ fillRequestWithAllPossibleParent(request); fillRequestWithAllPossibleChildren(request); Object elementAdapter = request.getTargetElement().getAdapter(EObject.class); if(elementAdapter instanceof EObject) { /* * Handle children */ handleSemanticChildren(request, cc, (EObject)elementAdapter); /* * Semantic of all the parent */ handleSemanticParents(request, cc, (EObject)elementAdapter); } if(DebugUtils.isDebugging()) { log.debug("***********************END : Group Request Advisor***********************************"); } // /* // * For all new child send request // * For all GrChild create new request from old one and use dispatcher // */ // if(cc != null && !cc.isEmpty()) { // return cc; // } // return null; } /** * Handle all the children of the group (graphical and non graphical children) * * @param request * @param cc * @param targetElement */ protected void handleSemanticChildren(final IGroupRequest request, CompositeCommand cc, EObject targetElement) { List<EObject> graphicalChildren = Lists.newArrayList(); /* * There is graphical childrne only when the node is moving */ if(request.getInitialRequest() instanceof ChangeBoundsRequest) { graphicalChildren = handleGraphicalChildren(request, cc); } /* * Unset */ for(Entry<EReference, EObject> oldChildrenEntry : Utils.getOldChildren(request).entries()) { Multimap<EReference, EObject> allActualChildren = request.getChildrenEReferenceMap(); EReference ref = oldChildrenEntry.getKey(); if(ref != null) { if(!allActualChildren.containsEntry(oldChildrenEntry.getValue(), ref)) { EReference eOpposite = ref.getEOpposite(); if(!graphicalChildren.contains(oldChildrenEntry.getValue())) { if(eOpposite != null && !eOpposite.isContainment() && !ref.isContainment()) { RemoveValueRequest rmVa = new RemoveValueRequest(targetElement, ref, Collections.singletonList(oldChildrenEntry.getValue())); RemoveValueCommand rmCmd = new RemoveValueCommand(rmVa); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("From handling parent : Remove ");////$NON-NLS-1$ stringBuilder.append(Utils.getCorrectLabel(ref)); stringBuilder.append(" from ");////$NON-NLS-1$ stringBuilder.append(Utils.getCorrectLabel(targetElement)); stringBuilder.append(" value ");////$NON-NLS-1$ stringBuilder.append(Utils.getCorrectLabel(oldChildrenEntry.getValue()));////$NON-NLS-1$ rmCmd.setLabel(stringBuilder.toString()); if(rmCmd != null) { cc.compose(rmCmd); } } } } } } /* * Set semantic */ Set<EObject> newChildren = Sets.newHashSet(); for(Entry<EReference, EObject> entry : request.getChildrenEReferenceMap().entries()) { EReference ref = entry.getKey(); if(ref != null) { EReference eOpposite = ref.getEOpposite(); if(!graphicalChildren.contains(entry.getValue())) { newChildren.add(entry.getValue()); if(eOpposite != null && !eOpposite.isContainment()) { SetDeferredRequest setRq = new SetDeferredRequest(request.getHostRequest().getEditingDomain(), request.getTargetElement(), ref, entry.getValue()); DeferredSetValueCommand setCmd = new DeferredSetValueCommand(setRq); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("From handling parent : Set deferred");////$NON-NLS-1$ stringBuilder.append(Utils.getCorrectLabel(entry.getValue())); stringBuilder.append(" from ");////$NON-NLS-1$ stringBuilder.append(Utils.getCorrectLabel(ref)); stringBuilder.append(" value ");////$NON-NLS-1$ stringBuilder.append(Utils.getCorrectLabel(entry.getValue()));////$NON-NLS-1$ setCmd.setLabel(stringBuilder.toString()); if(setCmd != null) { cc.compose(setCmd); } } } } if(!newChildren.isEmpty()) { handleChangeParentNotificationCommand(request, cc, newChildren); } } } /** * Create a notification to user to ask him is he want to add its new children as graphical child of its parent group * * @param request * {@link InitialContext} {@link IGroupRequest} * @param cc * {@link CompositeCommand} to compose new commands * @param newChildren * List of all new children */ protected void handleChangeParentNotificationCommand(final IGroupRequest request, CompositeCommand cc, Set<EObject> newChildren) { /* * Create dialog to suggest user to move views */ final IGraphicalEditPart host = request.getHostRequest(); /* * Get editpart of all children */ Iterable<IGroupNotifier> listOfChidren = Iterables.transform(newChildren, new Function<EObject, IGroupNotifier>() { public IGroupNotifier apply(EObject arg0) { Collection<IGroupNotifier> notifiers = getListenerRegistry().get(arg0); for(IGroupNotifier notifier : notifiers) { IGraphicalEditPart notifierEditPart = notifier.getHostEditPart(); if(notifierEditPart != null) { if(EcoreUtil.isAncestor(host.getNotationView().getDiagram(), notifierEditPart.getNotationView())) { return notifier; } } } return null; } }); final Iterable<IGroupNotifier> listOfFilteredChidren = Iterables.filter(listOfChidren, Predicates.notNull()); List<IGroupNotifier> automaticChildren = Lists.newArrayList(); List<IGroupNotifier> nonAutomaticChildren = Lists.newArrayList(); /* * Dispatch children */ dispatchChildren(request, host, listOfFilteredChidren, automaticChildren, nonAutomaticChildren); IGraphicalEditPart hostEditPart = request.getHostRequest(); /* * Command to change graphical parent for element when we can guess graphical parent */ for(IGroupNotifier notifier : automaticChildren) { IGraphicalEditPart notifierEditPart = notifier.getHostEditPart(); IGraphicalEditPart hostCompartmentEditPart = request.getNodeDescpitor().getCompartmentPartFromView(hostEditPart); Request initialRequest = request.getInitialRequest(); Rectangle hostBounds = null; if(initialRequest instanceof ChangeBoundsRequest) { hostBounds = Utils.getAbslotueRequestBounds((ChangeBoundsRequest)initialRequest, hostCompartmentEditPart); } else { hostBounds = Utils.getAbsoluteBounds(hostCompartmentEditPart); } Rectangle childBounds = Utils.getAbsoluteBounds(notifierEditPart); MoveElementsCommand mvCmd = new MoveElementsCommand(new MoveRequest(hostCompartmentEditPart.getNotationView(), notifierEditPart.getNotationView())); /* * Integrate view into new compartment */ if(mvCmd != null && mvCmd.canExecute()) { cc.compose(mvCmd); } /* * Set view to the new location */ SetBoundsCommand setBoundCommand = new SetBoundsCommand(WorkspaceEditingDomainFactory.INSTANCE.getEditingDomain(host.getNotationView().eResource().getResourceSet()), "test", new EObjectAdapter(notifierEditPart.getNotationView()), childBounds.translate(hostBounds.getLocation().negate())); if(setBoundCommand != null && setBoundCommand.canExecute()) { cc.compose(setBoundCommand); } } /* * Create notification for element where we can NOT guess the graphical parent */ if(!nonAutomaticChildren.isEmpty()) { /* * create runnable to run the command */ ChangeGraphicalParentRunnable changeGraphicalParentRunnable = new ChangeGraphicalParentRunnable(host, request); final NotificationBuilder notification = createChangeGraphicalParentNotification(nonAutomaticChildren, changeGraphicalParentRunnable, hostEditPart); RunNotificationCommand runNotifCmd = new RunNotificationCommand(request.getHostRequest().getEditingDomain(), "Notification command", getWorkspaceFiles(changeGraphicalParentRunnable.getModifiedObject()), notification);////$NON-NLS-1$ cc.compose(runNotifCmd); } } /** * Dispatch all children in two category. * listOfFilteredChidren will contained all element to automatically add as graphical children * nonAutomaticChildren will contained all element to ask the user what to do with * * @param request * @param host * @param listOfFilteredChidren * @param automaticChildren * @param nonAutomaticChildren */ private void dispatchChildren(final IGroupRequest request, final IGraphicalEditPart host, final Iterable<IGroupNotifier> listOfFilteredChidren, List<IGroupNotifier> automaticChildren, List<IGroupNotifier> nonAutomaticChildren) { for(IGroupNotifier notifier : listOfFilteredChidren) { IGraphicalEditPart parentEditPart = getGraphicalParent(notifier.getHostEditPart()); if(parentEditPart != null) { EObject eObject = parentEditPart.resolveSemanticElement(); if(eObject instanceof ActivityGroup) { ActivityGroup group = (ActivityGroup)eObject; Collection<IGroupNotifier> groupNotifiers = listenners.get(group); if(!groupNotifiers.isEmpty()) { IGroupNotifier myGroupNotifier = groupNotifiers.iterator().next(); /* * Is the old container (an activity group) continaing the current moving element */ boolean isNewContainerVisuallyIncludeInOldContainer = myGroupNotifier.includes(Utils.getAbslotueRequestBounds((ChangeBoundsRequest)request.getInitialRequest(), host)); /* * Can the old container (an activity group) be a model a the current moving element */ boolean canTheOldContainerBeAModelParentOfTheNewContainer = myGroupNotifier.getHostGroupDescriptor().canIBeModelParentOf(host.resolveSemanticElement().eClass()); /* * */ boolean canTheNewContainerBeModelParentOfTheOldContianer = request.getNodeDescpitor().canIBeModelParentOf(host.resolveSemanticElement().eClass()); /* * Is the current moving element containing the old container */ IGroupNotifier currentElementNotifier = getIGroupNotifier(host); boolean isOldContainerVisuallyIncludeInNewContainer = currentElementNotifier.includes(Utils.getAbsoluteBounds(myGroupNotifier.getHostEditPart())); if(canTheOldContainerBeAModelParentOfTheNewContainer && isNewContainerVisuallyIncludeInOldContainer) { /* * Is going to be a new child of the old container */ automaticChildren.add(notifier); } else if(canTheNewContainerBeModelParentOfTheOldContianer && isOldContainerVisuallyIncludeInNewContainer) { /* * Is going to be a new container for the old container */ } else { /* * There is no containing relation between the groups */ if(currentElementNotifier.getHostGroupDescriptor().getContainmentReferenceFor(notifier.getHostEditPart().resolveSemanticElement().eClass()) == null) { nonAutomaticChildren.add(notifier); } else { // If containment link automatically add it to new group automaticChildren.add(notifier); } } } else { /* * No notifier where found */ nonAutomaticChildren.add(notifier); } } else { /* * If there is no container (which is group) */ if(!parentEditPart.equals(host)) { automaticChildren.add(notifier); } } } else { DebugUtils.getLog().error("Unable to retreive graphical parent of " + Utils.getCorrectLabel(notifier), null); } } } /** * Get the IGroupNotifier of the {@link IGraphicalEditPart} passed in argument * * @param host * @return */ private IGroupNotifier getIGroupNotifier(final IGraphicalEditPart host) { EditPolicy editPolicy = host.getEditPolicy(IGroupEditPolicies.GROUP_FRAMEWORK_NOTIFYING_ON_MOVE_EDIT_POLICY); if(editPolicy instanceof IGroupNotifier) { return (IGroupNotifier)editPolicy; } else { editPolicy = host.getEditPolicy(IGroupEditPolicies.GROUP_FRAMEWORK_NOTIFYING_ON_CREATION_EDIT_POLICY); if(editPolicy instanceof IGroupNotifier) { return (IGroupNotifier)editPolicy; } } throw new RuntimeException("Unable to retreive the IGroupNofier of the current group");////$NON-NLS-1$ } /** * Get the edit which represent the parent (semantically) of the edit part child passed in argument * * @param child * @return */ private IGraphicalEditPart getGraphicalParent(IGraphicalEditPart child) { IGraphicalEditPart result = child; EObject childElement = child.resolveSemanticElement(); if(child != null) { while(childElement == result.resolveSemanticElement()) { try { result = (IGraphicalEditPart)result.getParent(); } catch (ClassCastException e) { return result; } } } return result; } /** * Create a notification to ask user if he want to change the graphical parent of some elements * * @param listOfFilteredChidren * @param changeGraphicalParentRunnable * @return */ private NotificationBuilder createChangeGraphicalParentNotification(final Iterable<IGroupNotifier> listOfFilteredChidren, ChangeGraphicalParentRunnable changeGraphicalParentRunnable, final IGraphicalEditPart host) { return new NotificationBuilder().setType(Type.INFO).setAsynchronous(true).setTemporary(true).setDelay(3000).setTitle("Papyrus graphical modification").setComposite(new ICompositeCreator() { public Composite createComposite(Composite parent, FormToolkit toolkit) { return new IntegrateViewToConfigureComposite(parent, SWT.NONE, Lists.newArrayList(Lists.newArrayList(listOfFilteredChidren)), "New elements are visually contained in the current group (" + Utils.getCorrectLabel(host) + ").\n Please select the elements you want to integrate into this group figure."); } }).addAction(changeGraphicalParentRunnable); } /** * Handle graphical children * * @param request * {@link IGroupRequest} * @param cc * {@link CompositeCommand} to compose new commands * @return The list of all graphical children already handled */ protected List<EObject> handleGraphicalChildren(IGroupRequest request, CompositeCommand cc) { /* * List of all the graphical children */ List<EObject> graphicalChildren = new ArrayList<EObject>(); /* * Handle graphical children */ //Get graphical children Object targetEditPart = request.getTargetElement().getAdapter(IGraphicalEditPart.class); IGraphicalEditPart compartementEditPart = null; if(targetEditPart instanceof IGroupCompartmentEditPart) { compartementEditPart = ((IGraphicalEditPart)targetEditPart); } else if(targetEditPart instanceof IGraphicalEditPart) { IContainerNodeDescriptor nodeDescpitor = request.getNodeDescpitor(); if (nodeDescpitor != null){ compartementEditPart = nodeDescpitor.getCompartmentPartFromView((IGraphicalEditPart)targetEditPart); } } if(compartementEditPart != null) { /* * Transform an IGraphicalEditPart to the related GroupNotifyingEditPolicy */ @SuppressWarnings("unchecked") Iterable<GroupNotifyingOnMoveEditPolicy> policies = Iterables.transform(compartementEditPart.getChildren(), new Function<IGraphicalEditPart, GroupNotifyingOnMoveEditPolicy>() { public GroupNotifyingOnMoveEditPolicy apply(IGraphicalEditPart from) { EditPolicy editPolicy = from.getEditPolicy(IGroupEditPolicies.GROUP_FRAMEWORK_NOTIFYING_ON_MOVE_EDIT_POLICY); if(editPolicy instanceof GroupNotifyingOnMoveEditPolicy) { return (GroupNotifyingOnMoveEditPolicy)editPolicy; } return null; } }); for(final GroupNotifyingOnMoveEditPolicy p : policies) { if(p != null) { if(DebugUtils.isDebugging()) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("+++ Work for child "); stringBuilder.append(Utils.getCorrectLabel(p.getEObject())); stringBuilder.append(" +++"); DebugUtils.getLog().debug(stringBuilder.toString()); } Request initialRequest = request.getInitialRequest(); Request auxChReq = null; if(initialRequest instanceof ChangeBoundsRequest) { auxChReq = Utils.getChangeBoundsRequestCopy((ChangeBoundsRequest)initialRequest, p.getHostEditPart()); } else { auxChReq = initialRequest; } /* * Save graphical parent */ // auxChReq.getExtendedData().put(GROUP_FRAMEWORK_GRAPHICAL_PARENT, compartementEditPart.resolveSemanticElement()); graphicalChildren.add(p.getEObject()); Command childCommand = p.getCommand(auxChReq); if(childCommand != null && childCommand.canExecute()) { cc.compose(new CommandProxy(childCommand)); } } } } return graphicalChildren; } /** * Set and Unset semantic relation with parents * * @param request * {@link IGroupRequest} * @param cc * {@link CompositeCommand} to compose command * @param elementAdapter */ protected void handleSemanticParents(IGroupRequest request, CompositeCommand cc, Object elementAdapter) { EObject targetElement = (EObject)elementAdapter; Multimap<EReference, EObject> allActualParent = request.getParentEReferenceMap(); for(Entry<EReference, EObject> entry : Utils.getOldParents(request).entries()) { EReference ref = entry.getKey(); if(ref != null) { if(!allActualParent.containsEntry(ref, entry.getValue())) { EReference eOpposite = ref.getEOpposite(); if(eOpposite != null && !eOpposite.isContainment() && !eOpposite.isDerived()) { RemoveValueRequest rmVa = new RemoveValueRequest(targetElement, ref, Collections.singletonList(entry.getValue())); RemoveValueCommand rmCmd = new RemoveValueCommand(rmVa); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("From handling parent : Remove ");////$NON-NLS-1$ stringBuilder.append(Utils.getCorrectLabel(entry.getValue())); stringBuilder.append(" from ");////$NON-NLS-1$ stringBuilder.append(Utils.getCorrectLabel(targetElement)); stringBuilder.append(" value ");////$NON-NLS-1$ stringBuilder.append(Utils.getCorrectLabel(entry.getValue()));////$NON-NLS-1$ rmCmd.setLabel(stringBuilder.toString()); if(rmCmd != null) { cc.compose(rmCmd); } } // } } } } /* * Set semantic * If the EObject has not been already handle as graphical children */ for(Entry<EReference, EObject> entry : request.getParentEReferenceMap().entries()) { EReference ref = entry.getKey(); if(ref != null) { EReference eOpposite = ref.getEOpposite(); if(eOpposite != null && !eOpposite.isContainment() && !ref.isContainment()) { SetDeferredRequest setRq = new SetDeferredRequest(request.getHostRequest().getEditingDomain(), request.getTargetElement(), ref, entry.getValue()); DeferredSetValueCommand setCmd = new DeferredSetValueCommand(setRq); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("From handling parent : Set deferred ");////$NON-NLS-1$ stringBuilder.append(Utils.getCorrectLabel(ref)); stringBuilder.append(" from ");////$NON-NLS-1$ stringBuilder.append(Utils.getCorrectLabel(request.getTargetElement())); stringBuilder.append(" value ");////$NON-NLS-1$ stringBuilder.append(Utils.getCorrectLabel(entry.getValue()));////$NON-NLS-1$ setCmd.setLabel(stringBuilder.toString()); if(setCmd != null) { cc.compose(setCmd); } } } } } /** * Return a list of all IGroupRequestListenner which can be children of the target of the request * * @param request * @return */ protected Multimap<EReference, IGroupNotifier> fillRequestWithAllPossibleChildren(IGroupRequest request) { final Multimap<EReference, IGroupNotifier> result = fillReqestWithReferendedElement(request, false, false); /** * TODO filter graphical parent which currently moving * I1 in ActPart1 * ActPart1 move into ActPart2 * I1 should only reference ActPart1 */ /* * Debug */ DebugUtils.displayMultipmapDebug(CHILDREN_REFERENCES_ARE, request.getChildrenEReferenceMap()); return result; } public EObject getPossibleModelParent(IGroupRequest request) { Multimap<EReference, IGroupNotifier> parentsMap = fillReqestWithReferendedElement(request, true, true); List<IGroupNotifier> parents = Lists.newArrayList(parentsMap.values()); Collections.sort(parents); if(!parents.isEmpty()) { return parents.get(0).getEObject(); } return request.getHostRequest().resolveSemanticElement(); } /** * Return only current displayed listener * * @author adaussy * */ private static class ActiveListener implements Predicate<IGroupNotifier> { private Diagram currentDiagramDisplayed; public ActiveListener(Diagram currentDiagramDisplayed) { super(); this.currentDiagramDisplayed = currentDiagramDisplayed; Assert.isNotNull(currentDiagramDisplayed); } public boolean apply(IGroupNotifier input) { IGraphicalEditPart host = input.getHostEditPart(); if(host != null) { View primaryView = host.getPrimaryView(); if(primaryView != null) { return currentDiagramDisplayed.equals(primaryView.getDiagram()); } } return false; } } /** * Get all the element in references which are included or include (depending of the parameter include) * * @param request * {@link IGroupRequest} * @param newBounds * New bounds of the elements * @param references * All references to take into account * @param eReferenceMapToFillInRequest * Map in the request to fill * @param result * {@link Map} which link a {@link EReference} to a {@link IGroupNotifier} * @param containementOnly * true if we are looking for containing references only */ protected void getReferenceElements(IGroupRequest request, final Rectangle newBounds, final List<EReference> references, Multimap<EReference, Element> eReferenceMapToFillInRequest, Multimap<EReference, IGroupNotifier> result, boolean include, boolean containementOnly, Map<EStructuralFeature, EStructuralFeature> parentOpositeFeature) { Iterable<IGroupNotifier> activeListeners = Iterables.filter(getListenerRegistry().values(), new ActiveListener(getCurrentlyDisplayedDiagram(request))); for(IGroupNotifier input : activeListeners) { EObject inputEObject = input.getEObject(); if(inputEObject == null || !(inputEObject instanceof Element)) { continue; } Object adapter = request.getTargetElement().getAdapter(EObject.class); // EObject targetElement = null; // if(adapter instanceof EObject) { // targetElement = (EObject)adapter; // } if(inputEObject.equals(adapter)) { continue; } EReference refenceFounded = null; for(EReference ref : references) { EClass refType = ref.getEReferenceType(); EClass eoBjectType = inputEObject.eClass(); if(refType.isSuperTypeOf(eoBjectType)) { refenceFounded = ref; break; } } if(refenceFounded != null) { if((include && input.includes(newBounds)) || (!include && input.isIncludedIn(newBounds))) { if(containementOnly && parentOpositeFeature.get(refenceFounded) instanceof EReference && !((EReference)parentOpositeFeature.get(refenceFounded)).isContainment()) { continue; } eReferenceMapToFillInRequest.put(refenceFounded, (Element)inputEObject); result.put(refenceFounded, input); } } } } /** * Return the currently displayed diagram * * @param request * @return */ protected Diagram getCurrentlyDisplayedDiagram(IGroupRequest request) { IGraphicalEditPart graph = request.getHostRequest(); if(graph != null) { Object m = graph.getModel(); if(m instanceof View) { View v = (View)m; return v.getDiagram(); } } throw new RuntimeException("Unable to get the current diagram displayed");////$NON-NLS-1$ } /** * Return the list of all {@link IGroupNotifier} which can be parent of the target of the request * * @param request * @return */ protected Multimap<EReference, IGroupNotifier> fillRequestWithAllPossibleParent(IGroupRequest request) { final Multimap<EReference, IGroupNotifier> result = fillReqestWithReferendedElement(request, true, false); /* * Debug */ DebugUtils.displayMultipmapDebug(ALL_PARENT_REFERENCES_ARE, request.getParentEReferenceMap()); return result; } protected Multimap<EReference, IGroupNotifier> fillReqestWithReferendedElement(IGroupRequest request, boolean lookingForParent, boolean onlyContainment) { final Rectangle newBounds = getInitalTargetRequestNewBounds(request); final Multimap<EReference, IGroupNotifier> result = ArrayListMultimap.create(); if(request.getNodeDescpitor() == null) { return result; } List<EReference> references = null; if(lookingForParent) { references = request.getNodeDescpitor().getParentReferences(); } else { references = request.getNodeDescpitor().getChildrenReferences(); } final Multimap<EReference, IGroupNotifier> auxResult = ArrayListMultimap.create(); final Multimap<EReference, Element> eReferenceLookedForMap = ArrayListMultimap.create(); getReferenceElements(request, newBounds, references, eReferenceLookedForMap, auxResult, lookingForParent, onlyContainment, lookingForParent ? request.getNodeDescpitor().getParentEOppositeReferences() : null); /* * Filter ancestors */ for(EReference ref : eReferenceLookedForMap.keySet()) { /* * Filter descendant * Example : * 1 - ActPart1 include in Act1 then Act1 disappear * 2 - ActPart1 include in ActPart2 then ActPart1 disappear */ Object adapter = request.getTargetElement().getAdapter(EObject.class); if(adapter instanceof Element) { Element element = (Element)adapter; Predicate<Element> composedPredicate = Predicates.and(new SameContainerFilter(element), lookingForParent ? new DescendantsFilter(eReferenceLookedForMap.values()) : new AncestorFilter(eReferenceLookedForMap.values())); Collection<Element> filteredCollection = Collections2.filter(eReferenceLookedForMap.get(ref), composedPredicate); if(lookingForParent) { request.getParentEReferenceMap().putAll(ref, filteredCollection); } else { request.getChildrenEReferenceMap().putAll(ref, filteredCollection); } } } for(EReference ref : auxResult.keySet()) { /* * Filter descendant * Example : * 1 - ActPart1 include in Act1 then Act1 disappear * 2 - ActPart1 include in ActPart2 then ActPart1 disappear */ Iterable<IGroupNotifier> resultCollection = Iterables.filter(auxResult.get(ref), new DescendantsFilterIGroupNotifier(auxResult.values())); result.putAll(ref, resultCollection); } return result; } /** * Get the absolute bounds of the target of the request * * @param request * @return */ public static Rectangle getInitalTargetRequestNewBounds(final IGroupRequest request) { Request initialRequest = request.getInitialRequest(); if(initialRequest instanceof ChangeBoundsRequest) { return Utils.getAbslotueRequestBounds((ChangeBoundsRequest)initialRequest, request.getHostRequest()); } else if(initialRequest instanceof CreateViewRequest) { return Utils.getAbslotueRequestBounds((CreateViewRequest)initialRequest); } throw new RuntimeException(UNABLE_TO_GET_THE_INTIAL_TARGET_REQUEST_BOUNDS); } /** * Convenience method to get a list of workspaces files associated with <code>eObject</code>. * * @param eObject * the model object, may be <code>null</code> * @return the list of {@link IFile}s */ protected static List getWorkspaceFiles(EObject eObject) { List result = new ArrayList(); if(eObject != null) { Resource resource = eObject.eResource(); if(resource != null) { IFile file = WorkspaceSynchronizer.getFile(resource); if(file != null) { result.add(file); } } } return result; } /** * Convenience method to get a list of workspaces files associated with {@link EObject}s in <code>eObject</code>. * * @param eObjects * the list of model object * @return the list of {@link IFile}s */ protected static List getWorkspaceFiles(List eObjects) { List result = new ArrayList(); for(Iterator i = eObjects.iterator(); i.hasNext();) { Object next = i.next(); if(next instanceof EObject) { Resource resource = ((EObject)next).eResource(); if(resource != null) { IFile file = WorkspaceSynchronizer.getFile(resource); if(file != null) { result.add(file); } } } } return result; } /** * @return the listenners */ public Multimap<EObject, IGroupNotifier> getListenerRegistry() { return listenners; } }