package com.baselet.element.old.allinone;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.font.TextLayout;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.sourceforge.jlibeps.epsgraphics.EpsGraphics2D;
import com.baselet.control.HandlerElementMap;
import com.baselet.control.enums.AlignHorizontal;
import com.baselet.control.enums.Direction;
import com.baselet.control.enums.FormatLabels;
import com.baselet.control.enums.LineType;
import com.baselet.control.util.Utils;
import com.baselet.diagram.FontHandler;
import com.baselet.element.interfaces.GridElementDeprecatedAddons;
import com.baselet.element.old.OldGridElement;
// An interaction represents a synchronous/asynchronous message
// that is sent between two objects.
class Interaction {
private final int srcObj;
private final boolean srcObjHasControl;
private final int arrowKind; // 1=SYNC, 2= ASYNC, 3=EDGE, 4=FILLED
private final int lineKind; // 1=SOLID, 2=DOTTED
private final int destObj;
private final boolean destObjHasControl;
private final String methodName;
public Interaction(int srcObj, boolean srcObjHasControl, int arrowKind, int lineKind,
int destObj, boolean destObjHasControl, String methodName) {
this.srcObj = srcObj;
this.srcObjHasControl = srcObjHasControl;
this.arrowKind = arrowKind;
this.lineKind = lineKind;
this.destObj = destObj;
this.destObjHasControl = destObjHasControl;
this.methodName = methodName;
}
public boolean hasControl(int objNum) {
return srcObjHasControl && srcObj == objNum ||
destObjHasControl && destObj == objNum;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Interaction)) {
return false;
}
Interaction i = (Interaction) o;
return srcObj == i.srcObj &&srcObjHasControl == i.srcObjHasControl &&
arrowKind == i.arrowKind && destObj == i.destObj &&
destObjHasControl == i.destObjHasControl &&
methodName == null || methodName.equals(i.methodName);
}
@Override
public int hashCode() {
return (methodName != null ? methodName.hashCode() : 1) +srcObj +
(srcObjHasControl ? 1 : 0) + arrowKind + destObj + (destObjHasControl ? 1 : 0);
}
public int getSrcObj() {
return srcObj;
}
public boolean getSrcObjHasControl() {
return srcObjHasControl;
}
public int getArrowKind() {
return arrowKind;
}
public int getLineKind() {
return lineKind;
}
public int getDestObj() {
return destObj;
}
public boolean getDestObjHasControl() {
return destObjHasControl;
}
public String getMethodName() {
return methodName;
}
}
// Contains all interactions entered by the user and
// offers various comfort-functions for finding and
// working with interactions
class InteractionManagement {
private final Set<Interaction>[] level;
// private Set[] level;
@SuppressWarnings("unchecked")
InteractionManagement(int numLevels) {
level = new HashSet[numLevels];
for (int i = 0; i < numLevels; i++) {
level[i] = new HashSet<Interaction>();
}
}
public boolean controlBoxExists(int levelNum, int objNum) {
Iterator<Interaction> it = level[levelNum - 1].iterator();
while (it.hasNext()) {
Interaction ia = it.next();
if (ia.hasControl(objNum)) {
return true;
}
}
return false;
}
public void add(int numLevel, Interaction i) {
level[numLevel - 1].add(i);
}
public Set<Interaction> getInteractionsInLevel(int levelNum) {
return level[levelNum - 1];
}
public int getNumLevels() {
return level.length;
}
}
@SuppressWarnings("serial")
public class SequenceDiagram extends OldGridElement {
public int controlFlowBoxWidth = 20;
// the dimensions for the rectangle(s) (=objects) in the first line
public int rectDistance; // computed distance between two columns
public int rectHeight; // computed
public int rectWidth; // computed
// int extraTextSpace = 0;
public int borderDistance = 10; // d between the component bordeR and the diagram
private int yOffsetforTitle = 0;
public int rectToFirstLevelDistance = 0;
public int levelHeight = 30;// LME//60;
// these two constants are important for the arrowhead
public int arrowX = 5;
public int arrowY = 5;
public static final int SYNC = 1;
public static final int ASYNC = 2;
public static final int EDGE = 3; // LME
public static final int FILLED = 4; // LME
public static final int SOLID = 1; // LME
public static final int DOTTED = 2; // LME
private Map<String, Integer> labeltonumber;
private int levelNum = 0;
private InteractionManagement im;
@Override
public void paintEntity(Graphics g) {
rectDistance = 60;
zoomValues();
float zoom = HandlerElementMap.getHandlerForElement(this).getZoomFactor();
// Some unimportant initialization stuff; setting color, font
// quality, etc. You should not have to change this.
Graphics2D g2 = (Graphics2D) g;
FontHandler fontHandler = HandlerElementMap.getHandlerForElement(this).getFontHandler();
g2.setFont(fontHandler.getFont());
g2.setColor(fgColor);
// draw the border
g2.drawRect(0, 0, getRectangle().width - 1, getRectangle().height - 1);
levelNum = 1;
Vector<String> lines = Utils.decomposeStrings(getPanelAttributes());
if (lines.size() == 0) {
return;
}
if (lines.elementAt(0).startsWith("title:")) {
String title = lines.elementAt(0).substring("title:".length());
if (title != null && title.length() > 0) {
fontHandler.writeText(g2, title, (int) (5 * zoom), (int) fontHandler.getFontSize() + (int) fontHandler.getDistanceBetweenTexts(), AlignHorizontal.LEFT);
int titlewidth = (int) fontHandler.getTextWidth(title);
int ty = (int) (8 * zoom) + (int) (fontHandler.getFontSize() + fontHandler.getDistanceBetweenTexts());
g2.drawLine(0, ty, titlewidth + (int) (10 * zoom), ty);
g2.drawLine(titlewidth + (int) (10 * zoom), ty, titlewidth + ty + (int) (10 * zoom), 0);
lines.remove(0);
yOffsetforTitle = (int) (25 * zoom);
}
}
else {
yOffsetforTitle = 0;
}
for (int i = 1; i < lines.size(); i++) {
String element = lines.elementAt(i);
if (element.indexOf("iframe{") >= 0) {
element = "9999->0:" + element; // dummy: space for interactionframe
lines.set(i, element);
}
else if (element.indexOf("iframe}") >= 0) {
element = "9999<-0:" + element; // dummy: space for interactionframe
lines.set(i, element);
}
// AB: match whitespace characters from the beginning to the end of the line
if (lines.elementAt(i).matches("\\A\\s*\\z")) {
continue;
}
levelNum++;
}
if (lines.size() == 0) {
return; // return if it only has the title line (Issue 146)
}
String firstLine = lines.elementAt(0);
Vector<String> obj = Utils.decomposeStrings(firstLine, "|");
int numObjects = obj.size();
// LABEL ADDING
// get the labels of the Sequencediagram
StringBuilder sb = new StringBuilder(""); // delete the ids from the header
labeltonumber = new HashMap<String, Integer>();
Pattern p_label = Pattern.compile("([^\\~]+)(\\~([a-zA-Z0-9]+))?(\\_)?");
for (int i = 1; i <= numObjects; i++) {
Matcher m = p_label.matcher(obj.get(i - 1));
if (m.matches() && m.group(2) != null) {
labeltonumber.put(m.group(3), i);
sb.append("|").append(m.group(1)).append(m.group(4) == null ? "" : m.group(4));
}
else {
sb.append("|").append(obj.get(i - 1));
}
if (!labeltonumber.containsKey(Integer.toString(i))) // only write number if no other label has this number
{
labeltonumber.put(Integer.toString(i), i); // columnnumber as label for backward compatibility
}
}
String newhead = sb.toString();
obj = Utils.decomposeStrings(newhead.length() > 0 ? newhead.substring(1) : "", "|");
// LABELADDING STOP (exchanged parseInteger Methods with labeltonumber.get methods
calcWidthOfLineHeaderBoxes(g2, fontHandler, obj, numObjects);
// parse the messages
int curLevel = 0;
im = new InteractionManagement(levelNum);
String boxStrings = "";
for (int i = 1; i < lines.size(); i++) {
String methodName = "";
if (lines.elementAt(i).matches("\\A\\s*\\z")) {
continue;
}
curLevel++;
Vector<String> interactions = Utils.decomposeStrings(lines.elementAt(i), ";");
for (int j = 0; j < interactions.size(); j++) {
Pattern p = Pattern.compile("\\A(\\w+)(->>|->|-/>|.>>|.>|./>|->>>|.>>>|<<-|<-|</-|<<.|<.|</.|<<<-|<<<.)(\\w+)(:((\\w+)(,(\\w+))*))?(?::(.*))?\\z");
// Pattern.compile("\\A(\\d+)(->>|->|-/>|.>>|.>|./>|->>>|.>>>)(\\d+)(:((\\d+)(,(\\d+))*))*(?::(.*))?\\z");
// 1->2:1,2:methodName
// 1->2:abc
Matcher m = p.matcher(interactions.elementAt(j));
if (!m.matches()) {
continue;
}
Integer srcObj = labeltonumber.get(m.group(1));
Integer destObj = labeltonumber.get(m.group(3));
String methodNameFromText = m.group(9);
if (srcObj != null && destObj != null && methodNameFromText != null) {
Integer span = Math.abs(srcObj - destObj);
if (span != 0) {
double lineSpaceRequiredForMessage = fontHandler.getTextWidth(methodNameFromText) / span;
double totalDist = lineSpaceRequiredForMessage - rectWidth + controlFlowBoxWidth; // add the rectWidth (because the text can exceed the half rect to the left and right and add the controlFlowBoxWidth to avoid text overlapping with control flow boxes
rectDistance = (int) Math.max(rectDistance, totalDist);
}
}
int arrowKind = -1;
int lineKind = -1;
boolean reverse = false; // arrow direction flag
if (m.group(2).equals("->")) {
arrowKind = ASYNC;
lineKind = SOLID;
}
else if (m.group(2).equals("->>")) {
arrowKind = SYNC;
lineKind = SOLID;
}
else if (m.group(2).equals("-/>")) {
arrowKind = EDGE;
lineKind = SOLID;
} // LME
else if (m.group(2).equals("->>>")) {
arrowKind = FILLED;
lineKind = SOLID;
} // LME
else if (m.group(2).equals(".>")) {
arrowKind = ASYNC;
lineKind = DOTTED;
} // LME
else if (m.group(2).equals(".>>")) {
arrowKind = SYNC;
lineKind = DOTTED;
} // LME
else if (m.group(2).equals("./>")) {
arrowKind = EDGE;
lineKind = DOTTED;
} // LME
else if (m.group(2).equals(".>>>")) {
arrowKind = FILLED;
lineKind = DOTTED;
} // LME
else if (m.group(2).equals("<-")) {
arrowKind = ASYNC;
lineKind = SOLID;
reverse = true;
}
else if (m.group(2).equals("<<-")) {
arrowKind = SYNC;
lineKind = SOLID;
reverse = true;
}
else if (m.group(2).equals("</-")) {
arrowKind = EDGE;
lineKind = SOLID;
reverse = true;
} // LME
else if (m.group(2).equals("<<<-")) {
arrowKind = FILLED;
lineKind = SOLID;
reverse = true;
} // LME
else if (m.group(2).equals("<.")) {
arrowKind = ASYNC;
lineKind = DOTTED;
reverse = true;
} // LME
else if (m.group(2).equals("<<.")) {
arrowKind = SYNC;
lineKind = DOTTED;
reverse = true;
} // LME
else if (m.group(2).equals("</.")) {
arrowKind = EDGE;
lineKind = DOTTED;
reverse = true;
} // LME
else if (m.group(2).equals("<<<.")) {
arrowKind = FILLED;
lineKind = DOTTED;
reverse = true;
} // LME
String group = m.group(5);
if (group == null) {
group = "#";
String element = interactions.elementAt(j);
if (element.indexOf("iframe") >= 0) {
group += element.substring(element.indexOf("iframe")); // append info for interactionframe
}
}
else // LABLING ADD
{
String[] grouparray = group.split(",");
group = "";
for (String tmp : grouparray) {
Integer tempgroup = labeltonumber.get(tmp);
if (tempgroup != null) {
group += "," + tempgroup;
}
}
if (group.length() > 0) {
group = group.substring(1);
}
else {
if (grouparray.length == 1) {
group = "#";
methodName = grouparray[0];
}
}
} // STOP LABLING ADD
boxStrings += ";" + group; // LME: get alive Objects
boolean srcObjHasControl = srcObj != null ? group.contains(String.valueOf(srcObj)) : false;
boolean destObjHasControl = destObj != null ? group.contains(String.valueOf(destObj)) : false;
if (methodName == null || methodName.isEmpty()) {
methodName = methodNameFromText;
}
// LME: removed (in V6) since not necessary
// if(destObj==srcObj) levelNum++; //LME: expand the Entity's size
if (srcObj == null || destObj == null) {
continue;
}
if (!reverse) {
im.add(curLevel, new Interaction(srcObj, srcObjHasControl, arrowKind, lineKind, destObj, destObjHasControl, methodName)); // normal arrow direction 1->2
}
else {
im.add(curLevel, new Interaction(destObj, destObjHasControl, arrowKind, lineKind, srcObj, srcObjHasControl, methodName)); // reverse arrow direction 1<-2
}
} // #for
}
// end message parsing
// draw the first line of the sequence diagram
int ypos = borderDistance + yOffsetforTitle;
int xpos = borderDistance;
for (int i = 0; i < numObjects; i++) {
boolean underline = false;
String s = obj.elementAt(i);
if (s.startsWith(FormatLabels.UNDERLINE.getValue()) && s.endsWith(FormatLabels.UNDERLINE.getValue()) && s.length() > 2) {
underline = true;
s = s.substring(1, s.length() - 1);
}
TextLayout layout = new TextLayout(s, fontHandler.getFont(),
g2.getFontRenderContext());
g2.drawRect(xpos, ypos, rectWidth - 1, rectHeight - 1);
int dx = (rectWidth - 2 - (int) Math.floor(layout.getBounds().getWidth() + 1)) / 2;
int dy = (rectHeight - 2 - (int) Math.floor(layout.getBounds().getHeight() + 1)) / 2;
int tx = xpos + dx;
int ty = ypos + dy + (int) layout.getBounds().getHeight();
layout.draw(g2, tx, ty);
if (underline) {
g2.drawLine(tx,
ty + (int) fontHandler.getDistanceBetweenTexts() / 2,
tx + (int) layout.getBounds().getWidth(),
ty + (int) fontHandler.getDistanceBetweenTexts() / 2);
}
xpos += rectWidth + rectDistance;
}
// draw the messages
int maxTextXpos = drawMessages(g2);
maxTextXpos += 3 * fontHandler.getDistanceBetweenTexts(); // add extra space
if (boxStrings.length() > 1) {
try {
drawControlFlowBoxesWithLines(g2, boxStrings.substring(1), numObjects); // LME: 1,2;1,2;... cut first ;-character
} catch (ArrayIndexOutOfBoundsException e) {
// do nothing: this exception is thrown, when entering text,
// that is not rendered to an control flow box
}
}
// set our component to the correct size
int rWidth = rectWidth * numObjects + rectDistance * (numObjects - 1) + 2 * borderDistance;
int rHeight = 2 * borderDistance + yOffsetforTitle + rectHeight + rectToFirstLevelDistance + levelNum * levelHeight;
rWidth = rWidth > maxTextXpos ? rWidth : maxTextXpos;
// align the borders to the grid
rWidth += HandlerElementMap.getHandlerForElement(this).getGridSize() - rWidth % HandlerElementMap.getHandlerForElement(this).getGridSize();
rHeight += HandlerElementMap.getHandlerForElement(this).getGridSize() - rHeight % HandlerElementMap.getHandlerForElement(this).getGridSize();
setSize(rWidth, rHeight);
}
private void calcWidthOfLineHeaderBoxes(Graphics2D g2, FontHandler fontHandler, Vector<String> obj, int numObjects) {
// find out the width of the column with the longest text
double maxWidth = 0;
double maxHeight = 0;
for (int i = 0; i < numObjects; i++) {
String s = obj.elementAt(i);
if (s.startsWith(FormatLabels.UNDERLINE.getValue()) && s.endsWith(FormatLabels.UNDERLINE.getValue()) && s.length() > 2) {
s = s.substring(1, s.length() - 1);
}
TextLayout layout = new TextLayout(s, fontHandler.getFont(), g2.getFontRenderContext());
maxWidth = Math.max(layout.getBounds().getWidth(), maxWidth);
maxHeight = Math.max(layout.getBounds().getHeight(), maxHeight);
}
rectWidth = (int) Math.floor(maxWidth + 1) + 2 * (int) fontHandler.getDistanceBetweenTexts() + (int) fontHandler.getFontSize();
rectHeight = (int) Math.floor(maxHeight + 1) + (int) fontHandler.getDistanceBetweenTexts() + (int) fontHandler.getFontSize();
}
private int drawMessages(Graphics2D g2) {
float zoom = HandlerElementMap.getHandlerForElement(this).getZoomFactor();
int maxTextXpos = 0;
for (int i = 0; i < im.getNumLevels(); i++) {
Set<Interaction> interactions = im.getInteractionsInLevel(i + 1);
Iterator<Interaction> it = interactions.iterator();
while (it.hasNext()) {
Interaction ia = it.next();
if (ia.getSrcObj() == ia.getDestObj()) {
// draw an arc-arrow
int xTextOffset = 0;
int w = (int) (30 * zoom);
int h = (int) (levelHeight * 0.66);
int x = hCenterForObj(ia.getSrcObj()) - w / 2;
// nt y= vCenterForLevel(i+1) - h/2;
int ay = vCenterForLevel(i + 1) + (int) (5 * zoom); // + levelHeight/2 -1;
if (im.controlBoxExists(i + 1, ia.getSrcObj())) {
x += controlFlowBoxWidth / 2;
xTextOffset = controlFlowBoxWidth / 2;
}
g2.drawArc(x, ay, w, h, 90, -180);
Point p1 = new Point(x + w / 2, ay + h);
Point d1 = new Point(x + w / 2 + (int) (3 * zoom), p1.y - (int) (6 * zoom));
Point d2 = new Point(x + w / 2 + (int) (4 * zoom), p1.y + (int) (4 * zoom));
if (ia.getArrowKind() == ASYNC) { // Pfeil offen
g2.drawLine(p1.x, p1.y, d1.x, d1.y);
g2.drawLine(p1.x, p1.y, d2.x, d2.y);
}
else if (ia.getArrowKind() == SYNC) {
int[] xs = { p1.x, d1.x, d2.x };
int[] ys = { p1.y, d1.y, d2.y };
Color oldColor = g2.getColor();
g2.setColor(bgColor);
g2.fillPolygon(xs, ys, 3);
g2.setColor(oldColor);
g2.drawPolygon(xs, ys, 3);
}
else if (ia.getArrowKind() == EDGE) {
g2.drawLine(p1.x, p1.y, d2.x, d2.y);
}
else if (ia.getArrowKind() == FILLED) {
Polygon p = new Polygon();
p.addPoint(p1.x, p1.y);
p.addPoint(d1.x, d1.y);
p.addPoint(d2.x, d2.y);
g2.fillPolygon(p);
}
// print the methodname
if (ia.getMethodName() != null && !ia.getMethodName().equals("")) {
int fx1 = x + w + 2;
int fy1 = ay;
int fx2 = hCenterForObj(ia.getSrcObj()) + rectWidth / 2;
int fy2 = ay + h;
int tx = printMethodName(g2, ia.getMethodName(), fx1 + xTextOffset, fx2 + xTextOffset,
fy1, fy2, true, false);
maxTextXpos = maxTextXpos > tx ? maxTextXpos : tx;
}
}
else {
// draw an arrow from the source-object to the destination object
int begX = hCenterForObj(ia.getSrcObj());
int endX = ia.getSrcObj() < ia.getDestObj() ? hCenterForObj(ia.getDestObj()) - 1 : hCenterForObj(ia.getDestObj()) + 1;
int arrowY = vCenterForLevel(i + 1) + levelHeight / 2 - 1;
if (ia.getSrcObjHasControl()) { // LME: shrink arrow if box exists
begX += ia.getSrcObj() < ia.getDestObj() ? controlFlowBoxWidth / 2 : -controlFlowBoxWidth / 2;
}
if (ia.getDestObjHasControl()) { // LME: shrink arrow if box exists
endX += ia.getSrcObj() < ia.getDestObj() ? -controlFlowBoxWidth / 2 : controlFlowBoxWidth / 2;
}
drawArrow(g2, new Point(begX, arrowY), new Point(endX, arrowY), ia.getArrowKind(), ia.getLineKind());
if (ia.getMethodName() != null && !ia.getMethodName().equals("")) {
final int b = 2;
if (ia.getSrcObj() < ia.getDestObj()) {
int tx = printMethodName(g2, ia.getMethodName(), begX + b, endX - arrowX - b, arrowY - 1 - levelHeight / 1, arrowY - 1, false, true);
maxTextXpos = maxTextXpos > tx ? maxTextXpos : tx;
}
else {
int tx = printMethodName(g2, ia.getMethodName(), endX + arrowX + b, begX - b, arrowY - 1 - levelHeight / 2, arrowY - 1, false, true);
maxTextXpos = maxTextXpos > tx ? maxTextXpos : tx;
}
}
}
}
}
return maxTextXpos;
}
// prints the given methodName in an intelligent manner into
// the supplied rectangle.
// The method may put pixels anywhere into the supplied rectangle
// including the borders. (In other words eg. the point begX/endY
// may be set by this method.)
// if the fontsize gets very big this method may cross the vertical borders.
private int printMethodName(Graphics2D g2, String methodName,
int begX, int endX, int begY, int endY,
boolean centerVertically, boolean centerHorizontically) {
if (methodName == null || methodName.equals("")) {
log.error("SequenceDiagram->printMethodName was called with an invalid argument.");
return 0;
}
Font font = HandlerElementMap.getHandlerForElement(this).getFontHandler().getFont();
TextLayout layout = new TextLayout(methodName, font, g2.getFontRenderContext());
// draw it horizontally centered
int dx = centerHorizontically ? (endX - begX - (int) layout.getBounds().getWidth()) / 2 : 0;
int dy = centerVertically ? (endY - begY - (int) layout.getBounds().getHeight()) / 2 : 1;
layout.draw(g2, begX + dx, endY - dy);
return begX + dx + (int) layout.getBounds().getWidth();
}
public void drawControlFlowBoxesWithLines(Graphics2D g2, String s, int numObjects) { // LME
int level = 1;
StringTokenizer mainTokens = new StringTokenizer(s, ";");
int tokNum = mainTokens.countTokens();
int[][] tField = new int[numObjects][tokNum + 2];
Vector<Integer> interactionframes = new Vector<Integer>();
HashMap<String, String> interactionframesText = new HashMap<String, String>();
// collect all tokens into an array: create another view to sequentially access data of a specific object
// 1,2;#;1,3;1,3;1;3 will be transfomed to
// tField: [110]
// [000]
// [101]
// [101]
// [100]
// [001]
while (mainTokens.hasMoreTokens()) {
String main = mainTokens.nextToken();
if (main.indexOf("#") >= 0) { // if no box, clear entire row
for (int i = 0; i < numObjects; i++) {
tField[i][level - 1] = 0; // clear
}
if (main.indexOf("#iframe{") >= 0 || main.indexOf("#iframe}") >= 0) {
if (main.indexOf("#iframe{") >= 0) {
interactionframes.add(Integer.valueOf(level));
}
else {
interactionframes.add(Integer.valueOf(level * -1)); // distinguish betweeen start and end of the iframe
}
if (main.indexOf("iframe{:") >= 0) {
interactionframesText.put("" + level, main.substring(main.indexOf("iframe{:") + 8)); // put text into hashmap
}
}
level++;
}
else {
StringTokenizer innerT = new StringTokenizer(main, ",");
for (int i = 0; i < numObjects; i++) {
tField[i][level - 1] = 0; // clear
}
while (innerT.hasMoreTokens()) {
String is = innerT.nextToken();
int objNum = Integer.parseInt(is);
tField[objNum - 1][level - 1] = 1;
}
level++;
} // #else
} // #while
for (int actObjNum = 0; actObjNum < numObjects; actObjNum++) {
// controlFlowBoxWidth = controlFlowBoxWidth * getHandler().getGridSize() / 10;
// rectDistance = rectDistance / getHandler().getGridSize() / 10;
// levelHeight = levelHeight / getHandler().getGridSize() / 10;
int offset = 2;
int objNum = actObjNum + 1;
int x1 = hCenterForObj(objNum) - controlFlowBoxWidth / 2;
int startLevel = -1, boxSize = 0;
int lineX = hCenterForObj(actObjNum + 1);
int lineY1 = borderDistance + yOffsetforTitle + rectHeight;
for (int i = 0; i < tokNum + 1; i++) {
if (tField[actObjNum][i] == 1) {
if (startLevel == -1) {
startLevel = i;
}
boxSize++;
}
if (tField[actObjNum][i] == 0 && startLevel != -1) {
int y1 = vCenterForLevel(startLevel + offset) - levelHeight - 1;
g2.drawRect(x1, y1, controlFlowBoxWidth - 1, levelHeight * boxSize); // draw the box
g2.setStroke(Utils.getStroke(LineType.DASHED, 1)); // #draw the line between the boxes
g2.drawLine(lineX, lineY1, lineX, y1); // #
g2.setStroke(Utils.getStroke(LineType.SOLID, 1)); // #
lineY1 = y1 + levelHeight * boxSize; // #
startLevel = -1;
boxSize = 0;
}
} // #for(int i
// LME: draw the tail
int lineY2 = borderDistance + yOffsetforTitle + rectHeight + levelNum * levelHeight + rectToFirstLevelDistance;
g2.setStroke(Utils.getStroke(LineType.DASHED, 1));
g2.drawLine(lineX, lineY1, lineX, lineY2);
g2.setStroke(Utils.getStroke(LineType.SOLID, 1));
} // #for(int actObjNum
// LME: draw the interaction frames
int fullSets = interactionframes.size() - interactionframes.size() % 2;
if (fullSets >= 2) {
int pos = 0;
while (pos < fullSets) {
pos = recurseInteractionFrames(g2, interactionframes, interactionframesText, pos, 0);
pos++;
}
}
}
/**
* Recusion level step of interactionframes
* ie:
* iframe{
* iframe{
* }
* iframe{
* }
* }
*/
public int recurseInteractionFrames(Graphics2D g2, Vector<Integer> interactionframes, HashMap<String, String> interactionframesText, int pos, int recursionLevel) {
int pos1 = interactionframes.elementAt(pos);
int posX;
while (pos < interactionframes.size() && (posX = interactionframes.elementAt(pos)) > 0) { // traverse through iframes an same level
pos1 = posX;
pos++;
pos = recurseInteractionFrames(g2, interactionframes, interactionframesText, pos, recursionLevel + 1); // step on level deeper into recursion
if (pos1 <= 0) {
return pos;
}
int pos2 = interactionframes.elementAt(pos) * -1;
drawInteractionFrame(g2, pos1, pos2, recursionLevel, interactionframesText.get("" + pos1));
pos++;
}
return pos;
}
private void drawInteractionFrame(Graphics2D g2, int pos1, int pos2, int recursionLevel, String text) {
float zoom = HandlerElementMap.getHandlerForElement(this).getZoomFactor();
int pos11 = (pos1 + 1) * levelHeight + yOffsetforTitle;
int h = (pos2 - pos1) * levelHeight;
int x = (int) HandlerElementMap.getHandlerForElement(this).getFontHandler().getDistanceBetweenTexts() * 2 + recursionLevel * 4;
g2.drawRect(x, pos11, getRectangle().width - (int) HandlerElementMap.getHandlerForElement(this).getFontHandler().getDistanceBetweenTexts() * 4 - 1 - recursionLevel * 8, h);
int uLinePos = pos11 + (int) HandlerElementMap.getHandlerForElement(this).getFontHandler().getDistanceBetweenTexts() + (int) (HandlerElementMap.getHandlerForElement(this).getFontHandler().getFontSize() + (int) HandlerElementMap.getHandlerForElement(this).getFontHandler().getDistanceBetweenTexts());
int textPos = pos11 + (int) HandlerElementMap.getHandlerForElement(this).getFontHandler().getDistanceBetweenTexts() + (int) HandlerElementMap.getHandlerForElement(this).getFontHandler().getFontSize();
int textWidth = 0;
if (text == null || text.equals("")) {
text = " ";
}
g2.drawString(text, x + (int) (10 * zoom), textPos);
int pW = (int) HandlerElementMap.getHandlerForElement(this).getFontHandler().getTextWidth(text);
textWidth = pW > textWidth ? pW : textWidth;
g2.drawLine(x, uLinePos, x + textWidth + (int) (15 * zoom), uLinePos);
g2.drawLine(x + textWidth + (int) (15 * zoom), uLinePos, x + textWidth + (int) (25 * zoom), pos11 + (int) (10 * zoom));
g2.drawLine(x + textWidth + (int) (25 * zoom), pos11, x + textWidth + (int) (25 * zoom), pos11 + (int) (10 * zoom));
}
public void drawArrow(Graphics2D g2, Point srcObj, Point destObj, int arrowKind, int lineKind) {
Point p1, p2;
if (srcObj.x < destObj.x) {
p1 = new Point(destObj.x - arrowX, destObj.y + arrowY);
p2 = new Point(destObj.x - arrowX, destObj.y - arrowY);
}
else {
p1 = new Point(destObj.x + arrowX, destObj.y + arrowY);
p2 = new Point(destObj.x + arrowX, destObj.y - arrowY);
}
if (arrowKind == SYNC) {
g2.drawLine(p1.x, p1.y, p2.x, p2.y);
g2.drawLine(destObj.x, destObj.y, p1.x, p1.y);
g2.drawLine(destObj.x, destObj.y, p2.x, p2.y);
if (lineKind == DOTTED) {
g2.setStroke(Utils.getStroke(LineType.DASHED, 1));
}
g2.drawLine(srcObj.x, srcObj.y, p1.x, destObj.y);
}
else if (arrowKind == ASYNC) {
g2.drawLine(destObj.x, destObj.y, p1.x, p1.y);
g2.drawLine(destObj.x, destObj.y, p2.x, p2.y);
if (lineKind == DOTTED) {
g2.setStroke(Utils.getStroke(LineType.DASHED, 1));
}
g2.drawLine(srcObj.x, srcObj.y, destObj.x, destObj.y);
}
else if (arrowKind == EDGE) {
g2.drawLine(destObj.x, destObj.y, p2.x, p2.y);
if (lineKind == DOTTED) {
g2.setStroke(Utils.getStroke(LineType.DASHED, 1));
}
g2.drawLine(srcObj.x, srcObj.y, destObj.x, destObj.y);
}
else if (arrowKind == FILLED) {
Polygon p = new Polygon();
p.addPoint(p1.x, p1.y);
p.addPoint(p2.x, p2.y);
p.addPoint(destObj.x, destObj.y);
g2.fillPolygon(p);
if (lineKind == DOTTED) {
g2.setStroke(Utils.getStroke(LineType.DASHED, 1));
}
g2.drawLine(srcObj.x, srcObj.y, p1.x, destObj.y);
}
g2.setStroke(Utils.getStroke(LineType.SOLID, 1));
}
protected int hCenterForObj(int objNum) {
return objNum * rectWidth + (objNum - 1) * rectDistance + borderDistance - rectWidth / 2;
}
protected int vCenterForLevel(int level) {
return level * levelHeight +rectToFirstLevelDistance +
rectHeight + borderDistance + yOffsetforTitle - levelHeight / 2;
}
@Override
public Set<Direction> getResizeArea(int x, int y) {
return new HashSet<Direction>(); // deny size changes
}
public void zoomValues() {
float zoom = HandlerElementMap.getHandlerForElement(this).getZoomFactor();
controlFlowBoxWidth = (int) (20 * zoom);
rectDistance = (int) (rectDistance * zoom);
borderDistance = (int) (10 * zoom);
levelHeight = (int) (30 * zoom);
arrowX = (int) (5 * zoom);
arrowY = (int) (5 * zoom);
}
@Override
public boolean isDeprecated() {
return false;
}
@Override
public GridElementDeprecatedAddons getDeprecatedAddons() {
return new GridElementDeprecatedAddons() {
@Override
public void doBeforeExport() {
// Issue 159: the old all in one grid elements calculate their real size AFTER painting. although it's bad design it works for most cases, but batch-export can fail if the element width in the uxf is wrong (eg if it was created using another umlet-default-fontsize), therefore a pseudo-paint call is made to get the real size
paintEntity(new EpsGraphics2D());
}
};
}
}