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 | } |