package com.baselet.element.old.allinone;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;
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.util.Utils;
import com.baselet.element.interfaces.GridElementDeprecatedAddons;
import com.baselet.element.old.OldGridElement;
import com.baselet.element.old.activity.AEnd;
import com.baselet.element.old.activity.Activity;
import com.baselet.element.old.activity.Condition;
import com.baselet.element.old.activity.Const;
import com.baselet.element.old.activity.Container;
import com.baselet.element.old.activity.Element;
import com.baselet.element.old.activity.End;
import com.baselet.element.old.activity.EndIf;
import com.baselet.element.old.activity.EventRaise;
import com.baselet.element.old.activity.EventRecieve;
import com.baselet.element.old.activity.Fork;
import com.baselet.element.old.activity.GoTo;
import com.baselet.element.old.activity.If;
import com.baselet.element.old.activity.LineSpacer;
import com.baselet.element.old.activity.PartActivity;
import com.baselet.element.old.activity.Row;
import com.baselet.element.old.activity.Start;
import com.baselet.element.old.activity.StartElement;
import com.baselet.element.old.activity.StopElement;
import com.baselet.element.old.activity.Sync;
import com.baselet.element.sticking.StickingPolygon;
@SuppressWarnings("serial")
public class ActivityDiagramText extends OldGridElement {
private final AtomicBoolean autoInsertIF = new AtomicBoolean();
private ArrayList<Row> rows;
private ArrayList<Container> containers;
private Container root_container;
private Container current_container;
private HashMap<String, Element> elements;
private String title;
private Graphics2D graphics;
private ArrayList<GoTo> gotos;
private int goto_seperation_left;
private int goto_seperation_right;
private float zoom;
private static final String normalchars = "[^\\~\\>]";// "[ \\w\\\\\\(\\)]";
private static final String conditionChars = "[^\\]]";
private static final String title_pattern = "title\\:(" + normalchars + "+)";
private static final String line_pattern = "(\\t*)" + // tabs 1
"(\\[([ " + conditionChars + "]+)\\])?" + // conditions 2..3
"(" + // 4
"(" + // 5
"(Start)" + // start 6
"|(End|AEnd)" + // end7
"|(\\|)" + // linespacer 8
"|(If|Fork)|(EndIf|Sync)" + // if blocks 9/10
"|(While(\\[(" + normalchars + "*)\\])?)" + // while 11..13
"|(\\>(" + normalchars + "+))" + // recieve event 14..15
"|((" + normalchars + "+)\\>)" + // raise event 16..17
"|((" + normalchars + "+)\\.\\.)" + // partactivity 18..19
"|(" + normalchars + "+)" + // activity 20
")" +
"(\\~(" + normalchars + "+)?)?" + // ids 21..22
")?" +
"\\s*" +
"(\\-\\>(" + normalchars + "+))?" + // goto 23..24
"\\s*";
private void init(Graphics2D graphics) {
zoom = HandlerElementMap.getHandlerForElement(this).getZoomFactor();
this.graphics = graphics;
rows = new ArrayList<Row>();
gotos = new ArrayList<GoTo>();
containers = new ArrayList<Container>();
elements = new HashMap<String, Element>();
rows.add(new Row());
root_container = new Container(autoInsertIF, HandlerElementMap.getHandlerForElement(this), graphics, null, rows, 0);
current_container = root_container;
title = null;
goto_seperation_left = (int) (5 * zoom);
goto_seperation_right = (int) (5 * zoom);
// Some unimportant initialization stuff; setting color, font
// quality, etc. You should not have to change this.
this.graphics = graphics;
this.graphics.setFont(HandlerElementMap.getHandlerForElement(this).getFontHandler().getFont());
this.graphics.setColor(fgColor);
}
private String preparse(String line) {
String parsed_line = "";
Pattern p_empty = Pattern.compile("\\s*");
Pattern p_title = Pattern.compile(title_pattern);
if (!p_empty.matcher(line).matches()) {
Pattern p = Pattern.compile(line_pattern);
Matcher m_title = p_title.matcher(line);
if (m_title.matches()) {
parsed_line = null;
title = m_title.group(1);
}
else if (p.matcher(line).matches()) {
parsed_line = line;
}
else {
parsed_line = null;
}
}
return parsed_line;
}
public int getGotoPosition(Direction dir) {
if (Direction.LEFT.equals(dir)) {
if (goto_seperation_left + Const.GOTO_SEP * zoom < Const.DIAGRAM_PAD * zoom) {
goto_seperation_left += Const.GOTO_SEP * zoom;
}
return (int) (Const.DIAGRAM_PAD * zoom - goto_seperation_left);
}
else {
if (goto_seperation_right + Const.GOTO_SEP * zoom < Const.DIAGRAM_PAD * zoom) {
goto_seperation_right += Const.GOTO_SEP * zoom;
}
return (int) (root_container.getWidth() + Const.DIAGRAM_PAD * zoom + goto_seperation_right);
}
}
private Vector<String> preparse(Vector<String> lines) {
if (lines.isEmpty()) {
return lines;
}
Vector<String> parsed_lines = new Vector<String>();
Iterator<String> it = lines.iterator();
String current_line = this.preparse(it.next());
if (current_line == null) {
current_line = "";
}
while (current_line.equals("") && it.hasNext()) {
current_line = this.preparse(it.next());
if (current_line == null) {
current_line = "";
}
}
String previous_line = current_line;
int current_depth = 0;
int last_depth = 0;
while (it.hasNext()) {
current_line = this.preparse(it.next());
if (current_line != null) {
if (!current_line.equals("")) {
for (current_depth = 0; current_line.charAt(current_depth) == '\t'; current_depth++) {/* do nothing except increasing current depth */}
if (!previous_line.equals("") || current_depth == last_depth) {
parsed_lines.add(previous_line);
}
previous_line = current_line;
last_depth = current_depth;
}
else if (!previous_line.equals("")) {
parsed_lines.add(previous_line);
previous_line = current_line;
}
}
}
if (!previous_line.equals("")) {
parsed_lines.add(previous_line);
}
return parsed_lines;
}
private void addElement(Element e) {
current_container.addElement(e);
elements.put(e.getId(), e);
}
@Override
public void paintEntity(Graphics g) {
init((Graphics2D) g);
Vector<String> lines = Utils.decomposeStringsWithEmptyLines(getPanelAttributes());
lines = this.preparse(lines);
if (lines.size() == 0) {
return;
}
autoInsertIF.set(true);
while (lines.size() > 0 && lines.elementAt(0).startsWith("var:")) {
if (lines.elementAt(0).equals("var:noautoif")) {
autoInsertIF.set(false);
}
lines.remove(0);
}
int current_depth = 0;
Pattern p = Pattern.compile(line_pattern);
StartElement start_element = null;
Element current_element = null;
containers.add(root_container);
for (String line : lines) {
Matcher m = p.matcher(line);
Container closed_container = null;
if (m.matches()) {
Pattern p_empty = Pattern.compile("\\s*");
Matcher m_empty = p_empty.matcher(line);
/* NEW COLUMN IN CURRENT LAYER */
if (m_empty.matches()) {
/* start element was no start element (example: IF element without following container) */
if (start_element != null) {
addElement(start_element);
start_element = null;
}
if (!current_container.isRoot()) {
current_container.addColumn();
}
// empty line - no need to proceed with other stuff
continue;
}
/* DEPTH */
if (m.group(1) != null) {
/* NEW LAYER */
if (m.group(1).length() > current_depth) {
for (; current_depth < m.group(1).length(); current_depth++) {
current_container = current_container.addNewContainer();
containers.add(current_container);
if (start_element != null) {
current_container.setStartElement(start_element);
elements.put(start_element.getId(), start_element);
start_element = null;
}
}
}
else {
/* start element was no start element (example: IF element without following container) */
if (start_element != null) {
addElement(start_element);
start_element = null;
}
/* CLOSE LAYER(s) */
if (m.group(1).length() < current_depth) {
for (; current_depth > m.group(1).length(); current_depth--) {
closed_container = current_container;
if (!current_container.isRoot()) {
current_container = current_container.close();
}
}
}
}
}
Element e = null;
/* BEDINGUNG */
if (m.group(2) != null) {
String input = "";
if (m.group(3) != null) {
input = m.group(3);
}
e = new Condition(HandlerElementMap.getHandlerForElement(this), input, graphics);
current_element = e;
}
if (e != null) {
addElement(e);
}
e = null;
if (m.group(4) != null) {
String id = m.group(22);
/* START */
if (m.group(6) != null) {
e = new Start(HandlerElementMap.getHandlerForElement(this), graphics);
}
/* END */
else if (m.group(7) != null) {
if (m.group(7).equals("AEnd")) {
e = new AEnd(HandlerElementMap.getHandlerForElement(this), graphics, id);
}
else {
e = new End(HandlerElementMap.getHandlerForElement(this), graphics, id);
}
}
/* LINESPACER */
else if (m.group(8) != null) {
e = new LineSpacer(HandlerElementMap.getHandlerForElement(this), graphics);
}
/* IF/FORK */
else if (m.group(9) != null) {
// these elements are processed as soon as a the
// new container is opened (or as single elements
// if none is openend
if (m.group(9).equals("Fork")) {
start_element = new Fork(HandlerElementMap.getHandlerForElement(this), graphics, id);
}
else {
start_element = new If(HandlerElementMap.getHandlerForElement(this), graphics, id);
}
current_element = start_element;
}
/* ENDIF/SYNC */
else if (m.group(10) != null) {
// set as stop element if a container has been closed
StopElement se;
if (m.group(10).equals("Sync")) {
se = new Sync(HandlerElementMap.getHandlerForElement(this), graphics, id);
}
else {
se = new EndIf(HandlerElementMap.getHandlerForElement(this), graphics, id);
}
if (closed_container != null) {
closed_container.setStopElement(se);
elements.put(se.getId(), se);
}
else {
e = se;
}
current_element = se;
}
/* WHILE */
else if (m.group(11) != null) {
current_container = current_container.addNewWhile(m.group(13));
current_depth++;
}
/* GET EVENT */
else if (m.group(15) != null) {
e = new EventRecieve(HandlerElementMap.getHandlerForElement(this), graphics, m.group(15), id);
}
else if (m.group(17) != null) {
e = new EventRaise(HandlerElementMap.getHandlerForElement(this), graphics, m.group(17), id);
}
else if (m.group(19) != null) {
e = new PartActivity(HandlerElementMap.getHandlerForElement(this), m.group(19), graphics, id);
}
else if (m.group(20) != null) {
e = new Activity(HandlerElementMap.getHandlerForElement(this), m.group(20), graphics, id);
}
}
if (e != null) {
current_element = e;
addElement(e);
}
/* GOTO */
if (m.group(23) != null) {
if (m.group(24) != null && current_element != null) {
String connect_to = m.group(24);
gotos.add(new GoTo(graphics, current_element, connect_to));
current_element.setTerminated();
}
}
}
}
// if a Startelement was the last element
if (start_element != null) {
addElement(start_element);
}
// close opened containers
while (!current_container.isRoot()) {
current_container = current_container.close();
}
// remove empty columns of containers (maybe there if only a goto element was there)
for (Container c : containers) {
c.removeEmptyColumns();
}
// PROCESS GOTO ELEMENTS
ArrayList<GoTo> valid_gotos = new ArrayList<GoTo>();
for (GoTo go : gotos) {
Element from = go.getFromElement();
go.setToElement(elements.get(go.getToElementId()));
Element to = go.getToElement();
if (from != null && to != null) {
valid_gotos.add(go);
boolean fromleft = from.getRow().isLeft(from);
boolean fromright = from.getRow().isRight(from);
boolean toleft = to.getRow().isLeft(to);
boolean toright = to.getRow().isRight(to);
if (fromleft) {
go.setDirection(Direction.LEFT);
if (!toleft) {
rows = to.getRow().makeExclusiveLeft(to, rows);
}
}
else if (toleft) {
go.setDirection(Direction.LEFT);
rows = from.getRow().makeExclusiveLeft(from, rows);
}
else if (fromright) {
go.setDirection(Direction.RIGHT);
if (!toright) {
rows = to.getRow().makeExclusiveRight(to, rows);
}
}
else if (toright) {
go.setDirection(Direction.RIGHT);
rows = from.getRow().makeExclusiveRight(from, rows);
}
else {
go.setDirection(Direction.LEFT);
rows = from.getRow().makeExclusiveLeft(from, rows);
rows = to.getRow().makeExclusiveLeft(to, rows);
}
}
}
// draw title
int offset = 0;
int width = (int) (root_container.getWidth() + Const.DIAGRAM_PAD * zoom * 2);
int height = 0;
if (title != null) {
offset += (int) (25 * zoom);
height += (int) (25 * zoom);
if (title.length() > 0) {
HandlerElementMap.getHandlerForElement(this).getFontHandler().writeText(graphics, title, (int) (10 * zoom), (int) HandlerElementMap.getHandlerForElement(this).getFontHandler().getFontSize() + (int) HandlerElementMap.getHandlerForElement(this).getFontHandler().getDistanceBetweenTexts(), AlignHorizontal.LEFT);
int titlewidth = (int) HandlerElementMap.getHandlerForElement(this).getFontHandler().getTextWidth(title);
int ty = (int) HandlerElementMap.getHandlerForElement(this).getFontHandler().getFontSize() + (int) HandlerElementMap.getHandlerForElement(this).getFontHandler().getDistanceBetweenTexts() + (int) (8 * zoom);
graphics.drawLine(0, ty, titlewidth + (int) (10 * zoom), ty);
graphics.drawLine(titlewidth + (int) (10 * zoom), ty, titlewidth + ty + (int) (10 * zoom), 0);
}
}
/* COMPUTE POSITIONS */
for (Row r : rows) {
offset = r.setElementYPosition(offset);
}
root_container.setX((int) (root_container.getLeftWidth() + Const.DIAGRAM_PAD * zoom));
if (Const.DEBUG) {
root_container.printData("");
}
for (Row r : rows) {
height += r.getHeight();
}
if (width < Const.MIN_WIDTH * zoom) {
width = (int) (Const.MIN_WIDTH * zoom);
}
if (height < Const.MIN_HEIGHT * zoom) {
height = (int) (Const.MIN_HEIGHT * zoom);
}
// draw diagram
this.setSize(width, height);
graphics.drawRect(0, 0, getRectangle().width - 1, getRectangle().height - 1);
root_container.paint();
// draw goto elements
for (GoTo goTo : valid_gotos) {
goTo.paint(HandlerElementMap.getHandlerForElement(this).getZoomFactor(), getGotoPosition(goTo.getDirection()));
}
}
@Override
public Set<Direction> getResizeArea(int x, int y) {
return new HashSet<Direction>(); // deny size changes
}
@Override
public StickingPolygon generateStickingBorder() {
return null;
}
@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());
}
};
}
}