Coverage Report - org.argouml.uml.diagram.ui.FigCompartment
 
Classes in this File Line Coverage Branch Coverage Complexity
FigCompartment
0%
0/117
0%
0/50
1.943
FigCompartment$FigPort
0%
0/12
0%
0/6
1.943
FigCompartment$FigSeparator
0%
0/6
N/A
1.943
 
 1  
 /* $Id: FigCompartment.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: FigCompartment.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.Color;
 42  
 import java.awt.Dimension;
 43  
 import java.awt.Rectangle;
 44  
 import java.util.ArrayList;
 45  
 import java.util.Collection;
 46  
 import java.util.List;
 47  
 
 48  
 import org.apache.log4j.Logger;
 49  
 import org.argouml.model.InvalidElementException;
 50  
 import org.argouml.notation.NotationProvider;
 51  
 import org.argouml.uml.diagram.DiagramSettings;
 52  
 import org.tigris.gef.presentation.Fig;
 53  
 import org.tigris.gef.presentation.FigRect;
 54  
 
 55  
 /**
 56  
  * Presentation logic for a UML List Compartment. <p>
 57  
  * 
 58  
  * The UML defines a Name Compartment, and a List Compartment. 
 59  
  * This class implements the latter.<p>
 60  
  * 
 61  
  * A List Compartment is a boxed compartment,
 62  
  * containing vertically stacked figs,
 63  
  * which is common to e.g. an operations
 64  
  * compartment and an attributes compartment.<p>
 65  
  * 
 66  
  * The bigPort is filled with a border. All other figs contained 
 67  
  * in this group may not be filled.<p>
 68  
  * 
 69  
  * The size calculation done here supports vertically 
 70  
  * stacked sub-figs of this group and supports all 
 71  
  * compartment specializations.
 72  
  * 
 73  
  * @author Bob Tarling
 74  
  */
 75  
 public abstract class FigCompartment extends ArgoFigGroup {
 76  
 
 77  0
     private static final Logger LOG = Logger.getLogger(FigCompartment.class);
 78  
 
 79  
     private Fig bigPort;
 80  
     
 81  
     private static final int MIN_HEIGHT = FigNodeModelElement.NAME_FIG_HEIGHT;
 82  
 
 83  
     /**
 84  
      * A separator line that may be wider than the compartment.
 85  
      */
 86  0
     private Fig externalSeparatorFig = new FigSeparator(X0, Y0, 11, LINE_WIDTH);
 87  
 
 88  
 
 89  
     private void constructFigs(int x, int y, int w, int h) {
 90  0
         bigPort = new FigPort(X0, Y0, w, h);
 91  0
         bigPort.setFilled(false);
 92  0
         bigPort.setLineWidth(0);
 93  
 
 94  0
         addFig(bigPort);
 95  0
     }
 96  
     
 97  
     /**
 98  
      * Construct a new FigCompartment.
 99  
      * 
 100  
      * @param owner owning UML element
 101  
      * @param bounds rectangle describing bounds of compartment
 102  
      * @param settings render settings
 103  
      */
 104  
     public FigCompartment(Object owner, Rectangle bounds,
 105  
             DiagramSettings settings) {
 106  0
         super(owner, settings);
 107  0
         constructFigs(bounds.x, bounds.y, bounds.width, bounds.height);
 108  0
     }
 109  
     
 110  
     /**
 111  
      * If a boxed compartment is set to invisible then remove all its
 112  
      * children.
 113  
      * This is to save on resources and increase efficiency as multiple
 114  
      * figs need not exist and be resized, moved etc if they are not visible.
 115  
      * If a compartment is later made visible then its child figs are rebuilt
 116  
      * from the model.
 117  
      * {@inheritDoc}
 118  
      */
 119  
     @Override
 120  
     public void setVisible(boolean visible) {
 121  0
         if (isVisible() == visible) {
 122  0
             return;
 123  
         }
 124  0
         super.setVisible(visible);
 125  0
         if (externalSeparatorFig != null) {
 126  0
             externalSeparatorFig.setVisible(visible);
 127  
         }
 128  0
         if (visible) {
 129  0
             populate();
 130  
         } else {
 131  0
             for (int i = getFigs().size() - 1; i >= 0; --i) {
 132  0
                 Fig f = getFigAt(i);
 133  0
                 if (f instanceof CompartmentFigText) {
 134  0
                     removeFig(f);
 135  
                 }
 136  
             }
 137  
         }
 138  0
     }
 139  
 
 140  
     @Override
 141  
     public void addFig(Fig fig) {
 142  0
         if (fig != getBigPort()
 143  
                 && !(fig instanceof CompartmentFigText)
 144  
                 && !(fig instanceof FigSeparator)) {
 145  0
             LOG.error("Illegal Fig added to a FigEditableCompartment");
 146  0
             throw new IllegalArgumentException(
 147  
                     "A FigEditableCompartment can only "
 148  
                     + "contain CompartmentFigTexts, "
 149  
                     + "received a " + fig.getClass().getName());
 150  
         }
 151  0
         super.addFig(fig);
 152  0
     }
 153  
 
 154  
     
 155  
 
 156  
     /**
 157  
      * @return the bigPort
 158  
      */
 159  
     public Fig getBigPort() {
 160  0
         return bigPort;
 161  
     }
 162  
 
 163  
     /**
 164  
      * The minimum width is the minimum width of the child with the widest
 165  
      * minimum width.
 166  
      * The minimum height is the total minimum height of all child figs.
 167  
      * @return the minimum width
 168  
      */
 169  
     @Override
 170  
     public Dimension getMinimumSize() {
 171  0
         int minWidth = 0;
 172  0
         int minHeight = 0;
 173  0
         for (Fig fig : (Collection<Fig>) getFigs()) {
 174  0
             if (fig.isVisible() && fig != getBigPort()) {
 175  0
                 int fw = fig.getMinimumSize().width;
 176  0
                 if (fw > minWidth) {
 177  0
                     minWidth = fw;
 178  
                 }
 179  0
                 minHeight += fig.getMinimumSize().height;
 180  0
             }
 181  
         }
 182  
 
 183  0
         minHeight += 2; // 2 Pixel padding after compartment
 184  
         
 185  0
         minHeight = Math.max(minHeight, MIN_HEIGHT);
 186  
         
 187  0
         return new Dimension(minWidth, minHeight);
 188  
     }
 189  
 
 190  
     @Override
 191  
     protected void setBoundsImpl(int x, int y, int w, int h) {
 192  0
         Rectangle oldBounds = getBounds();
 193  
 
 194  0
         Dimension minimumSize = getMinimumSize();
 195  0
         int newW = Math.max(w, minimumSize.width);
 196  0
         int newH = Math.max(h, minimumSize.height);
 197  
 
 198  0
         int currentHeight = 0;
 199  
 
 200  0
         for  (Fig fig : (List<Fig>) getFigs()) {
 201  0
             if (fig.isVisible() && fig != getBigPort()) {
 202  0
                 int fh = fig.getMinimumSize().height;
 203  
 
 204  0
                 fig.setBounds(x, y + currentHeight, newW, fh);
 205  0
                 currentHeight += fh;
 206  0
             }
 207  
         }
 208  0
         getBigPort().setBounds(x, y, newW, newH);
 209  0
         calcBounds();
 210  0
         firePropChange("bounds", oldBounds, getBounds());
 211  0
     }
 212  
     
 213  
     /**
 214  
      * Create a new model element for the compartment.
 215  
      */
 216  
     protected abstract void createModelElement();
 217  
 
 218  
     @Override
 219  
     public void setFilled(boolean f) {
 220  
         // Only the bigPort may be filled
 221  0
         super.setFilled(false);
 222  
 //        bigPort.setFilled(f);
 223  0
     }
 224  
 
 225  
     @Deprecated //see parent
 226  
     @Override
 227  
     public boolean getFilled() {
 228  0
         return isFilled();
 229  
     }
 230  
 
 231  
     @Override
 232  
     public boolean isFilled() {
 233  0
         return bigPort.isFilled();
 234  
     }
 235  
 
 236  
     /**
 237  
      * This operation shall return a name unique for this type of 
 238  
      * compartment. Potential use: show at the top in the compartment 
 239  
      * as described in the UML, or as an identification string for 
 240  
      * the compartment type. <p>
 241  
      * See UML 1.4.2 OMG, chapter 5.24.1.2: Compartment name.
 242  
      * 
 243  
      * @return the name of the compartment 
 244  
      */
 245  
     public abstract String getName();
 246  
     
 247  
     /**
 248  
      * Implemented in the subclass to indicate the primary type of model element
 249  
      * the compartment is designed to hold.
 250  
      * @return a model element type
 251  
      */
 252  
     public abstract Object getCompartmentType();
 253  
     
 254  
     /**
 255  
      * @return the collection of UML objects 
 256  
      *              on which this compartment is based
 257  
      */
 258  
     protected abstract Collection getUmlCollection();
 259  
 
 260  
     /**
 261  
      * @return the type of the notationProvider 
 262  
      *              used to handle the text in the compartment 
 263  
      */
 264  
     protected abstract int getNotationType();
 265  
     
 266  
     /**
 267  
      * Fills the Fig by adding all figs within.
 268  
      */
 269  
     public void populate() {
 270  0
         if (!isVisible()) {
 271  0
             return;
 272  
         }
 273  
 
 274  0
         int xpos = bigPort.getX();
 275  0
         int ypos = bigPort.getY();
 276  
 
 277  0
         List<CompartmentFigText> figs = getElementFigs();
 278  
         // We remove all of them:
 279  0
         for (Fig f : figs) {
 280  0
             removeFig(f);    
 281  
         }
 282  
 
 283  
         // We are going to add the ones still valid & new ones
 284  
         // in the right sequence:
 285  0
         FigSingleLineTextWithNotation comp = null;
 286  
         try {
 287  0
             int acounter = -1;
 288  0
             for (Object umlObject : getUmlCollection()) {
 289  0
                 comp = findCompartmentFig(figs, umlObject);
 290  0
                 acounter++;                
 291  
 
 292  
                 // TODO: Some of these magic numbers probably assume a line
 293  
                 // width of 1.  Replace with appropriate constants/variables.
 294  
                 
 295  
                 // If we don't have a fig for this UML object, we'll need to add
 296  
                 // one. We set the bounds, but they will be reset later.
 297  0
                 if (comp == null) {
 298  0
                     comp = createFigText(umlObject, new Rectangle(
 299  
                             xpos + 1 /*?LINE_WIDTH?*/,
 300  
                             ypos + 1 /*?LINE_WIDTH?*/ + acounter
 301  
                             * ROWHEIGHT,
 302  
                             0,
 303  
                             ROWHEIGHT - 2 /*? 2*LINE_WIDTH? */), 
 304  
                             getSettings());
 305  
                 } else {
 306  
                     /* This one is still usable, so let's retain it, */
 307  
                     /* but its position may have been changed: */
 308  0
                     Rectangle b = comp.getBounds();
 309  0
                     b.y = ypos + 1 /*?LINE_WIDTH?*/ + acounter * ROWHEIGHT;
 310  
                     // bounds not relevant here, but I am perfectionist...
 311  0
                     comp.setBounds(b);
 312  
                 }
 313  
                 /* We need to set a new notationprovider, since 
 314  
                  * the Notation language may have been changed:  */
 315  0
                 comp.initNotationProviders();
 316  0
                 addFig(comp); // add it again (but now in the right sequence)
 317  
 
 318  
                 // Now put the text in
 319  
                 // We must handle the case where the text is null
 320  0
                 String ftText = comp.getNotationProvider().toString(umlObject,
 321  
                         comp.getNotationSettings());
 322  0
                 if (ftText == null) {
 323  0
                     ftText = "";
 324  
                 }
 325  0
                 comp.setText(ftText);
 326  
 
 327  0
                 comp.setBotMargin(0);
 328  0
             }
 329  0
         } catch (InvalidElementException e) {
 330  
             // TODO: It would be better here to continue the loop and try to
 331  
             // build the rest of the compartment. Hence try/catch should be
 332  
             // internal to the loop.
 333  0
             LOG.debug("Attempted to populate a FigEditableCompartment" 
 334  
                     + " using a deleted model element - aborting", e);
 335  0
         } 
 336  
 
 337  0
         if (comp != null) {
 338  0
             comp.setBotMargin(6); // the last one needs extra space below it
 339  
         }
 340  0
     }
 341  
     
 342  
     
 343  
     /**
 344  
      * @return null
 345  
      * @deprecated for 0.27.3 by tfmorris.  Subclasses must implement
 346  
      * {@link #createFigText(Object, Rectangle, DiagramSettings, 
 347  
      * NotationProvider)}
 348  
      * which will become abstract in the future when this deprecated method is
 349  
      * removed.
 350  
      */
 351  
     @Deprecated
 352  
     protected FigSingleLineTextWithNotation createFigText(
 353  
             int x, int y, int w, int h, Fig aFig, NotationProvider np) {
 354  
         // No longer abstract to allow subclasses to remove, so we provide a
 355  
         // null default implementation
 356  0
         return null;
 357  
     }
 358  
 
 359  
     /**
 360  
      * Factory method to create a FigSingleLineTextWithNotation 
 361  
      * which must be implemented by all subclasses. 
 362  
      * It will become abstract after the release of 0.28 to
 363  
      * enforce this requirement.
 364  
      * 
 365  
      * @param owner owning UML element
 366  
      * @param bounds position and size
 367  
      * @param settings render settings
 368  
      * @param np notation provider
 369  
      * @return a FigSingleLineText which can be used to display the text.
 370  
      */
 371  
     @SuppressWarnings("deprecation")
 372  
     protected FigSingleLineTextWithNotation createFigText(Object owner, 
 373  
             Rectangle bounds, 
 374  
             @SuppressWarnings("unused") DiagramSettings settings, 
 375  
             NotationProvider np) {
 376  
 
 377  
         // If this is not overridden it will revert to the old behavior
 378  
         // All internal subclasses have been updated, but this if for 
 379  
         // compatibility of non-ArgoUML extensions.
 380  0
         FigSingleLineTextWithNotation comp = createFigText(
 381  
                     bounds.x,
 382  
                     bounds.y,
 383  
                     bounds.width,
 384  
                     bounds.height,
 385  
                     this.getBigPort(),
 386  
                     np);
 387  0
         comp.setOwner(owner);
 388  0
         return comp;
 389  
     }
 390  
     
 391  
     /**
 392  
      * @param owner owning UML element
 393  
      * @param bounds position and size
 394  
      * @param settings the render settings
 395  
      * @return a FigSingleLineText with notation provider 
 396  
      *                  which can be used to display the text
 397  
      */
 398  
     abstract FigSingleLineTextWithNotation createFigText(Object owner, 
 399  
             Rectangle bounds, 
 400  
             DiagramSettings settings);
 401  
     
 402  
     /**
 403  
      * Returns the new size of the FigGroup (e.g. attributes or
 404  
      * operations) after calculation new bounds for all sub-figs,
 405  
      * considering their minimal sizes; FigGroup need not be
 406  
      * displayed; no update event is fired.<p>
 407  
      *
 408  
      * This method has side effects that are sometimes used.
 409  
      *
 410  
      * @param x x
 411  
      * @param y y
 412  
      * @param w w
 413  
      * @param h h
 414  
      * @return the new dimension
 415  
      */
 416  
     @SuppressWarnings("unused")
 417  
     public Dimension updateFigGroupSize(
 418  
                        int x,
 419  
                        int y,
 420  
                        int w,
 421  
                        int h,
 422  
                        boolean checkSize,
 423  
                        int rowHeight) {
 424  0
         return getMinimumSize();
 425  
     }
 426  
     
 427  
     /* Find the compartment fig for this umlObject: */
 428  
     private CompartmentFigText findCompartmentFig(List<CompartmentFigText> figs, 
 429  
             Object umlObject) {
 430  0
         for (CompartmentFigText fig : figs) {
 431  0
             if (fig.getOwner() == umlObject) {
 432  0
                 return fig;
 433  
             }
 434  
         }
 435  0
         return null;
 436  
     }
 437  
 
 438  
     private List<CompartmentFigText> getElementFigs() {
 439  0
         final List<CompartmentFigText> figs =
 440  
             new ArrayList<CompartmentFigText>(getFigs().size());
 441  
         
 442  0
         for (Object f : getFigs()) {
 443  0
             if (f instanceof CompartmentFigText) {
 444  0
                 figs.add((CompartmentFigText) f);
 445  
             }
 446  
         }
 447  0
         return figs;
 448  
     }
 449  
 
 450  
     @Override
 451  
     public void setLineColor(Color col) {
 452  0
         super.setLineColor(col);
 453  0
         externalSeparatorFig.setFillColor(col);
 454  0
     }
 455  
 
 456  
     @Override
 457  
     public void setLineWidth(int w) {
 458  0
         super.setLineWidth(0);
 459  0
         bigPort.setLineWidth(0);
 460  0
         externalSeparatorFig.setHeight(w);
 461  0
     }
 462  
 
 463  
     @Override
 464  
     public void setFillColor(Color col) {
 465  0
         super.setFillColor(col);
 466  0
         externalSeparatorFig.setFillColor(getLineColor());
 467  0
     }
 468  
 
 469  
     /**
 470  
      * Set new bounds for the external separator line (if it exists).
 471  
      * 
 472  
      * @param r the new bounds
 473  
      */
 474  
     public void setExternalSeparatorFigBounds(Rectangle r) {
 475  0
         externalSeparatorFig.setBounds(r);
 476  0
     }
 477  
 
 478  
     /**
 479  
      * @return separator figure
 480  
      */
 481  
     public Fig getSeparatorFig() {
 482  0
         return externalSeparatorFig;
 483  
     }
 484  
     
 485  
     /**
 486  
      * Fig representing a horizontal line separator for compartment. <p>
 487  
      * 
 488  
      * This is a horizontal line, but implemented as a rectangle 
 489  
      * filled with the line color, since using a FigLine would draw the line 
 490  
      * around the start and end coordinates with a line width > 1.
 491  
      */
 492  
     private static class FigSeparator extends FigRect {
 493  
         /**
 494  
          * Constructor.
 495  
          *
 496  
          * @param x coordinate
 497  
          * @param y coordinate
 498  
          * @param len length of the line
 499  
          */
 500  
         FigSeparator(int x, int y, int len, int lineWidth) {
 501  0
             super(x, y, len, lineWidth);
 502  0
             setLineWidth(0);
 503  0
             super.setFilled(true);
 504  0
         }
 505  
 
 506  
         @Override
 507  
         public void setFilled(boolean filled) {
 508  
             // Override superclass to do nothing.
 509  
             // Fill property cannot be changed.
 510  0
         }
 511  
         
 512  
         @Override
 513  
         public void setLineWidth(int width) {
 514  
             // Override superclass to do nothing.
 515  
             // Line width cannot be changed.
 516  0
         }
 517  
         
 518  
     }
 519  
     
 520  
     /**
 521  
      * Fig representing a horizontal line separator for compartment. <p>
 522  
      * 
 523  
      * This is a horizontal line, but implemented as a rectangle 
 524  
      * filled with the line color, since using a FigLine would draw the line 
 525  
      * around the start and end coordinates with a line width > 1.
 526  
      */
 527  
     private static class FigPort extends FigRect {
 528  
         /**
 529  
          * Constructor.
 530  
          *
 531  
          * @param x coordinate
 532  
          * @param y coordinate
 533  
          * @param len length of the line
 534  
          */
 535  
         FigPort(int x, int y, int len, int lineWidth) {
 536  0
             super(x, y, len, lineWidth);
 537  0
             super.setLineWidth(0);
 538  0
             super.setFillColor(null);
 539  0
             super.setFilled(false);
 540  0
         }
 541  
 
 542  
         @Override
 543  
         public void setFilled(boolean filled) {
 544  
             // Override superclass to do nothing.
 545  
             // Fill property cannot be changed.
 546  0
         }
 547  
         @Override
 548  
         public void setFillColor(Color color) {
 549  
             // Override superclass to do nothing.
 550  
             // Fill property cannot be changed.
 551  0
         }
 552  
         @Override
 553  
         public void setLineWidth(int width) {
 554  
             // Override superclass to do nothing.
 555  
             // Line width property cannot be changed.
 556  0
         }
 557  
         
 558  
         /**
 559  
          * The hit method in GEF {Fig.hit(Rectangle)} does not register a hit
 560  
          * inside the Fig if the FIg is not filled. When not filled only a hit
 561  
          * on the border is registered.
 562  
          * 
 563  
          * This override is a workaround until GEF is fixed.
 564  
          * 
 565  
          * We require GEF to all a Fig to be set so that filled = true but
 566  
          * have fill color as null (transparent). That way the base
 567  
          * functionality will work for us.
 568  
          * 
 569  
          * @param r
 570  
          *                the rectangular hit area
 571  
          * @return true if the hit rectangle strikes this fig
 572  
          */
 573  
         public boolean hit(Rectangle r) {
 574  0
             if (!isVisible() || !isSelectable())
 575  0
                 return false;
 576  0
             final int cornersHit =
 577  
                 countCornersContained(r.x, r.y, r.width, r.height);
 578  0
             return cornersHit > 0;
 579  
         }
 580  
     }
 581  
 }