/***************************************************************************** * Copyright (c) 2009-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: * Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation * *****************************************************************************/ package org.eclipse.papyrus.uml.diagram.common.service; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.ImageFigure; import org.eclipse.draw2d.Locator; import org.eclipse.draw2d.PositionConstants; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPolicy; import org.eclipse.gmf.runtime.diagram.core.listener.DiagramEventBroker; import org.eclipse.gmf.runtime.diagram.core.listener.NotificationListener; import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.editpolicies.XYLayoutEditPolicy; import org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoration; import org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecorator; import org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorTarget; import org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorTarget.Direction; import org.eclipse.gmf.runtime.draw2d.ui.mapmode.IMapMode; import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil; import org.eclipse.gmf.runtime.notation.DecorationNode; import org.eclipse.gmf.runtime.notation.DescriptionStyle; import org.eclipse.gmf.runtime.notation.Diagram; import org.eclipse.gmf.runtime.notation.Node; import org.eclipse.gmf.runtime.notation.NotationPackage; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.papyrus.uml.diagram.common.Activator; import org.eclipse.papyrus.uml.diagram.common.layout.OverlayLocator; import org.eclipse.papyrus.uml.diagram.common.util.DiagramEditPartsUtil; import org.eclipse.papyrus.uml.diagram.common.util.Util; import org.eclipse.swt.graphics.Image; import org.eclipse.uml2.uml.Classifier; import org.eclipse.uml2.uml.Element; import org.eclipse.uml2.uml.NamedElement; import org.eclipse.uml2.uml.Operation; import org.eclipse.uml2.uml.Property; import org.eclipse.uml2.uml.Type; import org.eclipse.uml2.uml.UMLPackage; /** * * The decorator to represent inherited element This decorator adds a small * image ( the generalization icon) next to the UML Element which are inherited. * 3 positions are defined for the decoration : * <ul> * <li>if the UML Element is represented like an affixed child node : in {@link PositionConstants#NORTH_WEST} or {@link PositionConstants#SOUTH_EAST} * following its position/parent and margin =1</li> * <li>else if the element is in a compartment list : {@link PositionConstants#EAST} and margin =-1</li> * <li>else {@link PositionConstants#SOUTH_EAST} and margin = -1</li> * </ul> */ public class InheritedDecorator implements IDecorator { /** the object to be decorated */ private IDecoratorTarget decoratorTarget; /** the decoration being displayed */ private IDecoration decoration; /** the plugin where owning the icons for the UML Element */ public static final String pluginID = "org.eclipse.papyrus.uml.diagram.common"; //$NON-NLS-1$ /** the image path */ public static final String imagePath = "/icons/hyperlink_13x13.gif"; //$NON-NLS-1$ /** the image used added to represent an inherited element */ private static final Image ICON_HYPERLINK = Activator.getPluginIconImage(pluginID, imagePath); /** * Creates a new <code>AbstractDecorator</code> for the decorator target * passed in. * * @param decoratorTarget * the object to be decorated */ public InheritedDecorator(IDecoratorTarget decoratorTarget) { this.decoratorTarget = decoratorTarget; } /** * Gets the object to be decorated. * * @return Returns the object to be decorated */ protected IDecoratorTarget getDecoratorTarget() { return decoratorTarget; } /** * @return Returns the decoration. */ public IDecoration getDecoration() { return decoration; } /** * @param decoration * The decoration to set. */ public void setDecoration(IDecoration decoration) { this.decoration = decoration; } /** * Removes the decoration if it exists and sets it to null. */ protected void removeDecoration() { if(decoration != null) { decoratorTarget.removeDecoration(decoration); decoration = null; } } /** * getDecoratorTargetClassifier Utility method to determine if the * decoratorTarget is a supported type for this decorator and return the * associated Classifier element. * * @param decoratorTarget * IDecoratorTarget to check and return valid Classifier target. * @return node Node if IDecoratorTarget can be supported, null otherwise. */ static public Node getDecoratorTargetNode(IDecoratorTarget decoratorTarget) { DescriptionStyle descStyle = null; View node = (View)decoratorTarget.getAdapter(View.class); if(node != null && !(node instanceof Diagram)) { descStyle = (DescriptionStyle)node.getStyle(NotationPackage.eINSTANCE.getDescriptionStyle()); if(descStyle != null) { return (Node)node; } } return null; } /** * Creates the appropriate review decoration if all the criteria is * satisfied by the view passed in. */ public void refresh() { removeDecoration(); Node node = getDecoratorTargetNode(getDecoratorTarget()); IGraphicalEditPart gep = (IGraphicalEditPart)getDecoratorTarget().getAdapter(IGraphicalEditPart.class); if(node != null) { DescriptionStyle descStyle = getDescriptionStyle(node); if(descStyle != null) { if(isInherited(node)) { // if(Util.isAffixedChildNode(gep)) { // setDecoration(getDecoratorTarget().addDecoration(figure, // locator, false)); // } else { // setDecoration(getDecoratorTarget().addShapeDecoration(figure, // getDirection(node), -1, false)); // } if(gep != null && gep.getRoot() != null) {// if the gep has // no parent, we // can't test if // the container // is a // compartment // list // (because, we // call the // method // DiagramEditPartsUtil.getEditPartFromView((View)container, // gep); IFigure figure = getFigure(ICON_HYPERLINK); if(isInCompartmentList(node) && !Util.isAffixedChildNode(gep)) { setDecoration(getDecoratorTarget().addShapeDecoration(figure, getDirection(node), -1, false)); } else { Locator locator = new OverlayLocator(gep.getFigure(), getDirection(node)); setDecoration(getDecoratorTarget().addDecoration(figure, locator, false)); } } } } } } /** * Returns a figure corresponding to this image * * @param image * a image * @return a figure corresponding to this image */ public IFigure getFigure(Image image) { IMapMode mm = MapModeUtil.getMapMode(((IGraphicalEditPart)getDecoratorTarget().getAdapter(IGraphicalEditPart.class)).getFigure()); ImageFigure fig = new ImageFigure(); fig.setImage(image); fig.setSize(mm.DPtoLP(image.getBounds().width), mm.DPtoLP(image.getBounds().height)); return fig; } /** * Returns the direction to set the decorator for the node * * @param node * the node * @return the direction to set the decorator for the node direction can be * : * <ul> * <li> {@link PositionConstants#NORTH_WEST} or {@link PositionConstants#SOUTH_EAST}</li> if the node is an Affixed Child Node * <li>{@link PositionConstants#EAST}</li> if the node is in a compartment list * <li>{@link PositionConstants#SOUTH_EAST}</li> in other cases * </ul> */ protected Direction getDirection(Node node) { IGraphicalEditPart gep = (IGraphicalEditPart)getDecoratorTarget().getAdapter(IGraphicalEditPart.class); assert gep != null; // test if its an affixed ChildNode // if(Util.isAffixedChildNode(gep)) { // // IBorderItemLocator loc = // ((BorderNamedElementEditPart)gep).getBorderItemLocator(); // int location = loc.getCurrentSideOfParent(); // if(PositionConstants.NONE == location) { //sometimes // getBorderItemLocator doesn't work correctly! // location = PositionConstants.NORTH_WEST; // } // switch(location) { // case PositionConstants.NORTH: // case PositionConstants.NORTH_WEST: // case PositionConstants.WEST: // case PositionConstants.SOUTH_WEST: // // return IDecoratorTarget.Direction.NORTH_WEST; // default: // return IDecoratorTarget.Direction.SOUTH_EAST; // } // } if(gep.getParent() != null) { if(isInCompartmentList(node) && !Util.isAffixedChildNode(gep)) { return IDecoratorTarget.Direction.EAST; } } return IDecoratorTarget.Direction.SOUTH_WEST; } /** * Tests if the compartment is a compartment list * * @param node * the node on which we want add an Overlay * @return <code>true</code> if the compartment is managed by an {@link XYLayoutEditPolicy} */ protected boolean isInCompartmentList(Node node) { IGraphicalEditPart gep = (IGraphicalEditPart)getDecoratorTarget().getAdapter(IGraphicalEditPart.class); if(gep != null && gep.getRoot() != null) { EObject container = node.eContainer(); if(container instanceof View) { EditPart EP = DiagramEditPartsUtil.getEditPartFromView((View)container, gep); EditPolicy editPolicy = EP.getEditPolicy(EditPolicy.LAYOUT_ROLE); if(!(editPolicy instanceof XYLayoutEditPolicy)) {// we are in a // compartment // list return true; } } } return false; } /** * Tests if the node is an inherited element * * @param node * a node * @return <code>true</code> if the node is an inherited element <code>false</code> if not */ protected boolean isInherited(Node node) { EObject element = node.getElement(); if(element instanceof Element) { EObject container = node.eContainer(); EObject graphicalParent = null; if(container instanceof DecorationNode) { graphicalParent = ((DecorationNode)container).getElement(); } else if(container instanceof View) { graphicalParent = ((View)container).getElement(); } if(graphicalParent instanceof Property || graphicalParent instanceof Classifier) { Classifier classifier = null; if(graphicalParent instanceof Property) { Type type = ((Property)graphicalParent).getType(); if(type instanceof Classifier) { classifier = (Classifier)type; } } else { classifier = (Classifier)graphicalParent; } if(classifier != null) { EList<NamedElement> inheritedMembers = classifier.getInheritedMembers(); return inheritedMembers.contains(element); } } } return false; } /** * getDescriptionStyle Accessor to retrieve the description style from a * Node. * * @param node * Node to retrieve the description style from. * @return DescriptionStyle style object */ protected DescriptionStyle getDescriptionStyle(Node node) { return (DescriptionStyle)node.getStyle(NotationPackage.eINSTANCE.getDescriptionStyle()); } /** * A listener used to listen the change of location and type (for Property) */ private NotificationListener notificationListener = new NotificationListener() { /** * * @see org.eclipse.gmf.runtime.diagram.core.listener.NotificationListener#notifyChanged(org.eclipse.emf.common.notify.Notification) * * @param notification */ public void notifyChanged(Notification notification) { if(notification.getEventType() == Notification.REMOVE) { if(notification.getNotifier() instanceof Classifier) { IGraphicalEditPart gep = (IGraphicalEditPart)getDecoratorTarget().getAdapter(IGraphicalEditPart.class); assert gep != null; // we remove the listener on the container (because it's // changing DiagramEventBroker.getInstance(gep.getEditingDomain()).removeNotificationListener((EObject)notification.getNotifier(), notificationListener); } } // we update the listeners It's useful when an Element with overlay // changes of parent deactivate(); activate(); refresh(); } }; /** * Adds listeners on * <ul> * <li>Affixed Child Node</li> * <li>graphical parent, when its a {@link Property} (we add the listener on its Type)</li> * </ul> */ public void activate() { IGraphicalEditPart gep = (IGraphicalEditPart)getDecoratorTarget().getAdapter(IGraphicalEditPart.class); assert gep != null; View view = ((View)gep.getModel()); if(view instanceof Node) { // the location of the decorator can change if it's an Affixed Child // Node if(isInherited((Node)view) && Util.isAffixedChildNode(gep)) { DiagramEventBroker.getInstance(gep.getEditingDomain()).addNotificationListener(gep.getNotationView(), notificationListener); } } // if the graphical parent is a Property, we add a listener on the type // of the property, to refresh the decoration EObject parent = view.eContainer(); if(parent instanceof DecorationNode) { parent = parent.eContainer(); } if(parent instanceof View) { EObject el = ((View)parent).getElement(); if(el instanceof Property) { DiagramEventBroker.getInstance(gep.getEditingDomain()).addNotificationListener(el, UMLPackage.eINSTANCE.getTypedElement_Type(), notificationListener); } } /* * We listen the changes on the UML parent, in order to know if the * element is changing of parent Adding a listener using the following * EReference doesn't work UMLPackage.eINSTANCE.getElement_Owner(); * UMLPackage.eINSTANCE.getProperty_Class(); * UMLPackage.eINSTANCE.getNamedElement_Namespace(); that's why we * listen the parent */ if(view.getElement() instanceof Element) { Element semanticElement = (Element)view.getElement(); /* * We need add a listener only if the element is an element which * can be inherited, like Property, Operation, Signal, Classifier... */ if(semanticElement != null && canBeInherited(semanticElement)) { // we listen if the container of the element changes! if(semanticElement.eContainer() != null) { DiagramEventBroker.getInstance(gep.getEditingDomain()).addNotificationListener(semanticElement.eContainer(), notificationListener); } } } } /** * Tests if the element can be inherited * * @param semanticElement * the element to test * @return <code>true</code> if the element can be inherited */ protected boolean canBeInherited(Element semanticElement) { /* * maybe we could replace these tests by RedefinableElement? or not? */ if(semanticElement instanceof Classifier) { return true; } else if(semanticElement instanceof Property) { return true; } else if(semanticElement instanceof Operation) { return true; } return false; } /** * Removes the listeners and the decorations */ public void deactivate() { removeDecoration(); IGraphicalEditPart gep = (IGraphicalEditPart)getDecoratorTarget().getAdapter(IGraphicalEditPart.class); assert gep != null; DiagramEventBroker.getInstance(gep.getEditingDomain()).removeNotificationListener(gep.getNotationView(), notificationListener); View view = ((View)gep.getModel()); EObject parent = view.eContainer(); if(parent instanceof View) { EObject el = ((View)parent).getElement(); if(el instanceof Property) { DiagramEventBroker.getInstance(gep.getEditingDomain()).removeNotificationListener(el, UMLPackage.eINSTANCE.getTypedElement_Type(), notificationListener); } } if(view.getElement() instanceof Element) { Element semanticElement = (Element)view.getElement(); if(semanticElement != null) { if(semanticElement.eContainer() != null) { DiagramEventBroker.getInstance(gep.getEditingDomain()).removeNotificationListener(semanticElement.eContainer(), notificationListener); } } } } }