package com.baselet.element.sequence_aio.facet; import com.baselet.control.basics.Line1D; import com.baselet.control.basics.geom.PointDouble; import com.baselet.control.enums.AlignHorizontal; import com.baselet.control.enums.AlignVertical; import com.baselet.diagram.draw.DrawHandler; import com.baselet.diagram.draw.TextSplitter; public class StateInvariant implements LifelineOccurrence { /** the width of the ellipse which builds the left and right border (no text is drawn in this) */ private static final double ROUND_PART_WIDTH = 20; /** how much of the round part width can be used for drawing text */ private static final double ROUND_PART_TEXT_PERCENTAGE = 0.3; /** the space between the text and the border line */ private static final double VERTICAL_BORDER_PADDING = 5; private static final double CURLY_BRACKETS_Y_PADDING = 2; /** the minimum width, to avoid splitting text with very short words e.g. print "I am I" in one line instead of 3*/ private static final double MIN_WIDTH = 50; private final String[] lines; private final StateInvariantStyle style; /** * * @param text the text, lines need to be separated by \n * @param style how it should be drawn */ public StateInvariant(String text, StateInvariantStyle style) { super(); this.style = style; if (style == StateInvariantStyle.CURLY_BRACKETS) { lines = ("{" + text + "}").split("\n"); } else { lines = text.split("\n"); } } @Override public Line1D draw(DrawHandler drawHandler, PointDouble topLeft, PointDouble size) { double height = getHeight(drawHandler, size.x); double topY = topLeft.y + (size.y - height) / 2; Line1D interruptedLifeline = new Line1D(topY, topY + height); if (style == StateInvariantStyle.STATE) { // draw border and adjust textY and textLeftX drawHandler.drawArc(topLeft.x, topY, ROUND_PART_WIDTH * 2, height, 90, 180, true); drawHandler.drawArc(topLeft.x + size.x - ROUND_PART_WIDTH * 2, topY, ROUND_PART_WIDTH * 2, height, 270, 180, true); drawHandler.drawLine(topLeft.x + ROUND_PART_WIDTH, topY, topLeft.x + size.x - ROUND_PART_WIDTH, topY); drawHandler.drawLine(topLeft.x + ROUND_PART_WIDTH, topY + height, topLeft.x + size.x - ROUND_PART_WIDTH, topY + height); TextSplitter.drawText(drawHandler, lines, topLeft.x + ROUND_PART_WIDTH * (1 - ROUND_PART_TEXT_PERCENTAGE), topY, size.x - ROUND_PART_WIDTH * (1 - ROUND_PART_TEXT_PERCENTAGE) * 2, height, AlignHorizontal.CENTER, AlignVertical.CENTER); } else if (style == StateInvariantStyle.CURLY_BRACKETS) { TextSplitter.drawText(drawHandler, lines, topLeft.x, topY, size.x, height, AlignHorizontal.CENTER, AlignVertical.CENTER); } return interruptedLifeline; } @Override public double getMinWidth(DrawHandler drawHandler) { double minWidth = Math.max(MIN_WIDTH, TextSplitter.getTextMinWidth(lines, drawHandler)); if (style == StateInvariantStyle.CURLY_BRACKETS) { return minWidth; } else { return minWidth + ROUND_PART_WIDTH * (1 - ROUND_PART_TEXT_PERCENTAGE) * 2; } } @Override public double getAdditionalYHeight(DrawHandler drawHandler, PointDouble size) { return getHeight(drawHandler, size.x) - size.y; } private double getHeight(DrawHandler drawHandler, double totalWidth) { if (style == StateInvariantStyle.STATE) { totalWidth -= ROUND_PART_WIDTH * (1 - ROUND_PART_TEXT_PERCENTAGE) * 2; } if (style == StateInvariantStyle.CURLY_BRACKETS) { return TextSplitter.getSplitStringHeight(lines, totalWidth, drawHandler) + CURLY_BRACKETS_Y_PADDING * 2; } else { return TextSplitter.getSplitStringHeight(lines, totalWidth, drawHandler) + VERTICAL_BORDER_PADDING * 2; } } public enum StateInvariantStyle { STATE, CURLY_BRACKETS // UML 2.5 also allows a NOTE as a StateInvariant but this is not supported by this diagram (yet) } }