diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/.classpath b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/.classpath new file mode 100644 index 0000000..2d9e006 --- /dev/null +++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/.project b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/.project new file mode 100644 index 0000000..b37e6b7 --- /dev/null +++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/.project @@ -0,0 +1,17 @@ + + + VioletPlugin.SequenceDiagram + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/horstmann.eclipse.code.formatter.xml b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/horstmann.eclipse.code.formatter.xml new file mode 100644 index 0000000..bef0923 --- /dev/null +++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/horstmann.eclipse.code.formatter.xml @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/pom.xml b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/pom.xml new file mode 100644 index 0000000..1336f5e --- /dev/null +++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/pom.xml @@ -0,0 +1,62 @@ + + + 4.0.0 + com.horstmann.violet.plugin + com.horstmann.violet.plugin.sequencediagram + Violet UML Editor Sequence Diagram Plugin + 2.0.0-SNAPSHOT + + jar + + + com.horstmann.violet.framework + com.horstmann.violet.framework + 2.0.0-SNAPSHOT + + + + + + src/main/resources + + + src/main/java + + **/*.gif + **/*.jpg + **/*.properties + **/*.xml + + + + + + maven-compiler-plugin + 2.0.2 + + 1.6 + 1.6 + + + + + + + web.sourceforge.net + Violet's Maven Repository + + sftp://web.sourceforge.net/home/groups/v/vi/violet/htdocs/maven2/repo + + + + + + violet.repo + Violet's Maven repository (public access) + http://violet.sourceforge.net/maven2/repo/ + + + \ No newline at end of file diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/ActivationBarNode.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/ActivationBarNode.java new file mode 100644 index 0000000..640a781 --- /dev/null +++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/ActivationBarNode.java @@ -0,0 +1,727 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.sequence; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.List; + +import javax.lang.model.type.NullType; + +import com.horstmann.violet.product.diagram.abstracts.Direction; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.product.diagram.abstracts.node.RectangularNode; + +/** + * An activation bar in a sequence diagram. This activation bar is hang on a lifeline (implicit parameter) + */ +public class ActivationBarNode extends RectangularNode +{ + + @Override + public boolean addChild(INode n, Point2D p) + { + if (!n.getClass().isAssignableFrom(ActivationBarNode.class)) + { + return false; + } + n.setParent(this); + n.setGraph(getGraph()); + n.setLocation(p); + addChild(n, getChildren().size()); + return true; + } + + + @Override + public void removeChild(INode node) + { + super.removeChild(node); + } + + @Override + public boolean addConnection(IEdge edge) + { + INode endingNode = edge.getEnd(); + INode startingNode = edge.getStart(); + if (startingNode == endingNode) + { + return false; + } + if (edge instanceof CallEdge) + { + return isCallEdgeAcceptable((CallEdge) edge); + + } + else if (edge instanceof ReturnEdge) + { + return isReturnEdgeAcceptable((ReturnEdge) edge); + } + return false; + } + + @Override + public void removeConnection(IEdge e) + { + super.removeConnection(e); + } + + @Override + public Point2D getConnectionPoint(IEdge e) + { + boolean isCallEdge = e.getClass().isAssignableFrom(CallEdge.class); + boolean isReturnEdge = e.getClass().isAssignableFrom(ReturnEdge.class); + boolean isActivationBarNodeOnStart = e.getStart() != null + && e.getStart().getClass().isAssignableFrom(ActivationBarNode.class); + boolean isActivationBarNodeOnEnd = e.getEnd() != null && e.getEnd().getClass().isAssignableFrom(ActivationBarNode.class); + boolean isLifelineNodeOnEnd = e.getEnd() != null && e.getEnd().getClass().isAssignableFrom(LifelineNode.class); + if (isCallEdge) + { + if (isActivationBarNodeOnStart && isActivationBarNodeOnEnd) + { + ActivationBarNode startingNode = (ActivationBarNode) e.getStart(); + ActivationBarNode endingNode = (ActivationBarNode) e.getEnd(); + LifelineNode startingLifelineNode = startingNode.getImplicitParameter(); + LifelineNode endingLifelineNode = endingNode.getImplicitParameter(); + boolean isSameLifelineNode = startingLifelineNode != null && endingLifelineNode != null + && startingLifelineNode.equals(endingLifelineNode); + boolean isDifferentLifelineNodes = startingLifelineNode != null && endingLifelineNode != null + && !startingLifelineNode.equals(endingLifelineNode); + // Case 1 : two activation bars connected on differents + // LifeLines + if (isDifferentLifelineNodes && isActivationBarNodeOnStart && isActivationBarNodeOnEnd) + { + boolean isStartingNode = this.equals(e.getStart()); + boolean isEndingNode = this.equals(e.getEnd()); + if (isStartingNode) + { + Point2D startingNodeLocation = getLocationOnGraph(); + Point2D endingNodeLocation = e.getEnd().getLocationOnGraph(); + Direction d = e.getDirection(this); + if (d.getX() > 0) + { + double x = startingNodeLocation.getX(); + double y = endingNodeLocation.getY(); + return new Point2D.Double(x, y); + } + else + { + double x = startingNodeLocation.getX() + DEFAULT_WIDTH; + double y = endingNodeLocation.getY(); + return new Point2D.Double(x, y); + } + } + if (isEndingNode) + { + Point2D endingNodeLocation = getLocationOnGraph(); + Direction d = e.getDirection(this); + if (d.getX() > 0) + { + double x = endingNodeLocation.getX(); + double y = endingNodeLocation.getY(); + return new Point2D.Double(x, y); + } + else + { + double x = endingNodeLocation.getX() + DEFAULT_WIDTH; + double y = endingNodeLocation.getY(); + return new Point2D.Double(x, y); + } + } + } + // Case 2 : two activation bars connected on same lifeline (self + // call) + if (isSameLifelineNode && isActivationBarNodeOnStart && isActivationBarNodeOnEnd) + { + boolean isStartingNode = this.equals(e.getStart()); + boolean isEndingNode = this.equals(e.getEnd()); + if (isStartingNode) + { + Point2D startingNodeLocation = getLocationOnGraph(); + Point2D endingNodeLocation = e.getEnd().getLocation(); + double x = startingNodeLocation.getX() + DEFAULT_WIDTH; + double y = startingNodeLocation.getY() + endingNodeLocation.getY() - CALL_YGAP / 2; + return new Point2D.Double(x, y); + } + if (isEndingNode) + { + Point2D endingNodeLocation = getLocationOnGraph(); + double x = endingNodeLocation.getX() + DEFAULT_WIDTH; + double y = endingNodeLocation.getY(); + return new Point2D.Double(x, y); + } + } + } + if (isActivationBarNodeOnStart && isLifelineNodeOnEnd) + { + Direction d = e.getDirection(this); + Point2D startingNodeLocation = getLocationOnGraph(); + if (d.getX() > 0) + { + double x = startingNodeLocation.getX(); + double y = startingNodeLocation.getY() + CALL_YGAP / 2; + return new Point2D.Double(x, y); + } + else + { + double x = startingNodeLocation.getX() + DEFAULT_WIDTH; + double y = startingNodeLocation.getY() + CALL_YGAP / 2; + return new Point2D.Double(x, y); + } + } + } + if (isReturnEdge) + { + if (isActivationBarNodeOnStart && isActivationBarNodeOnEnd) + { + ActivationBarNode startingNode = (ActivationBarNode) e.getStart(); + ActivationBarNode endingNode = (ActivationBarNode) e.getEnd(); + LifelineNode startingLifelineNode = startingNode.getImplicitParameter(); + LifelineNode endingLifelineNode = endingNode.getImplicitParameter(); + boolean isDifferentLifelineNodes = startingLifelineNode != null && endingLifelineNode != null + && !startingLifelineNode.equals(endingLifelineNode); + // Case 1 : two activation bars connected on differents + // LifeLines + if (isDifferentLifelineNodes && isActivationBarNodeOnStart && isActivationBarNodeOnEnd) + { + boolean isStartingNode = this.equals(e.getStart()); + boolean isEndingNode = this.equals(e.getEnd()); + if (isStartingNode) + { + Point2D startingNodeLocation = getLocationOnGraph(); + Rectangle2D startingNodeBounds = getBounds(); + Direction d = e.getDirection(this); + if (d.getX() > 0) + { + double x = startingNodeLocation.getX(); + double y = startingNodeLocation.getY() + startingNodeBounds.getHeight(); + return new Point2D.Double(x, y); + } + else + { + double x = startingNodeLocation.getX() + DEFAULT_WIDTH; + double y = startingNodeLocation.getY() + startingNodeBounds.getHeight(); + return new Point2D.Double(x, y); + } + } + if (isEndingNode) + { + Point2D startingNodeLocation = e.getStart().getLocationOnGraph(); + Rectangle2D startingNodeBounds = e.getStart().getBounds(); + Point2D endingNodeLocation = getLocationOnGraph(); + Direction d = e.getDirection(this); + if (d.getX() > 0) + { + double x = endingNodeLocation.getX(); + double y = startingNodeLocation.getY() + startingNodeBounds.getHeight(); + return new Point2D.Double(x, y); + } + else + { + double x = endingNodeLocation.getX() + DEFAULT_WIDTH; + double y = startingNodeLocation.getY() + startingNodeBounds.getHeight(); + return new Point2D.Double(x, y); + } + } + } + } + } + // Default case + Direction d = e.getDirection(this); + if (d.getX() > 0) + { + double y = getBounds().getMinY(); + double x = getBounds().getMaxX(); + return new Point2D.Double(x, y); + } + else + { + double y = getBounds().getMinY(); + double x = getBounds().getX(); + return new Point2D.Double(x, y); + } + + } + + + + /** + * + * @return true if this activation bar is connected to another one from another lifeline with a CallEdge AND if this activation + * bar is the STARTING node of this edge + */ + private boolean isCallingNode() + { + LifelineNode currentLifelineNode = getImplicitParameter(); + for (IEdge edge : getGraph().getAllEdges()) + { + if (edge.getStart() != this) + { + continue; + } + if (!edge.getClass().isAssignableFrom(CallEdge.class)) + { + continue; + } + INode endingNode = edge.getEnd(); + if (!endingNode.getClass().isAssignableFrom(ActivationBarNode.class)) + { + continue; + } + if (((ActivationBarNode) endingNode).getImplicitParameter() == currentLifelineNode) + { + continue; + } + return true; + } + return false; + } + + /** + * + * @return true if this activation bar has been called by another activation bar + */ + private boolean isCalledNode() + { + LifelineNode currentLifelineNode = getImplicitParameter(); + for (IEdge edge : getGraph().getAllEdges()) + { + if (edge.getEnd() != this) + { + continue; + } + if (!edge.getClass().isAssignableFrom(CallEdge.class)) + { + continue; + } + INode startingNode = edge.getStart(); + if (!startingNode.getClass().isAssignableFrom(ActivationBarNode.class)) + { + continue; + } + if (((ActivationBarNode) startingNode).getImplicitParameter() == currentLifelineNode) + { + continue; + } + return true; + } + return false; + } + + @Override + public Rectangle2D getBounds() + { + Point2D nodeLocation = getLocation(); + // Height + double height = getHeight(); + // TODO : manage openbottom + Rectangle2D currentBounds = new Rectangle2D.Double(nodeLocation.getX(), nodeLocation.getY(), DEFAULT_WIDTH, height); + Rectangle2D snappedBounds = getGraph().getGrid().snap(currentBounds); + return snappedBounds; + } + + public double getHeight() + { + double height = DEFAULT_HEIGHT; + boolean isCallingNode = isCallingNode(); + if (isCallingNode) + { + height = getHeightWhenLinked(); + } + if (!isCallingNode) + { + height = getHeightWhenHasChildren(); + } + return height; + } + + /** + * If this activation bar calls another activation bar on another life line, its height must be greater than the activation bar + * which is called + * + * @return h + */ + private double getHeightWhenLinked() + { + double height = 0; + for (IEdge edge : getGraph().getAllEdges()) + { + if (!edge.getClass().isAssignableFrom(CallEdge.class)) + { + continue; + } + if (edge.getStart() == this) + { + INode endingNode = edge.getEnd(); + boolean isActivationBarNode = endingNode instanceof ActivationBarNode; + if (isActivationBarNode) + { + Rectangle2D endingNodeBounds = endingNode.getBounds(); + double newHeight = CALL_YGAP / 2 + endingNodeBounds.getHeight() + (endingNode.getLocationOnGraph().getY() - this.getLocationOnGraph().getY()) + CALL_YGAP / 2; + height = Math.max(height, newHeight); + } + } + } + return Math.max(DEFAULT_HEIGHT, height); + } + + /** + * + * @return + */ + private double getHeightWhenHasChildren() + { + double height = DEFAULT_HEIGHT; + int childVisibleNodesCounter = 0; + for (INode aNode : getChildren()) + { + if (aNode instanceof ActivationBarNode) + { + childVisibleNodesCounter++; + } + } + if (childVisibleNodesCounter > 0) + { + for (INode aNode : getChildren()) + { + if (aNode instanceof ActivationBarNode) + { + ActivationBarNode anActivationBarNode = (ActivationBarNode) aNode; + double h = anActivationBarNode.getHeight(); + double v = anActivationBarNode.getLocation().getY(); + double maxY = v + h; + height = Math.max(height, maxY); + } + } + height = height + CALL_YGAP; + } + return height; + } + + @Override + public void setLocation(Point2D aPoint) + { + INode parentNode = getParent(); + // Special use case : when this node is connected to another activation bar on another life line, + // we adjust its location to keep it relative to the node it is connected to + if (parentNode != null && parentNode.getClass().isAssignableFrom(LifelineNode.class)) { + LifelineNode lifelineNode = getImplicitParameter(); + Rectangle2D topRectangle = lifelineNode.getTopRectangle(); + if (aPoint.getY() <= topRectangle.getHeight() + CALL_YGAP / 2) { + aPoint = new Point2D.Double(aPoint.getX(), topRectangle.getHeight() + CALL_YGAP / 2); + } + for (IEdge edge : getConnectedEdges()) { + if (!edge.getClass().isAssignableFrom(CallEdge.class)) + { + continue; + } + if (edge.getEnd() == this) + { + INode startingNode = edge.getStart(); + Point2D startingNodeLocationOnGraph = startingNode.getLocationOnGraph(); + Point2D lifelineLocationOnGraph = getImplicitParameter().getLocationOnGraph(); + this.verticalGapBetweenConnectedActivationBars = aPoint.getY() - startingNodeLocationOnGraph.getY() + lifelineLocationOnGraph.getY(); + if (this.verticalGapBetweenConnectedActivationBars < CALL_YGAP / 2) { + double minY = startingNodeLocationOnGraph.getY() - lifelineLocationOnGraph.getY() + CALL_YGAP / 2; + aPoint = new Point2D.Double(aPoint.getX(), minY); + this.verticalGapBetweenConnectedActivationBars = CALL_YGAP / 2; + } + break; + } + } + } + super.setLocation(aPoint); + } + + @Override + public Point2D getLocation() + { + INode parentNode = getParent(); + // Case 1 : self call on activation bar + if (parentNode != null && parentNode.getClass().isAssignableFrom(ActivationBarNode.class)) { + Point2D rawLocation = super.getLocation(); + double horizontalLocation = getHorizontalLocation(); + double verticalLocation = rawLocation.getY(); + verticalLocation = Math.max(verticalLocation, CALL_YGAP); + Point2D adjustedLocation = new Point2D.Double(horizontalLocation, verticalLocation); + super.setLocation(adjustedLocation); + return adjustedLocation; + } + // Case 2 : connected to another activation bar on another life line + if (parentNode != null && parentNode.getClass().isAssignableFrom(LifelineNode.class)) { + for (IEdge edge : getConnectedEdges()) { + if (!edge.getClass().isAssignableFrom(CallEdge.class)) + { + continue; + } + if (edge.getEnd() == this) + { + INode startingNode = edge.getStart(); + Point2D startingNodeLocationOnGraph = startingNode.getLocationOnGraph(); + Point2D lifelineLocationOnGraph = getImplicitParameter().getLocationOnGraph(); + double x = getHorizontalLocation(); + double y = startingNodeLocationOnGraph.getY() - lifelineLocationOnGraph.getY() + this.verticalGapBetweenConnectedActivationBars; + Point2D adjustedLocation = new Point2D.Double(x, y); + super.setLocation(adjustedLocation); + return adjustedLocation; + } + } + } + // Case 3 : just attached to a lifeline + if (parentNode != null && parentNode.getClass().isAssignableFrom(LifelineNode.class)) { + Point2D rawLocation = super.getLocation(); + double horizontalLocation = getHorizontalLocation(); + double verticalLocation = rawLocation.getY(); + Point2D adjustedLocation = new Point2D.Double(horizontalLocation, verticalLocation); + super.setLocation(adjustedLocation); + return adjustedLocation; + } + + return super.getLocation(); + } + + @Override + public void draw(Graphics2D g2) + { + // Translate g2 if node has parent + Point2D nodeLocationOnGraph = getLocationOnGraph(); + Point2D nodeLocation = getLocation(); + Rectangle2D b = getBounds(); + Point2D g2Location = new Point2D.Double(nodeLocationOnGraph.getX() - nodeLocation.getX(), nodeLocationOnGraph.getY() + - nodeLocation.getY()); + g2.translate(g2Location.getX(), g2Location.getY()); + // Perform painting + super.draw(g2); + Color oldColor = g2.getColor(); + g2.setColor(Color.WHITE); + g2.fill(b); + g2.setColor(oldColor); + g2.draw(b); + // Restore g2 original location + g2.translate(-g2Location.getX(), -g2Location.getY()); + // Reset location for next draw + // Draw its children + for (INode node : getChildren()) + { + node.draw(g2); + } + } + + /** + * Gets the participant's life line of this call. Note : method's name is ot set to getLifeLine to keep compatibility with older + * versions + * + * @return the participant's life line + */ + public LifelineNode getImplicitParameter() + { + if (this.lifeline == null) { + INode aParent = this.getParent(); + List nodeStack = new ArrayList(); + nodeStack.add(aParent); + while (!nodeStack.isEmpty()) { + INode aNode = nodeStack.get(0); + if (LifelineNode.class.isInstance(aNode)) { + this.lifeline = (LifelineNode) aNode; + } + if (ActivationBarNode.class.isInstance(aNode)) { + INode aNodeParent = aNode.getParent(); + nodeStack.add(aNodeParent); + } + nodeStack.remove(0); + } + } + return lifeline; + } + + /** + * Sets the participant's life line of this call. Note : method's name is ot set to setLifeLine to keep compatibility with older + * versions + * + * @param newValue the participant's lifeline + */ + public void setImplicitParameter(LifelineNode newValue) + { + // Nothing to do. Just here to keep compatibility. + } + + private boolean isReturnEdgeAcceptable(ReturnEdge edge) + { + INode endingNode = edge.getEnd(); + INode startingNode = edge.getStart(); + Class startingNodeClass = startingNode.getClass(); + Class endingNodeClass = endingNode.getClass(); + if (!startingNodeClass.isAssignableFrom(ActivationBarNode.class) + || !endingNodeClass.isAssignableFrom(ActivationBarNode.class)) + { + return false; + } + ActivationBarNode startingActivationBarNode = (ActivationBarNode) startingNode; + ActivationBarNode endingActivationBarNode = (ActivationBarNode) endingNode; + if (startingActivationBarNode.getImplicitParameter() == endingActivationBarNode.getImplicitParameter()) + { + return false; + } + if (!isCalledNode()) + { + return false; + } + return true; + } + + private boolean isCallEdgeAcceptable(CallEdge edge) + { + INode endingNode = edge.getEnd(); + INode startingNode = edge.getStart(); + Point2D endingNodePoint = edge.getEndLocation(); + Class startingNodeClass = (startingNode != null ? startingNode.getClass() : NullType.class); + Class endingNodeClass = (endingNode != null ? endingNode.getClass() : NullType.class); + // Case 1 : classic connection between activation bars + if (startingNodeClass.isAssignableFrom(ActivationBarNode.class) + && endingNodeClass.isAssignableFrom(ActivationBarNode.class)) + { + return true; + } + // Case 2 : an activation bar creates a new class instance + if (startingNodeClass.isAssignableFrom(ActivationBarNode.class) && endingNodeClass.isAssignableFrom(LifelineNode.class)) + { + ActivationBarNode startingActivationBarNode = (ActivationBarNode) startingNode; + LifelineNode startingLifeLineNode = startingActivationBarNode.getImplicitParameter(); + LifelineNode endingLifeLineNode = (LifelineNode) endingNode; + Rectangle2D topRectangle = endingLifeLineNode.getTopRectangle(); + if (startingLifeLineNode != endingLifeLineNode && topRectangle.contains(endingNodePoint)) + { + return true; + } + } + // Case 3 : classic connection between activation bars but the ending + // bar doesn't exist and need to be automatically created + if (startingNodeClass.isAssignableFrom(ActivationBarNode.class) && endingNodeClass.isAssignableFrom(LifelineNode.class)) + { + ActivationBarNode startingActivationBarNode = (ActivationBarNode) startingNode; + LifelineNode startingLifeLineNode = startingActivationBarNode.getImplicitParameter(); + LifelineNode endingLifeLineNode = (LifelineNode) endingNode; + Rectangle2D topRectangle = endingLifeLineNode.getTopRectangle(); + if (startingLifeLineNode != endingLifeLineNode && !topRectangle.contains(endingNodePoint)) + { + ActivationBarNode newActivationBar = new ActivationBarNode(); + Point2D edgeStartLocation = edge.getEndLocation(); + double x = edgeStartLocation.getX(); + double y = edgeStartLocation.getY(); + Point2D newActivationBarLocation = new Point2D.Double(x, y); + endingNode.addChild(newActivationBar, newActivationBarLocation); + edge.setEnd(newActivationBar); + return true; + } + } + // Case 4 : self call on an activation bar + if (startingNodeClass.isAssignableFrom(ActivationBarNode.class) && endingNodeClass.isAssignableFrom(LifelineNode.class)) + { + ActivationBarNode startingActivationBarNode = (ActivationBarNode) startingNode; + LifelineNode startingLifeLineNode = startingActivationBarNode.getImplicitParameter(); + LifelineNode endingLifeLineNode = (LifelineNode) endingNode; + Rectangle2D topRectangle = endingLifeLineNode.getTopRectangle(); + if (startingLifeLineNode == endingLifeLineNode && !topRectangle.contains(endingNodePoint)) + { + ActivationBarNode newActivationBar = new ActivationBarNode(); + Point2D edgeStartLocation = edge.getStartLocation(); + double x = edgeStartLocation.getX(); + double y = edgeStartLocation.getY() + CALL_YGAP / 2; + Point2D newActivationBarLocation = new Point2D.Double(x, y); + startingNode.addChild(newActivationBar, newActivationBarLocation); + edge.setEnd(newActivationBar); + return true; + } + } + // Case 5 : self call on an activation bar but its child which is called + // doesn"t exist and need to be created automatically + if (startingNodeClass.isAssignableFrom(ActivationBarNode.class) && endingNodeClass.isAssignableFrom(NullType.class)) + { + ActivationBarNode newActivationBar = new ActivationBarNode(); + int lastNodePos = startingNode.getChildren().size(); + startingNode.addChild(newActivationBar, lastNodePos); + edge.setEnd(newActivationBar); + return true; + } + return false; + } + + /** + * Finds an edge in the graph connected to start and end nodes + * + * @param g the graph + * @param start the start node + * @param end the end node + * @return the edge or null if no one is found + */ + private IEdge findEdge(INode start, INode end) + { + for (IEdge e : getGraph().getAllEdges()) + { + if (e.getStart() == start && e.getEnd() == end) return e; + } + return null; + } + + /** + * @return x location relative to the parent + */ + private double getHorizontalLocation() + { + INode parentNode = getParent(); + if (parentNode != null && parentNode.getClass().isAssignableFrom(ActivationBarNode.class)) + { + return DEFAULT_WIDTH / 2; + } + if (parentNode != null && parentNode.getClass().isAssignableFrom(LifelineNode.class)) + { + LifelineNode lifeLineNode = (LifelineNode) parentNode; + Rectangle2D lifeLineTopRectangle = lifeLineNode.getTopRectangle(); + return lifeLineTopRectangle.getWidth() / 2 - DEFAULT_WIDTH / 2; + } + return 0; + } + + + + + + + /** The lifeline that embeds this activation bar in the sequence diagram */ + private transient LifelineNode lifeline; + + /** When this node is connected to another activation bar, we keep the vertical gap to be able to have ending node location relative to the starting one */ + private double verticalGapBetweenConnectedActivationBars = CALL_YGAP / 2; + + /** Default with */ + private static int DEFAULT_WIDTH = 16; + + /** Default height */ + private static int DEFAULT_HEIGHT = 30; + + /** Default vertical gap between two call nodes and a call node and an implicit node */ + public static int CALL_YGAP = 20; +} diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/CallEdge.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/CallEdge.java new file mode 100644 index 0000000..3b5157e --- /dev/null +++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/CallEdge.java @@ -0,0 +1,144 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.sequence; + +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.util.ArrayList; + +import com.horstmann.violet.product.diagram.abstracts.edge.SegmentedLineEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.product.diagram.abstracts.property.ArrowHead; + +/** + * An edge that joins two call nodes. Typically, call edges are used in sequence diagram to represent calls between entities (call + * nodes). + */ +public class CallEdge extends SegmentedLineEdge +{ + /** + * Default constructor + */ + public CallEdge() + { + setSignal(false); + } + + /** + * Gets the signal property. + * + * @return true if this is an edge that represents an asynchronus signal + */ + public boolean isSignal() + { + return signal; + } + + /** + * Sets the signal property. + * + * @param newValue true if this is an edge that represents an asynchronus signal + */ + public void setSignal(boolean newValue) + { + signal = newValue; + + } + + @Override + public ArrowHead getEndArrowHead() + { + if (signal) + { + return ArrowHead.V; + } + return ArrowHead.BLACK_TRIANGLE; + } + + @Override + public Line2D getConnectionPoints() + { + ArrayList points = getPoints(); + Point2D p1 = points.get(0); + Point2D p2 = points.get(points.size() - 1); + return new Line2D.Double(p1, p2); + } + + @Override + public ArrayList getPoints() + { + INode endingNode = getEnd(); + INode startingNode = getStart(); + if (isActivationBarsOnSameLifeline(startingNode, endingNode)) + { + return getPointsForLoopOnActivationBarNode(startingNode, endingNode); + } + return getPointsForActivationBarsOnDifferentLifeLines(startingNode, endingNode); + } + + private boolean isActivationBarsOnSameLifeline(INode startingNode, INode endingNode) + { + if (startingNode.getClass().isAssignableFrom(ActivationBarNode.class) + && endingNode.getClass().isAssignableFrom(ActivationBarNode.class)) + { + ActivationBarNode startingActivationBarNode = (ActivationBarNode) startingNode; + ActivationBarNode endingActivationBarNode = (ActivationBarNode) endingNode; + if (startingActivationBarNode.getImplicitParameter() == endingActivationBarNode.getImplicitParameter()) + { + return true; + } + } + return false; + } + + private ArrayList getPointsForActivationBarsOnDifferentLifeLines(INode startingNode, INode endingNode) + { + Point2D startingPoint = startingNode.getConnectionPoint(this); + Point2D endingPoint = endingNode.getConnectionPoint(this); + ArrayList a = new ArrayList(); + a.add(startingPoint); + a.add(endingPoint); + return a; + } + + private ArrayList getPointsForLoopOnActivationBarNode(INode startingNode, INode endingNode) + { + ArrayList a = new ArrayList(); + Point2D startingPoint = startingNode.getConnectionPoint(this); + Point2D endingPoint = endingNode.getConnectionPoint(this); + Point2D p = startingPoint; + Point2D q = new Point2D.Double(endingPoint.getX() + LOOP_GAP, p.getY()); + Point2D r = new Point2D.Double(q.getX(), endingPoint.getY()); + Point2D s = endingPoint; + a.add(p); + a.add(q); + a.add(r); + a.add(s); + return a; + } + + /** Indicate if the node represents an asynchonus signal */ + private boolean signal; + + /** Horizintal gap used to connected two activation bars on the same lifeline */ + private static int LOOP_GAP = 15; +} diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/CallEdgeBeanInfo.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/CallEdgeBeanInfo.java new file mode 100644 index 0000000..aef2071 --- /dev/null +++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/CallEdgeBeanInfo.java @@ -0,0 +1,48 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.sequence; + +import java.beans.IntrospectionException; +import java.beans.PropertyDescriptor; +import java.beans.SimpleBeanInfo; + +/** + * The bean info for the CallEdge type. + */ +public class CallEdgeBeanInfo extends SimpleBeanInfo +{ + public PropertyDescriptor[] getPropertyDescriptors() + { + try + { + return new PropertyDescriptor[] + { + new PropertyDescriptor("middleLabel", CallEdge.class), + new PropertyDescriptor("signal", CallEdge.class) + }; + } + catch (IntrospectionException exception) + { + return null; + } + } +} diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/LifelineNode.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/LifelineNode.java new file mode 100644 index 0000000..680f657 --- /dev/null +++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/LifelineNode.java @@ -0,0 +1,326 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.sequence; + +import java.awt.BasicStroke; +import java.awt.Graphics2D; +import java.awt.Stroke; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Collection; +import java.util.List; + +import com.horstmann.violet.product.diagram.abstracts.Direction; +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.product.diagram.abstracts.node.RectangularNode; +import com.horstmann.violet.product.diagram.abstracts.property.MultiLineString; + +/** + * An object node in a scenario diagram. + */ +public class LifelineNode extends RectangularNode +{ + /** + * Construct an object node with a default size + */ + public LifelineNode() + { + name = new MultiLineString(); + name.setUnderlined(true); + } + + /** + * Sets the name property value. + * + * @param newValue the name of this object + */ + public void setName(MultiLineString n) + { + name = n; + } + + /** + * Gets the name property value. + * + * @return the name of this object + */ + public MultiLineString getName() + { + return name; + } + + public boolean addConnection(IEdge e) + { + return false; + } + + @Override + public boolean addChild(INode n, Point2D p) + { + if (!n.getClass().isAssignableFrom(ActivationBarNode.class)) + { + return false; + } + n.setParent(this); + n.setGraph(getGraph()); + n.setLocation(p); + addChild(n, getChildren().size()); + return true; + } + + /** + * Looks for the node which is located just after the given point + * + * @param p + * @return the node we found or null if there's no node after this point + */ + private INode getNearestNodeAfterThisPoint(Point2D p) + { + double y = p.getY(); + INode nearestNodeAfterThisPoint = null; + // Step 1 : we look for the closest node + for (INode childNode : getChildren()) + { + if (nearestNodeAfterThisPoint == null) + { + nearestNodeAfterThisPoint = childNode; + } + Point2D childLocation = childNode.getLocation(); + Point2D nearestNodeLocation = nearestNodeAfterThisPoint.getLocation(); + double childY = childLocation.getY(); + double nearestY = nearestNodeLocation.getY(); + double currentNodeGap = childY - y; + double nearestNodeGap = nearestY - y; + if (currentNodeGap > 0 && Math.abs(currentNodeGap) < Math.abs(nearestNodeGap)) + { + nearestNodeAfterThisPoint = childNode; + } + } + // Step 2 : if nothing found, we return null + if (nearestNodeAfterThisPoint == null) + { + return null; + } + // Step 3 : as by default we set the first child node as the nearest one + // We check if it is not before p + Point2D nearestChildLocation = nearestNodeAfterThisPoint.getLocation(); + if (y > nearestChildLocation.getY()) + { + return null; + } + // Step 4 : we return the closest node after p + return nearestNodeAfterThisPoint; + } + + @Override + public Point2D getConnectionPoint(IEdge e) + { + Direction d = e.getDirection(this); + Point2D locationOnGraph = getLocationOnGraph(); + Rectangle2D topRectBounds = getTopRectangle(); + if (d.getX() > 0) + { + return new Point2D.Double(locationOnGraph.getX(), locationOnGraph.getY() + topRectBounds.getHeight() / 2); + } + return new Point2D.Double(locationOnGraph.getX() + topRectBounds.getWidth(), locationOnGraph.getY() + topRectBounds.getHeight() / 2); + } + + @Override + public Point2D getLocation() + { + IGraph currentGraph = getGraph(); + if (currentGraph == null) + { + return new Point2D.Double(0, 0); + } + Collection edges = currentGraph.getAllEdges(); + for (IEdge edge : edges) + { + if (edge instanceof CallEdge) + { + INode endingNode = edge.getEnd(); + if (endingNode == this) + { + INode startingNode = edge.getStart(); + Point2D locationOnGraph = startingNode.getLocationOnGraph(); + Point2D realLocation = super.getLocation(); + Point2D fixedLocation = new Point2D.Double(realLocation.getX(), locationOnGraph.getY() + - getTopRectangleHeight() / 2 + ActivationBarNode.CALL_YGAP / 2); + return fixedLocation; + } + } + } + Point2D realLocation = super.getLocation(); + Point2D fixedLocation = new Point2D.Double(realLocation.getX(), 0); + return fixedLocation; + } + + /** + * Returns the rectangle at the top of the object node. + * + * @return the top rectangle + */ + public Rectangle2D getTopRectangle() + { + double topWidth = getTopRectangleWidth(); + double topHeight = getTopRectangleHeight(); + Rectangle2D topRectangle = new Rectangle2D.Double(0, 0, topWidth, topHeight); + Rectangle2D snappedRectangle = getGraph().getGrid().snap(topRectangle); + return snappedRectangle; + } + + private double getTopRectangleHeight() + { + Rectangle2D bounds = name.getBounds(); + double topHeight = Math.max(bounds.getHeight(), DEFAULT_TOP_HEIGHT); + return topHeight; + } + + private double getTopRectangleWidth() + { + Rectangle2D bounds = name.getBounds(); + double topWidth = Math.max(bounds.getWidth(), DEFAULT_WIDTH); + return topWidth; + } + + @Override + public Rectangle2D getBounds() + { + double topRectWidth = getTopRectangle().getWidth(); + double height = getLocalHeight(); + Point2D nodeLocation = getLocation(); + Rectangle2D bounds = new Rectangle2D.Double(nodeLocation.getX(), nodeLocation.getY(), topRectWidth, height); + Rectangle2D scaledBounds = getScaledBounds(bounds); + Rectangle2D snappedBounds = getGraph().getGrid().snap(scaledBounds); + return snappedBounds; + } + + public double getLocalHeight() { + double topRectHeight = getTopRectangle().getHeight(); + double height = topRectHeight; // default initial height + List children = getChildren(); + for (INode n : children) + { + if (n.getClass().isAssignableFrom(ActivationBarNode.class)) + { + // We are looking for the last activation bar node to get the total height needed + height = Math.max(height, n.getBounds().getMaxY()); + } + } + height = height + ActivationBarNode.CALL_YGAP * 2; + return height; + } + + private Rectangle2D getScaledBounds(Rectangle2D bounds) + { + double x = bounds.getX(); + double y = bounds.getY(); + double w = bounds.getWidth(); + double h = bounds.getHeight(); + double diffY = this.maxYOverAllLifeLineNodes - bounds.getMaxY(); + if (diffY > 0) + { + h = h + diffY; + } + return new Rectangle2D.Double(x, y, w, h); + } + + @Override + public Rectangle2D getShape() + { + Point2D currentLocation = getLocation(); + Rectangle2D topRectangle = getTopRectangle(); + double x = currentLocation.getX(); + double y = currentLocation.getY(); + double w = topRectangle.getWidth(); + double h = topRectangle.getHeight(); + return new Rectangle2D.Double(x, y, w, h); + } + + public void draw(Graphics2D g2) + { + super.draw(g2); + Rectangle2D top = getShape(); + g2.draw(top); + name.draw(g2, top); + double xmid = top.getCenterX(); + Line2D line = new Line2D.Double(xmid, top.getMaxY(), xmid, getMaxYOverAllLifeLineNodes()); + Stroke oldStroke = g2.getStroke(); + g2.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0.0f, new float[] + { + 5.0f, + 5.0f + }, 0.0f)); + g2.draw(line); + g2.setStroke(oldStroke); + // Draw its children + for (INode node : getChildren()) + { + node.draw(g2); + } + } + + private double getMaxYOverAllLifeLineNodes() + { + double maxY = this.getLocalHeight(); + IGraph graph = getGraph(); + if (graph == null) + { + return maxY; + } + Collection nodes = graph.getAllNodes(); + for (INode node : nodes) + { + if (!node.getClass().isAssignableFrom(LifelineNode.class)) + { + continue; + } + LifelineNode aLifeLineNode = (LifelineNode) node; + double localY = aLifeLineNode.getLocalHeight() + aLifeLineNode.getLocationOnGraph().getY(); + maxY = Math.max(maxY, localY); + } + this.maxYOverAllLifeLineNodes = maxY; + return maxY; + } + + public boolean contains(Point2D p) + { + Rectangle2D bounds = getBounds(); + return bounds.getX() <= p.getX() && p.getX() <= bounds.getX() + bounds.getWidth(); + } + + public LifelineNode clone() + { + LifelineNode cloned = (LifelineNode) super.clone(); + cloned.name = name.clone(); + return cloned; + } + + private MultiLineString name; + private transient double maxYOverAllLifeLineNodes = 0; + private static int DEFAULT_TOP_HEIGHT = 60; + private static int DEFAULT_WIDTH = 80; + private static int DEFAULT_HEIGHT = 120; +} diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/ReturnEdge.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/ReturnEdge.java new file mode 100644 index 0000000..906fbda --- /dev/null +++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/ReturnEdge.java @@ -0,0 +1,78 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.sequence; + +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.util.ArrayList; + +import com.horstmann.violet.product.diagram.abstracts.edge.SegmentedLineEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.product.diagram.abstracts.property.ArrowHead; +import com.horstmann.violet.product.diagram.abstracts.property.LineStyle; + +/** + * An edge that joins two call nodes. + */ +public class ReturnEdge extends SegmentedLineEdge +{ + + @Override + public ArrowHead getEndArrowHead() + { + return ArrowHead.V; + } + + @Override + public LineStyle getLineStyle() + { + return LineStyle.DOTTED; + } + + @Override + public Line2D getConnectionPoints() + { + ArrayList points = getPoints(); + Point2D p1 = points.get(0); + Point2D p2 = points.get(points.size() - 1); + return new Line2D.Double(p1, p2); + } + + @Override + public ArrayList getPoints() + { + INode endingNode = getEnd(); + INode startingNode = getStart(); + return getPointsForNodesOnDifferentLifeLines(startingNode, endingNode); + } + + private ArrayList getPointsForNodesOnDifferentLifeLines(INode startingNode, INode endingNode) + { + Point2D startingPoint = startingNode.getConnectionPoint(this); + Point2D endingPoint = endingNode.getConnectionPoint(this); + ArrayList a = new ArrayList(); + a.add(startingPoint); + a.add(endingPoint); + return a; + } + +} diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramConstant.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramConstant.java new file mode 100644 index 0000000..5e34bd8 --- /dev/null +++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramConstant.java @@ -0,0 +1,8 @@ +package com.horstmann.violet.product.diagram.sequence; + +public interface SequenceDiagramConstant +{ + + public static final String SEQUENCE_DIAGRAM_STRINGS = "properties.SequenceDiagramGraphStrings"; + +} diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramGraph.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramGraph.java new file mode 100644 index 0000000..c5562ed --- /dev/null +++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramGraph.java @@ -0,0 +1,105 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.sequence; + +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.ResourceBundle; + +import com.horstmann.violet.product.diagram.abstracts.AbstractGraph; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.product.diagram.common.DiagramLinkNode; +import com.horstmann.violet.product.diagram.common.NoteEdge; +import com.horstmann.violet.product.diagram.common.NoteNode; + +/** + * A UML sequence diagram. + */ +public class SequenceDiagramGraph extends AbstractGraph +{ + + + + + @Override + public boolean addNode(INode newNode, Point2D p) + { + INode foundNode = findNode(p); + if (foundNode == null && newNode.getClass().isAssignableFrom(ActivationBarNode.class)) { + return false; + } + return super.addNode(newNode, p); + } + + + + public List getNodePrototypes() + { + return NODE_PROTOTYPES; + } + + public List getEdgePrototypes() + { + return EDGE_PROTOTYPES; + } + + private static final List NODE_PROTOTYPES = new ArrayList(); + + private static final List EDGE_PROTOTYPES = new ArrayList(); + + static + { + ResourceBundle rs = ResourceBundle.getBundle(SequenceDiagramConstant.SEQUENCE_DIAGRAM_STRINGS, Locale.getDefault()); + + LifelineNode lifelineNode = new LifelineNode(); + lifelineNode.setToolTip(rs.getString("node0.tooltip")); + NODE_PROTOTYPES.add(lifelineNode); + + ActivationBarNode activationBarNode = new ActivationBarNode(); + activationBarNode.setToolTip(rs.getString("node1.tooltip")); + NODE_PROTOTYPES.add(activationBarNode); + + NoteNode noteNode = new NoteNode(); + noteNode.setToolTip(rs.getString("node2.tooltip")); + NODE_PROTOTYPES.add(noteNode); + + DiagramLinkNode diagramLinkNode = new DiagramLinkNode(); + diagramLinkNode.setToolTip(rs.getString("node3.tooltip")); + NODE_PROTOTYPES.add(diagramLinkNode); + + CallEdge callEdge = new CallEdge(); + callEdge.setToolTip(rs.getString("edge0.tooltip")); + EDGE_PROTOTYPES.add(callEdge); + + ReturnEdge returnEdge = new ReturnEdge(); + returnEdge.setToolTip(rs.getString("edge1.tooltip")); + EDGE_PROTOTYPES.add(returnEdge); + + NoteEdge noteEdge = new NoteEdge(); + noteEdge.setToolTip(rs.getString("edge2.tooltip")); + EDGE_PROTOTYPES.add(noteEdge); + } + +} diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramPlugin.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramPlugin.java new file mode 100644 index 0000000..1fd221b --- /dev/null +++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramPlugin.java @@ -0,0 +1,79 @@ +package com.horstmann.violet.product.diagram.sequence; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +import com.horstmann.violet.framework.plugin.IDiagramPlugin; +import com.horstmann.violet.framework.plugin.extensionpoint.Violet016FileFilterExtensionPoint; +import com.horstmann.violet.product.diagram.abstracts.IGraph; + +/** + * Describes sequence diagram graph type + * + * @author Alexandre de Pellegrin + * + */ +public class SequenceDiagramPlugin implements IDiagramPlugin, Violet016FileFilterExtensionPoint +{ + + @Override + public String getDescription() + { + return "Sequence UML diagram"; + } + + @Override + public String getProvider() + { + return "Alexandre de Pellegrin / Cays S. Horstmann"; + } + + @Override + public String getVersion() + { + return "1.0.0"; + } + + @Override + public String getName() + { + return this.rs.getString("menu.sequence_diagram.text"); + } + + @Override + public String getFileExtension() + { + return this.rs.getString("files.seq.extension"); + } + + @Override + public String getFileExtensionName() + { + return this.rs.getString("files.seq.name"); + } + + @Override + public Class getGraphClass() + { + return SequenceDiagramGraph.class; + } + + + + public Map getMappingToKeepViolet016Compatibility() + { + Map replaceMap = new HashMap(); + replaceMap.put("com.horstmann.violet.CallEdge", CallEdge.class.getName()); + replaceMap.put("com.horstmann.violet.CallNode", ActivationBarNode.class.getName()); + replaceMap.put("com.horstmann.violet.ImplicitParameterNode", LifelineNode.class.getName()); + replaceMap.put("com.horstmann.violet.ReturnEdge", ReturnEdge.class.getName()); + replaceMap.put("com.horstmann.violet.SequenceDiagramGraph", SequenceDiagramGraph.class.getName()); + return replaceMap; + } + + private ResourceBundle rs = ResourceBundle.getBundle(SequenceDiagramConstant.SEQUENCE_DIAGRAM_STRINGS, Locale.getDefault()); + + +} diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/META-INF/services/com.horstmann.violet.framework.plugin.IDiagramPlugin b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/META-INF/services/com.horstmann.violet.framework.plugin.IDiagramPlugin new file mode 100644 index 0000000..ef7ea16 --- /dev/null +++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/META-INF/services/com.horstmann.violet.framework.plugin.IDiagramPlugin @@ -0,0 +1 @@ +com.horstmann.violet.product.diagram.sequence.SequenceDiagramPlugin \ No newline at end of file diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/properties/SequenceDiagramGraphStrings.properties b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/properties/SequenceDiagramGraphStrings.properties new file mode 100644 index 0000000..05f01e4 --- /dev/null +++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/properties/SequenceDiagramGraphStrings.properties @@ -0,0 +1,10 @@ +node0.tooltip=Object lifeline +node1.tooltip=Activation bar +node2.tooltip=Note +node3.tooltip=Linked diagram +edge0.tooltip=Call / Create message +edge1.tooltip=Return message +edge2.tooltip=Note connector +menu.sequence_diagram.text=Sequence diagram +files.seq.name=Sequence Diagram Files +files.seq.extension=.seq.violet diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/properties/SequenceDiagramGraphStrings_fr.properties b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/properties/SequenceDiagramGraphStrings_fr.properties new file mode 100644 index 0000000..3e6bb8c --- /dev/null +++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/properties/SequenceDiagramGraphStrings_fr.properties @@ -0,0 +1,10 @@ +node0.tooltip=Ligne de vie de l'objet +node1.tooltip=Barre d'activation +node2.tooltip=Note +node3.tooltip=Diagramme li\u00E9 +edge0.tooltip=Message d'appel / de cr\u00E9ation +edge1.tooltip=Message de retour +edge2.tooltip=Connexion \u00E0 la note +menu.sequence_diagram.text=Diagramme de s\u00e9quence +files.seq.name=Diagramme de s\u00e9quence +files.seq.extension=.seq.violet \ No newline at end of file