| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| ClassdiagramNode |
|
| 1.4827586206896552;1.483 |
| 1 | /* $Id: ClassdiagramNode.java 17863 2010-01-12 20:07:22Z 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-2008 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.static_structure.layout; | |
| 40 | ||
| 41 | import java.awt.Dimension; | |
| 42 | import java.awt.Point; | |
| 43 | import java.util.ArrayList; | |
| 44 | import java.util.List; | |
| 45 | ||
| 46 | import org.argouml.uml.diagram.layout.LayoutedNode; | |
| 47 | import org.argouml.uml.diagram.static_structure.ui.FigComment; | |
| 48 | import org.argouml.uml.diagram.static_structure.ui.FigInterface; | |
| 49 | import org.argouml.uml.diagram.static_structure.ui.FigPackage; | |
| 50 | import org.tigris.gef.presentation.Fig; | |
| 51 | import org.tigris.gef.presentation.FigNode; | |
| 52 | ||
| 53 | /** | |
| 54 | * This class represents a node in the classdiagram (a class, interface or | |
| 55 | * package). | |
| 56 | * <p> | |
| 57 | * | |
| 58 | * Things a node has to know: | |
| 59 | * <ul> | |
| 60 | * <li>Up- and downlinks for positioning in the hierarchy | |
| 61 | * <li>Weight of this node. This weight has to be strongly influenced by the | |
| 62 | * parent-nodes, because otherwise the order of nodes in the current row will | |
| 63 | * not be compatible with the order of the nodes in the row above. | |
| 64 | * </ul> | |
| 65 | */ | |
| 66 | class ClassdiagramNode implements LayoutedNode, Comparable { | |
| 67 | ||
| 68 | /** | |
| 69 | * Constant to be used as an initializer when this node is not placed at an | |
| 70 | * column. | |
| 71 | */ | |
| 72 | public static final int NOCOLUMN = -1; | |
| 73 | ||
| 74 | /** | |
| 75 | * Constant to be used as an initializer when this node has no rank assigned | |
| 76 | * yet. | |
| 77 | */ | |
| 78 | public static final int NORANK = -1; | |
| 79 | ||
| 80 | /** | |
| 81 | * Constant to be used as an initializer when this node has no weight. | |
| 82 | */ | |
| 83 | public static final int NOWEIGHT = -1; | |
| 84 | ||
| 85 | /** | |
| 86 | * The current column of this node. | |
| 87 | */ | |
| 88 | 0 | private int column = NOCOLUMN; |
| 89 | ||
| 90 | /** | |
| 91 | * List of the nodes that contain the figures, which | |
| 92 | * are sources of edges with the figure of this node as destination. | |
| 93 | */ | |
| 94 | 0 | private List<ClassdiagramNode> downlinks = |
| 95 | new ArrayList<ClassdiagramNode>(); | |
| 96 | ||
| 97 | /** | |
| 98 | * Offset used for edges, which have this node as the "upper" node. | |
| 99 | */ | |
| 100 | 0 | private int edgeOffset = 0; |
| 101 | ||
| 102 | /** | |
| 103 | * The Fig that this ClassdiagramNode represents during the layout process. | |
| 104 | */ | |
| 105 | 0 | private FigNode figure = null; |
| 106 | ||
| 107 | /** | |
| 108 | * The preferred X coordinate for the node. Hint only. May not be used. | |
| 109 | */ | |
| 110 | 0 | private int placementHint = -1; |
| 111 | ||
| 112 | /** | |
| 113 | * The current rank (i.e. row number) of this node. | |
| 114 | */ | |
| 115 | 0 | private int rank = NORANK; |
| 116 | ||
| 117 | /** | |
| 118 | * List of nodes that contain the figures, which are destinations of edges | |
| 119 | * with the figure of this node as source. | |
| 120 | */ | |
| 121 | 0 | private List<ClassdiagramNode> uplinks = new ArrayList<ClassdiagramNode>(); |
| 122 | ||
| 123 | /** | |
| 124 | * The 'weight' of this node. This is a computed | |
| 125 | * attribute that is used during the horizontal placement process. It's | |
| 126 | * based on the position of the 'uplinked' objects. The actual purpose is to | |
| 127 | * minimize the number of link crossings in the diagram. Since we don't | |
| 128 | * compute the actual number of link crossings, we look where our uplinked | |
| 129 | * objects are, and then try to place our object in a way, that we can | |
| 130 | * expect to have a minimal number of crossings. | |
| 131 | */ | |
| 132 | 0 | private float weight = NOWEIGHT; |
| 133 | ||
| 134 | private static final float UPLINK_FACTOR = 5; | |
| 135 | ||
| 136 | /** | |
| 137 | * Construct a new ClassdiagramNode representing the given Fig. | |
| 138 | * | |
| 139 | * @param f | |
| 140 | * represents the figure in the diagram, that peers this layout | |
| 141 | * node. | |
| 142 | */ | |
| 143 | 0 | public ClassdiagramNode(FigNode f) { |
| 144 | 0 | setFigure(f); |
| 145 | 0 | } |
| 146 | ||
| 147 | /** | |
| 148 | * Add a new downlinked node to this node. | |
| 149 | * | |
| 150 | * @param newDownlink | |
| 151 | * The node to be added with a dowlink. | |
| 152 | */ | |
| 153 | public void addDownlink(ClassdiagramNode newDownlink) { | |
| 154 | 0 | downlinks.add(newDownlink); |
| 155 | 0 | } |
| 156 | ||
| 157 | /** | |
| 158 | * Add a constant to the rank of this node. | |
| 159 | * | |
| 160 | * @param n | |
| 161 | * The value to add. | |
| 162 | */ | |
| 163 | public void addRank(int n) { | |
| 164 | 0 | setRank(n + getRank()); |
| 165 | 0 | } |
| 166 | ||
| 167 | /** | |
| 168 | * Add an uplink to this node. | |
| 169 | * | |
| 170 | * @param newUplink | |
| 171 | * represents the new uplinks. | |
| 172 | */ | |
| 173 | public void addUplink(ClassdiagramNode newUplink) { | |
| 174 | 0 | uplinks.add(newUplink); |
| 175 | 0 | } |
| 176 | ||
| 177 | /** | |
| 178 | * Calculate the weight of this node. The function distinguishes between | |
| 179 | * note-nodes and standard-nodes, because a note should be positioned to the | |
| 180 | * right of its first related node, if it exists. Therefor the weight is a | |
| 181 | * function of the weight of the related node. For standard-nodes the weight | |
| 182 | * is a function of up-/downlinks, column and uplink factor. | |
| 183 | * | |
| 184 | * @return The weight of this node. | |
| 185 | */ | |
| 186 | public float calculateWeight() { | |
| 187 | 0 | weight = 0; |
| 188 | 0 | for (ClassdiagramNode node : uplinks) { |
| 189 | 0 | weight = Math.max(weight, node.getWeight() |
| 190 | * UPLINK_FACTOR | |
| 191 | * (1 + 1 / Math.max(1, node.getColumn() + UPLINK_FACTOR))); | |
| 192 | } | |
| 193 | 0 | weight += getSubtreeWeight() |
| 194 | + (1 / Math.max(1, getColumn() + UPLINK_FACTOR)); | |
| 195 | 0 | return weight; |
| 196 | } | |
| 197 | ||
| 198 | /** | |
| 199 | * The "natural order" for ClassdiagramNodes is defined by the following | |
| 200 | * order. | |
| 201 | * <ul> | |
| 202 | * <li>First standalone, then linked nodes | |
| 203 | * <li>First Packages, then Interfaces/Classes/Notes | |
| 204 | * <li>increasing rank (rownumber) | |
| 205 | * <li>decreasing weight | |
| 206 | * <li>name of model object | |
| 207 | * <li>increasing hashcode (for uniqueness) | |
| 208 | * </ul> | |
| 209 | * | |
| 210 | * @see java.lang.Comparable#compareTo(java.lang.Object) | |
| 211 | */ | |
| 212 | public int compareTo(Object arg0) { | |
| 213 | 0 | ClassdiagramNode node = (ClassdiagramNode) arg0; |
| 214 | 0 | int result = 0; |
| 215 | 0 | result = |
| 216 | Boolean.valueOf(node.isStandalone()).compareTo( | |
| 217 | Boolean.valueOf(isStandalone())); | |
| 218 | 0 | if (result == 0) { |
| 219 | 0 | result = this.getTypeOrderNumer() - node.getTypeOrderNumer(); |
| 220 | } | |
| 221 | 0 | if (result == 0) { |
| 222 | 0 | result = this.getRank() - node.getRank(); |
| 223 | } | |
| 224 | 0 | if (result == 0) { |
| 225 | 0 | result = (int) Math.signum(node.getWeight() - this.getWeight()); |
| 226 | } | |
| 227 | 0 | if (result == 0) { |
| 228 | 0 | result = String.valueOf(this.getFigure().getOwner()).compareTo( |
| 229 | String.valueOf(node.getFigure().getOwner())); | |
| 230 | } | |
| 231 | 0 | if (result == 0) { |
| 232 | 0 | result = node.hashCode() - this.hashCode(); |
| 233 | } | |
| 234 | //LOG.debug(result + " node1: " + this + ", node2 " + node); | |
| 235 | 0 | return result; |
| 236 | } | |
| 237 | ||
| 238 | /** | |
| 239 | * @return The column of this node. | |
| 240 | */ | |
| 241 | public int getColumn() { | |
| 242 | 0 | return column; |
| 243 | } | |
| 244 | ||
| 245 | ||
| 246 | /** | |
| 247 | * @return The downlinks of this node. | |
| 248 | */ | |
| 249 | public List<ClassdiagramNode> getDownNodes() { | |
| 250 | 0 | return downlinks; |
| 251 | } | |
| 252 | ||
| 253 | /** | |
| 254 | * Get the offset which shall be used for edges with this node as parent. | |
| 255 | * | |
| 256 | * @return The offset | |
| 257 | */ | |
| 258 | public int getEdgeOffset() { | |
| 259 | 0 | return edgeOffset; |
| 260 | } | |
| 261 | ||
| 262 | /** | |
| 263 | * Get the underlying figure of this node. | |
| 264 | * | |
| 265 | * @return The figure. | |
| 266 | */ | |
| 267 | public FigNode getFigure() { | |
| 268 | 0 | return figure; |
| 269 | } | |
| 270 | ||
| 271 | /** | |
| 272 | * Get the level in the inheritance hierarchy for this node. | |
| 273 | * | |
| 274 | * @return The level. | |
| 275 | */ | |
| 276 | public int getLevel() { | |
| 277 | 0 | int result = 0; |
| 278 | 0 | for (ClassdiagramNode node : uplinks) { |
| 279 | 0 | result = |
| 280 | (node == this) ? result : Math.max( | |
| 281 | node.getLevel() + 1, result); | |
| 282 | } | |
| 283 | 0 | return result; |
| 284 | } | |
| 285 | ||
| 286 | /** | |
| 287 | * Get the location of the underlying figure in the diagram. | |
| 288 | * | |
| 289 | * @return The location. | |
| 290 | */ | |
| 291 | public Point getLocation() { | |
| 292 | 0 | return getFigure().getLocation(); |
| 293 | } | |
| 294 | ||
| 295 | /** | |
| 296 | * Get the current placement hint (X coordinate in the row). | |
| 297 | * | |
| 298 | * @return The placement hint for this node. | |
| 299 | */ | |
| 300 | public int getPlacementHint() { | |
| 301 | 0 | return placementHint; |
| 302 | } | |
| 303 | ||
| 304 | /** | |
| 305 | * @return The rank for this node. | |
| 306 | */ | |
| 307 | public int getRank() { | |
| 308 | 0 | return rank == NORANK ? getLevel() : rank; |
| 309 | } | |
| 310 | ||
| 311 | /** | |
| 312 | * Return the size of the figure associated with this | |
| 313 | * layout node. | |
| 314 | * | |
| 315 | * @return The size of the associated figure. | |
| 316 | */ | |
| 317 | public Dimension getSize() { | |
| 318 | 0 | return getFigure().getSize(); |
| 319 | } | |
| 320 | ||
| 321 | /** | |
| 322 | * Get the weight of the subtree defined by this node. Impact on weight is | |
| 323 | * decreasing with increasing hierarchical distance | |
| 324 | * | |
| 325 | * @return The weight of the subtree. | |
| 326 | */ | |
| 327 | private float getSubtreeWeight() { | |
| 328 | ||
| 329 | 0 | float w = 1; |
| 330 | 0 | for (ClassdiagramNode node : downlinks) { |
| 331 | 0 | w += node.getSubtreeWeight() / UPLINK_FACTOR; |
| 332 | } | |
| 333 | 0 | return w; |
| 334 | } | |
| 335 | ||
| 336 | /** | |
| 337 | * Get the type order number of this node. This number may be used to | |
| 338 | * influence the sort order of ClassdiagramNodes. | |
| 339 | * | |
| 340 | * @return Type order number. | |
| 341 | */ | |
| 342 | public int getTypeOrderNumer() { | |
| 343 | 0 | int result = 99; |
| 344 | 0 | if (getFigure() instanceof FigPackage) { |
| 345 | 0 | result = 0; |
| 346 | 0 | } else if (getFigure() instanceof FigInterface) { |
| 347 | 0 | result = 1; |
| 348 | } | |
| 349 | 0 | return result; |
| 350 | } | |
| 351 | ||
| 352 | /** | |
| 353 | * Get the uplinks of this node. | |
| 354 | * | |
| 355 | * @return The uplinks of this node. | |
| 356 | */ | |
| 357 | public List<ClassdiagramNode> getUpNodes() { | |
| 358 | 0 | return uplinks; |
| 359 | } | |
| 360 | ||
| 361 | /** | |
| 362 | * Return the weight of this node, which is used for positioning in a row. | |
| 363 | * | |
| 364 | * @return The weight of this node. | |
| 365 | */ | |
| 366 | public float getWeight() { | |
| 367 | 0 | return weight; |
| 368 | } | |
| 369 | ||
| 370 | /** | |
| 371 | * Check if this node is associated with a note. | |
| 372 | * | |
| 373 | * @return Result of test. | |
| 374 | */ | |
| 375 | public boolean isComment() { | |
| 376 | 0 | return (getFigure() instanceof FigComment); |
| 377 | } | |
| 378 | ||
| 379 | /** | |
| 380 | * Check if this node is associated with a package. | |
| 381 | * | |
| 382 | * @return Result of test. | |
| 383 | */ | |
| 384 | public boolean isPackage() { | |
| 385 | 0 | return (getFigure() instanceof FigPackage); |
| 386 | } | |
| 387 | ||
| 388 | /** | |
| 389 | * Test whether this node has no connection to other nodes. Return | |
| 390 | * <code>true</code> if node has no connections, <code>false</code> | |
| 391 | * otherwise. | |
| 392 | * | |
| 393 | * @return Result of test. | |
| 394 | */ | |
| 395 | public boolean isStandalone() { | |
| 396 | 0 | return uplinks.isEmpty() && downlinks.isEmpty(); |
| 397 | } | |
| 398 | ||
| 399 | /** | |
| 400 | * Set the column of this node. A re-calculation of the weight is performed, | |
| 401 | * because the column is an input parameter for the weight. | |
| 402 | * | |
| 403 | * @param newColumn | |
| 404 | * The new column. | |
| 405 | */ | |
| 406 | public void setColumn(int newColumn) { | |
| 407 | 0 | column = newColumn; |
| 408 | 0 | calculateWeight(); |
| 409 | 0 | } |
| 410 | ||
| 411 | /** | |
| 412 | * Set the offset for edges to this node. | |
| 413 | * | |
| 414 | * @param newOffset | |
| 415 | * Offset for edges with this node as one endpoint. | |
| 416 | */ | |
| 417 | public void setEdgeOffset(int newOffset) { | |
| 418 | 0 | edgeOffset = newOffset; |
| 419 | 0 | } |
| 420 | ||
| 421 | /** | |
| 422 | * Set the Fig represented by this node. | |
| 423 | * | |
| 424 | * @param newFigure | |
| 425 | * represents the new value of figure. | |
| 426 | */ | |
| 427 | public void setFigure(FigNode newFigure) { | |
| 428 | 0 | figure = newFigure; |
| 429 | 0 | } |
| 430 | ||
| 431 | /** | |
| 432 | * Set the location of the Fig associated with this node. | |
| 433 | * | |
| 434 | * @param newLocation | |
| 435 | * represents the new location for this figure. | |
| 436 | */ | |
| 437 | @SuppressWarnings("unchecked") | |
| 438 | public void setLocation(Point newLocation) { | |
| 439 | 0 | Point oldLocation = getFigure().getLocation(); |
| 440 | ||
| 441 | 0 | getFigure().setLocation(newLocation); |
| 442 | 0 | int xTrans = newLocation.x - oldLocation.x; |
| 443 | 0 | int yTrans = newLocation.y - oldLocation.y; |
| 444 | 0 | for (Fig fig : (List<Fig>) getFigure().getEnclosedFigs()) { |
| 445 | 0 | fig.translate(xTrans, yTrans); |
| 446 | } | |
| 447 | 0 | } |
| 448 | ||
| 449 | /** | |
| 450 | * A placementhint gives an indication where it might be feasible to place | |
| 451 | * this node. It is used by the layouter, and there is no guarantee that it | |
| 452 | * will be used. | |
| 453 | * | |
| 454 | * @param hint | |
| 455 | * x coordinate of the desired placement | |
| 456 | */ | |
| 457 | public void setPlacementHint(int hint) { | |
| 458 | 0 | placementHint = hint; |
| 459 | 0 | } |
| 460 | ||
| 461 | /** | |
| 462 | * Set the rank | |
| 463 | * | |
| 464 | * @param newRank | |
| 465 | * represents the new value of rank. | |
| 466 | */ | |
| 467 | public void setRank(int newRank) { | |
| 468 | 0 | rank = newRank; |
| 469 | 0 | } |
| 470 | ||
| 471 | /** | |
| 472 | * Set the weight for this node. | |
| 473 | * | |
| 474 | * @param w | |
| 475 | * The new weight of this node. | |
| 476 | */ | |
| 477 | public void setWeight(float w) { | |
| 478 | 0 | weight = w; |
| 479 | 0 | } |
| 480 | ||
| 481 | } |