Coverage Report - org.argouml.uml.diagram.use_case.UseCaseDiagramGraphModel
 
Classes in this File Line Coverage Branch Coverage Complexity
UseCaseDiagramGraphModel
1%
2/144
0%
0/138
9.091
 
 1  
 /* $Id: UseCaseDiagramGraphModel.java 17866 2010-01-12 20:45:46Z linus $
 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  
  *    tfmorris
 11  
  *****************************************************************************
 12  
  *
 13  
  * Some portions of this file was previously release using the BSD License:
 14  
  */
 15  
 
 16  
 // Copyright (c) 1996-2006 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.use_case;
 40  
 
 41  
 import java.beans.PropertyChangeEvent;
 42  
 import java.beans.VetoableChangeListener;
 43  
 import java.util.ArrayList;
 44  
 import java.util.Collection;
 45  
 import java.util.Collections;
 46  
 import java.util.Iterator;
 47  
 import java.util.List;
 48  
 
 49  
 
 50  
 import org.apache.log4j.Logger;
 51  
 import org.argouml.model.Model;
 52  
 import org.argouml.uml.CommentEdge;
 53  
 import org.argouml.uml.diagram.UMLMutableGraphSupport;
 54  
 
 55  
 /**
 56  
  * This class defines a bridge between the UML meta-model
 57  
  * representation of the design and the GraphModel interface used by
 58  
  * GEF.<p>
 59  
  *
 60  
  * This class handles only UML Use Case Diagrams.<p>
 61  
  */
 62  973
 public class UseCaseDiagramGraphModel
 63  
         extends UMLMutableGraphSupport
 64  
         implements VetoableChangeListener {
 65  
     /**
 66  
      * Logger.
 67  
      */
 68  900
     private static final Logger LOG =
 69  
         Logger.getLogger(UseCaseDiagramGraphModel.class);
 70  
 
 71  
     ///////////////////////////////////////////////////////////////////////////
 72  
     //
 73  
     // Methods that implement the GraphModel itself
 74  
     //
 75  
     ///////////////////////////////////////////////////////////////////////////
 76  
 
 77  
     /**
 78  
      * Return all ports on a node or edge supplied as argument.<p>
 79  
      *
 80  
      * The only objects on our diagram that have any ports are use
 81  
      * cases and actors, and they each have one - themself.<p>
 82  
      *
 83  
      * @param nodeOrEdge  A model element, for whom the list of ports is
 84  
      *                    wanted.
 85  
      *
 86  
      * @return            A List of the ports found.
 87  
      */
 88  
     public List getPorts(Object nodeOrEdge) {
 89  0
         if (Model.getFacade().isAActor(nodeOrEdge)) {
 90  0
             List result = new ArrayList();
 91  0
             result.add(nodeOrEdge);
 92  0
             return result;
 93  0
         } else if (Model.getFacade().isAUseCase(nodeOrEdge)) {
 94  0
             List result = new ArrayList();
 95  0
             result.add(nodeOrEdge);
 96  0
             return result;
 97  
         }
 98  
 
 99  0
         return Collections.EMPTY_LIST;
 100  
     }
 101  
 
 102  
 
 103  
     /**
 104  
      * Return the node or edge that owns the given port.<p>
 105  
      *
 106  
      * In our implementation the only objects with ports, use
 107  
      * themselves as the port, so are there own owner.<p>
 108  
      *
 109  
      * @param port  The port, whose owner is wanted.
 110  
      *
 111  
      * @return      The owner of the port.
 112  
      */
 113  
     public Object getOwner(Object port) {
 114  0
         return port;
 115  
     }
 116  
 
 117  
 
 118  
     /**
 119  
      * Return all edges going to given port.<p>
 120  
      *
 121  
      * The only objects with ports on the use case diagram are actors
 122  
      * and use cases.  In each case we find the attached association
 123  
      * ends, and build a list of them as the incoming ports.<p>
 124  
      *
 125  
      * @param port  The port for which we want to know the incoming edges.
 126  
      *
 127  
      * @return      A list of objects which are the incoming edges.
 128  
      */
 129  
     public List getInEdges(Object port) {
 130  0
         if (Model.getFacade().isAActor(port) 
 131  
                 || Model.getFacade().isAUseCase(port)) {
 132  0
             List result = new ArrayList();
 133  0
             Collection ends = Model.getFacade().getAssociationEnds(port);
 134  0
             if (ends == null) {
 135  0
                 return Collections.EMPTY_LIST;
 136  
             }
 137  0
             for (Object ae : ends) {
 138  0
                 result.add(Model.getFacade().getAssociation(ae));
 139  
             }
 140  0
             return result;
 141  
         }
 142  0
         return Collections.EMPTY_LIST;
 143  
     }
 144  
 
 145  
 
 146  
     /**
 147  
      * Return all edges going from the given port.<p>
 148  
      *
 149  
      * <em>Needs more work</em>.  This would seem superficially to be
 150  
      * identical to {@link #getInEdges}, but in our implementation we
 151  
      * return an empty list.<p>
 152  
      *
 153  
      * @param port  The port for which we want to know the outgoing edges.
 154  
      *
 155  
      * @return      A list of objects which are the outgoing edges. Currently
 156  
      *              return the empty list.
 157  
      */
 158  
     public List getOutEdges(Object port) {
 159  0
         return Collections.EMPTY_LIST;
 160  
     }
 161  
 
 162  
     ///////////////////////////////////////////////////////////////////////////
 163  
     //
 164  
     // Methods that implement the MutableGraphModel interface
 165  
     //
 166  
     ///////////////////////////////////////////////////////////////////////////
 167  
 
 168  
     /**
 169  
      * Determine if the given node can validly be placed on this
 170  
      * graph.<p>
 171  
      *
 172  
      * This is simply a matter of determining if the node is an actor
 173  
      * or use case.<p>
 174  
      *
 175  
      * <em>Note</em>. This is inconsistent with {@link #addNode},
 176  
      * which will not allow a node to be added to the graph if it is
 177  
      * already there.<p>
 178  
      *
 179  
      * @param node  The node to be considered
 180  
      *
 181  
      * @return      <code>true</code> if the given object is a valid node in
 182  
      *              this graph, <code>false</code> otherwise.
 183  
      */
 184  
     @Override
 185  
     public boolean canAddNode(Object node) {
 186  0
         if (Model.getFacade().isAAssociation(node)
 187  
                 && !Model.getFacade().isANaryAssociation(node)) {
 188  
             // A binary association is not a node so reject.
 189  0
             return false;
 190  
         }
 191  0
         if (super.canAddNode(node)) {
 192  0
             return true;
 193  
         }
 194  0
         if (containsNode(node)) {
 195  0
             return false;
 196  
         }
 197  0
         return Model.getFacade().isAActor(node)
 198  
             || Model.getFacade().isAUseCase(node)
 199  
             || Model.getFacade().isAPackage(node);
 200  
     }
 201  
 
 202  
 
 203  
     /**
 204  
      * Determine if the given edge can validly be placed on this graph.<p>
 205  
      *
 206  
      * We cannot do so if the edge is already on the graph (unlike
 207  
      * nodes they may not appear more than once).<p>
 208  
      *
 209  
      * Otherwise, for all valid types of edge (binary association,
 210  
      * generalization, extend, include, dependency) we get the two
 211  
      * ends. If they are both nodes already on the graph we are OK,
 212  
      * otherwise we cannot place the edge on the graph.<p>
 213  
      *
 214  
      * @param edge  The edge to be considered
 215  
      *
 216  
      * @return      <code>true</code> if the given object is a valid edge in
 217  
      *              this graph, <code>false</code> otherwise.
 218  
      */
 219  
     @Override
 220  
     public boolean canAddEdge(Object edge)  {
 221  0
         if (edge == null) {
 222  0
             return false;
 223  
         }
 224  0
         if (containsEdge(edge)) {
 225  0
             return false;
 226  
         }
 227  
 
 228  
         // Get the two ends of any valid edge
 229  0
         Object sourceModelElement = null;
 230  0
         Object destModelElement = null;
 231  0
         if (Model.getFacade().isAAssociation(edge)) {
 232  
 
 233  
             // Only allow binary associations
 234  
 
 235  0
             Collection conns = Model.getFacade().getConnections(edge);
 236  0
             Iterator iter = conns.iterator();
 237  
 
 238  0
             if (conns.size() < 2) {
 239  0
                 return false;
 240  
             }
 241  
 
 242  0
             Object associationEnd0 = iter.next();
 243  0
             Object associationEnd1 = iter.next();
 244  
 
 245  
             // Give up if the assocation ends don't have a type defined
 246  
 
 247  0
             if ((associationEnd0 == null) || (associationEnd1 == null)) {
 248  0
                 return false;
 249  
             }
 250  
 
 251  0
             sourceModelElement = Model.getFacade().getType(associationEnd0);
 252  0
             destModelElement = Model.getFacade().getType(associationEnd1);
 253  0
         } else if (Model.getFacade().isAGeneralization(edge)) {
 254  0
             sourceModelElement = Model.getFacade().getSpecific(edge);
 255  0
             destModelElement = Model.getFacade().getGeneral(edge);
 256  0
         } else if (Model.getFacade().isAExtend(edge)) {
 257  0
             sourceModelElement = Model.getFacade().getBase(edge);
 258  0
             destModelElement = Model.getFacade().getExtension(edge);
 259  0
         } else if (Model.getFacade().isAInclude(edge)) {
 260  
 
 261  0
             sourceModelElement = Model.getFacade().getBase(edge);
 262  0
             destModelElement = Model.getFacade().getAddition(edge);
 263  0
         } else if (Model.getFacade().isADependency(edge)) {
 264  
 
 265  
             // A dependency potentially has many clients and suppliers. We only
 266  
             // consider the first of each (not clear that we should really
 267  
             // accept the case where there is more than one of either)
 268  
 
 269  0
             Collection clients   = Model.getFacade().getClients(edge);
 270  0
             Collection suppliers = Model.getFacade().getSuppliers(edge);
 271  
 
 272  0
             if (clients == null || clients.isEmpty() 
 273  
                     || suppliers == null || suppliers.isEmpty()) {
 274  0
                 return false;
 275  
             }
 276  0
             sourceModelElement = clients.iterator().next();
 277  0
             destModelElement = suppliers.iterator().next();
 278  
 
 279  0
         } else if (edge instanceof CommentEdge) {
 280  0
             sourceModelElement = ((CommentEdge) edge).getSource();
 281  0
             destModelElement = ((CommentEdge) edge).getDestination();
 282  
         } else {
 283  0
             return false;
 284  
         }
 285  
 
 286  
         // Both ends must be defined and nodes that are on the graph already.
 287  0
         if (sourceModelElement == null || destModelElement == null) {
 288  0
             LOG.error("Edge rejected. Its ends are not attached to anything");
 289  0
             return false;
 290  
         }
 291  
 
 292  0
         if (!containsNode(sourceModelElement)
 293  
                 && !containsEdge(sourceModelElement)) {
 294  0
             LOG.error("Edge rejected. Its source end is attached to "
 295  
                     + sourceModelElement
 296  
                     + " but this is not in the graph model");
 297  0
             return false;
 298  
         }
 299  0
         if (!containsNode(destModelElement)
 300  
                 && !containsEdge(destModelElement)) {
 301  0
             LOG.error("Edge rejected. Its destination end is attached to "
 302  
                     + destModelElement
 303  
                     + " but this is not in the graph model");
 304  0
             return false;
 305  
         }
 306  
 
 307  0
         return true;
 308  
     }
 309  
 
 310  
 
 311  
     /**
 312  
      * Add the given node to the graph, if valid.<p>
 313  
      *
 314  
      * We add the node if it is not already on the graph, and
 315  
      * (assuming it to be an actor or use case) add it to the owned
 316  
      * elements for the model.<p>
 317  
      *
 318  
      * <em>Needs more work</em>. In adding the node to the owned
 319  
      * elements of the model namespace, we are implicitly making it
 320  
      * public visibility (it could be private to this namespace).<p>
 321  
      *
 322  
      * <em>Note</em>.  This method is inconsistent with
 323  
      * {@link #canAddNode}, which will allow a node to be added to the
 324  
      * graph if it is already there.<p>
 325  
      *
 326  
      * @param node  The node to be added to the graph.
 327  
      */
 328  
     @Override
 329  
     public void addNode(Object node) {
 330  
 
 331  0
         LOG.debug("adding usecase node");
 332  
 
 333  
         // Give up if we are already on the graph. This is a bit inconistent
 334  
         // with canAddNode above.
 335  
 
 336  0
         if (!canAddNode(node)) {
 337  0
             return;
 338  
         }
 339  
 
 340  
         // Add the node, check that it is an actor or use case and add it to
 341  
         // the model namespace.
 342  
 
 343  0
         getNodes().add(node);
 344  
 
 345  0
         if (Model.getFacade().isAUMLElement(node)
 346  
                 && Model.getFacade().getNamespace(node) == null) {
 347  0
             Model.getCoreHelper().addOwnedElement(getHomeModel(), node);
 348  
         }
 349  
 
 350  
         // Tell GEF its changed
 351  
 
 352  0
         fireNodeAdded(node);
 353  0
     }
 354  
 
 355  
 
 356  
     /**
 357  
      * Add the given edge to the graph, if valid.<p>
 358  
      *
 359  
      * We add the edge if it is not already on the graph, and
 360  
      * (assuming it to be an association, generalization, extend,
 361  
      * include or dependency) add it to the owned elements for the
 362  
      * model.<p>
 363  
      *
 364  
      * <em>Needs more work</em>. In adding the edge to the owned
 365  
      * elements of the model namespace, we are implicitly making it
 366  
      * public visibility (it could be private to this namespace).<p>
 367  
      *
 368  
      * @param edge  The edge to be added to the graph.
 369  
      */
 370  
     @Override
 371  
     public void addEdge(Object edge) {
 372  0
         if (edge == null) {
 373  0
             throw new IllegalArgumentException("Cannot add a null edge");
 374  
         }
 375  
 
 376  0
         if (getDestPort(edge) == null || getSourcePort(edge) == null) {
 377  0
             throw new IllegalArgumentException(
 378  
                     "The source and dest port should be provided on an edge");
 379  
         }
 380  
 
 381  0
         if (LOG.isInfoEnabled()) {
 382  0
             LOG.info("Adding an edge of type "
 383  
                    + edge.getClass().getName()
 384  
                    + " to use case diagram.");
 385  
         }
 386  
 
 387  0
         if (!canAddEdge(edge)) {
 388  0
             LOG.info("Attempt to add edge rejected");
 389  0
             return;
 390  
         }
 391  
 
 392  
         // Add the element and place it in the namespace of the model
 393  0
         getEdges().add(edge);
 394  
 
 395  
         // TODO: assumes public
 396  0
         if (Model.getFacade().isAUMLElement(edge)
 397  
                 && Model.getFacade().getNamespace(edge) == null) {
 398  0
             Model.getCoreHelper().addOwnedElement(getHomeModel(), edge);
 399  
         }
 400  
 
 401  
         // Tell GEF
 402  
 
 403  0
         fireEdgeAdded(edge);
 404  0
     }
 405  
 
 406  
     /**
 407  
      * Add the various types of edge that may be connected with the
 408  
      * given node.<p>
 409  
      *
 410  
      * For use cases we may find extend and include relationships. For
 411  
      * classifiers (effectively actors and use cases) we may find
 412  
      * associations. For generalizable elements (effectively actors
 413  
      * and use cases again) we may find generalizations and
 414  
      * specializations. For ModelElements (effectively actors and use
 415  
      * cases again) we may find dependencies.<p>
 416  
      *
 417  
      * @param node  The node whose edges are to be added.
 418  
      */
 419  
     @Override
 420  
     public void addNodeRelatedEdges(Object node) {
 421  0
         super.addNodeRelatedEdges(node);
 422  
 
 423  0
         if (Model.getFacade().isAUseCase(node)) {
 424  0
             List relations = new ArrayList();
 425  
 
 426  0
             relations.addAll(Model.getFacade().getIncludes(node));
 427  0
             relations.addAll(Model.getFacade().getIncluders(node));
 428  0
             relations.addAll(Model.getFacade().getExtends(node));
 429  0
             relations.addAll(Model.getFacade().getExtenders(node));
 430  
 
 431  0
             for (Object relation : relations) {
 432  0
                 if (canAddEdge(relation)) {
 433  0
                     addEdge(relation);
 434  
                 }
 435  
             }
 436  
         }
 437  
 
 438  0
         if (Model.getFacade().isAClassifier(node)) {
 439  0
             Collection ends = Model.getFacade().getAssociationEnds(node);
 440  0
             for (Object ae : ends) {
 441  0
                 if (canAddEdge(Model.getFacade().getAssociation(ae))) {
 442  0
                     addEdge(Model.getFacade().getAssociation(ae));
 443  
                 }
 444  
             }
 445  
         }
 446  
 
 447  0
         if (Model.getFacade().isAGeneralizableElement(node)) {
 448  0
             Collection gn = Model.getFacade().getGeneralizations(node);
 449  0
             for (Object g : gn) {
 450  0
                 if (canAddEdge(g)) {
 451  0
                     addEdge(g);
 452  
                 }
 453  
             }
 454  0
             Collection sp = Model.getFacade().getSpecializations(node);
 455  0
             for (Object s : sp) {
 456  0
                 if (canAddEdge(s)) {
 457  0
                     addEdge(s);
 458  
                 }
 459  
             }
 460  
         }
 461  
 
 462  0
         if (Model.getFacade().isAUMLElement(node)) {
 463  0
             Collection dependencies =
 464  
                 new ArrayList(Model.getFacade().getClientDependencies(node));
 465  
 
 466  0
             dependencies.addAll(Model.getFacade().getSupplierDependencies(node));
 467  
 
 468  0
             for (Object dependency : dependencies) {
 469  0
                 if (canAddEdge(dependency)) {
 470  0
                     addEdge(dependency);
 471  
                 }
 472  
             }
 473  
         }
 474  0
     }
 475  
 
 476  
 
 477  
 
 478  
     /**
 479  
      * Determine if the two given ports can be connected by a kind of
 480  
      * edge to be determined by the ports.<p>
 481  
      *
 482  
      * <em>Note</em>. There appears to be a problem with the
 483  
      * implementation, since it suggests actors cannot connect. In
 484  
      * fact generalization is permitted, and this works, suggesting
 485  
      * this method is not actually invoked in the current
 486  
      * implementation of ArgoUML.<p>
 487  
      *
 488  
      * @param fromP  The source port of the connection
 489  
      *
 490  
      * @param toP    The destination port of the connection.
 491  
      *
 492  
      * @return       <code>true</code> if the two given ports can be connected
 493  
      *               by a kind of edge to be determined by the
 494  
      *               ports. <code>false</code> otherwise.
 495  
      */
 496  
     @Override
 497  
     public boolean canConnect(Object fromP, Object toP) {
 498  
 
 499  
         // Suggest that actors may not connect (see JavaDoc comment about
 500  
         // this).
 501  
 
 502  0
         if (Model.getFacade().isAActor(fromP)
 503  
                 && Model.getFacade().isAActor(toP)) {
 504  0
             return false;
 505  
         }
 506  
 
 507  
         // Everything else is OK
 508  
 
 509  0
         return true;
 510  
     }
 511  
 
 512  
 
 513  
     ///////////////////////////////////////////////////////////////////////////
 514  
     //
 515  
     // Methods that implement the VetoableChangeListener interface
 516  
     //
 517  
     ///////////////////////////////////////////////////////////////////////////
 518  
 
 519  
     /**
 520  
      * Called when a property of interest has been changed - in this
 521  
      * case the owned elements of the model. Provided to implement the
 522  
      * {@link VetoableChangeListener} interface.<p>
 523  
      *
 524  
      * We could throw a PropertyVetoException if we wished to allow
 525  
      * the change to be rolled back, but we don't.<p>
 526  
      *
 527  
      * @param pce  The event that triggered us, and from which we can extract
 528  
      *             the name of the property that triggered us.
 529  
      */
 530  
     public void vetoableChange(PropertyChangeEvent pce) {
 531  
 
 532  
         // Only interested in the "ownedElement" property. Either something has
 533  
         // been added to the namespace for this model, or removed. In the
 534  
         // latter case the "something" will be in the old value of the
 535  
         // property, which is the collection of owned elements, and the new value
 536  
         // will be the element import describing the model element and the
 537  
         // model from which it was removed
 538  
 
 539  0
         if ("ownedElement".equals(pce.getPropertyName())) {
 540  0
             List oldOwned = (List) pce.getOldValue();
 541  
 
 542  0
             Object eo = /*(MElementImport)*/ pce.getNewValue();
 543  0
             Object  me = Model.getFacade().getModelElement(eo);
 544  
 
 545  
             // If the element import is in the old owned, it means it must have
 546  
             // been removed. Make sure the associated model element is removed.
 547  
 
 548  0
             if (oldOwned.contains(eo)) {
 549  
 
 550  0
                 LOG.debug("model removed " + me);
 551  
 
 552  
                 // Remove a node
 553  
 
 554  0
                 if ((Model.getFacade().isAActor(me))
 555  
                     || (Model.getFacade().isAUseCase(me))) {
 556  
 
 557  0
                     removeNode(me);
 558  0
                 } else if ((Model.getFacade().isAAssociation(me))
 559  
                          || (Model.getFacade().isAGeneralization(me))
 560  
                          || (Model.getFacade().isAExtend(me))
 561  
                          || (Model.getFacade().isAInclude(me))
 562  
                          || (Model.getFacade().isADependency(me))) {
 563  
                     // Remove an edge
 564  0
                     removeEdge(me);
 565  
                 }
 566  
             } else {
 567  
                 // Something was added - nothing for us to worry about
 568  0
                 LOG.debug("model added " + me);
 569  
             }
 570  
         }
 571  0
     }
 572  
 
 573  
     /**
 574  
      * The UID.
 575  
      */
 576  
     static final long serialVersionUID = -8516841965639203796L;
 577  
 
 578  
 }