Coverage Report - org.argouml.uml.diagram.UMLMutableGraphSupport
 
Classes in this File Line Coverage Branch Coverage Complexity
UMLMutableGraphSupport
9%
13/132
2%
2/84
3.296
 
 1  
 /* $Id: UMLMutableGraphSupport.java 17850 2010-01-12 19:53:35Z 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-2007 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;
 40  
 
 41  
 import java.util.ArrayList;
 42  
 import java.util.Collection;
 43  
 import java.util.Dictionary;
 44  
 import java.util.Iterator;
 45  
 import java.util.List;
 46  
 import java.util.Map;
 47  
 
 48  
 import org.apache.log4j.Logger;
 49  
 import org.argouml.kernel.Project;
 50  
 import org.argouml.model.DiDiagram;
 51  
 import org.argouml.model.Model;
 52  
 import org.argouml.model.UmlException;
 53  
 import org.argouml.uml.CommentEdge;
 54  
 import org.tigris.gef.base.Editor;
 55  
 import org.tigris.gef.base.Globals;
 56  
 import org.tigris.gef.base.Mode;
 57  
 import org.tigris.gef.base.ModeManager;
 58  
 import org.tigris.gef.graph.MutableGraphSupport;
 59  
 
 60  
 
 61  
 /**
 62  
  * UMLMutableGraphSupport is a helper class which extends
 63  
  * MutableGraphSupport to provide additional helper and common methods
 64  
  * for UML Diagrams.
 65  
  *
 66  
  * @author mkl@tigris.org
 67  
  * @since November 14, 2002, 10:20 PM
 68  
  */
 69  
 public abstract class UMLMutableGraphSupport extends MutableGraphSupport {
 70  
 
 71  
     /**
 72  
      * Logger.
 73  
      */
 74  900
     private static final Logger LOG =
 75  
         Logger.getLogger(UMLMutableGraphSupport.class);
 76  
 
 77  
     private DiDiagram diDiagram;
 78  
 
 79  
     /**
 80  
      * Contains all the nodes in the graphmodel/diagram.
 81  
      */
 82  2116
     private List nodes = new ArrayList();
 83  
 
 84  
     /**
 85  
      * Contains all the edges in the graphmodel/diagram.
 86  
      */
 87  2116
     private List edges = new ArrayList();
 88  
 
 89  
     /**
 90  
      * The owning namespace or "home model" of this diagram, not all
 91  
      * ModelElements in this graph are in the home model, but if they are added
 92  
      * and don't already have a model, they are placed in the "home model".
 93  
      * Also, elements from other models will have their FigNodes add a line to
 94  
      * say what their model is.
 95  
      */
 96  
     private Object homeModel;
 97  
 
 98  
     /**
 99  
      * The project this graph model is in.
 100  
      */
 101  
     private Project project;
 102  
     
 103  
     /**
 104  
      * Constructor.
 105  
      *
 106  
      * @see org.tigris.gef.graph.MutableGraphSupport
 107  
      */
 108  
     public UMLMutableGraphSupport() {
 109  2116
         super();
 110  2116
     }
 111  
 
 112  
     /**
 113  
      * Get all the nodes from the graphmodel/diagram.
 114  
      *
 115  
      * @see org.tigris.gef.graph.MutableGraphSupport#getNodes()
 116  
      * @return List of nodes in the graphmodel/diagram
 117  
      */
 118  
     public List getNodes() {
 119  148
         return nodes;
 120  
     }
 121  
 
 122  
     /**
 123  
      * Get all the edges from the graphmodel/diagram.
 124  
      *
 125  
      * @return List of edges in the graphmodel/diagram
 126  
      */
 127  
     public List getEdges() {
 128  148
         return edges;
 129  
     }
 130  
     
 131  
     /*
 132  
      * @see org.tigris.gef.graph.MutableGraphModel#containsNode(java.lang.Object)
 133  
      */
 134  
     public boolean containsNode(Object node) {
 135  0
         return nodes.contains(node);
 136  
     }
 137  
 
 138  
     /**
 139  
      * @param edge the candidate edge
 140  
      * @return true if it is contained
 141  
      */
 142  
     public boolean constainsEdge(Object edge) {
 143  0
         return edges.contains(edge);
 144  
     }
 145  
 
 146  
     /**
 147  
      * Remove a node from the diagram and notify GEF.
 148  
      *
 149  
      * @param node node to remove
 150  
      */
 151  
     @Override
 152  
     public void removeNode(Object node) {
 153  0
         if (!containsNode(node)) {
 154  0
             return;
 155  
         }
 156  0
         nodes.remove(node);
 157  0
         fireNodeRemoved(node);
 158  0
     }
 159  
 
 160  
     /**
 161  
      * Remove an edge from the graphmodel and notify GEF.
 162  
      *
 163  
      * @param edge edge to remove
 164  
      */
 165  
     @Override
 166  
     public void removeEdge(Object edge) {
 167  0
         if (!containsEdge(edge)) {
 168  0
             return;
 169  
         }
 170  0
         edges.remove(edge);
 171  0
         fireEdgeRemoved(edge);
 172  0
     }
 173  
 
 174  
     /**
 175  
      * Assume that anything can be connected to anything unless overridden
 176  
      * in a subclass.
 177  
      *
 178  
      * {@inheritDoc}
 179  
      */
 180  
     public boolean canConnect(Object fromP, Object toP) {
 181  0
         return true;
 182  
     }
 183  
 
 184  
 
 185  
     /**
 186  
      * The connect method without specifying a connection
 187  
      * type is unavailable in the ArgoUML implmentation.
 188  
      *
 189  
      * {@inheritDoc}
 190  
      */
 191  
     public Object connect(Object fromPort, Object toPort) {
 192  0
         throw new UnsupportedOperationException(
 193  
                 "The connect method is not supported");
 194  
     }
 195  
 
 196  
     /**
 197  
      * Get the namespace, also known as homemodel, which owns the diagram.
 198  
      *
 199  
      * @return the homemodel
 200  
      */
 201  
     public Object getHomeModel() {
 202  0
         return homeModel;
 203  
     }
 204  
 
 205  
     /**
 206  
      * Set the namespace or homemodel of the diagram.  This will become the
 207  
      * default namespace for any model elements which are created on 
 208  
      * the diagram.
 209  
      *
 210  
      * @param ns the namespace
 211  
      */
 212  
     public void setHomeModel(Object ns) {
 213  2097
         if (!Model.getFacade().isANamespace(ns)) {
 214  0
             throw new IllegalArgumentException();
 215  
         }
 216  2097
         homeModel = ns;
 217  2097
     }
 218  
 
 219  
     /**
 220  
      * The connect method specifying a connection
 221  
      * type by class is unavailable in the ArgoUML implementation.
 222  
      * TODO: This should be unsupported. Use the 3 Object version
 223  
      *
 224  
      * {@inheritDoc}
 225  
      */
 226  
     public Object connect(Object fromPort, Object toPort, Class edgeClass) {
 227  0
         return connect(fromPort, toPort, (Object) edgeClass);
 228  
     }
 229  
 
 230  
     /**
 231  
      * Construct and add a new edge of the given kind and connect
 232  
      * the given ports.
 233  
      *
 234  
      * @param fromPort   The originating port to connect
 235  
      *
 236  
      * @param toPort     The destination port to connect
 237  
      *
 238  
      * @param edgeType  The type of edge to create. This is one of the types
 239  
      *                  returned by the methods of
 240  
      *                  <code>org.argouml.model.MetaTypes</code>
 241  
      *
 242  
      * @return           The type of edge created (the same as
 243  
      *                   <code>edgeClass</code> if we succeeded,
 244  
      *                   <code>null</code> otherwise)
 245  
      */
 246  
     public Object connect(Object fromPort, Object toPort, Object edgeType) {
 247  
         // If this was an association then there will be relevant
 248  
         // information to fetch out of the mode arguments.  If it
 249  
         // not an association then these will be passed forward
 250  
         // harmlessly as null.
 251  0
         Editor curEditor = Globals.curEditor();
 252  0
         ModeManager modeManager = curEditor.getModeManager();
 253  0
         Mode mode = modeManager.top();
 254  0
         Dictionary args = mode.getArgs();
 255  0
         Object style = args.get("aggregation"); //MAggregationKind
 256  0
         Boolean unidirectional = (Boolean) args.get("unidirectional");
 257  0
         Object model = getProject().getModel();
 258  
 
 259  
         // Create the UML connection of the given type between the
 260  
         // given model elements.
 261  
         // default aggregation (none)
 262  0
         Object connection =
 263  
             buildConnection(
 264  
                 edgeType, fromPort, style, toPort,
 265  
                 null, unidirectional,
 266  
                 model);
 267  
 
 268  0
         if (connection == null) {
 269  0
             if (LOG.isDebugEnabled()) {
 270  0
                 LOG.debug("Cannot make a " + edgeType
 271  
                         + " between a " + fromPort.getClass().getName()
 272  
                         + " and a " + toPort.getClass().getName());
 273  
             }
 274  0
             return null;
 275  
         }
 276  
 
 277  0
         addEdge(connection);
 278  0
         if (LOG.isDebugEnabled()) {
 279  0
             LOG.debug("Connection type" + edgeType
 280  
                       + " made between a " + fromPort.getClass().getName()
 281  
                       + " and a " + toPort.getClass().getName());
 282  
         }
 283  0
         return connection;
 284  
     }
 285  
 
 286  
     /**
 287  
      * Construct and add a new edge of the given kind and connect
 288  
      * the given ports.
 289  
      *
 290  
      * @param fromPort   The originating port to connect
 291  
      *
 292  
      * @param toPort     The destination port to connect
 293  
      *
 294  
      * @param edgeType   An indicator of the edge type to create.
 295  
      *
 296  
      * @param styleAttributes key/value pairs from which to style the edge.
 297  
      *
 298  
      * @return           The type of edge created (the same as
 299  
      *                   <code>edgeClass</code> if we succeeded,
 300  
      *                   <code>null</code> otherwise)
 301  
      */
 302  
     public Object connect(Object fromPort, Object toPort, Object edgeType,
 303  
             Map styleAttributes) {
 304  0
         return null;
 305  
     }
 306  
 
 307  
 
 308  
     /*
 309  
      * @see org.tigris.gef.graph.MutableGraphModel#canAddNode(java.lang.Object)
 310  
      */
 311  
     public boolean canAddNode(Object node) {
 312  0
         if (node == null) {
 313  0
             return false;
 314  
         }
 315  0
         if (Model.getFacade().isAComment(node)) {
 316  0
             return true;
 317  
         }
 318  0
         return false;
 319  
     }
 320  
 
 321  
     /**
 322  
      * Return the source end of an edge.
 323  
      *
 324  
      * @param edge  The edge for which we want the source port.
 325  
      *
 326  
      * @return      The source port for the edge, or <code>null</code> if the
 327  
      *              edge given is of the wrong type or has no source defined.
 328  
      */
 329  
     public Object getSourcePort(Object edge) {
 330  
 
 331  0
         if (edge instanceof CommentEdge) {
 332  0
             return ((CommentEdge) edge).getSource();
 333  0
         } else if (Model.getFacade().isARelationship(edge)
 334  
             || Model.getFacade().isATransition(edge)
 335  
             || Model.getFacade().isAAssociationEnd(edge))  {
 336  0
             return Model.getUmlHelper().getSource(edge);
 337  0
         } else if (Model.getFacade().isALink(edge)) {
 338  0
             return Model.getCommonBehaviorHelper().getSource(edge);
 339  
         }
 340  
 
 341  
         // Don't know what to do otherwise
 342  
 
 343  0
         LOG.error(this.getClass().toString() + ": getSourcePort("
 344  
                 + edge.toString() + ") - can't handle");
 345  
 
 346  0
         return null;
 347  
     }
 348  
 
 349  
 
 350  
     /**
 351  
      * Return the destination end of an edge.
 352  
      *
 353  
      * @param edge  The edge for which we want the destination port.
 354  
      *
 355  
      * @return      The destination port for the edge, or <code>null</code> if
 356  
      *              the edge given is otf the wrong type or has no destination
 357  
      *              defined.
 358  
      */
 359  
     public Object getDestPort(Object edge) {
 360  0
         if (edge instanceof CommentEdge) {
 361  0
             return ((CommentEdge) edge).getDestination();
 362  0
         } else if (Model.getFacade().isAAssociation(edge)) {
 363  0
             List conns = new ArrayList(Model.getFacade().getConnections(edge));
 364  0
             return conns.get(1);
 365  0
         } else if (Model.getFacade().isARelationship(edge)
 366  
                 || Model.getFacade().isATransition(edge)
 367  
                 || Model.getFacade().isAAssociationEnd(edge)) {
 368  0
             return Model.getUmlHelper().getDestination(edge);
 369  0
         } else if (Model.getFacade().isALink(edge)) {
 370  0
             return Model.getCommonBehaviorHelper().getDestination(edge);
 371  
         }
 372  
 
 373  
         // Don't know what to do otherwise
 374  
 
 375  0
         LOG.error(this.getClass().toString() + ": getDestPort("
 376  
                 + edge.toString() + ") - can't handle");
 377  
 
 378  0
         return null;
 379  
     }
 380  
 
 381  
 
 382  
     /*
 383  
      * @see org.tigris.gef.graph.MutableGraphModel#canAddEdge(java.lang.Object)
 384  
      */
 385  
     public boolean canAddEdge(Object edge) {
 386  0
         if (edge instanceof CommentEdge) {
 387  0
             CommentEdge ce = (CommentEdge) edge;
 388  0
             return isConnectionValid(CommentEdge.class,
 389  
                     ce.getSource(),
 390  
                     ce.getDestination());
 391  0
         } else if (edge != null 
 392  
                 && Model.getUmlFactory().isConnectionType(edge)) {
 393  0
             return isConnectionValid(edge.getClass(),
 394  
                 Model.getUmlHelper().getSource(edge),
 395  
                 Model.getUmlHelper().getDestination(edge));
 396  
         }
 397  0
         return false;
 398  
     }
 399  
 
 400  
     /*
 401  
      * @see org.tigris.gef.graph.MutableGraphModel#addNodeRelatedEdges(java.lang.Object)
 402  
      */
 403  
     public void addNodeRelatedEdges(Object node) {
 404  0
         if (Model.getFacade().isAModelElement(node)) {
 405  0
             List specs =
 406  
                 new ArrayList(Model.getFacade().getClientDependencies(node));
 407  0
             specs.addAll(Model.getFacade().getSupplierDependencies(node));
 408  0
             Iterator iter = specs.iterator();
 409  0
             while (iter.hasNext()) {
 410  0
                 Object dependency = iter.next();
 411  0
                 if (canAddEdge(dependency)) {
 412  0
                     addEdge(dependency);
 413  
                     // return;
 414  
                 }
 415  0
             }
 416  
         }
 417  
         
 418  
         // Commentlinks for comments. Iterate over all the comment links
 419  
         // to find the comment and annotated elements.
 420  
 
 421  0
         Collection cmnt = new ArrayList();
 422  0
         if (Model.getFacade().isAComment(node)) {
 423  0
             cmnt.addAll(Model.getFacade().getAnnotatedElements(node));
 424  
         }
 425  
         // TODO: Comments are on Element in UML 2.x
 426  0
         if (Model.getFacade().isAModelElement(node)) {
 427  0
             cmnt.addAll(Model.getFacade().getComments(node));
 428  
         }
 429  0
         Iterator iter = cmnt.iterator();
 430  0
         while (iter.hasNext()) {
 431  0
             Object ae = iter.next();
 432  0
             CommentEdge ce = new CommentEdge(node, ae);
 433  0
             if (canAddEdge(ce)) {
 434  0
                 addEdge(ce);
 435  
             }
 436  0
         }
 437  0
     }
 438  
 
 439  
     /**
 440  
      * Create an edge of the given type and connect it to the
 441  
      * given nodes.
 442  
      *
 443  
      * @param edgeType       the UML object type of the connection
 444  
      * @param fromElement    the UML object for the "from" element
 445  
      * @param fromStyle      the aggregationkind for the connection
 446  
      *                       in case of an association
 447  
      * @param toElement      the UML object for the "to" element
 448  
      * @param toStyle        the aggregationkind for the connection
 449  
      *                       in case of an association
 450  
      * @param unidirectional for association and associationrole
 451  
      * @param namespace      the namespace to use if it can't be determined
 452  
      * @return               the newly build connection (UML object)
 453  
      */
 454  
     protected Object buildConnection(
 455  
             Object edgeType,
 456  
             Object fromElement,
 457  
             Object fromStyle,
 458  
             Object toElement,
 459  
             Object toStyle,
 460  
             Object unidirectional,
 461  
             Object namespace) {
 462  
 
 463  0
         Object connection = null;
 464  0
         if (edgeType == CommentEdge.class) {
 465  0
             connection =
 466  
                 buildCommentConnection(fromElement, toElement);
 467  
         } else {
 468  
             try {
 469  0
                 connection =
 470  
                     Model.getUmlFactory().buildConnection(
 471  
                             edgeType,
 472  
                             fromElement,
 473  
                             fromStyle,
 474  
                             toElement,
 475  
                             toStyle,
 476  
                             unidirectional,
 477  
                             namespace);
 478  0
                 LOG.info("Created " + connection + " between " 
 479  
                         + fromElement + " and " + toElement);
 480  0
             } catch (UmlException ex) {
 481  
                 // fail silently as we expect users to accidentally drop
 482  
                 // on to wrong component
 483  0
             } catch (IllegalArgumentException iae) {
 484  
                 // idem, e.g. for a generalization with leaf/root object
 485  
                 // TODO: but showing the message in the statusbar would help
 486  
                 // TODO: IllegalArgumentException should not be used for
 487  
                 // events we expect to happen. We need a different way of
 488  
                 // catching well-formedness rules.
 489  0
                 LOG.warn("IllegalArgumentException caught", iae);
 490  0
             }
 491  
         }
 492  0
         return connection;
 493  
     }
 494  
 
 495  
     /**
 496  
      * Builds the model behind a connection between a comment and
 497  
      * the annotated modelelement.
 498  
      *
 499  
      * @param from The comment or annotated element.
 500  
      * @param to The comment or annotated element.
 501  
      * @return A commentEdge representing the model behind the connection
 502  
      *         between a comment and an annotated modelelement.
 503  
      */
 504  
     public CommentEdge buildCommentConnection(Object from, Object to) {
 505  0
         if (from == null || to == null) {
 506  0
             throw new IllegalArgumentException("Either fromNode == null "
 507  
                                        + "or toNode == null");
 508  
         }
 509  0
         Object comment = null;
 510  0
         Object annotatedElement = null;
 511  0
         if (Model.getFacade().isAComment(from)) {
 512  0
             comment = from;
 513  0
             annotatedElement = to;
 514  
         } else {
 515  0
             if (Model.getFacade().isAComment(to)) {
 516  0
                 comment = to;
 517  0
                 annotatedElement = from;
 518  
             } else {
 519  0
                 return null;
 520  
             }
 521  
         }
 522  
 
 523  0
         CommentEdge connection = new CommentEdge(from, to);
 524  0
         Model.getCoreHelper().addAnnotatedElement(comment, annotatedElement);
 525  0
         return connection;
 526  
 
 527  
     }
 528  
 
 529  
     /**
 530  
      * Checks if some type of edge is valid to connect two
 531  
      * types of node.
 532  
      *
 533  
      * @param edgeType  the UML object type of the connection
 534  
      * @param fromElement     the UML object type of the "from"
 535  
      * @param toElement       the UML object type of the "to"
 536  
      * @return true if valid
 537  
      */
 538  
     protected boolean isConnectionValid(
 539  
             Object edgeType,
 540  
             Object fromElement,
 541  
             Object toElement) {
 542  
 
 543  0
         if (!nodes.contains(fromElement) || !nodes.contains(toElement)) {
 544  
             // The connection is not valid unless both nodes are
 545  
             // in this graph model.
 546  0
             return false;
 547  
         }
 548  
 
 549  0
         if (edgeType.equals(CommentEdge.class)) {
 550  0
             return ((Model.getFacade().isAComment(fromElement)
 551  
                    && Model.getFacade().isAModelElement(toElement))
 552  
                  || (Model.getFacade().isAComment(toElement)
 553  
                    && Model.getFacade().isAModelElement(fromElement)));
 554  
         }
 555  0
         return Model.getUmlFactory().isConnectionValid(
 556  
                 edgeType,
 557  
                 fromElement,
 558  
                 toElement,
 559  
                 true);
 560  
     }
 561  
 
 562  
     /**
 563  
      * Package scope. Only the factory is supposed to set this.
 564  
      * @param dd
 565  
      */
 566  
     void setDiDiagram(DiDiagram dd) {
 567  0
         diDiagram = dd;
 568  0
     }
 569  
 
 570  
     /**
 571  
      * Get the object that represents this diagram
 572  
      * in the DiagramInterchangeModel.
 573  
      *
 574  
      * @return the Diagram Interchange Diagram.
 575  
      */
 576  
     public DiDiagram getDiDiagram() {
 577  0
         return diDiagram;
 578  
     }
 579  
 
 580  
     /**
 581  
      * Return true if the current targets may be removed from the diagram.
 582  
      *
 583  
      * @param figs a collection with the selected figs
 584  
      * @return true if the targets may be removed
 585  
      */
 586  
     public boolean isRemoveFromDiagramAllowed(Collection figs) {
 587  836
         return !figs.isEmpty();
 588  
     }
 589  
     
 590  
     /**
 591  
      * Set the project that the graph model is inside.
 592  
      * @param p the project
 593  
      */
 594  
     public void setProject(Project p) {
 595  2116
         project = p;
 596  2116
     }
 597  
     
 598  
     /**
 599  
      * Get the project that the graph model is inside.
 600  
      * @return the project
 601  
      */
 602  
     public Project getProject() {
 603  0
         return project;
 604  
     }
 605  
 }