/***************************************************************************** * Copyright (c) 2010 Atos Origin. * * * 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: * Atos Origin - Initial API and implementation * *****************************************************************************/ package org.eclipse.papyrus.uml.diagram.common.groups.core.utils; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.Status; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.RoundedRectangle; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.emf.common.command.Command; import org.eclipse.emf.common.command.CompoundCommand; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.edit.command.AddCommand; import org.eclipse.emf.edit.command.RemoveCommand; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPartViewer; import org.eclipse.gef.LayerConstants; import org.eclipse.gef.editparts.LayerManager; import org.eclipse.gef.requests.ChangeBoundsRequest; import org.eclipse.gmf.runtime.diagram.ui.editparts.CompartmentEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.IPrimaryEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeCompartmentEditPart; import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewAndElementRequest; import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants; import org.eclipse.gmf.runtime.notation.Bounds; import org.eclipse.gmf.runtime.notation.LayoutConstraint; import org.eclipse.gmf.runtime.notation.Node; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.papyrus.commands.wrappers.GEFtoEMFCommandWrapper; import org.eclipse.papyrus.uml.diagram.common.groups.commands.ChangeModelParentCommand; import org.eclipse.papyrus.uml.diagram.common.groups.core.groupcontainment.GroupContainmentRegistry; import org.eclipse.papyrus.uml.diagram.common.groups.groupcontainment.AbstractContainerNodeDescriptor; import org.eclipse.papyrus.uml.diagram.common.groups.utils.GraphicalAndModelElementComparator; import org.eclipse.papyrus.uml.diagram.common.groups.utils.GraphicalAndModelElementComparator.Mode; /** * This class provides utility methods useful for the group framework. * * @author vhemery and adaussy */ public class Utils { /** * Find the containers which may contain an edit part in the given bounds. * * @param bounds * the new or old bounds of the item * @param diagramPart * the diagram edit part * @return the list of edit parts that are registered through the group framework and that can contain an element within the given bounds */ public static List<IGraphicalEditPart> findPossibleParents(Rectangle bounds, DiagramEditPart diagramPart) { if(diagramPart == null) { return Collections.emptyList(); } Set<IGraphicalEditPart> groupParts = new HashSet<IGraphicalEditPart>(); //For all object in diagram, find edit parts for(Object view : diagramPart.getViewer().getEditPartRegistry().keySet()) { if(view instanceof View) { Object editpart = diagramPart.getViewer().getEditPartRegistry().get(view); if(editpart instanceof IGraphicalEditPart) { IGraphicalEditPart part = (IGraphicalEditPart)editpart; //If this group is handled by the group framework if(GroupContainmentRegistry.isContainerConcerned(part)) { //recover group descriptor of this part AbstractContainerNodeDescriptor desc = GroupContainmentRegistry.getContainerDescriptor(part); //Look if I can get the Eclass from the IdoneContainer //And I look if its contain the element we want it to be compared with if(desc.getContentArea(part).contains(bounds)) { groupParts.add(part); } } } } } return new ArrayList<IGraphicalEditPart>(groupParts); } /** * Find containers which may be chosen as graphical and as model parent of the element which has already been created * * @param graphicalParentsToComplete * an empty list that will be filled with edits part of available graphical parents (e.g. new ArrayList()) * @param modelParentsToComplete * an empty list that will be filled with edits part of available graphical parents (e.g. new ArrayList()) * @param childPart * Edit part of the element we want to find out which may be its containers * @param doTransalte * if true compute the list of all graphical and model parent after moving and false compute the list before moving * @return true if succeed */ @SuppressWarnings("unchecked") public static boolean createComputedListsOfParents(List<IGraphicalEditPart> graphicalParentsToComplete, List<IGraphicalEditPart> modelParentsToComplete, IGraphicalEditPart childPart, ChangeBoundsRequest request, boolean doTransalte) { Collection<View> diagramViews = new ArrayList<View>(childPart.getViewer().getEditPartRegistry().keySet()); Object _elementView = childPart.getModel(); if(_elementView instanceof View) { diagramViews.remove(_elementView); View myGroupView = (View)_elementView; withdrawGraphicalSonsOf(diagramViews, myGroupView); } Rectangle bounds = null; EClass childType = null; if(childPart != null) { bounds = Utils.getAbsoluteBounds(childPart).getCopy(); childType = childPart.resolveSemanticElement().eClass(); } if(doTransalte) { bounds = request.getTransformedRectangle(bounds); } return createComputedListsOfParents(graphicalParentsToComplete, modelParentsToComplete, bounds, childType, diagramViews, childPart); } /** * Find containers which may be chosen as graphical and as model parent of the element (after creation) * * @param graphicalParentsToComplete * an empty list that will be filled with edits part of available graphical parents (e.g. new ArrayList()) * @param modelParentsToComplete * an empty list that will be filled with edits part of available graphical parents (e.g. new ArrayList()) * @param creationRequest * request of creation * @param anyPart * An edit part to get the viewer * @param child * The EClass of the element to create * @param elementName * Name of the element to be created (name used to look for default size FIXME) * @return true if succeed */ @SuppressWarnings("unchecked") public static boolean createComputedListsOfParents(List<IGraphicalEditPart> graphicalParentsToComplete, List<IGraphicalEditPart> modelParentsToComplete, CreateViewAndElementRequest creationRequest, IGraphicalEditPart anyPart, EClass child) { Collection<View> diagramViews = new ArrayList<View>(anyPart.getViewer().getEditPartRegistry().keySet()); Dimension size = creationRequest.getSize(); //FIXME : Add a correct default size // If size == null then a default size is used to create the bounds of the new elements if(size == null || size.isEmpty()) { size = new Dimension(0, 0); } Rectangle bounds = new Rectangle(creationRequest.getLocation(), size); return createComputedListsOfParents(graphicalParentsToComplete, modelParentsToComplete, bounds, child, diagramViews, anyPart); } /** * Find containers which may be chosen as graphical and as model parent of the element to create * * @param graphicalParentsToComplete * an empty list that will be filled with edits part of available graphical parents (e.g. new ArrayList()) * @param modelParentsToComplete * an empty list that will be filled with edits part of available model parents e.g. new ArrayList()) * @param bounds * Bounds of the element * @param request * createElementRequest of the current request * @param views * Collection of view to iteration on * @param anyPart * an edit part of the diagram to get the viewer * @return true if successful */ private static boolean createComputedListsOfParents(List<IGraphicalEditPart> graphicalParentsToComplete, List<IGraphicalEditPart> modelParentsToComplete, Rectangle bounds, EClass child, Collection<View> views, IGraphicalEditPart anyPart) { if(views.isEmpty()) { return false; } for(Object view : views) { if(view instanceof View) { Object editpart = anyPart.getViewer().getEditPartRegistry().get(view); if(editpart instanceof IGraphicalEditPart) { IGraphicalEditPart part = (IGraphicalEditPart)editpart; if(GroupContainmentRegistry.isContainerConcerned(part)) { AbstractContainerNodeDescriptor desc = GroupContainmentRegistry.getContainerDescriptor(part); //Check if the current part contains the element //FIXME replace this piece of code by isItVisualyContained if(desc.getContentArea(part).contains(bounds)) { if(child instanceof EClass) { if(desc.canIBeModelParentOf(child)) { // If an edit part can be a model parent then is also a possible graphical parent graphicalParentsToComplete.add(part); modelParentsToComplete.add(part); } else { if(desc.canIBeGraphicalParentOf(child)) { graphicalParentsToComplete.add(part); } } } } } } } } //Try to reduce the number of available parents by removing if(graphicalParentsToComplete.size() > 1) withdrawUselessAncestorsElements(graphicalParentsToComplete, Mode.GRAPHICAL_AND_MODEL); if(modelParentsToComplete.size() > 1) withdrawUselessAncestorsElements(modelParentsToComplete, Mode.MODEL); //FIXME Sort the two list in order to have the most relevant parent first (lowest surface ) return true; } /** * This method complete the list childsToComplete to add element which are visually contained in the element in creation "parent" and which can * be graphical children of this new elements * * @param childsToComplete * Empty list which will contain in the newly created element "parent" and which can be graphical children of this new elements * @param creationRequest * request of creation * @param anyPart * An edit part to get the viewer * @param descriptor * Descriptor of the element in creation * @param ElementTypeName * Name of the element to be created (name used to look for default size FIXME) * @return true if succeed */ public static boolean createComputedListsOfVisualYRelatedElements(List<IGraphicalEditPart> childsToComplete, CreateViewAndElementRequest creationRequest, IGraphicalEditPart anyPart, AbstractContainerNodeDescriptor descriptor) { Collection<View> diagramViews = new ArrayList<View>(anyPart.getViewer().getEditPartRegistry().keySet()); Dimension size = creationRequest.getSize(); //FIXME : Add a correct default size // If size == null then a default size is used to create the bounds of the new elements if(size == null || size.isEmpty()) { size = new Dimension(0, 0); } Rectangle bounds = new Rectangle(creationRequest.getLocation(), size); createComputedListsOfVisualyRelatedElements(childsToComplete, bounds, descriptor, diagramViews, anyPart); return true; } /** * This method complete the list childsToComplete to add element which are visually contained in the element which is moving ("parent") and which * can * be graphical children of this new elements * * @param childsToComplete * Empty list which will contain in the newly created element "parent" and which can be graphical children of this new elements * @param request * {@link ChangeBoundsRequest} * @param parentPart * {@link IGraphicalEditPart} of the parent * @param descriptor * {@link AbstractContainerNodeDescriptor} of the parent * @return true is succeed */ public static boolean createComputedListsOfVisuallyRelatedElements(List<IGraphicalEditPart> childsToComplete, ChangeBoundsRequest request, IGraphicalEditPart parentPart, AbstractContainerNodeDescriptor descriptor, boolean doTransalte) { Collection<View> diagramViews = new ArrayList<View>(parentPart.getViewer().getEditPartRegistry().keySet()); diagramViews.remove(parentPart.getModel()); Rectangle bounds = null; IGraphicalEditPart compartmentEditPart = (IGraphicalEditPart)Utils.getCompartementEditPartFromMainEditPart(parentPart.getViewer().getEditPartRegistry(), parentPart); if(compartmentEditPart == null) { compartmentEditPart = parentPart; } if(parentPart != null) { bounds = Utils.getAbsoluteBounds(compartmentEditPart).getCopy(); } if(doTransalte) { //bounds.translate(request.getMoveDelta()); bounds = request.getTransformedRectangle(bounds); } createComputedListsOfVisualyRelatedElements(childsToComplete, bounds, descriptor, diagramViews, parentPart); return true; } /*** * Compute a list of all group which include the bounds of my element which has been moved with the following request * * @param element * Element which we can know the groups which it intersect * @param request * {@link ChangeBoundsRequest} of the moving group * @param doTranslate * if true translate the bound of the element. (used to find the difference between element before and after the command) * @return The list of {@link IGraphicalEditPart} group that which has their compartment edit part which intersect the compartment of my */ public static List<IGraphicalEditPart> createComputeListsOfAllGroupContainerVisually(IGraphicalEditPart element, ChangeBoundsRequest request, boolean doTranslate, IGraphicalEditPart movingParent) { List<IGraphicalEditPart> result = new ArrayList<IGraphicalEditPart>(); EditPartViewer viewer = element.getViewer(); Map editPartRegistry = null; if(viewer != null) { editPartRegistry = viewer.getEditPartRegistry(); } // Test on entrances if(editPartRegistry == null || element == null) { return null; } //Get the compartment edit part IGraphicalEditPart myCompartmentEditPart = (IGraphicalEditPart)getCompartementEditPartFromMainEditPart(editPartRegistry, element); Rectangle myBounds = null; /* * Find the bounds of the compartment if none use the figure bounds */ if(myCompartmentEditPart != null) { myBounds = getAbsoluteBounds(myCompartmentEditPart).getCopy(); } else { myBounds = getAbsoluteBounds(element).getCopy(); } /* * If use the translated figure use as bounds for the childs its bounds translated with the delta move */ if(doTranslate) { myBounds = myBounds.translate(request.getMoveDelta()); } Collection<View> diagramViews = new ArrayList<View>(editPartRegistry.keySet()); Object _elementView = element.getModel(); diagramViews.remove(_elementView); /* * Withdraw from the collection of element all elements which are graphical child in myGroupView */ if(_elementView instanceof View) { View myGroupView = (View)_elementView; withdrawGraphicalSonsOf(diagramViews, myGroupView); } /* * For all graphical edits parts try to know if it visually contained the child */ for(Object view : diagramViews) { if(view instanceof View) { Object editpart = editPartRegistry.get(view); if(editpart instanceof IGraphicalEditPart) { IGraphicalEditPart part = (IGraphicalEditPart)editpart; IGraphicalEditPart partCompartment = (IGraphicalEditPart)getCompartementEditPartFromMainEditPart(editPartRegistry, part); if(GroupContainmentRegistry.isContainerConcerned(part) && partCompartment != null) { Rectangle partBounds = getAbsoluteBounds(partCompartment); if((part.getParent().equals(movingParent) || partCompartment.equals(movingParent)) && doTranslate) { partBounds = request.getTransformedRectangle(partBounds); } if(partBounds.contains(myBounds)) { result.add(part); } } } } } return result; } /** * Function which withdraw from a list of view all view which are descendant of the myView parameter * (This function is called recursively * * @param views * List of the view * @param myView * The view we want to remove the descendant */ private static void withdrawGraphicalSonsOf(Collection<View> views, View myView) { for(Object o : myView.getChildren()) { if(o instanceof View) { View childView = (View)o; withdrawGraphicalSonsOf(views, childView); views.remove(childView); } } } /** * Find containers which may be chosen as graphical and as model parent of the element * * @param childsToComplete * Empty list which will contain in the newly created element "parent" and which can be graphical children of this new elements * @param parentsBounds * Bounds of the element * @param descriptors * Descriptor of the element in creation * @param views * Collection of view to iteration on * @param anyPart * an edit part of the diagram to get the viewer * @return true if succeed */ private static boolean createComputedListsOfVisualyRelatedElements(List<IGraphicalEditPart> childsToComplete, Rectangle parentsBounds, AbstractContainerNodeDescriptor descriptor, Collection<View> views, IGraphicalEditPart anyPart) { /* * 1 - Compute the EClass(s) which the element can be parent of : listEclass * 2 - Iterate on views : v * 2.1 - If v correspond to a main editPart : part * 2.1.1 - If v correspond to a a EClass which belong to listEclass * 2.1.1.1 - If part is visually contained is the parent then add to the list * 3 - Withdraw useless elements */ //Set<AbstractContainerNodeDescriptor> descriptors = GroupContainmentRegistry.getDescriptorsWithContainerEClass(parentEclass); List<EClass> possibleChildrenEClass = new ArrayList<EClass>(descriptor.getPossibleGraphicalChildren()); if(!possibleChildrenEClass.isEmpty()) { for(Object view : views) { if(view instanceof View) { View childView = (View)view; EObject element = childView.getElement(); if(element != null) { EClass childEclass = (element.eClass()); Object editpart = anyPart.getViewer().getEditPartRegistry().get(childView); if(editpart instanceof IGraphicalEditPart) { IGraphicalEditPart part = (IGraphicalEditPart)editpart; if(isMainEditPart(part)) { for(EClass possibleChild : possibleChildrenEClass) { if(possibleChild.isSuperTypeOf(childEclass)) { if(isItVisualyContained(parentsBounds, part)) { childsToComplete.add(part); break; } } } } } } } } if(childsToComplete.size() > 1) withdrawUselessDescendantElements(childsToComplete, Mode.GRAPHICAL_AND_MODEL); } return true; } /** * This methods is used to know if an edit part is contained in a rectangle * * @param parentBounds * Rectangle of the parent * @param child * IGraphicalEditPart of the element we want to test * @return true if the bounds of child are contained in parentsBounds */ private static boolean isItVisualyContained(Rectangle parentBounds, IGraphicalEditPart child) { if(child.getParent() instanceof IGraphicalEditPart) { // an edit part is considered the "main" edit part of an element if it is not contained in an edit part of the same element (e.g. not a label nor a compartment) // Rectangle bounds = child.getFigure().getBounds().getCopy(); // // ((IGraphicalEditPart)child.getParent()).getFigure().translateToAbsolute(bounds); Rectangle bounds = Utils.getAbsoluteBounds(child); if(bounds != null) { if(parentBounds.contains(bounds)) { return true; } } } return false; } /** * Return true if the edit part is the main editPart of a model element (e.g the label is not the main edit part of an activityPartition * * @param part * IGraphicalEditPart to test * @return */ public static boolean isMainEditPart(IGraphicalEditPart part) { EObject currentElement = part.resolveSemanticElement(); EditPart parentEditPart = part.getParent(); if(parentEditPart instanceof IGraphicalEditPart) { return !currentElement.equals(((IGraphicalEditPart)parentEditPart).resolveSemanticElement()); } else { return true; } } /** * This method remove all elements from the list which have a parent in the (graphical model) or model in the list * * @param listToModify * List of elements (IGraphicalEditPart * @param mode * if GraphicalAndModelElementComparator.MODEL then it only take care of model parent if * GraphicalAndModelElementComparator.GRAPHICAL_AND_MODEL it take care of graphical and logical relationship * @return true if succeed */ private static void withdrawUselessAncestorsElements(List<IGraphicalEditPart> listToModify, Mode mode) { if(!listToModify.isEmpty()) { GraphicalAndModelElementComparator comparator = new GraphicalAndModelElementComparator(listToModify.get(0)); //Select the comparator mode comparator.setMode(mode); /** * Keep in the list only elements which which have no smaller element ( with this comparator) * */ for(int element = 0; element < listToModify.size(); element++) { for(int elementToCompare = element + 1; elementToCompare < listToModify.size(); elementToCompare++) { int compare = comparator.compare((IGraphicalEditPart)listToModify.get(element), (IGraphicalEditPart)listToModify.get(elementToCompare)); if(compare < 0) { listToModify.remove(element); element--; elementToCompare = listToModify.size(); } else if(compare > 0) { listToModify.remove(elementToCompare); elementToCompare--; } } } } } /** * This method will withdraw all EObject directly referenced by object which are also referenced by one of its parents (parent by reference and * parent by containment) * * @param object */ public static void withDrawRedundantElementReferenced(EObject object) { /* * Algo : * 1 - Select all references which are Group Framework concerned and which represent a reference to a parent * 2 - Create a Set directlyReferencedByElement of EObject directly referenced by object * 3 - Iterate thought parents to see if one of them also point at an element of directlyReferencedByElement */ /* * 1 - Select all references which are Group Framework concerned and which represent a reference to a parent */ Set<EReference> groupFrameworkReferences = GroupContainmentRegistry.getAllERefencesFromNodeToGroup(); HashMap<EObject, EReference> referencingGroupsAndTheirRelation = new HashMap<EObject, EReference>(); Set<EObject> elementToVosit = new HashSet<EObject>(); for(EReference ref : groupFrameworkReferences) { if(object.eClass().getEAllReferences().contains(ref)) { // list of groups containing the object List<EObject> groups; if(ref.isMany()) { groups = (List<EObject>)object.eGet(ref); } else { groups = Collections.singletonList((EObject)object.eGet(ref)); } if(!ref.isContainment()) { /* * 2 - Create a Set directlyReferencedByElement of EObject directly referenced by object */ for(EObject element : groups) { if(!referencingGroupsAndTheirRelation.containsKey(element) && !ref.isDerived()) { referencingGroupsAndTheirRelation.put(element, ref); } } } for(EObject group : groups) { if(group != null) { elementToVosit.add(group); } } } } Set<EObject> elementAlreadyVisited = new HashSet<EObject>(); for(EObject visitingElement : elementToVosit) { withDrawRedundantElementReferenced(object, groupFrameworkReferences, referencingGroupsAndTheirRelation, elementAlreadyVisited, visitingElement); } } /** * This method will withdraw all EObject directly referenced by object which are also referenced by one of its parents (parent by reference and * parent by containment). This method is called recursively * * @param originalEObject * The EObject on which you want to check the reference * @param groupFrameworkReferences * All the EReference which are used on the groupFramework and which represent a relation from a element to its parent * @param directlyReferencedByElement * Set of elements which are directly referenced by the original element * @param elementAlreadyVisited * Set of element already visited. Used to avoid infinite loop * @param visitingElement * The current element which is being visited */ private static void withDrawRedundantElementReferenced(EObject originalEObject, Set<EReference> groupFrameworkReferences, Map<EObject, EReference> directlyReferencedByElement, Set<EObject> elementAlreadyVisited, EObject visitingElement) { if(visitingElement != null) { elementAlreadyVisited.add(visitingElement); for(EReference ref : groupFrameworkReferences) { if(visitingElement != null) { if(visitingElement.eClass().getEAllReferences().contains(ref)) { List<EObject> groups; if(ref.isMany()) { groups = (List<EObject>)visitingElement.eGet(ref); } else { groups = Collections.singletonList((EObject)visitingElement.eGet(ref)); } for(EObject currentElementParentGroup : groups) { //If it belong to the directly referenced element then if(directlyReferencedByElement.containsKey(currentElementParentGroup)) { withdrawEObjectFromReference(originalEObject, currentElementParentGroup, directlyReferencedByElement.get(currentElementParentGroup)); // parents already handled in the first recursion (as direct parent group) } else if(elementAlreadyVisited.contains(currentElementParentGroup)) { // element already met, avoid infinite loop org.eclipse.papyrus.uml.diagram.common.groups.Activator.getDefault().getLog().log(new Status(Status.WARNING, org.eclipse.papyrus.uml.diagram.common.groups.Activator.PLUGIN_ID, "There is a circle element reference")); } else { //else iterate recursively also on this group's parents withDrawRedundantElementReferenced(originalEObject, groupFrameworkReferences, directlyReferencedByElement, elementAlreadyVisited, currentElementParentGroup); //elementToVosit.add(currentCompareElement); } } } } } } } /** * This method is used to with an element of a reference * * @param source * EObject from which the reference start * @param destination * EObject to which the reference start * @param ref * EReference */ private static void withdrawEObjectFromReference(EObject source, EObject destination, EReference ref) { if(ref != null && source != null && destination != null) { if(ref != null && ref.isMany()) { Collection<EObject> collection = (Collection<EObject>)source.eGet(ref); if(collection.contains(destination)) { collection.remove(destination); } } else if(ref != null && !ref.isMany()) { source.eUnset(ref); } } } /** * This method remove all elements from the list which have a descendant in the (graphical model) or model in the list * * @param listToModify * List of elements (IGraphicalEditPart * @param mode * if GraphicalAndModelElementComparator.MODEL then it only take care of model parent if * GraphicalAndModelElementComparator.GRAPHICAL_AND_MODEL it take care of graphical and logical relationship * @return true if succeed */ private static boolean withdrawUselessDescendantElements(List<IGraphicalEditPart> listToModify, Mode mode) { if(!listToModify.isEmpty()) { GraphicalAndModelElementComparator comparator = new GraphicalAndModelElementComparator(listToModify.get(0)); //Select the comparator mode comparator.setMode(mode); for(int element = 0; element < listToModify.size(); element++) { for(int elementToCompare = element + 1; elementToCompare < listToModify.size(); elementToCompare++) { int compare = comparator.compare((IGraphicalEditPart)listToModify.get(element), (IGraphicalEditPart)listToModify.get(elementToCompare)); if(compare > 0) { listToModify.remove(element); element--; elementToCompare = listToModify.size(); } else if(compare < 0) { listToModify.remove(elementToCompare); elementToCompare--; } } } } return true; } /** * Find the children edit parts which may be contained by the group in the given bounds. * * @param contentArea * the new or old content area the group * @param groupEditPart * the group edit part * @param diagramPart * the diagram edit part * @return the list of edit parts that are within the given bounds and which element can be children according to the group framework */ public static List<IGraphicalEditPart> findPossibleChildren(Rectangle contentArea, IGraphicalEditPart groupEditPart, DiagramEditPart diagramPart) { AbstractContainerNodeDescriptor descriptor = GroupContainmentRegistry.getContainerDescriptor(groupEditPart); if(diagramPart == null || descriptor == null) { return Collections.emptyList(); } Set<IGraphicalEditPart> groupParts = new HashSet<IGraphicalEditPart>(); for(Object view : diagramPart.getViewer().getEditPartRegistry().keySet()) { if(view instanceof View) { Object editpart = diagramPart.getViewer().getEditPartRegistry().get(view); if(editpart instanceof IGraphicalEditPart && editpart instanceof IPrimaryEditPart && !groupEditPart.equals(editpart)) { IGraphicalEditPart part = (IGraphicalEditPart)editpart; // check bounds boolean boundsOK = false; if(groupEditPart.getChildren().contains(editpart)) { // graphically contained part will follow the move. boundsOK = true; } else { Rectangle figBounds = part.getFigure().getBounds().getCopy(); part.getFigure().translateToAbsolute(figBounds); if(contentArea.contains(figBounds)) { boundsOK = true; } } if(boundsOK) { // check group can contain EObject child = part.resolveSemanticElement(); for(EReference refToChildren : descriptor.getChildrenReferences()) { if(refToChildren.getEReferenceType().isInstance(child)) { groupParts.add(part); break; } } } } } } return new ArrayList<IGraphicalEditPart>(groupParts); } // Debug purpose public static void drawRect(IGraphicalEditPart editPart, Rectangle refContentArea) { RoundedRectangle rectFeedback = new RoundedRectangle(); rectFeedback.setBounds(refContentArea); rectFeedback.setCornerDimensions(new Dimension(0, 0)); rectFeedback.setLineWidth(2); rectFeedback.setLineStyle(Graphics.LINE_DASH); rectFeedback.setForegroundColor(editPart.getFigure().getForegroundColor()); rectFeedback.setOpaque(true); rectFeedback.setFill(false); IFigure layer = LayerManager.Helper.find(editPart).getLayer(LayerConstants.FEEDBACK_LAYER); layer.add(rectFeedback); } /** * Get the command to change the graphical parent of an element * * @param childPart * the child edit part to change parent * @param newParent * the new graphical parent * @return the command or null if no effect */ public static Command getUpdateGraphicalParentCmd(IGraphicalEditPart childPart, IGraphicalEditPart newParent) { if(childPart.getParent().equals(newParent)) { return null; } ChangeBoundsRequest request = new ChangeBoundsRequest(); request.setMoveDelta(new Point(0, 0)); request.setSizeDelta(new Dimension(0, 0)); request.setEditParts(childPart); Point loc = childPart.getFigure().getBounds().getLocation().getCopy(); childPart.getFigure().translateToAbsolute(loc); request.setLocation(loc); request.setType(RequestConstants.REQ_DROP); org.eclipse.gef.commands.Command cmd = newParent.getCommand(request); if(cmd != null && cmd.canExecute()) { return new GEFtoEMFCommandWrapper(cmd); } else { return null; } } /** * Get the command to add a reference from a parent group edit part to its new child * * @param newParentpart * the new parent edit part which contains children by reference * @param newChildPart * the child edit part * @return the command or null */ public static Command getAddReferenceToChildCmd(IGraphicalEditPart newParentpart, IGraphicalEditPart newChildPart) { AbstractContainerNodeDescriptor desc = GroupContainmentRegistry.getContainerDescriptor(newParentpart); EObject parent = newParentpart.resolveSemanticElement(); EObject child = newChildPart.resolveSemanticElement(); // get the better child reference to use EReference usedReference = getBestReferenceAmongList(desc.getChildrenReferences(), child); if(usedReference != null) { return new AddCommand(newParentpart.getEditingDomain(), parent, usedReference, child); } else { // no possible child reference return null; } } /** * Get the best reference (nearest to child's type) to a child among the list * * @param childrenReferences * the possible children references * @param child * the child eobject to choose a referencefor * @return the most precise child reference or null */ public static EReference getBestReferenceAmongList(List<EReference> childrenReferences, EObject child) { EReference usedReference = null; for(EReference ref : childrenReferences) { if(ref.getEReferenceType().isInstance(child)) { if(usedReference == null || ref.getEReferenceType().getEAllSuperTypes().contains(usedReference.getEReferenceType())) { // the ref feature is more precise than the previously selected one. Use it instead. usedReference = ref; } } } return usedReference; } /** * Get the command to remove a reference from a parent group edit part to its old child * * @param oldParentpart * the old parent edit part which contains children by reference * @param oldChildPart * the child edit part * @return the command or null */ public static Command getRemoveReferenceToChildCmd(IGraphicalEditPart oldParentpart, IGraphicalEditPart oldChildPart) { AbstractContainerNodeDescriptor desc = GroupContainmentRegistry.getContainerDescriptor(oldParentpart); EObject parent = oldParentpart.resolveSemanticElement(); EObject child = oldChildPart.resolveSemanticElement(); CompoundCommand globalCmd = new CompoundCommand(); // get the better child reference to use EReference usedReference = null; for(EReference ref : desc.getChildrenReferences()) { if(parent.eGet(ref) instanceof List<?>) { if(((List<?>)parent.eGet(ref)).contains(child)) { // remove the reference to the child Command cmd = new RemoveCommand(oldParentpart.getEditingDomain(), parent, usedReference, child); if(cmd.canExecute()) { globalCmd.append(cmd); } } } } if(!globalCmd.isEmpty()) { return globalCmd; } else { return null; } } /** * Get the bounds of an edit part * * @param part * edit part to find bounds * @return part's bounds in absolute coordinates */ public static Rectangle getAbsoluteBounds(IGraphicalEditPart part) { // take bounds from figure part.getTopGraphicEditPart().refresh(); Rectangle bounds = part.getFigure().getBounds().getCopy(); if(part.getNotationView() instanceof Node) { // rather update with up to date model bounds Node node = (Node)part.getNotationView(); LayoutConstraint cst = node.getLayoutConstraint(); if(cst instanceof Bounds) { Bounds b = (Bounds)cst; Point parentLoc = part.getFigure().getParent().getBounds().getLocation(); if(b.getX() > 0) { bounds.x = b.getX() + parentLoc.x; } if(b.getY() > 0) { bounds.y = b.getY() + parentLoc.y; } if(b.getHeight() != -1) { bounds.height = b.getHeight(); } if(b.getWidth() != -1) { bounds.width = b.getWidth(); } } } part.getFigure().getParent().translateToAbsolute(bounds); return bounds; } /** * This method compute the delta between to IGraphicalEditPart. * * @param oldParent * Old IGraphicalEditPart * @param newParent * New IGraphicalEditPart * @return Return a DDimention between the two bounds (often use to translate point or Rectangle) */ public static Dimension computeDeltaToChangeParent(IGraphicalEditPart oldParent, IGraphicalEditPart newParent) { Rectangle hostBounds = Utils.getAbsoluteBounds(oldParent); Rectangle parentBounds = Utils.getAbsoluteBounds(newParent); Dimension delta = hostBounds.getLocation().getDifference(parentBounds.getLocation()); return delta; } public static Dimension computeDeltaToChangeParent(IGraphicalEditPart oldParent, Rectangle newParent) { Rectangle hostBounds = Utils.getAbsoluteBounds(oldParent); Dimension delta = hostBounds.getLocation().getDifference(newParent.getLocation()); return delta; } /** * Give the reference object which can reference the child for the parent type part * * @param parentType * EClass of the parent OBject you want to know the EReference * @param childType * EClass of the child you want to test * @return null if no reference is found */ public static EReference getContainmentEReference(EClass parentType, EClass childType) { List<EReference> result = new ArrayList<EReference>(); EReference usedReference = null; for(EReference reference : parentType.getEAllContainments()) { if(reference.getEReferenceType().isSuperTypeOf(childType) && !reference.isDerived()) { result.add(reference); } } //Select the best containment relation for(EReference ref : result) { if(usedReference == null || ref.getEReferenceType().getEAllSuperTypes().contains(usedReference.getEReferenceType())) { // the ref feature is more precise than the previously selected one. Use it instead. usedReference = ref; } } return usedReference; } /** * * @param editPartRegistry * Check if the object is contained in the editPartRegistery * @param _child * @return */ public static boolean isContainedInRegistery(Map editPartRegistry, Object _child) { if(_child instanceof IGraphicalEditPart) { return editPartRegistry.containsKey(((IGraphicalEditPart)_child).getModel()); } return false; } /** * Test is the element is a compartment edit part that can be used to create the child * * @param editPartRegistry * @param _child * @return */ public static boolean isAGoodCompartementEditPart(Map editPartRegistry, Object _child) { return _child instanceof CompartmentEditPart && isContainedInRegistery(editPartRegistry, _child) && ((EditPart)_child) instanceof ShapeCompartmentEditPart; } /** * Get the compartment editPart from a parent editPart * * @param editPartRegistry * EditPartRegistery * @param parentEditPart * EditPart of the parent * @return the CompartementEditPart and null if not found */ public static EditPart getCompartementEditPartFromMainEditPart(Map editPartRegistry, EditPart parentEditPart) { EditPart resultCompartmentEditPart = null; //An edit part has been found if(parentEditPart instanceof CompartmentEditPart) { resultCompartmentEditPart = parentEditPart; return resultCompartmentEditPart; } else { List<EditPart> potentialCompartementPart = new ArrayList<EditPart>(); for(Object _child : parentEditPart.getChildren()) { if(isAGoodCompartementEditPart(editPartRegistry, _child)) { potentialCompartementPart.add((EditPart)_child); } } if(potentialCompartementPart.size() == 1) { resultCompartmentEditPart = potentialCompartementPart.get(0); return resultCompartmentEditPart; } else if(potentialCompartementPart.size() == 1) { //FIXME find a correct behavior if several potential CompartementPart (should normally never be the case) resultCompartmentEditPart = potentialCompartementPart.get(0); return resultCompartmentEditPart; } } return resultCompartmentEditPart; } /** * Get the child map needed for {@link ChangeModelParentCommand} * * @param elementType * {@link EClass} of the elemnt you want to find the default model parent * @param getHost * Host of the editPolicy * @param newIgraphicalParent * {@link IGraphicalEditPart} to complete with the new {@link IGraphicalEditPart} of the defautl model parent * @return */ public static DefaultModelParent getDefaultModelParent(EClass elementType, IGraphicalEditPart getHost) { IGraphicalEditPart hostParent = getHost; while(hostParent != null) { EObject hostParentElement = hostParent.resolveSemanticElement(); if(GroupContainmentRegistry.getDescriptorsWithContainerEClass(hostParentElement.eClass()).isEmpty()) { for(EReference containmentRelation : hostParentElement.eClass().getEAllContainments()) { if(containmentRelation.getEReferenceType().isSuperTypeOf(elementType)) { return new DefaultModelParent(hostParent, containmentRelation); } } } hostParent = (IGraphicalEditPart)hostParent.getParent(); } return null; } public static boolean isRequestGroupFrameworkConcerned(ChangeBoundsRequest request) { for(Object editPart : request.getEditParts()) { if(editPart instanceof IGraphicalEditPart) { IGraphicalEditPart iGraphicalEditPart = (IGraphicalEditPart)editPart; boolean isNodeConcerned = GroupContainmentRegistry.isNodeConcerned(iGraphicalEditPart); boolean isGroupConcerned = GroupContainmentRegistry.isContainerConcerned(iGraphicalEditPart); if(isGroupConcerned || isNodeConcerned) { return true; } } } return false; } }