Coverage Report - org.argouml.uml.diagram.ui.FigAssociation
 
Classes in this File Line Coverage Branch Coverage Complexity
FigAssociation
0%
0/190
0%
0/98
2.766
FigAssociation$EndDecoration
0%
0/18
N/A
2.766
FigAssociationEndAnnotation
0%
0/58
0%
0/40
2.766
FigMultiplicity
0%
0/5
N/A
2.766
FigOrdering
0%
0/21
0%
0/14
2.766
FigRole
0%
0/11
0%
0/4
2.766
 
 1  
 /* $Id: FigAssociation.java 18724 2010-09-06 18:45:59Z bobtarling $
 2  
  *****************************************************************************
 3  
  * Copyright (c) 2009 Contributors - see below
 4  
  * All rights reserved. This program and the accompanying materials
 5  
  * are made available under the terms of the Eclipse Public License v1.0
 6  
  * which accompanies this distribution, and is available at
 7  
  * http://www.eclipse.org/legal/epl-v10.html
 8  
  *
 9  
  * Contributors:
 10  
  *    bobtarling
 11  
  *****************************************************************************
 12  
  *
 13  
  * Some portions of this file was previously release using the BSD License:
 14  
  */
 15  
 
 16  
 // Copyright (c) 1996-2009 The Regents of the University of California. All
 17  
 // Rights Reserved. Permission to use, copy, modify, and distribute this
 18  
 // software and its documentation without fee, and without a written
 19  
 // agreement is hereby granted, provided that the above copyright notice
 20  
 // and this paragraph appear in all copies.  This software program and
 21  
 // documentation are copyrighted by The Regents of the University of
 22  
 // California. The software program and documentation are supplied "AS
 23  
 // IS", without any accompanying services from The Regents. The Regents
 24  
 // does not warrant that the operation of the program will be
 25  
 // uninterrupted or error-free. The end-user understands that the program
 26  
 // was developed for research purposes and is advised not to rely
 27  
 // exclusively on the program for any reason.  IN NO EVENT SHALL THE
 28  
 // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
 29  
 // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
 30  
 // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
 31  
 // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
 32  
 // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
 33  
 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 34  
 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
 35  
 // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
 36  
 // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
 37  
 // UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 38  
 
 39  
 package org.argouml.uml.diagram.ui;
 40  
 
 41  
 import java.awt.Color;
 42  
 import java.awt.Graphics;
 43  
 import java.awt.Point;
 44  
 import java.awt.Rectangle;
 45  
 import java.awt.event.MouseEvent;
 46  
 import java.beans.PropertyChangeEvent;
 47  
 import java.util.Collection;
 48  
 import java.util.HashSet;
 49  
 import java.util.Iterator;
 50  
 import java.util.Set;
 51  
 import java.util.Vector;
 52  
 
 53  
 import org.apache.log4j.Logger;
 54  
 import org.argouml.model.AddAssociationEvent;
 55  
 import org.argouml.model.AssociationChangeEvent;
 56  
 import org.argouml.model.AttributeChangeEvent;
 57  
 import org.argouml.model.Model;
 58  
 import org.argouml.notation.NotationProviderFactory2;
 59  
 import org.argouml.ui.ArgoJMenu;
 60  
 import org.argouml.ui.targetmanager.TargetManager;
 61  
 import org.argouml.uml.diagram.DiagramEdgeSettings;
 62  
 import org.argouml.uml.diagram.DiagramSettings;
 63  
 import org.tigris.gef.presentation.ArrowHead;
 64  
 import org.tigris.gef.presentation.ArrowHeadComposite;
 65  
 import org.tigris.gef.presentation.ArrowHeadDiamond;
 66  
 import org.tigris.gef.presentation.ArrowHeadGreater;
 67  
 import org.tigris.gef.presentation.ArrowHeadNone;
 68  
 import org.tigris.gef.presentation.FigNode;
 69  
 import org.tigris.gef.presentation.FigText;
 70  
 
 71  
 
 72  
 /**
 73  
  * This class represents the Fig of a binary association on a diagram.
 74  
  *
 75  
  */
 76  
 public class FigAssociation extends FigEdgeModelElement {
 77  
 
 78  0
     private static final Logger LOG = Logger.getLogger(FigAssociation.class);
 79  
 
 80  
     /**
 81  
      * Group for the FigTexts concerning the source association end.
 82  
      */
 83  
     private EndDecoration srcEnd;
 84  
 
 85  
     /**
 86  
      * Group for the FigTexts concerning the dest association end.
 87  
      */
 88  
     private EndDecoration destEnd;
 89  
 
 90  
     /**
 91  
      * Group for the FigTexts concerning the name and stereotype of the
 92  
      * association itself.
 93  
      */
 94  
     private FigTextGroup middleGroup;
 95  
 
 96  
     /**
 97  
      * Constructor used by PGML parser.
 98  
      * 
 99  
      * @param diagramEdgeSettings the destination uml association-end element
 100  
      * @param settings rendering settings
 101  
      */
 102  
     public FigAssociation(
 103  
             final DiagramEdgeSettings diagramEdgeSettings, 
 104  
             final DiagramSettings settings) {
 105  0
         super(diagramEdgeSettings.getOwner(), settings);
 106  
         
 107  0
         createNameLabel(getOwner(), settings);
 108  
 
 109  0
         Object sourceAssociationEnd =
 110  
             diagramEdgeSettings.getSourceConnector();
 111  0
         Object destAssociationEnd =
 112  
             diagramEdgeSettings.getDestinationConnector();
 113  0
         if (sourceAssociationEnd == null || destAssociationEnd == null) {
 114  
             // If we have no source and dest connector then we assume this is
 115  
             // load of an old UML1.4 diagram from before this data was saved
 116  
             // in PGML. For UML1.4 we can assume the source is first connection
 117  
             // and destination is last connection stored in repository for this
 118  
             // association.
 119  0
             Iterator it =
 120  
                 Model.getFacade().getConnections(getOwner()).iterator();
 121  
             
 122  0
             sourceAssociationEnd = it.next();
 123  0
             destAssociationEnd = it.next();
 124  
         }
 125  
 
 126  0
         createEndFigs(
 127  
                 sourceAssociationEnd,
 128  
                 destAssociationEnd,
 129  
                 settings, 45);
 130  
         
 131  0
         setBetweenNearestPoints(true);
 132  
         
 133  0
         initializeNotationProvidersInternal(getOwner());
 134  
         
 135  0
         if (Model.getFacade().getUmlVersion().charAt(0) == '2') {
 136  0
             Model.getPump().addModelEventListener(this, getOwner(), 
 137  
                     new String[] {"navigableOwnedEnd"});
 138  
         }
 139  0
     }
 140  
     
 141  
     protected void removeFromDiagramImpl() {
 142  0
         if (Model.getFacade().getUmlVersion().charAt(0) == '2') {
 143  0
             Model.getPump().removeModelEventListener(this, 
 144  
                     getOwner(), 
 145  
                     new String[] {"navigableOwnedEnd"});
 146  
         }
 147  0
         super.removeFromDiagramImpl();
 148  0
     }
 149  
     
 150  
     public void propertyChange(PropertyChangeEvent pce) {
 151  0
         if (Model.getFacade().getUmlVersion().charAt(0) == '2'
 152  
                 && pce instanceof AssociationChangeEvent
 153  
                 && pce.getPropertyName().equals("navigableOwnedEnd")) {
 154  0
             srcEnd.getGroup().determineArrowHead();
 155  0
             destEnd.getGroup().determineArrowHead();
 156  0
             applyArrowHeads();
 157  0
             damage();
 158  
         }
 159  0
         super.propertyChange(pce);
 160  0
     }
 161  
     
 162  
     
 163  
     /**
 164  
      * Called by the constructor to create the Figs at each end
 165  
      * of the association.
 166  
      * TODO: This is temporary during refactoring process. We should
 167  
      * override setDestFigNode and setSourceFigNode and create the ends there.
 168  
      * That will allow the same pattern to work for UML2 where we cannot assume
 169  
      * the connection order.
 170  
      * 
 171  
      * @param sourceAssociationEnd
 172  
      * @param destAssociationEnd
 173  
      * @param settings
 174  
      */
 175  
     private void createEndFigs(
 176  
             final Object sourceAssociationEnd,
 177  
             final Object destAssociationEnd,
 178  
             final DiagramSettings settings,
 179  
             final int displacementAngle) {
 180  0
         srcEnd = createEnd(
 181  
                 sourceAssociationEnd, 
 182  
                 settings, 0, 5, 180 - displacementAngle, 5);
 183  0
         destEnd = createEnd(
 184  
                 destAssociationEnd, 
 185  
                 settings, 100, -5, displacementAngle, 5);
 186  0
     }
 187  
     
 188  
     private EndDecoration createEnd(
 189  
             final Object endOwner,
 190  
             final DiagramSettings settings,
 191  
             final int percentPostionOnLine,
 192  
             final int pathDelta,
 193  
             final int displacementAngle,
 194  
             final int displacementDistance) {
 195  0
         return new EndDecoration(endOwner, settings,
 196  
                 percentPostionOnLine,
 197  
                 pathDelta,
 198  
                 displacementAngle,
 199  
                 displacementDistance);
 200  
     }
 201  
 
 202  
     /**
 203  
      * Create the main draggable label for the association.
 204  
      * This can be overridden in subclasses to change behaviour.
 205  
      * TODO: Consider introducing this to FigEdgeModelElement and
 206  
      * using throughout all edges.
 207  
      * 
 208  
      * @param owner owning uml element
 209  
      * @param settings rendering settings
 210  
      */
 211  
     protected void createNameLabel(Object owner, DiagramSettings settings) {
 212  0
         middleGroup = new FigTextGroup(owner, settings);
 213  
      
 214  
         // let's use groups to construct the different text sections at
 215  
         // the association
 216  0
         if (getNameFig() != null) {
 217  0
             middleGroup.addFig(getNameFig());
 218  
         }
 219  0
         middleGroup.addFig(getStereotypeFig());
 220  0
         addPathItem(middleGroup,
 221  
                 new PathItemPlacement(this, middleGroup, 50, 25));
 222  0
         ArgoFigUtil.markPosition(this, 50, 0, 90, 25, Color.yellow);
 223  0
     }
 224  
 
 225  
     @Override
 226  
     public void renderingChanged() {
 227  0
         super.renderingChanged();
 228  
         /* This fixes issue 4987: */
 229  0
         srcEnd.renderingChanged();
 230  0
         destEnd.renderingChanged();
 231  0
         if (middleGroup != null) {
 232  0
             middleGroup.renderingChanged();
 233  
         }
 234  0
     }
 235  
 
 236  
 
 237  
     @Override
 238  
     protected void initNotationProviders(Object own) {
 239  0
         initializeNotationProvidersInternal(own);
 240  0
     }
 241  
 
 242  
     private void initializeNotationProvidersInternal(Object own) {
 243  0
         super.initNotationProviders(own);
 244  0
         srcEnd.initNotationProviders();
 245  0
         destEnd.initNotationProviders();
 246  0
     }
 247  
 
 248  
     /*
 249  
      * @see org.argouml.uml.diagram.ui.FigEdgeModelElement#updateListeners(java.lang.Object, java.lang.Object)
 250  
      */
 251  
     @Override
 252  
     public void updateListeners(Object oldOwner, Object newOwner) {
 253  0
         Set<Object[]> listeners = new HashSet<Object[]>();
 254  0
         if (newOwner != null) {
 255  0
             listeners.add(
 256  
                     new Object[] {newOwner,
 257  
                                   new String[] {"isAbstract", "remove"}
 258  
                     });
 259  
         }
 260  0
         updateElementListeners(listeners);
 261  
         /* No further listeners required in this case - the rest is handled 
 262  
          * by the notationProvider and sub-Figs. */
 263  0
     }
 264  
 
 265  
     /*
 266  
      * @see org.argouml.uml.diagram.ui.FigEdgeModelElement#getNotationProviderType()
 267  
      */
 268  
     @Override
 269  
     protected int getNotationProviderType() {
 270  0
         return NotationProviderFactory2.TYPE_ASSOCIATION_NAME;
 271  
     }
 272  
     
 273  
     /**
 274  
      * Get the source classifier that the association was drawn from.
 275  
      * Note that source and destination are not necessarily meaningful
 276  
      * regarding associations. Although the edge may originally have been
 277  
      * drawn by the user in a certain direction it in no way indicates the
 278  
      * direction of the association.
 279  
      * @return the classifier at the source end of the association or null
 280  
      * if the association is not yet attached (TODO can we ensure that this is
 281  
      * never null?).
 282  
      */
 283  
     protected Object getSource() {
 284  0
         if (srcEnd == null) {
 285  0
             return null;
 286  
         }
 287  0
         return Model.getFacade().getClassifier(srcEnd.getOwner());
 288  
     }
 289  
     
 290  
     /**
 291  
      * Get the destination classifier that the association was drawn from.
 292  
      * Note that source and destination are not necessarily meaningful
 293  
      * regarding associations. Although the edge may originally have been
 294  
      * drawn by the user in a certain direction it in no way indicates the
 295  
      * direction of the association.
 296  
      * @return the classifier at the destination end of the association or null
 297  
      * if the association is not yet attached (TODO can we ensure that this is
 298  
      * never null?).
 299  
      */
 300  
     protected Object getDestination() {
 301  0
         if (destEnd == null) {
 302  0
             return null;
 303  
         }
 304  0
         return Model.getFacade().getClassifier(destEnd.getOwner());
 305  
     }
 306  
     
 307  
     /**
 308  
      * Get the model element at the source end of the edge. This is not the
 309  
      * same as the owner of the node at the source end, rather it is the
 310  
      * element that connects the element of the edge to the element of the
 311  
      * node.
 312  
      * Mostly this returns null as the edge connects directly to the node but
 313  
      * implementations such as the Fig for association will return an
 314  
      * association end that connects the association to the classifier.
 315  
      * @return the model element that connects the edge to the node (or null
 316  
      * if the edge requires no such connector.
 317  
      */
 318  
     public Object getSourceConnector() {
 319  0
         if (srcEnd == null) {
 320  0
             return null;
 321  
         }
 322  0
         return srcEnd.getOwner();
 323  
     }
 324  
     
 325  
     /**
 326  
      * Get the model element at the destination end of the edge. This is not
 327  
      * the same as the owner of the node at the source end, rather it is the
 328  
      * element that connects the element of the edge to the element of the
 329  
      * node.
 330  
      * Mostly this returns null as the edge connects directly to the node but
 331  
      * implementations such as the Fig for association will return an
 332  
      * association end that connects the association to the classifier.
 333  
      * @return the model element that connects the edge to the node (or null
 334  
      * if the edge requires no such connector.
 335  
      */
 336  
     public Object getDestinationConnector() {
 337  0
         if (destEnd == null) {
 338  0
             return null;
 339  
         }
 340  0
         return destEnd.getOwner();
 341  
     }
 342  
     
 343  
     
 344  
 
 345  
     /*
 346  
      * @see org.argouml.uml.diagram.ui.FigEdgeModelElement#textEdited(org.tigris.gef.presentation.FigText)
 347  
      */
 348  
     @Override
 349  
     protected void textEdited(FigText ft) {
 350  
 
 351  0
         if (getOwner() == null) {
 352  0
             return;
 353  
         }
 354  0
         super.textEdited(ft);
 355  
         
 356  0
         Collection conn = Model.getFacade().getConnections(getOwner());
 357  0
         if (conn == null || conn.size() == 0) {
 358  0
             return;
 359  
         }
 360  
 
 361  0
         if (ft == srcEnd.getRole()) {
 362  0
             srcEnd.getRole().textEdited();
 363  0
         } else if (ft == destEnd.getRole()) {
 364  0
             destEnd.getRole().textEdited();
 365  0
         } else if (ft == srcEnd.getMult()) {
 366  0
             srcEnd.getMult().textEdited();
 367  0
         } else if (ft == destEnd.getMult()) {
 368  0
             destEnd.getMult().textEdited();
 369  
         }
 370  0
     }
 371  
 
 372  
     /*
 373  
      * @see org.argouml.uml.diagram.ui.FigEdgeModelElement#textEditStarted(org.tigris.gef.presentation.FigText)
 374  
      */
 375  
     @Override
 376  
     protected void textEditStarted(FigText ft) {
 377  0
         if (ft == srcEnd.getRole()) {
 378  0
             srcEnd.getRole().textEditStarted();
 379  0
         } else if (ft == destEnd.getRole()) {
 380  0
             destEnd.getRole().textEditStarted();
 381  0
         } else if (ft == srcEnd.getMult()) {
 382  0
             srcEnd.getMult().textEditStarted();
 383  0
         } else if (ft == destEnd.getMult()) {
 384  0
             destEnd.getMult().textEditStarted();
 385  
         } else {
 386  0
             super.textEditStarted(ft);
 387  
         }
 388  0
     }
 389  
 
 390  
     /**
 391  
      * Choose the arrowhead style for each end. <p>
 392  
      * 
 393  
      * TODO: This is called from paint(). Would it not better 
 394  
      * be called from renderingChanged()?
 395  
      */
 396  
     protected void applyArrowHeads() {
 397  0
         if (srcEnd == null || destEnd == null) {
 398  
             /* This only happens if model-change events arrive 
 399  
              * before we are completely constructed. */
 400  0
             return;
 401  
         }
 402  0
         int sourceArrowType = srcEnd.getArrowType();
 403  0
         int destArrowType = destEnd.getArrowType();
 404  
 
 405  0
         if (!getSettings().isShowBidirectionalArrows()
 406  
                 && sourceArrowType > 2
 407  
                 && destArrowType > 2) {
 408  0
             sourceArrowType -= 3;
 409  0
             destArrowType -= 3;
 410  
         }
 411  
         
 412  0
         setSourceArrowHead(FigAssociationEndAnnotation
 413  
                 .ARROW_HEADS[sourceArrowType]);
 414  0
         setDestArrowHead(FigAssociationEndAnnotation
 415  
                 .ARROW_HEADS[destArrowType]);
 416  0
     }
 417  
     
 418  
     /*
 419  
      * @see org.tigris.gef.ui.PopupGenerator#getPopUpActions(java.awt.event.MouseEvent)
 420  
      */
 421  
     @Override
 422  
     public Vector getPopUpActions(MouseEvent me) {
 423  0
         Vector popUpActions = super.getPopUpActions(me);
 424  
         /* Check if multiple items are selected: */
 425  0
         boolean ms = TargetManager.getInstance().getTargets().size() > 1;
 426  
         /* None of the menu-items below apply
 427  
          * when multiple modelelements are selected:*/
 428  0
         if (ms) {
 429  0
             return popUpActions;
 430  
         }
 431  
 
 432  
         // x^2 + y^2 = r^2  (equation of a circle)
 433  0
         Point firstPoint = this.getFirstPoint();
 434  0
         Point lastPoint = this.getLastPoint();
 435  0
         int length = getPerimeterLength();
 436  
 
 437  0
         int rSquared = (int) (.3 * length);
 438  
 
 439  
         // max distance is set at 100 pixels, (rSquared = 100^2)
 440  0
         if (rSquared > 100) {
 441  0
             rSquared = 10000;
 442  
         } else {
 443  0
             rSquared *= rSquared;
 444  
         }
 445  
 
 446  0
         int srcDeterminingFactor =
 447  
             getSquaredDistance(me.getPoint(), firstPoint);
 448  0
         int destDeterminingFactor =
 449  
             getSquaredDistance(me.getPoint(), lastPoint);
 450  
 
 451  0
         if (srcDeterminingFactor < rSquared
 452  
             && srcDeterminingFactor < destDeterminingFactor) {
 453  
 
 454  0
             ArgoJMenu multMenu =
 455  
                 new ArgoJMenu("menu.popup.multiplicity");
 456  
 
 457  0
             multMenu.add(ActionMultiplicity.getSrcMultOne());
 458  0
             multMenu.add(ActionMultiplicity.getSrcMultZeroToOne());
 459  0
             multMenu.add(ActionMultiplicity.getSrcMultOneToMany());
 460  0
             multMenu.add(ActionMultiplicity.getSrcMultZeroToMany());
 461  0
             popUpActions.add(popUpActions.size() - getPopupAddOffset(),
 462  
                     multMenu);
 463  
 
 464  0
             ArgoJMenu aggMenu = new ArgoJMenu("menu.popup.aggregation");
 465  
 
 466  0
             aggMenu.add(ActionAggregation.getSrcAggNone());
 467  0
             aggMenu.add(ActionAggregation.getSrcAgg());
 468  0
             aggMenu.add(ActionAggregation.getSrcAggComposite());
 469  0
             popUpActions.add(popUpActions.size() - getPopupAddOffset(),
 470  
                     aggMenu);
 471  0
         } else if (destDeterminingFactor < rSquared) {
 472  0
             ArgoJMenu multMenu =
 473  
                 new ArgoJMenu("menu.popup.multiplicity");
 474  0
             multMenu.add(ActionMultiplicity.getDestMultOne());
 475  0
             multMenu.add(ActionMultiplicity.getDestMultZeroToOne());
 476  0
             multMenu.add(ActionMultiplicity.getDestMultOneToMany());
 477  0
             multMenu.add(ActionMultiplicity.getDestMultZeroToMany());
 478  0
             popUpActions.add(popUpActions.size() - getPopupAddOffset(),
 479  
                     multMenu);
 480  
 
 481  0
             ArgoJMenu aggMenu = new ArgoJMenu("menu.popup.aggregation");
 482  0
             aggMenu.add(ActionAggregation.getDestAggNone());
 483  0
             aggMenu.add(ActionAggregation.getDestAgg());
 484  0
             aggMenu.add(ActionAggregation.getDestAggComposite());
 485  0
             popUpActions
 486  
                     .add(popUpActions.size() - getPopupAddOffset(), aggMenu);
 487  
         }
 488  
         // else: No particular options for right click in middle of line
 489  
 
 490  
         // Options available when right click anywhere on line
 491  0
         Object association = getOwner();
 492  0
         if (association != null) {
 493  
             // Navigability menu with suboptions built dynamically to
 494  
             // allow navigability from atart to end, from end to start
 495  
             // or bidirectional
 496  0
             Collection ascEnds = Model.getFacade().getConnections(association);
 497  0
             Iterator iter = ascEnds.iterator();
 498  0
             Object ascStart = iter.next();
 499  0
             Object ascEnd = iter.next();
 500  
 
 501  0
             if (Model.getFacade().isAClassifier(
 502  
                     Model.getFacade().getType(ascStart))
 503  
                     && Model.getFacade().isAClassifier(
 504  
                             Model.getFacade().getType(ascEnd))) {
 505  0
                 ArgoJMenu navMenu =
 506  
                     new ArgoJMenu("menu.popup.navigability");
 507  
 
 508  0
                 navMenu.add(ActionNavigability.newActionNavigability(
 509  
                     ascStart,
 510  
                     ascEnd,
 511  
                     ActionNavigability.BIDIRECTIONAL));
 512  0
                 navMenu.add(ActionNavigability.newActionNavigability(
 513  
                     ascStart,
 514  
                     ascEnd,
 515  
                     ActionNavigability.STARTTOEND));
 516  0
                 navMenu.add(ActionNavigability.newActionNavigability(
 517  
                     ascStart,
 518  
                     ascEnd,
 519  
                     ActionNavigability.ENDTOSTART));
 520  
 
 521  0
                 popUpActions.add(popUpActions.size() - getPopupAddOffset(),
 522  
                         navMenu);
 523  
             }
 524  
         }
 525  
 
 526  0
         return popUpActions;
 527  
     }
 528  
 
 529  
     /**
 530  
      * Updates the multiplicity fields.
 531  
      */
 532  
     protected void updateMultiplicity() {
 533  0
         if (getOwner() != null 
 534  
                 && srcEnd.getOwner() != null 
 535  
                 && destEnd.getOwner() != null) {
 536  0
             srcEnd.getMult().setText();
 537  0
             destEnd.getMult().setText();
 538  
         }
 539  0
     }
 540  
 
 541  
     /*
 542  
      * @see org.tigris.gef.presentation.Fig#paint(java.awt.Graphics)
 543  
      */
 544  
     @Override
 545  
     public void paint(Graphics g) {
 546  0
         if (getOwner() == null ) {
 547  0
             LOG.error("Trying to paint a FigAssociation without an owner. ");
 548  
         } else {
 549  0
             applyArrowHeads(); 
 550  
         }
 551  0
         if (getSourceArrowHead() != null && getDestArrowHead() != null) {
 552  0
             getSourceArrowHead().setLineColor(getLineColor());
 553  0
             getDestArrowHead().setLineColor(getLineColor());
 554  
         }
 555  0
         super.paint(g);
 556  0
     }
 557  
 
 558  
     /*
 559  
      * @see org.argouml.uml.diagram.ui.FigEdgeModelElement#paintClarifiers(java.awt.Graphics)
 560  
      */
 561  
     @Override
 562  
     public void paintClarifiers(Graphics g) {
 563  0
         indicateBounds(getNameFig(), g);
 564  0
         indicateBounds(srcEnd.getMult(), g);
 565  0
         indicateBounds(srcEnd.getRole(), g);
 566  0
         indicateBounds(destEnd.getMult(), g);
 567  0
         indicateBounds(destEnd.getRole(), g);
 568  0
         super.paintClarifiers(g);
 569  0
     }
 570  
 
 571  
     /**
 572  
      * @return Returns the middleGroup.
 573  
      */
 574  
     protected FigTextGroup getMiddleGroup() {
 575  0
         return middleGroup;
 576  
     }
 577  
     
 578  
     /**
 579  
      * Lays out the association edges as any other edge except for
 580  
      * special rules for an association that loops back to the same
 581  
      * class. For this it is snapped back to the bottom right corner
 582  
      * if it resized to the point of not being visible.
 583  
      * @see org.tigris.gef.presentation.FigEdgePoly#layoutEdge()
 584  
      */
 585  
     @Override
 586  
     protected void layoutEdge() {
 587  0
         FigNode sourceFigNode = getSourceFigNode();
 588  0
         Point[] points = getPoints();
 589  0
         if (points.length < 3
 590  
                 && sourceFigNode != null
 591  
                 && getDestFigNode() == sourceFigNode) {
 592  0
             Rectangle rect = new Rectangle(
 593  
                     sourceFigNode.getX() + sourceFigNode.getWidth() - 20,
 594  
                     sourceFigNode.getY() + sourceFigNode.getHeight() - 20,
 595  
                     40,
 596  
                     40);
 597  0
             points = new Point[5];
 598  0
             points[0] = new Point(rect.x, rect.y + rect.height / 2);
 599  0
             points[1] = new Point(rect.x, rect.y + rect.height);
 600  0
             points[2] = new Point(rect.x + rect.width , rect.y + rect.height);
 601  0
             points[3] = new Point(rect.x + rect.width , rect.y);
 602  0
             points[4] = new Point(rect.x + rect.width / 2, rect.y);
 603  0
             setPoints(points);
 604  0
         } else {
 605  0
             super.layoutEdge();
 606  
         }
 607  0
     }
 608  
     
 609  
     /**
 610  
      * If the name is updated, update the bounds of the middle group.
 611  
      * This makes the selection box appear correctly during prop-panel edit.
 612  
      * This is a temporary solution, until a better architecture is decided 
 613  
      * upon, see issue 5477 and 
 614  
      * http://argouml.tigris.org/issues/show_bug.cgi?id=5621#desc19.
 615  
      * @see org.argouml.uml.diagram.ui.FigEdgeModelElement#updateNameText()
 616  
      */
 617  
     protected void updateNameText() {
 618  0
         super.updateNameText();
 619  
         // TODO: Without the null check the following throws a NPE so many
 620  
         // times when it is called from FigEdgeModelElement.modelChanged(),
 621  
         // we need to think about it.
 622  0
         if (middleGroup != null) {
 623  0
             middleGroup.calcBounds();
 624  
         }
 625  0
     }
 626  
     
 627  
 
 628  
     /**
 629  
      * 
 630  
      */
 631  
     class EndDecoration {
 632  
         private FigAssociationEndAnnotation group;
 633  
         private FigMultiplicity mult;
 634  
         
 635  
         EndDecoration(
 636  
             final Object endOwner,
 637  
             final DiagramSettings settings,
 638  
             final int percentPostionOnLine,
 639  
             final int pathDelta,
 640  
             final int displacementAngle,
 641  0
             final int displacementDistance) {
 642  0
             mult = new FigMultiplicity(endOwner, settings);
 643  0
             addPathItem(mult, 
 644  
                     new PathItemPlacement(FigAssociation.this, mult, 
 645  
                             percentPostionOnLine, pathDelta, 
 646  
                             displacementAngle, displacementDistance));
 647  0
             ArgoFigUtil.markPosition(
 648  
                     FigAssociation.this, percentPostionOnLine, pathDelta, 
 649  
                     displacementAngle, displacementDistance, Color.green);
 650  
             
 651  0
             group = new FigAssociationEndAnnotation(
 652  
                     FigAssociation.this, endOwner, settings);
 653  0
             addPathItem(group, 
 654  
                     new PathItemPlacement(FigAssociation.this, group, 
 655  
                             percentPostionOnLine, pathDelta, 
 656  
                             -displacementAngle, displacementDistance));
 657  0
             ArgoFigUtil.markPosition(
 658  
                     FigAssociation.this, percentPostionOnLine, pathDelta, 
 659  
                     -displacementAngle, displacementDistance, Color.blue);
 660  0
         }
 661  
 
 662  
         public FigAssociationEndAnnotation getGroup() {
 663  0
             return group;
 664  
         }
 665  
 
 666  
         public FigMultiplicity getMult() {
 667  0
             return mult;
 668  
         }
 669  
         
 670  
         public FigRole getRole() {
 671  0
             return group.getRole();
 672  
         }
 673  
         
 674  
         public int getArrowType() {
 675  0
             return group.getArrowType();
 676  
         }
 677  
         
 678  
         public void renderingChanged() {
 679  0
             mult.renderingChanged();
 680  0
             group.renderingChanged();
 681  0
         }
 682  
         
 683  
         public void initNotationProviders() {
 684  0
             mult.initNotationProviders();
 685  0
         }
 686  
         
 687  
         public Object getOwner() {
 688  0
             return mult.getOwner();
 689  
         }
 690  
     }
 691  
     
 692  
 } /* end class FigAssociation */
 693  
 
 694  
 /**
 695  
  * A Fig representing the multiplicity of some model element.
 696  
  * This has potential reuse for other edges showing multiplicity. <p>
 697  
  * 
 698  
  * The owner is an AssociationEnd.
 699  
  * 
 700  
  * @author Bob Tarling
 701  
  */
 702  
 class FigMultiplicity extends FigSingleLineTextWithNotation {
 703  
 
 704  
     FigMultiplicity(Object owner, DiagramSettings settings) {
 705  0
         super(owner, new Rectangle(X0, Y0, 90, 20), settings, false,
 706  
                 "multiplicity");
 707  0
         setTextFilled(false);
 708  0
         setJustification(FigText.JUSTIFY_CENTER);
 709  0
     }
 710  
 
 711  
     @Override
 712  
     protected int getNotationProviderType() {
 713  0
         return NotationProviderFactory2.TYPE_MULTIPLICITY;
 714  
     }
 715  
 
 716  
 }
 717  
 
 718  
 /**
 719  
  * A textual Fig representing the ordering of some model element,
 720  
  * i.e. "{ordered}" or nothing.
 721  
  * This has potential reuse for other edges showing ordering. <p>
 722  
  * 
 723  
  * This Fig is not editable by the user.
 724  
  * 
 725  
  * @author Bob Tarling
 726  
  */
 727  0
 class FigOrdering extends FigSingleLineText {
 728  
 
 729  
     private static final long serialVersionUID = 5385230942216677015L;
 730  
     
 731  
     FigOrdering(Object owner, DiagramSettings settings) {
 732  0
         super(owner, new Rectangle(X0, Y0, 90, 20), settings, false, 
 733  
                 "ordering");
 734  0
         setTextFilled(false);
 735  0
         setJustification(FigText.JUSTIFY_CENTER);
 736  0
         setEditable(false);
 737  0
     }
 738  
     
 739  
     @Override
 740  
     protected void setText() {
 741  0
         assert getOwner() != null;
 742  0
         if (getSettings().getNotationSettings().isShowProperties()) {
 743  0
             setText(getOrderingName(Model.getFacade().getOrdering(getOwner())));
 744  
         } else {
 745  0
             setText("");
 746  
         }
 747  0
         damage();
 748  0
     }
 749  
 
 750  
     /**
 751  
      * Returns the name of the OrderingKind.
 752  
      *
 753  
      * @param orderingKind the kind of ordering
 754  
      * @return "{ordered}" or "", the latter if null or unordered
 755  
      */
 756  
     private String getOrderingName(Object orderingKind) {
 757  0
         if (orderingKind == null) {
 758  0
             return "";
 759  
         }
 760  0
         if (Model.getFacade().getName(orderingKind) == null) {
 761  0
             return "";
 762  
         }
 763  0
         if ("".equals(Model.getFacade().getName(orderingKind))) {
 764  0
             return "";
 765  
         }
 766  0
         if ("unordered".equals(Model.getFacade().getName(orderingKind))) {
 767  0
             return "";
 768  
         }
 769  
         // TODO: I18N
 770  0
         return "{" + Model.getFacade().getName(orderingKind) + "}";
 771  
     }
 772  
 }
 773  
 
 774  
 /**
 775  
  * A Fig representing the association end role of some model element.
 776  
  * This class is designed as a composite part and should always be 
 777  
  * part of a FigGroup.
 778  
  * @author Bob Tarling
 779  
  */
 780  0
 class FigRole extends FigSingleLineTextWithNotation {
 781  
 
 782  
     FigRole(Object owner, DiagramSettings settings) {
 783  0
         super(owner, new Rectangle(X0, Y0, 90, 20), settings, false, 
 784  
                 (String[]) null 
 785  
         // no need to listen to these property changes - the 
 786  
         // notationProvider takes care of this.
 787  
                 /*, new String[] {"name", "visibility", "stereotype"}*/
 788  
                 );
 789  0
         setTextFilled(false);
 790  0
         setJustification(FigText.JUSTIFY_CENTER);
 791  0
         setText();
 792  0
     }
 793  
 
 794  
     protected int getNotationProviderType() {
 795  0
         return NotationProviderFactory2.TYPE_ASSOCIATION_END_NAME;
 796  
     }
 797  
     
 798  
     /**
 799  
      * Property change listener to recalculate bounds of enclosing
 800  
      * group whenever any properties of the FigRole get changed.
 801  
      * This is only really needed for the name, see issue 5621.
 802  
      * 
 803  
      * @param pce The property change event to process.
 804  
      * @see org.argouml.uml.diagram.ui.FigSingleLineTextWithNotation#propertyChange(java.beans.PropertyChangeEvent)
 805  
      */
 806  
     @Override
 807  
     public void propertyChange(PropertyChangeEvent pce) {
 808  0
         super.propertyChange(pce);
 809  0
         assert(getGroup() != null);
 810  0
         this.getGroup().calcBounds();
 811  0
     }
 812  
 
 813  
 }
 814  
 
 815  
 /**
 816  
  * The arrowhead and the group of labels shown at the association end: 
 817  
  * the role name and the ordering property. 
 818  
  * This does not include the multiplicity. <p>
 819  
  * 
 820  
  * This class does not yet support arrows for a FigAssociationEnd, 
 821  
  * as is used for N-ary associations.
 822  
  */
 823  0
 class FigAssociationEndAnnotation extends FigTextGroup {
 824  
 
 825  
     private static final long serialVersionUID = 1871796732318164649L;
 826  
     
 827  0
     private static final ArrowHead NAV_AGGR =
 828  
         new ArrowHeadComposite(ArrowHeadDiamond.WhiteDiamond,
 829  
                    new ArrowHeadGreater());
 830  
 
 831  0
     private static final ArrowHead NAV_COMP =
 832  
         new ArrowHeadComposite(ArrowHeadDiamond.BlackDiamond,
 833  
                    new ArrowHeadGreater());
 834  
 
 835  
     // These are a list of arrow types. Positioning is important as we subtract
 836  
     // 3 to convert a navigable arrow to a non navigable with the same
 837  
     // aggregation
 838  
     private static final int NONE = 0;
 839  
     private static final int AGGREGATE = 1;
 840  
     private static final int COMPOSITE = 2;
 841  
     private static final int NAV_NONE = 3;
 842  
     private static final int NAV_AGGREGATE = 4;
 843  
     private static final int NAV_COMPOSITE = 5;
 844  
     
 845  
     /**
 846  
      * All the arrow head types.
 847  
      */
 848  0
     public static final ArrowHead[] ARROW_HEADS = new ArrowHead[6];
 849  
     static {
 850  0
         ARROW_HEADS[NONE] = ArrowHeadNone.TheInstance;
 851  0
         ARROW_HEADS[AGGREGATE] = ArrowHeadDiamond.WhiteDiamond;
 852  0
         ARROW_HEADS[COMPOSITE] = ArrowHeadDiamond.BlackDiamond;
 853  0
         ARROW_HEADS[NAV_NONE] = new ArrowHeadGreater();
 854  0
         ARROW_HEADS[NAV_AGGREGATE] = NAV_AGGR;
 855  0
         ARROW_HEADS[NAV_COMPOSITE] = NAV_COMP;
 856  0
     }
 857  
     
 858  
     private FigRole role;
 859  
     private FigOrdering ordering;
 860  0
     private int arrowType = 0;
 861  
     private FigEdgeModelElement figEdge;
 862  
 
 863  
     FigAssociationEndAnnotation(FigEdgeModelElement edge, Object owner,
 864  
             DiagramSettings settings) {
 865  0
         super(owner, settings);
 866  0
         figEdge = edge;
 867  
         
 868  0
         role = new FigRole(owner, settings);
 869  0
         addFig(role);
 870  
 
 871  0
         ordering = new FigOrdering(owner, settings);
 872  0
         addFig(ordering);
 873  
 
 874  0
         determineArrowHead();
 875  0
         Model.getPump().addModelEventListener(this, owner, 
 876  
                 new String[] {"isNavigable", "aggregation", "participant"});
 877  0
     }
 878  
 
 879  
     /*
 880  
      * @see org.tigris.gef.presentation.Fig#removeFromDiagram()
 881  
      */
 882  
     @Override
 883  
     public void removeFromDiagram() {
 884  0
         Model.getPump().removeModelEventListener(this, 
 885  
                 getOwner(), 
 886  
                 new String[] {"isNavigable", "aggregation", "participant"});
 887  0
         super.removeFromDiagram();
 888  0
     }
 889  
     
 890  
     /*
 891  
      * @see org.tigris.gef.presentation.Fig#propertyChange(java.beans.PropertyChangeEvent)
 892  
      */
 893  
     @Override
 894  
     public void propertyChange(PropertyChangeEvent pce) {
 895  0
         if (pce instanceof AttributeChangeEvent
 896  
             && (pce.getPropertyName().equals("isNavigable")
 897  
                 || pce.getPropertyName().equals("aggregation"))) {
 898  0
             determineArrowHead();
 899  0
             ((FigAssociation) figEdge).applyArrowHeads();
 900  0
             damage();
 901  
         }
 902  0
         if (pce instanceof AddAssociationEvent
 903  
                 && pce.getPropertyName().equals("participant")) {
 904  0
             figEdge.determineFigNodes();
 905  
         }
 906  
 
 907  0
         String pName = pce.getPropertyName();
 908  0
         if (pName.equals("editing")
 909  
                 && Boolean.FALSE.equals(pce.getNewValue())) {
 910  
             // Finished editing.
 911  
             // Parse the text that was edited.
 912  
             // Only the role is editable, hence:
 913  0
             role.textEdited();
 914  0
             calcBounds();
 915  0
             endTrans();
 916  0
         } else if (pName.equals("editing")
 917  
                 && Boolean.TRUE.equals(pce.getNewValue())) {
 918  
 //            figEdge.showHelp(role.getParsingHelp());
 919  
 //            role.setText();
 920  0
             role.textEditStarted();
 921  
         } else {
 922  
             // Pass everything else to superclass
 923  0
             super.propertyChange(pce);
 924  
         }
 925  0
     }
 926  
 
 927  
     /**
 928  
      * Decide which arrow head should appear
 929  
      */
 930  
     void determineArrowHead() {
 931  0
         assert getOwner() != null;
 932  
 
 933  0
         Object ak =  Model.getFacade().getAggregation(getOwner());
 934  0
         boolean nav = Model.getFacade().isNavigable(getOwner());
 935  
 
 936  0
         if (nav) {
 937  0
             if (Model.getAggregationKind().getNone().equals(ak)
 938  
                     || (ak == null)) {
 939  0
                 arrowType = NAV_NONE;
 940  0
             } else if (Model.getAggregationKind().getAggregate()
 941  
                     .equals(ak)) {
 942  0
                 arrowType = NAV_AGGREGATE;
 943  0
             } else if (Model.getAggregationKind().getComposite()
 944  
                     .equals(ak)) {
 945  0
                 arrowType = NAV_COMPOSITE;
 946  
             }
 947  
         } else {
 948  0
             if (Model.getAggregationKind().getNone().equals(ak)
 949  
                     || (ak == null)) {
 950  0
                 arrowType = NONE;
 951  0
             } else if (Model.getAggregationKind().getAggregate()
 952  
                     .equals(ak)) {
 953  0
                 arrowType = AGGREGATE;
 954  0
             } else if (Model.getAggregationKind().getComposite()
 955  
                     .equals(ak)) {
 956  0
                 arrowType = COMPOSITE;
 957  
             }
 958  
         }
 959  0
     }
 960  
     
 961  
     /**
 962  
      * @return the current arrow type of this end of the association
 963  
      */
 964  
     public int getArrowType() {
 965  0
         return arrowType;
 966  
     }
 967  
 
 968  
     FigRole getRole() {
 969  0
         return role;
 970  
     }
 971  
 }