Coverage Report - org.argouml.uml.diagram.ui.FigStereotypesGroup
 
Classes in this File Line Coverage Branch Coverage Complexity
FigStereotypesGroup
0%
0/165
0%
0/84
3.421
 
 1  
 /* $Id: FigStereotypesGroup.java 17741 2010-01-10 03:50:08Z bobtarling $
 2  
  *******************************************************************************
 3  
  * Copyright (c) 2009-2010 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  
  *    Bob Tarling
 11  
  *******************************************************************************
 12  
  *
 13  
  * Some portions of this file was previously release using the BSD License:
 14  
  */
 15  
 // $Id: FigStereotypesGroup.java 17741 2010-01-10 03:50:08Z bobtarling $
 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.Dimension;
 42  
 import java.awt.Image;
 43  
 import java.awt.Rectangle;
 44  
 import java.beans.PropertyChangeEvent;
 45  
 import java.util.ArrayList;
 46  
 import java.util.Collection;
 47  
 import java.util.List;
 48  
 
 49  
 import org.apache.log4j.Logger;
 50  
 import org.argouml.kernel.Project;
 51  
 import org.argouml.model.AddAssociationEvent;
 52  
 import org.argouml.model.Model;
 53  
 import org.argouml.model.RemoveAssociationEvent;
 54  
 import org.argouml.uml.diagram.DiagramSettings;
 55  
 import org.tigris.gef.presentation.Fig;
 56  
 import org.tigris.gef.presentation.FigRect;
 57  
 import org.tigris.gef.presentation.FigText;
 58  
 
 59  
 /**
 60  
  * A Fig designed to be the child of some FigNode or FigEdge to display the
 61  
  * stereotypes of the model element represented by the parent Fig.
 62  
  * Currently, multiple stereotypes are shown stacked one on top of the other,
 63  
  * each enclosed by guillemets.<p>
 64  
  * 
 65  
  * The minimum width of this fig is the largest minimum width of its child
 66  
  * figs. The minimum height of this fig is the total minimum height of its child
 67  
  * figs.<p>
 68  
  * 
 69  
  * The owner of this Fig is the UML element that is extended 
 70  
  * with the stereotypes. We are listening to changes to the model: 
 71  
  * addition and removal of stereotypes. <p>
 72  
  * 
 73  
  * This fig supports showing one keyword 
 74  
  * as the first "stereotype" in the list. <p>
 75  
  * 
 76  
  * There is no way to remove a keyword fig, once added. <p>
 77  
  * 
 78  
  * TODO: Allow for UML2 style display where all stereotypes are displayed in
 79  
  * the same guillemet pair and are delimited by commas. The style should be
 80  
  * changeable by calling getOrientation(Orientation). The swidget Orientation
 81  
  * class can be used for this.
 82  
  * @author Bob Tarling
 83  
  */
 84  
 public class FigStereotypesGroup extends ArgoFigGroup {
 85  
 
 86  
     private Fig bigPort;
 87  
     
 88  
     /**
 89  
      * Logger.
 90  
      */
 91  0
     private static final Logger LOG =
 92  
         Logger.getLogger(FigStereotypesGroup.class);
 93  
 
 94  
     /**
 95  
      * One UML keyword is allowed. These are not strictly stereotypes but are
 96  
      * displayed as such. e.g. &lt;&lt;interface&gt;&gt;
 97  
      */
 98  
     private String keyword;
 99  
 
 100  0
     private int stereotypeCount = 0;
 101  
     
 102  0
     private boolean hidingStereotypesWithIcon = false;
 103  
     
 104  
     private void constructFigs(int x, int y, int w, int h) {
 105  0
         bigPort = new FigRect(x, y, w, h, LINE_COLOR, FILL_COLOR);
 106  0
         addFig(bigPort);
 107  0
         bigPort.setFilled(false);
 108  
 
 109  
         /* Do not show border line, make transparent: */
 110  0
         setLineWidth(0);
 111  0
         setFilled(false);
 112  0
     }
 113  
 
 114  
     /**
 115  
      * The constructor.
 116  
      * 
 117  
      * @param owner owning UML element
 118  
      * @param bounds position and size
 119  
      * @param settings render settings
 120  
      */
 121  
     public FigStereotypesGroup(Object owner, Rectangle bounds, 
 122  
             DiagramSettings settings) {
 123  0
         super(owner, settings);
 124  0
         constructFigs(bounds.x, bounds.y, bounds.width, bounds.height);
 125  0
         Model.getPump().addModelEventListener(this, owner, "stereotype");
 126  0
         populate();
 127  0
     }
 128  
 
 129  
     
 130  
     /*
 131  
      * @see org.tigris.gef.presentation.Fig#removeFromDiagram()
 132  
      */
 133  
     @Override
 134  
     public void removeFromDiagram() {
 135  
         /* Remove all items in the group, 
 136  
          * otherwise the model event listeners remain: 
 137  
          * TODO: Why does a FigGroup not do this? */
 138  0
         for (Object f : getFigs()) {
 139  0
             ((Fig) f).removeFromDiagram();
 140  
         }
 141  0
         super.removeFromDiagram();
 142  0
         Model.getPump()
 143  
                 .removeModelEventListener(this, getOwner(), "stereotype");
 144  0
     }
 145  
 
 146  
     /**
 147  
      * @return the bigport
 148  
      * @deprecated for 0.27.2. For backward compatibility only. The visibility
 149  
      *             of this method will be changed to private in the next release
 150  
      *             when FigStereotypesCompartment is removed.
 151  
      */
 152  
     @Deprecated
 153  
     protected Fig getBigPort() {
 154  0
         return bigPort;
 155  
     }
 156  
     
 157  
     @Override
 158  
     public void propertyChange(PropertyChangeEvent event) {
 159  0
         if (event instanceof AddAssociationEvent) {
 160  0
             AddAssociationEvent aae = (AddAssociationEvent) event;
 161  0
             if (event.getPropertyName().equals("stereotype")) {
 162  0
                 Object stereotype = aae.getChangedValue();
 163  0
                 if (findFig(stereotype) == null) {
 164  0
                     FigText stereotypeTextFig =
 165  
                         new FigStereotype(stereotype, 
 166  
                                 getBoundsForNextStereotype(),
 167  
                                 getSettings());
 168  0
                     stereotypeCount++;
 169  0
                     addFig(stereotypeTextFig);
 170  0
                     reorderStereotypeFigs();
 171  0
                     damage();
 172  
                 }
 173  0
             } else {
 174  0
                 LOG.warn("Unexpected property " + event.getPropertyName());
 175  
             }
 176  
         }
 177  0
         if (event instanceof RemoveAssociationEvent) {
 178  0
             if (event.getPropertyName().equals("stereotype")) {
 179  0
                 RemoveAssociationEvent rae = (RemoveAssociationEvent) event;
 180  0
                 Object stereotype = rae.getChangedValue();
 181  0
                 Fig f = findFig(stereotype);
 182  0
                 if (f != null) {
 183  0
                     removeFig(f);
 184  0
                     f.removeFromDiagram(); // or vice versa?
 185  0
                     --stereotypeCount;
 186  
                 }
 187  0
             } else {
 188  0
                 LOG.warn("Unexpected property " + event.getPropertyName());
 189  
             }
 190  
         }
 191  0
     }
 192  
 
 193  
     /**
 194  
      * Keep the Figs which are likely invisible at the end of the list.
 195  
      */
 196  
     private void reorderStereotypeFigs() {
 197  0
         List<Fig> allFigs = getFigs();
 198  0
         List<Fig> figsWithIcon = new ArrayList<Fig>();
 199  0
         List<Fig> figsWithOutIcon = new ArrayList<Fig>();
 200  0
         List<Fig> others = new ArrayList<Fig>();
 201  
 
 202  
         // TODO: This doesn't do anything special with keywords.
 203  
         // They should probably go first.
 204  0
         for (Fig f : allFigs) {
 205  0
             if (f instanceof FigStereotype) {
 206  0
                 FigStereotype s = (FigStereotype) f;
 207  0
                 if (getIconForStereotype(s) != null) {
 208  0
                     figsWithIcon.add(s);
 209  
                 } else {
 210  0
                     figsWithOutIcon.add(s);
 211  
                 }
 212  0
             } else {
 213  0
                 others.add(f);
 214  
             }
 215  
         }
 216  
 
 217  0
         List<Fig> n = new ArrayList<Fig>();
 218  
         
 219  0
         n.addAll(others);
 220  0
         n.addAll(figsWithOutIcon);
 221  0
         n.addAll(figsWithIcon);        
 222  
         
 223  0
         setFigs(n);
 224  0
     }
 225  
 
 226  
     private FigStereotype findFig(Object stereotype) {
 227  0
         for (Object f : getFigs()) {
 228  0
             if (f instanceof FigStereotype) {
 229  0
                 FigStereotype fs = (FigStereotype) f;
 230  0
                 if (fs.getOwner() == stereotype) {
 231  0
                     return fs;
 232  
                 }
 233  0
             }
 234  
         }
 235  0
         return null;
 236  
     }
 237  
     
 238  
     /**
 239  
      * Get all the child figs that represent the individual stereotypes
 240  
      * @return a List of the stereotype Figs
 241  
      */
 242  
     List<FigStereotype> getStereotypeFigs() {
 243  0
         final List<FigStereotype> stereotypeFigs =
 244  
             new ArrayList<FigStereotype>();
 245  0
         for (Object f : getFigs()) {
 246  0
             if (f instanceof FigStereotype) {
 247  0
                 FigStereotype fs = (FigStereotype) f;
 248  0
                 stereotypeFigs.add(fs);
 249  0
             }
 250  
         }
 251  0
         return stereotypeFigs;
 252  
     }
 253  
     
 254  
     private FigKeyword findFigKeyword() {
 255  0
         for (Object f : getFigs()) {
 256  0
             if (f instanceof FigKeyword) {
 257  0
                 return (FigKeyword) f;
 258  
             }
 259  
         }
 260  0
         return null;
 261  
     }
 262  
     
 263  
     /**
 264  
      * TODO: This should become private and only called from constructor
 265  
      *
 266  
      * @see org.argouml.uml.diagram.ui.FigCompartment#populate()
 267  
      */
 268  
     public void populate() {
 269  
        
 270  0
         stereotypeCount = 0;
 271  0
         Object modelElement = getOwner();
 272  0
         if (modelElement == null) {
 273  
             // TODO: This block can be removed after issue 4075 is tackled
 274  0
             LOG.debug("Cannot populate the stereotype compartment "
 275  
                      + "unless the parent has an owner.");
 276  0
             return;
 277  
         }
 278  
         
 279  0
         if (LOG.isDebugEnabled()) {
 280  0
             LOG.debug("Populating stereotypes compartment for "
 281  
                     + Model.getFacade().getName(modelElement));
 282  
         }
 283  
 
 284  
         /* This will contain the Figs that we do not need anymore: */
 285  0
         Collection<Fig> removeCollection = new ArrayList<Fig>(getFigs());
 286  
 
 287  
         //There is one fig more in the group than (stereotypes + keyword):
 288  0
         if (keyword != null) {
 289  0
             FigKeyword keywordFig = findFigKeyword();
 290  0
             if (keywordFig == null) {
 291  
                 // The keyword fig does not exist yet.
 292  
                 // Let's create one:
 293  0
                 keywordFig =
 294  
                     new FigKeyword(keyword, 
 295  
                             getBoundsForNextStereotype(),
 296  
                             getSettings());
 297  
                 // bounds not relevant here
 298  0
                 addFig(keywordFig);
 299  
             } else {
 300  
                 // The keyword fig already exists.
 301  0
                 removeCollection.remove(keywordFig);
 302  
             }
 303  0
             ++stereotypeCount;
 304  
         }
 305  
 
 306  0
         for (Object stereo : Model.getFacade().getStereotypes(modelElement)) {
 307  0
             FigStereotype stereotypeTextFig = findFig(stereo);
 308  0
             if (stereotypeTextFig == null) {
 309  0
                 stereotypeTextFig =
 310  
                     new FigStereotype(stereo, 
 311  
                             getBoundsForNextStereotype(),
 312  
                             getSettings());
 313  
                 // bounds not relevant here
 314  0
                 addFig(stereotypeTextFig);
 315  
             } else {
 316  
              // The stereotype fig already exists.
 317  0
                 removeCollection.remove(stereotypeTextFig);
 318  
             }
 319  0
             ++stereotypeCount;
 320  0
         }
 321  
         
 322  
         //cleanup of unused FigText's
 323  0
         for (Fig f : removeCollection) {
 324  0
             if (f instanceof FigStereotype || f instanceof FigKeyword) {
 325  0
                 removeFig(f);
 326  
             }
 327  
         }
 328  
 
 329  0
         reorderStereotypeFigs();
 330  
 
 331  
         // remove all stereotypes that have a graphical icon
 332  0
         updateHiddenStereotypes();
 333  
 
 334  0
     }
 335  
     
 336  
     /**
 337  
      * Get the number of stereotypes contained in this group
 338  
      * @return the number of stereotypes in this group
 339  
      */
 340  
     public int getStereotypeCount() {
 341  0
         return stereotypeCount;
 342  
     }
 343  
     
 344  
     private Rectangle getBoundsForNextStereotype() {
 345  0
         return new Rectangle(
 346  
                 bigPort.getX() + 1,
 347  
                 bigPort.getY() + 1
 348  
                 + (stereotypeCount
 349  
                 * ROWHEIGHT),
 350  
                 0,
 351  
                 ROWHEIGHT - 2);
 352  
     }
 353  
 
 354  
     private void updateHiddenStereotypes() {
 355  0
         List<Fig> figs = getFigs();
 356  0
         for (Fig f : figs) {
 357  0
             if (f instanceof FigStereotype) {
 358  0
                 FigStereotype fs = (FigStereotype) f;
 359  0
                 fs.setVisible(getIconForStereotype(fs) == null
 360  
                         || !isHidingStereotypesWithIcon());
 361  0
             }
 362  
         }
 363  0
     }
 364  
 
 365  
     private Image getIconForStereotype(FigStereotype fs) {
 366  
         // TODO: Find a way to replace this dependency on Project
 367  0
         Project project = getProject();
 368  0
         if (project == null) {
 369  0
             LOG.warn("getProject() returned null");
 370  0
             return null;
 371  
         }
 372  0
         Object owner = fs.getOwner();
 373  0
         if (owner == null) {
 374  
             // keywords which look like a stereotype (e.g. <<interface>>) have
 375  
             // no owner
 376  0
             return null;
 377  
         } else {
 378  0
             return project.getProfileConfiguration().getFigNodeStrategy()
 379  
                     .getIconForStereotype(owner);
 380  
         }
 381  
     }
 382  
 
 383  
     /*
 384  
      * @see org.tigris.gef.presentation.Fig#setBoundsImpl(int, int, int, int)
 385  
      */
 386  
     @Override
 387  
     protected void setBoundsImpl(int x, int y, int w, int h) {
 388  0
         Rectangle oldBounds = getBounds();
 389  
 
 390  0
         Dimension minimumSize = getMinimumSize();
 391  0
         int newW = Math.max(w, minimumSize.width);
 392  0
         int newH = Math.max(h, minimumSize.height);
 393  
         
 394  0
         int yy = y;
 395  0
         for  (Fig fig : (Collection<Fig>) getFigs()) {
 396  0
             if (fig != bigPort) {
 397  0
                 fig.setBounds(x, yy, newW,
 398  
                               fig.getMinimumSize().height);
 399  0
                 yy += fig.getMinimumSize().height;
 400  
             }
 401  
         }
 402  0
         bigPort.setBounds(x, y, newW, newH);
 403  0
         calcBounds();
 404  0
         firePropChange("bounds", oldBounds, getBounds());
 405  0
     }
 406  
 
 407  
     /**
 408  
      * Allows a parent Fig to specify some keyword text to display amongst the
 409  
      * stereotypes.
 410  
      * An example of this usage is to display &lt;&lt;interface&gt;&gt;
 411  
      * on FigInterface.
 412  
      * @param word the text of the pseudo stereotype
 413  
      */
 414  
     public void setKeyword(String word) {
 415  0
         keyword = word;
 416  0
         populate();
 417  0
     }
 418  
 
 419  
     /**
 420  
      * @return true if textual stereotypes are being hidden in preference to
 421  
      *         displaying icon.
 422  
      */
 423  
     public boolean isHidingStereotypesWithIcon() {
 424  0
         return hidingStereotypesWithIcon;
 425  
     }
 426  
 
 427  
     /**
 428  
      * Turn on/off textual stereotype display in preference to icon.
 429  
      * 
 430  
      * @param hideStereotypesWithIcon true to hide textual stereotypes and
 431  
      *                show icon instead.
 432  
      */
 433  
     public void setHidingStereotypesWithIcon(boolean hideStereotypesWithIcon) {
 434  0
         this.hidingStereotypesWithIcon = hideStereotypesWithIcon;
 435  0
         updateHiddenStereotypes();
 436  0
     }
 437  
     
 438  
     @Override
 439  
     public Dimension getMinimumSize() {
 440  
         // if there are no stereotypes, we return (0,0), preventing 
 441  
         // double lines in the class (see issue 4939)
 442  0
         Dimension dim = null;
 443  0
         Object modelElement = getOwner();
 444  
         
 445  0
         if (modelElement != null) {
 446  0
             List<FigStereotype> stereos = getStereotypeFigs();
 447  0
             if (stereos.size() > 0 || keyword != null) {
 448  0
                 int minWidth = 0;
 449  0
                 int minHeight = 0;
 450  
                 //set new bounds for all included figs
 451  0
                 for (Fig fig : (Collection<Fig>) getFigs()) {
 452  0
                     if (fig.isVisible() && fig != bigPort) {
 453  0
                         int fw = fig.getMinimumSize().width;
 454  0
                         if (fw > minWidth) {
 455  0
                             minWidth = fw;
 456  
                         }
 457  0
                         minHeight += fig.getMinimumSize().height;
 458  0
                     }
 459  
                 }
 460  
 
 461  0
                 minHeight += 2; // 2 Pixel padding after compartment
 462  0
                 dim = new Dimension(minWidth, minHeight);
 463  
             }
 464  
         }
 465  0
         if (dim == null) {
 466  0
             dim = new Dimension(0, 0);
 467  
         }
 468  0
         return dim;
 469  
     }
 470  
 }