Collapsible/Expandable UML Class Node Representation |
| Applies to: yFiles for Java 2.8, 2.7, 2.6, 2.5, 2.4, 2.3, 2.2, 2.1, 2.0 |
Type: Tips & Tricks
Categories this article belongs to:
| yFiles for Java | > yFiles Viewer | > Displaying and Editing Graphs | > Bringing Graph Elements to Life: The Realizer Concept |
Sample NodeRealizer implementation to represent UML class nodes.
Representations for UML class nodes can become quite huge when a class has many attributes and/or methods.
To counteract, a UML class node should be collapsible and expandable to reduce its size when needed.
The following sample code presents a NodeRealizer
implementation that provides such a collapse/expand feature.
It includes an inner class that defines a customized ViewMode
implementation to toggle the state of a UML class node.
package demo.view; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import javax.swing.JFrame; import y.base.NodeCursor; import y.geom.YPoint; import y.util.D; import y.util.YVersion; import y.view.EditMode; import y.view.Graph2D; import y.view.Graph2DView; import y.view.NodeLabel; import y.view.NodeRealizer; import y.view.ShapeNodeRealizer; import y.view.ViewMode; import y.view.YLabel; import y.view.hierarchy.GroupNodeRealizer; /** * NodeRealizer implementation that represents a UML class node. * This node realizer displays the following properties of a class * in UML notation: * <ul> * <li>class name * <li>stereotype property * <li>constraint property * <li>attribute list * <li>method list * <ul> * * Executing this class will display a sample instance of this realizer. */ public class UMLClassNodeRealizer extends ShapeNodeRealizer { private NodeLabel aLabel; // attributeLabel private NodeLabel mLabel; // methodLabel private NodeLabel sLabel; // stateLabel private boolean clipContent; private boolean omitDetails; private String constraint = ""; private String stereotype = ""; private static NodeLabel constraintLabel = new NodeLabel(); private static NodeLabel stereotypeLabel = new NodeLabel(); /** * Instantiates a new UMLClassNodeRealizer. */ public UMLClassNodeRealizer() { init(); } void init() { setShapeType(RECT_3D); getLabel().setModel(NodeLabel.INTERNAL); getLabel().setPosition(NodeLabel.TOP); getLabel().setFontSize(13); getLabel().setFontStyle(Font.BOLD); aLabel = new NodeLabel(); aLabel.bindRealizer(this); aLabel.setAlignment(YLabel.ALIGN_LEFT); aLabel.setModel(NodeLabel.FREE); mLabel = new NodeLabel(); mLabel.bindRealizer(this); mLabel.setAlignment(YLabel.ALIGN_LEFT); mLabel.setModel(NodeLabel.FREE); clipContent = true; omitDetails = false; sLabel = new NodeLabel(); sLabel.bindRealizer(this); sLabel.setPosition(NodeLabel.TOP_RIGHT); sLabel.setIcon(GroupNodeRealizer.defaultOpenGroupIcon); } /** * Instantiates a new UMLClassNodeRealzier as a copy of a given * realizer. */ public UMLClassNodeRealizer(NodeRealizer r) { super(r); if (r instanceof UMLClassNodeRealizer) { UMLClassNodeRealizer cnr = (UMLClassNodeRealizer)r; aLabel = (NodeLabel)cnr.aLabel.clone(); aLabel.bindRealizer(this); mLabel = (NodeLabel)cnr.mLabel.clone(); mLabel.bindRealizer(this); constraint = cnr.constraint; stereotype = cnr.stereotype; clipContent = cnr.clipContent; omitDetails = cnr.omitDetails; sLabel = (NodeLabel)cnr.sLabel.clone(); sLabel.bindRealizer(this); } else init(); } /** * Returns a UMLClassNodeRealizer that is a copy of the given * realizer. */ public NodeRealizer createCopy(NodeRealizer r) { return new UMLClassNodeRealizer(r); } ////////////////////////////////////////////////////////////////////////////// // SETTER & GETTER /////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// /** * Set the class name to be displayed by this realizer. */ public void setClassName(String name) { setLabelText(name); } /** * Returns the class name to be displayed by this realizer. */ public String getClassName() { return getLabelText(); } /** * Sets the constraint property of this realizer. */ public void setConstraint(String constraint) { this.constraint = constraint; } /** * Sets the stereotype property of this realizer. */ public void setStereotype(String stereotype) { this.stereotype = stereotype; } /** * Returns the constraint property of this realizer. */ public String getConstraint() { return constraint; } /** * Returns the stereotype property of this realizer. */ public String getStereotype() { return stereotype; } /** * Returns the node label that represents all added * method strings. */ public NodeLabel getMethodLabel() { return mLabel; } /** * Returns the node label that represents all added * attribute strings. */ public NodeLabel getAttributeLabel() { return aLabel; } /** * Returns the node label that represents the omitDetails * state. */ public NodeLabel getStateLabel() { return sLabel; } /** * Returns whether or not the display of the labels should be * clipped with the bounding box of the realizer. */ public boolean getClipContent() { return clipContent; } /** * Sets whether or not the display of the labels should be * clipped with the bounding box of the realizer. */ public void setClipContent(boolean clipping) { clipContent = clipping; } /** * Set whether or not this realizer should omit details when being displayed. */ public void setOmitDetails(boolean b) { omitDetails = b; if (omitDetails) sLabel.setIcon(GroupNodeRealizer.defaultClosedGroupIcon); else sLabel.setIcon(GroupNodeRealizer.defaultOpenGroupIcon); } /** * Returns whether or not this realizer should omit details when being displayed. */ public boolean getOmitDetails() { return omitDetails; } private void addToLabel(NodeLabel l, String s) { if (l.getText().length() > 0) l.setText(l.getText() + "\n" + s); else l.setText(s); } /** * Adds a class method label to this realizer. */ public void addMethod(String method) { addToLabel(mLabel, method); } /** * Adds a class attribute label to this realizer. */ public void addAttribute(String attr) { addToLabel(aLabel, attr); } /** * Set the size of this realizer automatically. This method will adapt the size * of this realizer so that the labels defined for it will fit within its * bounding box. */ public void fitContent() { double height = 3.0; double width = getLabel().getWidth() + 10; if (stereotype.length() > 0) { NodeLabel l = new NodeLabel(); l.setText("<<" + getStereotype() + ">>"); l.setModel(NodeLabel.FREE); l.bindRealizer(this); height += l.getHeight() + 5; width = Math.max(l.getWidth() + 10, width); } height += getLabel().getHeight() + 3; if (constraint.length() > 0) { NodeLabel l = new NodeLabel(); l.setText("{" + getConstraint() + "}"); l.setModel(NodeLabel.FREE); height += l.getHeight() + 5; width = Math.max(l.getWidth() + 10, width); } if (!omitDetails && !(aLabel.getText().equals("") && mLabel.getText().equals(""))) { height += 3; height += aLabel.getHeight() + 3; width = Math.max(aLabel.getWidth() + 10, width); height += 3; height += mLabel.getHeight() + 3; width = Math.max(mLabel.getWidth() + 10, width); } setSize(width, height); } ////////////////////////////////////////////////////////////////////////////// // GRAPHICS ///////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// /** * Paint the labels associated with this realizer. */ public void paintText(Graphics2D gfx) { Rectangle oldClip = null; if (clipContent) { oldClip = gfx.getClipBounds(); gfx.clipRect((int)x, (int)y, (int)width, (int)height); } sLabel.paint(gfx); double yoff = 3.0; if (stereotype.length() > 0) { NodeLabel l = new NodeLabel(); l.setText("<<" + getStereotype() + ">>"); l.setModel(NodeLabel.FREE); l.setOffset((getWidth() - l.getWidth()) / 2.0, yoff); l.bindRealizer(this); l.paint(gfx); yoff += l.getHeight() + 5; } NodeLabel label = getLabel(); label.setOffset((getWidth() - label.getWidth()) / 2.0, yoff); label.paint(gfx); yoff += label.getHeight() + 3; if (constraint.length() > 0) { NodeLabel l = new NodeLabel(); l.setText("{" + getConstraint() + "}"); l.setModel(NodeLabel.FREE); l.setOffset(getWidth() - l.getWidth() - 5.0, yoff); l.bindRealizer(this); l.paint(gfx); yoff += l.getHeight() + 5; } if (!omitDetails && !(aLabel.getText().equals("") && mLabel.getText().equals(""))) { gfx.setColor(getLineColor()); gfx.drawLine((int)x + 1, (int)(y + yoff), (int)(x + width - 1), (int)(y + yoff)); yoff += 3; aLabel.setOffset(3, yoff); aLabel.paint(gfx); yoff += aLabel.getHeight() + 3; gfx.drawLine((int)x + 1, (int)(y + yoff), (int)(x + width - 1), (int)(y + yoff)); yoff += 3; mLabel.setOffset(3, yoff); mLabel.paint(gfx); } if (clipContent) { gfx.setClip(oldClip); } } ////////////////////////////////////////////////////////////////////////////// // SERIALIZATION ///////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// /** * Serialization routine that allows this realizer to be written out * in YGF graph format. */ public void write(ObjectOutputStream out) throws IOException { out.writeByte(YVersion.VERSION_1); super.write(out); aLabel.write(out); mLabel.write(out); out.writeBoolean(clipContent); out.writeBoolean(omitDetails); out.writeObject(getStereotype()); out.writeObject(getConstraint()); } /** * Deserialization routine that allows this realizer to be read in * from YGF graph format. */ public void read(ObjectInputStream in) throws IOException, ClassNotFoundException { switch (in.readByte()) { case YVersion.VERSION_1: super.read(in); init(); aLabel.read(in); mLabel.read(in); clipContent = in.readBoolean(); omitDetails = in.readBoolean(); stereotype = (String)in.readObject(); constraint = (String)in.readObject(); break; default: D.fatal("Unsupported Format"); } } static class ToggleDetailsMode extends ViewMode { public void mouseClicked(MouseEvent ev) { double x = translateX(ev.getX()); double y = translateY(ev.getY()); Graph2D graph = getGraph2D(); for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) { if (graph.getRealizer(nc.node()) instanceof UMLClassNodeRealizer) { UMLClassNodeRealizer cnr = (UMLClassNodeRealizer)graph.getRealizer(nc.node()); NodeLabel l = cnr.getStateLabel(); if (l.getBox().contains(x, y)) { YPoint p = graph.getLocation(nc.node()); cnr.setOmitDetails(!cnr.getOmitDetails()); cnr.fitContent(); graph.setLocation(nc.node(), p); graph.updateViews(); break; } } } } } /** * Launcher method. Execute this method to see a sample instantiation of * this node realizer in action. */ public static void main(String[] args) { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Graph2DView view = new Graph2DView(); view.addViewMode(new ToggleDetailsMode()); frame.setContentPane(view); UMLClassNodeRealizer r = new UMLClassNodeRealizer(); r.setClassName("com.mycompany.MyClass"); r.setConstraint("abstract"); r.setStereotype("factory"); r.addAttribute("-graph"); r.addAttribute("-id"); r.addMethod("+setGraph(Graph)"); r.addMethod("+getGraph():Graph"); r.addMethod("+setID(int)"); r.addMethod("+getID():int"); r.fitContent(); view.getGraph2D().setDefaultNodeRealizer(r); view.getGraph2D().createNode(); view.addViewMode(new EditMode()); view.fitContent(); frame.pack(); frame.setVisible(true); } } |
| Resources: |
| Keywords: | UML - class - NodeRealizer - node - representation - rendering - graphical - collapse - expand - collapsing - expanding |


