Source code
/* $Id: UmlDiagramRenderer.java 19614 2011-07-20 12:10:13Z linus $
*****************************************************************************
* Copyright (c) 2009 Contributors - see below
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* bobtarling
*****************************************************************************
*
* Some portions of this file was previously release using the BSD License:
*/
// Copyright (c) 1996-2009 The Regents of the University of California. All
// Rights Reserved. Permission to use, copy, modify, and distribute this
// software and its documentation without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph appear in all copies. This software program and
// documentation are copyrighted by The Regents of the University of
// California. The software program and documentation are supplied "AS
// IS", without any accompanying services from The Regents. The Regents
// does not warrant that the operation of the program will be
// uninterrupted or error-free. The end-user understands that the program
// was developed for research purposes and is advised not to rely
// exclusively on the program for any reason. IN NO EVENT SHALL THE
// UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
package org.argouml.uml.diagram;
import java.util.Map;
import org.argouml.model.Model;
import org.argouml.uml.CommentEdge;
import org.argouml.uml.diagram.ui.FigCompartmentBox;
import org.argouml.uml.diagram.ui.FigEdgeModelElement;
import org.tigris.gef.base.Layer;
import org.tigris.gef.graph.GraphEdgeRenderer;
import org.tigris.gef.graph.GraphNodeRenderer;
import org.tigris.gef.presentation.Fig;
import org.tigris.gef.presentation.FigEdge;
import org.tigris.gef.presentation.FigNode;
/**
* Factory methods to create Figs based an model elements with supplementary
* data provided by a map of name value pairs.<p>
*
* Provides {@link org.tigris.gef.graph.GraphNodeRenderer#getFigNodeFor(
* Object, int, int, Map)} to implement the {@link GraphNodeRenderer}
* interface and {@link
* org.tigris.gef.graph.GraphEdgeRenderer#getFigEdgeFor(Object, Map)}
* to implement the {@link GraphEdgeRenderer} interface.
*/
public abstract class UmlDiagramRenderer
implements GraphNodeRenderer, GraphEdgeRenderer {
/**
* @deprecated for 0.27.3 by tfmorris. Only used by
* {@link DiagramFactory#createRenderingElement(Object, Object)}
* which is itself unused (and now deprecated).
*/
@Deprecated
public FigNode getFigNodeFor(
Object node, int x, int y,
Map styleAttributes) {
return null;
// if (node == null) {
// throw new IllegalArgumentException(
// "A model element must be supplied");
// }
// FigNode figNode = null;
// if (Model.getFacade().isAComment(node)) {
// figNode = new FigComment();
// } else if (Model.getFacade().isAStubState(node)) {
// return new FigStubState();
// } else if (Model.getFacade().isAAssociationClass(node)) {
// figNode = new FigClassAssociationClass(node, x, y, 10, 10);
// } else if (Model.getFacade().isAClass(node)) {
// figNode = new FigClass(node, x, y, 10, 10);
// } else if (Model.getFacade().isAInterface(node)) {
// figNode = new FigInterface();
// } else if (Model.getFacade().isAEnumeration(node)) {
// figNode = new FigEnumeration();
// } else if (Model.getFacade().isAStereotype(node)) {
// figNode = new FigStereotypeDeclaration();
// } else if (Model.getFacade().isADataType(node)) {
// figNode = new FigDataType();
// } else if (Model.getFacade().isAModel(node)) {
// figNode = new FigModel(node, x, y);
// } else if (Model.getFacade().isASubsystem(node)) {
// figNode = new FigSubsystem(node, x, y);
// } else if (Model.getFacade().isAPackage(node)) {
// figNode = new FigPackage(node, x, y);
// } else if (Model.getFacade().isAAssociation(node)) {
// figNode = new FigNodeAssociation();
// } else if (Model.getFacade().isAActor(node)) {
// figNode = new FigActor();
// } else if (Model.getFacade().isAUseCase(node)) {
// figNode = new FigUseCase();
// } else if (Model.getFacade().isAPartition(node)) {
// figNode = new FigPartition();
// } else if (Model.getFacade().isACallState(node)) {
// figNode = new FigCallState();
// } else if (Model.getFacade().isAObjectFlowState(node)) {
// figNode = new FigObjectFlowState();
// } else if (Model.getFacade().isASubactivityState(node)) {
// figNode = new FigSubactivityState();
// } else if (Model.getFacade().isAClassifierRole(node)) {
// figNode = new FigClassifierRole();
// } else if (Model.getFacade().isAMessage(node)) {
// figNode = new FigMessage();
// } else if (Model.getFacade().isANode(node)) {
// figNode = new FigMNode();
// } else if (Model.getFacade().isANodeInstance(node)) {
// figNode = new FigNodeInstance();
// } else if (Model.getFacade().isAComponent(node)) {
// figNode = new FigComponent();
// } else if (Model.getFacade().isAComponentInstance(node)) {
// figNode = new FigComponentInstance();
// } else if (Model.getFacade().isAObject(node)) {
// figNode = new FigObject();
// } else if (Model.getFacade().isAComment(node)) {
// figNode = new FigComment();
// } else if (Model.getFacade().isAActionState(node)) {
// figNode = new FigActionState();
// } else if (Model.getFacade().isAFinalState(node)) {
// figNode = new FigFinalState();
// } else if (Model.getFacade().isASubmachineState(node)) {
// figNode = new FigSubmachineState();
// } else if (Model.getFacade().isAConcurrentRegion(node)) {
// figNode = new FigConcurrentRegion();
// } else if (Model.getFacade().isASynchState(node)) {
// figNode = new FigSynchState();
// } else if (Model.getFacade().isACompositeState(node)) {
// figNode = new FigCompositeState();
// } else if (Model.getFacade().isAState(node)) {
// figNode = new FigSimpleState();
// } else if (Model.getFacade().isAPseudostate(node)) {
// Object pState = node;
// Object kind = Model.getFacade().getKind(pState);
// if (Model.getPseudostateKind().getInitial().equals(kind)) {
// figNode = new FigInitialState();
// } else if (Model.getPseudostateKind().getChoice()
// .equals(kind)) {
// figNode = new FigBranchState();
// } else if (Model.getPseudostateKind().getJunction()
// .equals(kind)) {
// figNode = new FigJunctionState();
// } else if (Model.getPseudostateKind().getFork().equals(kind)) {
// figNode = new FigForkState();
// } else if (Model.getPseudostateKind().getJoin().equals(kind)) {
// figNode = new FigJoinState();
// } else if (Model.getPseudostateKind().getShallowHistory()
// .equals(kind)) {
// figNode = new FigShallowHistoryState();
// } else if (Model.getPseudostateKind().getDeepHistory()
// .equals(kind)) {
// figNode = new FigDeepHistoryState();
// }
// }
//
// if (figNode == null) {
// throw new IllegalArgumentException(
// "Failed to construct a FigNode for " + node);
// }
// setStyleAttributes(figNode, styleAttributes);
//
// return figNode;
}
/**
* Set the fig style according to attributes. <p>
*
* TODO: This is never used! Can we remove?
*
* @param fig the fig to style.
* @param attributeMap a map of name value pairs
*/
private void setStyleAttributes(Fig fig, Map<String, String> attributeMap) {
String name;
String value;
for (Map.Entry<String, String> entry : attributeMap.entrySet()) {
name = entry.getKey();
value = entry.getValue();
if (fig instanceof FigCompartmentBox) {
FigCompartmentBox fcb = (FigCompartmentBox) fig;
if ("operationsVisible".equals(name)) {
fcb.showCompartment(
Model.getMetaTypes().getOperation(),
value.equalsIgnoreCase("true"));
} else if ("attributesVisible".equals(name)) {
fcb.showCompartment(
Model.getMetaTypes().getAttribute(),
value.equalsIgnoreCase("true"));
} else if ("enumerationLiteralsVisible".equals(name)) {
fcb.showCompartment(
Model.getMetaTypes().getEnumerationLiteral(),
value.equalsIgnoreCase("true"));
} else if ("extensionPointVisible".equals(name)) {
fcb.showCompartment(
Model.getMetaTypes().getExtensionPoint(),
value.equalsIgnoreCase("true"));
}
}
}
}
/**
* @deprecated for 0.27.3 by tfmorris. Use method of same name which takes
* a GraphModel argument in the subclasses of this class.
* @see org.tigris.gef.graph.GraphEdgeRenderer#getFigEdgeFor(java.lang.Object, java.util.Map)
*/
@Deprecated
public FigEdge getFigEdgeFor(Object edge, Map styleAttributes) {
return null;
// if (edge == null) {
// throw new IllegalArgumentException(
// "A model edge must be supplied");
// }
// FigEdge newEdge = null;
// if (Model.getFacade().isAAssociationClass(edge)) {
// newEdge = new FigAssociationClass();
// } else if (Model.getFacade().isAAssociationEnd(edge)) {
// newEdge = new FigAssociationEnd();
// } else if (Model.getFacade().isAAssociation(edge)) {
// newEdge = new FigAssociation();
// } else if (Model.getFacade().isALink(edge)) {
// newEdge = new FigLink();
// } else if (Model.getFacade().isAGeneralization(edge)) {
// newEdge = new FigGeneralization();
// } else if (Model.getFacade().isAPackageImport(edge)) {
// newEdge = new FigPermission();
// } else if (Model.getFacade().isAUsage(edge)) {
// newEdge = new FigUsage();
// } else if (Model.getFacade().isADependency(edge)) {
// if (Model.getExtensionMechanismsHelper().hasStereotype(edge,
// CoreFactory.REALIZE_STEREOTYPE)) {
// newEdge = new FigAbstraction();
// } else {
// newEdge = new FigDependency();
// }
// } else if (edge instanceof CommentEdge) {
// newEdge = null;
// } else if (Model.getFacade().isAAssociationRole(edge)) {
// newEdge = new FigAssociationRole();
// } else if (Model.getFacade().isATransition(edge)) {
// newEdge = new FigTransition();
// } else if (Model.getFacade().isAExtend(edge)) {
// newEdge = new FigExtend();
// } else if (Model.getFacade().isAInclude(edge)) {
// newEdge = new FigInclude();
// }
//
// if (newEdge == null) {
// throw new IllegalArgumentException(
// "Failed to construct a FigEdge for " + edge);
// }
//
// return newEdge;
}
/**
* Find the Figs in the given layer that should be the source and
* destination and attach these to either end of the FigEdge
* @param layer the layer to look for the FigNodes
* @param newEdge The edge to attach
*/
protected final void setPorts(Layer layer, FigEdge newEdge) {
Object modelElement = newEdge.getOwner();
if (newEdge.getSourcePortFig() == null) {
Object source;
if (modelElement instanceof CommentEdge) {
source = ((CommentEdge) modelElement).getSource();
} else {
source = Model.getUmlHelper().getSource(modelElement);
}
FigNode sourceNode = getNodePresentationFor(layer, source);
assert (sourceNode != null) : "No FigNode found for " + source;
setSourcePort(newEdge, sourceNode);
}
if (newEdge.getDestPortFig() == null) {
Object dest;
if (modelElement instanceof CommentEdge) {
dest = ((CommentEdge) modelElement).getDestination();
} else {
dest = Model.getUmlHelper().getDestination(newEdge.getOwner());
}
setDestPort(newEdge, getNodePresentationFor(layer, dest));
}
if (newEdge.getSourcePortFig() == null
|| newEdge.getDestPortFig() == null) {
throw new IllegalStateException("Edge of type "
+ newEdge.getClass().getName()
+ " created with no source or destination port");
}
}
private void setSourcePort(FigEdge edge, FigNode source) {
edge.setSourcePortFig(source);
edge.setSourceFigNode(source);
}
private void setDestPort(FigEdge edge, FigNode dest) {
edge.setDestPortFig(dest);
edge.setDestFigNode(dest);
}
/**
* Get the FigNode from the given layer that represents the given
* model element.
* The FigNode portion of an association class is returned in preference
* to the FigEdge portion.
* If no FigNode is found then a FIgEdge is searched for and the FigNode
* that acts as its edge port is returned.
* @param lay the layer containing the Fig
* @param modelElement the model element to find presentation for
* @return the FigNode presentation of the model element
*/
protected FigNode getNodePresentationFor(Layer lay, Object modelElement) {
assert modelElement != null : "A modelElement must be supplied";
for (Object fig : lay.getContentsNoEdges()) {
if (fig instanceof FigNode
&& modelElement.equals(((FigNode) fig).getOwner())) {
return ((FigNode) fig);
}
}
for (Object fig : lay.getContentsEdgesOnly()) {
if (fig instanceof FigEdgeModelElement
&& modelElement.equals(((FigEdgeModelElement) fig)
.getOwner())) {
return ((FigEdgeModelElement) fig).getEdgePort();
}
}
return null;
}
protected FigNode getFigNodeForAssociationEnd(
final ArgoDiagram diagram,
final Object associationEnd) {
final Object element;
element =
Model.getFacade().getClassifier(associationEnd);
return getNodePresentationFor(diagram.getLayer(), element);
}
protected void addEdge(Layer lay, FigEdge newEdge, Object edge) {
if (newEdge == null) {
throw new IllegalArgumentException(
"Don't know how to create FigEdge for model type "
+ edge.getClass().getName());
}
setPorts(lay, newEdge);
assert newEdge != null : "There has been no FigEdge created";
// newEdge.setDiElement(
// GraphChangeAdapter.getInstance().createElement(gm, edge));
assert newEdge != null : "There has been no FigEdge created";
assert (newEdge.getDestFigNode() != null)
: "The FigEdge has no dest node";
assert (newEdge.getDestPortFig() != null)
: "The FigEdge has no dest port";
assert (newEdge.getSourceFigNode() != null)
: "The FigEdge has no source node";
assert (newEdge.getSourcePortFig() != null)
: "The FigEdge has no source port";
lay.add(newEdge);
}
}
Run 1
The code exhibits significant readability issues. While the package structure and imports are clear, the class contains large blocks of commented-out code (hundreds of lines) that create massive visual noise and confusion. This violates the principle of clean, concise code and makes the actual active code difficult to locate. The 'Code Structure' criterion suffers because the meaningful logic is buried within deprecated stubs and commented sections. Nesting levels are moderate but the 'Clarity of intent' is severely compromised — methods like getFigNodeFor return null immediately, making their purpose unclear without reading extensive comments. 'Code length' is unnecessarily inflated due to dead code retention. Action granularity is generally acceptable where active code exists (one operation per line), but reading flow is completely disrupted by the alternating pattern of short active methods and massive commented blocks. The setStyleAttributes method is reasonably structured, but overall the file reads like an archaeological dig rather than maintained production code.
Run 2
The code scores poorly primarily due to massive commented-out blocks (hundreds of lines) that obscure the actual logic and inflate length. The structure is disorganized: the class mixes deprecated methods, private helpers, and protected methods without clear grouping. Nesting is moderate but the control flow is straightforward where code is active. Clarity of intent suffers because methods return null with no explanation (getFigNodeFor, getFigEdgeFor) and contain redundant null checks after earlier validation. Action granularity is acceptable—one action per line in most places—but reading flow is severely disrupted by the commented-out legacy code. The license header and Javadoc are verbose but not problematic. The active methods (setStyleAttributes, setPorts, getNodePresentationFor, addEdge) are readable but undermined by surrounding dead code. Overall, the code is maintainable only after significant cleanup, reducing readability from a potential 14-15 down to 7.
Run 3
The code has a clear class structure with logical method separation and good Javadoc, but readability is significantly harmed by massive blocks of commented-out code (hundreds of lines) in two methods. These dead code blocks obscure the active logic and make the file unnecessarily long. The active methods are mostly flat with low nesting and clear intent, using one action per line and helpful comments like `assert` messages. However, the `getNodePresentationFor` method uses two separate loops instead of one combined check, slightly reducing flow. The `setStyleAttributes` method has nested if-else chains that are acceptable but could be simplified. Overall, the active code is readable, but the overwhelming presence of commented-out legacy code drags the score down substantially.
Run 1
Code Structure: Methods separate rendering, port attachment, and presentation lookup logically, but obsolete stubs and unused helpers clutter the class. Nesting: Most active methods are shallow; style handling has moderately nested loops and conditionals. Clarity of intent: Descriptive method names and documentation explain core operations, although deprecated methods returning null and extensive commented-out implementations obscure current behavior. Code length: Excessive dead code, repeated assertions, and verbose legacy branches substantially inflate the file. Action granularity: Statements generally perform one action per line, with small helpers cleanly grouping related assignments. Reading flow: Active logic follows a reasonable sequence, but large inactive blocks, inconsistent whitespace, awkward line wrapping, and redundant checks interrupt scanning. Overall, the operational code is reasonably readable, while accumulated legacy material significantly reduces readability.
Run 2
Code Structure: Methods separate rendering, port attachment, and presentation lookup logically, but deprecated stubs and unused helpers clutter the class. Nesting: Active code has moderate, manageable nesting; the style handler combines a loop, type check, and conditional chain. Clarity of intent: Descriptive method names and Javadoc explain most operations, although null-returning factory stubs, raw Map parameters, and inconsistent naming weaken clarity. Code length: Large blocks of commented-out implementations dominate the file, while duplicate assertions and repetitive style branches add unnecessary length. Action granularity: Most statements perform one action per line, with small helpers making port updates explicit. Reading flow: Active methods generally follow natural sequences, but obsolete code, inconsistent whitespace, awkward line wrapping, and redundant checks interrupt scanning. The functioning portions are reasonably readable; substantial historical clutter prevents a higher score.
Run 3
Code Structure: Active functionality is reasonably separated into focused methods, but deprecated stubs, unused styling logic, and extensive commented-out implementations clutter the class. Nesting: Active code generally has shallow to moderate nesting; the styling loop adds several conditional levels. Clarity of intent: Descriptive method names and documentation explain port attachment and presentation lookup, although null-returning renderer stubs conflict with the class's stated factory purpose. Code length: Excessive retained legacy code and redundant assertions make the file substantially longer than necessary. Action granularity: Most statements perform one action, with small helpers grouping closely related updates appropriately. Reading flow: Active methods follow understandable sequences, but long disabled blocks, inconsistent indentation, awkward line breaks, and repeated checks interrupt scanning. Overall, the executable logic is fairly readable, while historical clutter significantly reduces the readability of the file as a whole.