// $Id: FigAssociation.java 16903 2009-03-19 20:45:44Z thn $ // Copyright (c) 1996-2009 The Regents of the University of California. All // Rights Reserved. Permission to use, copy, modify, and distribute this // software and its documentation without fee, and without a written // agreement is hereby granted, provided that the above copyright notice // and this paragraph appear in all copies. This software program and // documentation are copyrighted by The Regents of the University of // California. The software program and documentation are supplied "AS // IS", without any accompanying services from The Regents. The Regents // does not warrant that the operation of the program will be // uninterrupted or error-free. The end-user understands that the program // was developed for research purposes and is advised not to rely // exclusively on the program for any reason. IN NO EVENT SHALL THE // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, // UPDATES, ENHANCEMENTS, OR MODIFICATIONS. package org.argouml.uml.diagram.ui; import java.awt.Color; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.Vector; import org.apache.log4j.Logger; import org.argouml.model.AddAssociationEvent; import org.argouml.model.AttributeChangeEvent; import org.argouml.model.Model; import org.argouml.notation.NotationProviderFactory2; import org.argouml.ui.ArgoJMenu; import org.argouml.ui.targetmanager.TargetManager; import org.argouml.uml.diagram.DiagramSettings; import org.tigris.gef.base.Layer; import org.tigris.gef.presentation.ArrowHead; import org.tigris.gef.presentation.ArrowHeadComposite; import org.tigris.gef.presentation.ArrowHeadDiamond; import org.tigris.gef.presentation.ArrowHeadGreater; import org.tigris.gef.presentation.ArrowHeadNone; import org.tigris.gef.presentation.FigNode; import org.tigris.gef.presentation.FigText; /** * This class represents the Fig of a binary association on a diagram. * */ public class FigAssociation extends FigEdgeModelElement { private static final Logger LOG = Logger.getLogger(FigAssociation.class); /** * Group for the FigTexts concerning the source association end. */ private FigAssociationEndAnnotation srcGroup; /** * Group for the FigTexts concerning the dest association end. */ private FigAssociationEndAnnotation destGroup; /** * Group for the FigTexts concerning the name and stereotype of the * association itself. */ private FigTextGroup middleGroup; private FigMultiplicity srcMult; private FigMultiplicity destMult; /** * Don't call this constructor directly. It is public since this * is necessary for loading. Use the FigAssociation(Object, Layer) * constructor instead! * @deprecated for 0.27.3 by tfmorris. Use * {@link #FigAssociation(Object, DiagramSettings)}. */ @SuppressWarnings("deprecation") @Deprecated public FigAssociation() { super(); middleGroup = new FigTextGroup(); ////////////////////////////////////////////////////////////////// // NOTE: If this needs to be updated during the deprecation period // also update the constructor below ////////////////////////////////////////////////////////////////// // let's use groups to construct the different text sections at // the association if (getNameFig() != null) { middleGroup.addFig(getNameFig()); } middleGroup.addFig(getStereotypeFig()); // Placed perpendicular to midpoint of edge addPathItem(middleGroup, new PathItemPlacement(this, middleGroup, 50, 25)); ArgoFigUtil.markPosition(this, 50, 0, 90, 25, Color.yellow); srcMult = new FigMultiplicity(); // Placed at a 45 degree angle close to the end addPathItem(srcMult, new PathItemPlacement(this, srcMult, 0, 5, 135, 5)); ArgoFigUtil.markPosition(this, 0, 5, 135, 5, Color.green); ////////////////////////////////////////////////////////////////// // NOTE: If this needs to be updated during the deprecation period // also update the constructor below ////////////////////////////////////////////////////////////////// srcGroup = new FigAssociationEndAnnotation(this); addPathItem(srcGroup, new PathItemPlacement(this, srcGroup, 0, 5, -135, 5)); ArgoFigUtil.markPosition(this, 0, 5, -135, 5, Color.blue); destMult = new FigMultiplicity(); addPathItem(destMult, new PathItemPlacement(this, destMult, 100, -5, 45, 5)); ArgoFigUtil.markPosition(this, 100, -5, 45, 5, Color.red); ////////////////////////////////////////////////////////////////// // NOTE: If this needs to be updated during the deprecation period // also update the constructor below ////////////////////////////////////////////////////////////////// destGroup = new FigAssociationEndAnnotation(this); addPathItem(destGroup, new PathItemPlacement(this, destGroup, 100, -5, -45, 5)); ArgoFigUtil.markPosition(this, 100, -5, -45, 5, Color.orange); setBetweenNearestPoints(true); } /** * Constructor used by PGML parser. * * @param owner owning uml element * @param settings rendering settings */ public FigAssociation(Object owner, DiagramSettings settings) { super(owner, settings); createNameLabel(owner, settings); Object[] ends = // UML objects of AssociationEnd type Model.getFacade().getConnections(owner).toArray(); srcMult = new FigMultiplicity(ends[0], settings); addPathItem(srcMult, new PathItemPlacement(this, srcMult, 0, 5, 135, 5)); ArgoFigUtil.markPosition(this, 0, 5, 135, 5, Color.green); srcGroup = new FigAssociationEndAnnotation(this, ends[0], settings); addPathItem(srcGroup, new PathItemPlacement(this, srcGroup, 0, 5, -135, 5)); ArgoFigUtil.markPosition(this, 0, 5, -135, 5, Color.blue); destMult = new FigMultiplicity(ends[1], settings); addPathItem(destMult, new PathItemPlacement(this, destMult, 100, -5, 45, 5)); ArgoFigUtil.markPosition(this, 100, -5, 45, 5, Color.red); destGroup = new FigAssociationEndAnnotation(this, ends[1], settings); addPathItem(destGroup, new PathItemPlacement(this, destGroup, 100, -5, -45, 5)); ArgoFigUtil.markPosition(this, 100, -5, -45, 5, Color.orange); setBetweenNearestPoints(true); initializeNotationProvidersInternal(owner); } /** * Constructor that hooks the Fig to an existing UML element. * * @param edge the UML element * @param lay the layer * @deprecated for 0.27.3 by tfmorris. Use * {@link #FigAssociation(Object, DiagramSettings)}. */ @Deprecated public FigAssociation(Object edge, Layer lay) { this(); setOwner(edge); setLayer(lay); } /** * Create the main draggable label for the association. * This can be overridden in subclasses to change behaviour. * TODO: Consider introducing this to FigEdgeModelElement and * using throughout all edges. * * @param owner owning uml element * @param settings rendering settings */ protected void createNameLabel(Object owner, DiagramSettings settings) { middleGroup = new FigTextGroup(owner, settings); // let's use groups to construct the different text sections at // the association if (getNameFig() != null) { middleGroup.addFig(getNameFig()); } middleGroup.addFig(getStereotypeFig()); addPathItem(middleGroup, new PathItemPlacement(this, middleGroup, 50, 25)); ArgoFigUtil.markPosition(this, 50, 0, 90, 25, Color.yellow); } /** * Set the owner. * * @param owner ignored * @see org.argouml.uml.diagram.ui.FigEdgeModelElement#setOwner(java.lang.Object) * @deprecated for 0.27.3 by tfmorris. Set the owner in the constructor. */ @SuppressWarnings("deprecation") @Deprecated @Override public void setOwner(Object owner) { super.setOwner(owner); Object[] ends = Model.getFacade().getConnections(owner).toArray(); Object source = ends[0]; Object dest = ends[1]; srcGroup.setOwner(source); srcMult.setOwner(source); destGroup.setOwner(dest); destMult.setOwner(dest); } @Override public void renderingChanged() { super.renderingChanged(); /* This fixes issue 4987: */ srcMult.renderingChanged(); destMult.renderingChanged(); srcGroup.renderingChanged(); destGroup.renderingChanged(); middleGroup.renderingChanged(); } @Override protected void initNotationProviders(Object own) { initializeNotationProvidersInternal(own); } private void initializeNotationProvidersInternal(Object own) { super.initNotationProviders(own); srcMult.initNotationProviders(); destMult.initNotationProviders(); } /* * @see org.argouml.uml.diagram.ui.FigEdgeModelElement#updateListeners(java.lang.Object, java.lang.Object) */ @Override public void updateListeners(Object oldOwner, Object newOwner) { Set<Object[]> listeners = new HashSet<Object[]>(); if (newOwner != null) { listeners.add( new Object[] {newOwner, new String[] {"isAbstract", "remove"} }); } updateElementListeners(listeners); /* No further listeners required in this case - the rest is handled * by the notationProvider and sub-Figs. */ } /* * @see org.argouml.uml.diagram.ui.FigEdgeModelElement#getNotationProviderType() */ @Override protected int getNotationProviderType() { return NotationProviderFactory2.TYPE_ASSOCIATION_NAME; } /* * @see org.argouml.uml.diagram.ui.FigEdgeModelElement#textEdited(org.tigris.gef.presentation.FigText) */ @Override protected void textEdited(FigText ft) { if (getOwner() == null) { return; } super.textEdited(ft); Collection conn = Model.getFacade().getConnections(getOwner()); if (conn == null || conn.size() == 0) { return; } if (ft == srcGroup.getRole()) { srcGroup.getRole().textEdited(); } else if (ft == destGroup.getRole()) { destGroup.getRole().textEdited(); } else if (ft == srcMult) { srcMult.textEdited(); } else if (ft == destMult) { destMult.textEdited(); } } /* * @see org.argouml.uml.diagram.ui.FigEdgeModelElement#textEditStarted(org.tigris.gef.presentation.FigText) */ @Override protected void textEditStarted(FigText ft) { if (ft == srcGroup.getRole()) { srcGroup.getRole().textEditStarted(); } else if (ft == destGroup.getRole()) { destGroup.getRole().textEditStarted(); } else if (ft == srcMult) { srcMult.textEditStarted(); } else if (ft == destMult) { destMult.textEditStarted(); } else { super.textEditStarted(ft); } } /** * Choose the arrowhead style for each end. <p> * * TODO: This is called from paint(). Would it not better * be called from renderingChanged()? */ protected void applyArrowHeads() { if (srcGroup == null || destGroup == null) { /* This only happens if model-change events arrive * before we are completely constructed. */ return; } int sourceArrowType = srcGroup.getArrowType(); int destArrowType = destGroup.getArrowType(); if (!getSettings().isShowBidirectionalArrows() && sourceArrowType > 2 && destArrowType > 2) { sourceArrowType -= 3; destArrowType -= 3; } setSourceArrowHead(FigAssociationEndAnnotation .ARROW_HEADS[sourceArrowType]); setDestArrowHead(FigAssociationEndAnnotation .ARROW_HEADS[destArrowType]); } /* * @see org.tigris.gef.ui.PopupGenerator#getPopUpActions(java.awt.event.MouseEvent) */ @Override public Vector getPopUpActions(MouseEvent me) { Vector popUpActions = super.getPopUpActions(me); /* Check if multiple items are selected: */ boolean ms = TargetManager.getInstance().getTargets().size() > 1; /* None of the menu-items below apply * when multiple modelelements are selected:*/ if (ms) { return popUpActions; } // x^2 + y^2 = r^2 (equation of a circle) Point firstPoint = this.getFirstPoint(); Point lastPoint = this.getLastPoint(); int length = getPerimeterLength(); int rSquared = (int) (.3 * length); // max distance is set at 100 pixels, (rSquared = 100^2) if (rSquared > 100) { rSquared = 10000; } else { rSquared *= rSquared; } int srcDeterminingFactor = getSquaredDistance(me.getPoint(), firstPoint); int destDeterminingFactor = getSquaredDistance(me.getPoint(), lastPoint); if (srcDeterminingFactor < rSquared && srcDeterminingFactor < destDeterminingFactor) { ArgoJMenu multMenu = new ArgoJMenu("menu.popup.multiplicity"); multMenu.add(ActionMultiplicity.getSrcMultOne()); multMenu.add(ActionMultiplicity.getSrcMultZeroToOne()); multMenu.add(ActionMultiplicity.getSrcMultOneToMany()); multMenu.add(ActionMultiplicity.getSrcMultZeroToMany()); popUpActions.add(popUpActions.size() - getPopupAddOffset(), multMenu); ArgoJMenu aggMenu = new ArgoJMenu("menu.popup.aggregation"); aggMenu.add(ActionAggregation.getSrcAggNone()); aggMenu.add(ActionAggregation.getSrcAgg()); aggMenu.add(ActionAggregation.getSrcAggComposite()); popUpActions.add(popUpActions.size() - getPopupAddOffset(), aggMenu); } else if (destDeterminingFactor < rSquared) { ArgoJMenu multMenu = new ArgoJMenu("menu.popup.multiplicity"); multMenu.add(ActionMultiplicity.getDestMultOne()); multMenu.add(ActionMultiplicity.getDestMultZeroToOne()); multMenu.add(ActionMultiplicity.getDestMultOneToMany()); multMenu.add(ActionMultiplicity.getDestMultZeroToMany()); popUpActions.add(popUpActions.size() - getPopupAddOffset(), multMenu); ArgoJMenu aggMenu = new ArgoJMenu("menu.popup.aggregation"); aggMenu.add(ActionAggregation.getDestAggNone()); aggMenu.add(ActionAggregation.getDestAgg()); aggMenu.add(ActionAggregation.getDestAggComposite()); popUpActions .add(popUpActions.size() - getPopupAddOffset(), aggMenu); } // else: No particular options for right click in middle of line // Options available when right click anywhere on line Object association = getOwner(); if (association != null) { // Navigability menu with suboptions built dynamically to // allow navigability from atart to end, from end to start // or bidirectional Collection ascEnds = Model.getFacade().getConnections(association); Iterator iter = ascEnds.iterator(); Object ascStart = iter.next(); Object ascEnd = iter.next(); if (Model.getFacade().isAClassifier( Model.getFacade().getType(ascStart)) && Model.getFacade().isAClassifier( Model.getFacade().getType(ascEnd))) { ArgoJMenu navMenu = new ArgoJMenu("menu.popup.navigability"); navMenu.add(ActionNavigability.newActionNavigability( ascStart, ascEnd, ActionNavigability.BIDIRECTIONAL)); navMenu.add(ActionNavigability.newActionNavigability( ascStart, ascEnd, ActionNavigability.STARTTOEND)); navMenu.add(ActionNavigability.newActionNavigability( ascStart, ascEnd, ActionNavigability.ENDTOSTART)); popUpActions.add(popUpActions.size() - getPopupAddOffset(), navMenu); } } return popUpActions; } /** * Updates the multiplicity fields. */ protected void updateMultiplicity() { if (getOwner() != null && srcMult.getOwner() != null && destMult.getOwner() != null) { srcMult.setText(); destMult.setText(); } } /* * @see org.tigris.gef.presentation.Fig#paint(java.awt.Graphics) */ @Override public void paint(Graphics g) { if (getOwner() == null ) { LOG.error("Trying to paint a FigAssociation without an owner. "); } else { applyArrowHeads(); } if (getSourceArrowHead() != null && getDestArrowHead() != null) { getSourceArrowHead().setLineColor(getLineColor()); getDestArrowHead().setLineColor(getLineColor()); } super.paint(g); } /* * @see org.argouml.uml.diagram.ui.FigEdgeModelElement#paintClarifiers(java.awt.Graphics) */ @Override public void paintClarifiers(Graphics g) { indicateBounds(getNameFig(), g); indicateBounds(srcMult, g); indicateBounds(srcGroup.getRole(), g); indicateBounds(destMult, g); indicateBounds(destGroup.getRole(), g); super.paintClarifiers(g); } /** * @return Returns the middleGroup. */ protected FigTextGroup getMiddleGroup() { return middleGroup; } /** * Lays out the association edges as any other edge except for * special rules for an association that loops back to the same * class. For this it is snapped back to the bottom right corner * if it resized to the point of not being visible. * @see org.tigris.gef.presentation.FigEdgePoly#layoutEdge() */ @Override protected void layoutEdge() { FigNode sourceFigNode = getSourceFigNode(); Point[] points = getPoints(); if (points.length < 3 && sourceFigNode != null && getDestFigNode() == sourceFigNode) { Rectangle rect = new Rectangle( sourceFigNode.getX() + sourceFigNode.getWidth() - 20, sourceFigNode.getY() + sourceFigNode.getHeight() - 20, 40, 40); points = new Point[5]; points[0] = new Point(rect.x, rect.y + rect.height / 2); points[1] = new Point(rect.x, rect.y + rect.height); points[2] = new Point(rect.x + rect.width , rect.y + rect.height); points[3] = new Point(rect.x + rect.width , rect.y); points[4] = new Point(rect.x + rect.width / 2, rect.y); setPoints(points); } else { super.layoutEdge(); } } /** * If the name is updated, update the bounds of the middle group. * This makes the selection box appear correctly during prop-panel edit. * This is a temporary solution, until a better architecture is decided * upon, see issue 5477 and * http://argouml.tigris.org/issues/show_bug.cgi?id=5621#desc19. * @see org.argouml.uml.diagram.ui.FigEdgeModelElement#updateNameText() */ protected void updateNameText() { super.updateNameText(); // TODO: Without the null check the following throws a NPE so many // times when it is called from FigEdgeModelElement.modelChanged(), // we need to think about it. if (middleGroup != null) { middleGroup.calcBounds(); } } } /* end class FigAssociation */ /** * A Fig representing the multiplicity of some model element. * This has potential reuse for other edges showing multiplicity. <p> * * The owner is an AssociationEnd. * * @author Bob Tarling */ class FigMultiplicity extends FigSingleLineTextWithNotation { @SuppressWarnings("deprecation") @Deprecated FigMultiplicity() { super(X0, Y0, 90, 20, false, new String[] {"multiplicity"}); setTextFilled(false); setJustification(FigText.JUSTIFY_CENTER); } FigMultiplicity(Object owner, DiagramSettings settings) { super(owner, new Rectangle(X0, Y0, 90, 20), settings, false, "multiplicity"); setTextFilled(false); setJustification(FigText.JUSTIFY_CENTER); } @Override protected int getNotationProviderType() { return NotationProviderFactory2.TYPE_MULTIPLICITY; } } /** * A textual Fig representing the ordering of some model element, * i.e. "{ordered}" or nothing. * This has potential reuse for other edges showing ordering. <p> * * This Fig is not editable by the user. * * @author Bob Tarling */ class FigOrdering extends FigSingleLineText { private static final long serialVersionUID = 5385230942216677015L; @SuppressWarnings("deprecation") @Deprecated FigOrdering() { super(X0, Y0, 90, 20, false, "ordering"); setTextFilled(false); setJustification(FigText.JUSTIFY_CENTER); setEditable(false); } FigOrdering(Object owner, DiagramSettings settings) { super(owner, new Rectangle(X0, Y0, 90, 20), settings, false, "ordering"); setTextFilled(false); setJustification(FigText.JUSTIFY_CENTER); setEditable(false); } @Override protected void setText() { assert getOwner() != null; if (getSettings().getNotationSettings().isShowProperties()) { setText(getOrderingName(Model.getFacade().getOrdering(getOwner()))); } else { setText(""); } damage(); } /** * Returns the name of the OrderingKind. * * @param orderingKind the kind of ordering * @return "{ordered}" or "", the latter if null or unordered */ private String getOrderingName(Object orderingKind) { if (orderingKind == null) { return ""; } if (Model.getFacade().getName(orderingKind) == null) { return ""; } if ("".equals(Model.getFacade().getName(orderingKind))) { return ""; } if ("unordered".equals(Model.getFacade().getName(orderingKind))) { return ""; } // TODO: I18N return "{" + Model.getFacade().getName(orderingKind) + "}"; } } /** * A Fig representing the association end role of some model element. * * @author Bob Tarling */ class FigRole extends FigSingleLineTextWithNotation { /** * @deprecated for 0.27.3 by tfmorris. Use * {@link FigRole#FigRole(Object, DiagramSettings)}/ */ @SuppressWarnings("deprecation") @Deprecated FigRole() { super(X0, Y0, 90, 20, false, (String[]) null // no need to listen to these property changes - the // notationProvider takes care of registering these. /*, new String[] {"name", "visibility", "stereotype"}*/ ); setTextFilled(false); setJustification(FigText.JUSTIFY_CENTER); } FigRole(Object owner, DiagramSettings settings) { super(owner, new Rectangle(X0, Y0, 90, 20), settings, false, (String[]) null // no need to listen to these property changes - the // notationProvider takes care of this. /*, new String[] {"name", "visibility", "stereotype"}*/ ); setTextFilled(false); setJustification(FigText.JUSTIFY_CENTER); setText(); } protected int getNotationProviderType() { return NotationProviderFactory2.TYPE_ASSOCIATION_END_NAME; } /** * Property change listener to recalculate bounds of enclosing * group whenever any properties of the FigRole get changed. * This is only really needed for the name, see issue 5621. * @param pce The property change event to process. * @see org.argouml.uml.diagram.ui.FigSingleLineTextWithNotation#propertyChange(java.beans.PropertyChangeEvent) */ @Override public void propertyChange(PropertyChangeEvent pce) { super.propertyChange(pce); this.getGroup().calcBounds(); } } /** * The arrowhead and the group of labels shown at the association end: * the role name and the ordering property. * This does not include the multiplicity. <p> * * This class does not yet support arrows for a FigAssociationEnd, * as is used for N-ary associations. */ class FigAssociationEndAnnotation extends FigTextGroup { private static final long serialVersionUID = 1871796732318164649L; private static final ArrowHead NAV_AGGR = new ArrowHeadComposite(ArrowHeadDiamond.WhiteDiamond, new ArrowHeadGreater()); private static final ArrowHead NAV_COMP = new ArrowHeadComposite(ArrowHeadDiamond.BlackDiamond, new ArrowHeadGreater()); // These are a list of arrow types. Positioning is important as we subtract // 3 to convert a navigable arrow to a non navigable with the same // aggregation private static final int NONE = 0; private static final int AGGREGATE = 1; private static final int COMPOSITE = 2; private static final int NAV_NONE = 3; private static final int NAV_AGGREGATE = 4; private static final int NAV_COMPOSITE = 5; /** * All the arrow head types. */ public static final ArrowHead[] ARROW_HEADS = new ArrowHead[6]; static { ARROW_HEADS[NONE] = ArrowHeadNone.TheInstance; ARROW_HEADS[AGGREGATE] = ArrowHeadDiamond.WhiteDiamond; ARROW_HEADS[COMPOSITE] = ArrowHeadDiamond.BlackDiamond; ARROW_HEADS[NAV_NONE] = new ArrowHeadGreater(); ARROW_HEADS[NAV_AGGREGATE] = NAV_AGGR; ARROW_HEADS[NAV_COMPOSITE] = NAV_COMP; } private FigRole role; private FigOrdering ordering; private int arrowType = 0; private FigEdgeModelElement figEdge; @SuppressWarnings("deprecation") @Deprecated FigAssociationEndAnnotation(FigEdgeModelElement edge) { figEdge = edge; role = new FigRole(); addFig(role); ordering = new FigOrdering(); addFig(ordering); } FigAssociationEndAnnotation(FigEdgeModelElement edge, Object owner, DiagramSettings settings) { super(owner, settings); figEdge = edge; role = new FigRole(owner, settings); addFig(role); ordering = new FigOrdering(owner, settings); addFig(ordering); determineArrowHead(); Model.getPump().addModelEventListener(this, owner, new String[] {"isNavigable", "aggregation", "participant"}); } /** * @deprecated by mvw in 0.27.3. Use non-deprecated constructors instead. */ @SuppressWarnings("deprecation") @Deprecated @Override public void setOwner(Object owner) { if (owner != null) { if (!Model.getFacade().isAAssociationEnd(owner)) { throw new IllegalArgumentException( "An AssociationEnd was expected"); } super.setOwner(owner); ordering.setOwner(owner); role.setOwner(owner); role.setText(); determineArrowHead(); Model.getPump().addModelEventListener(this, owner, new String[] {"isNavigable", "aggregation", "participant"}); } } /* * @see org.tigris.gef.presentation.Fig#removeFromDiagram() */ @Override public void removeFromDiagram() { Model.getPump().removeModelEventListener(this, getOwner(), new String[] {"isNavigable", "aggregation", "participant"}); super.removeFromDiagram(); } /* * @see org.tigris.gef.presentation.Fig#propertyChange(java.beans.PropertyChangeEvent) */ @Override public void propertyChange(PropertyChangeEvent pce) { if (pce instanceof AttributeChangeEvent && (pce.getPropertyName().equals("isNavigable") || pce.getPropertyName().equals("aggregation"))) { determineArrowHead(); ((FigAssociation) figEdge).applyArrowHeads(); damage(); } if (pce instanceof AddAssociationEvent && pce.getPropertyName().equals("participant")) { figEdge.determineFigNodes(); } String pName = pce.getPropertyName(); if (pName.equals("editing") && Boolean.FALSE.equals(pce.getNewValue())) { // Finished editing. // Parse the text that was edited. // Only the role is editable, hence: role.textEdited(); calcBounds(); endTrans(); } else if (pName.equals("editing") && Boolean.TRUE.equals(pce.getNewValue())) { // figEdge.showHelp(role.getParsingHelp()); // role.setText(); role.textEditStarted(); } else { // Pass everything else to superclass super.propertyChange(pce); } } /** * Decide which arrow head should appear */ private void determineArrowHead() { assert getOwner() != null; Object ak = Model.getFacade().getAggregation(getOwner()); boolean nav = Model.getFacade().isNavigable(getOwner()); if (nav) { if (Model.getAggregationKind().getNone().equals(ak) || (ak == null)) { arrowType = NAV_NONE; } else if (Model.getAggregationKind().getAggregate() .equals(ak)) { arrowType = NAV_AGGREGATE; } else if (Model.getAggregationKind().getComposite() .equals(ak)) { arrowType = NAV_COMPOSITE; } } else { if (Model.getAggregationKind().getNone().equals(ak) || (ak == null)) { arrowType = NONE; } else if (Model.getAggregationKind().getAggregate() .equals(ak)) { arrowType = AGGREGATE; } else if (Model.getAggregationKind().getComposite() .equals(ak)) { arrowType = COMPOSITE; } } } /** * @return the current arrow type of this end of the association */ public int getArrowType() { return arrowType; } FigRole getRole() { return role; } }