package com.baselet.element.facet.specific; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import com.baselet.control.basics.geom.PointDouble; import com.baselet.control.constants.SharedConstants; import com.baselet.control.enums.AlignHorizontal; import com.baselet.diagram.draw.DrawHandler; import com.baselet.diagram.draw.DrawHandler.Layer; import com.baselet.diagram.draw.helper.ColorOwn; import com.baselet.element.draw.DrawHelper; import com.baselet.element.facet.Facet; import com.baselet.element.facet.KeyValueFacet; import com.baselet.element.facet.PropertiesParserState; import com.baselet.gui.AutocompletionText; public class HierarchyFacet extends Facet { private static final double ARROW_LENGTH = 12; private static final double CIRCLE_DIAMETER = 10; private static final String KEY = "type"; private static final class ReferencePoint { PointDouble p; boolean hasSymbol = false; public ReferencePoint(PointDouble p) { super(); this.p = p; } } private static final class Cache { HierarchyType type = HierarchyType.Actor; private int lineNr; private final List<ReferencePoint> points = new ArrayList<ReferencePoint>(); } public static enum HierarchyType { Actor, Package, WorkProcess; } public static final HierarchyFacet INSTANCE = new HierarchyFacet(); @Override public boolean checkStart(String line, PropertiesParserState state) { return true; } @Override public void handleLine(String line, PropertiesParserState state) { if (line.isEmpty()) { return; } Cache cache = state.getOrInitFacetResponse(HierarchyFacet.class, new Cache()); for (HierarchyType type : HierarchyType.values()) { if (line.equals(KEY + KeyValueFacet.SEP + type)) { cache.type = type; return; } } DrawHandler drawer = state.getDrawer(); drawer.setLayer(Layer.Foreground); ColorOwn bgBefore = drawer.getBackgroundColor(); drawer.setBackgroundColor(ColorOwn.TRANSPARENT); String lineWithoutTabs = line.replace("\t", ""); int tabCount = line.length() - lineWithoutTabs.length(); int border = 10; PointDouble upperLeftPoint = null; PointDouble lowerRightPoint = null; PointDouble textPos = null; if (cache.type == HierarchyType.Actor) { int actorDimension = 10; int actorHCenter = border + actorDimension + actorDimension * 5 * tabCount; int actorVTop = border + cache.lineNr * actorDimension * 6; DrawHelper.drawActor(drawer, actorHCenter, actorVTop, actorDimension); upperLeftPoint = new PointDouble(actorHCenter, actorVTop + actorDimension * 5.5 + ARROW_LENGTH); lowerRightPoint = new PointDouble(actorHCenter - actorDimension * 2, actorVTop + actorDimension * 2.5); drawLinesAndUpperLeftSymbol(lowerRightPoint, drawer, cache, lineWithoutTabs, tabCount, true); textPos = new PointDouble(actorHCenter + actorDimension * 2, actorVTop + actorDimension * 3); drawer.print(lineWithoutTabs, textPos, AlignHorizontal.LEFT); updateElementSize(state, lineWithoutTabs, lowerRightPoint, textPos, drawer.textWidth(lineWithoutTabs), DrawHelper.headToLegLength(actorDimension)); } else if (cache.type == HierarchyType.Package) { int fullHeight = 20; int fullWidth = 30; double xPos = border + tabCount * fullWidth * 1.4; double yPos = border + cache.lineNr * fullHeight * 1.6; DrawHelper.drawPackage(drawer, xPos, yPos, 5, 10, fullHeight, fullWidth); upperLeftPoint = new PointDouble(xPos + fullWidth * 0.3, yPos + fullHeight + CIRCLE_DIAMETER); lowerRightPoint = new PointDouble(xPos, yPos + fullHeight * 0.5); drawLinesAndUpperLeftSymbol(lowerRightPoint, drawer, cache, lineWithoutTabs, tabCount, false); textPos = new PointDouble(xPos + fullWidth * 1.15, yPos + fullHeight * 0.8); drawer.print(lineWithoutTabs, textPos, AlignHorizontal.LEFT); updateElementSize(state, lineWithoutTabs, lowerRightPoint, textPos, drawer.textWidth(lineWithoutTabs), fullHeight + SharedConstants.DEFAULT_GRID_SIZE); } else if (cache.type == HierarchyType.WorkProcess) { double fullHeight = 40; double fullWidth = 140; double xPos = border + tabCount * fullWidth; double yPos = border + cache.lineNr * fullHeight * 1.2; drawer.drawEllipse(xPos, yPos, fullWidth, fullHeight); upperLeftPoint = new PointDouble(xPos + fullWidth * 0.5, yPos + fullHeight + ARROW_LENGTH); lowerRightPoint = new PointDouble(xPos, yPos + fullHeight * 0.5); drawLinesAndUpperLeftSymbol(lowerRightPoint, drawer, cache, lineWithoutTabs, tabCount, true); textPos = new PointDouble(xPos + fullWidth / 2.0, yPos + fullHeight / 2.0 + drawer.textHeight(lineWithoutTabs) / 2.0); drawer.print(lineWithoutTabs, textPos, AlignHorizontal.CENTER); updateElementSize(state, lineWithoutTabs, lowerRightPoint, textPos, fullWidth / 2, fullHeight); } // store last point as reference if (tabCount == 0) { cache.points.clear(); } // for each tab which is missing in this line compared to the previous one, remove one stored point while (cache.points.size() > tabCount) { cache.points.remove(cache.points.size() - 1); } cache.points.add(new ReferencePoint(upperLeftPoint)); cache.lineNr++; drawer.setLayer(Layer.Background); drawer.setBackgroundColor(bgBefore); } private void updateElementSize(PropertiesParserState state, String lineWithoutTabs, PointDouble lowerRightPoint, PointDouble textPos, double widthAddon, double heightAddon) { state.updateMinimumSize(textPos.x + widthAddon, lowerRightPoint.y + heightAddon); } private static void drawLinesAndUpperLeftSymbol(PointDouble lowerRightPoint, DrawHandler drawer, Cache cache, String lineWithoutTabs, int tabCount, boolean arrow) { if (tabCount != 0) { try { ReferencePoint ref = cache.points.get(tabCount - 1); PointDouble p1 = new PointDouble(lowerRightPoint.x, lowerRightPoint.y); PointDouble p2 = new PointDouble(ref.p.x, lowerRightPoint.y); PointDouble p3 = new PointDouble(ref.p.x, ref.p.y); drawer.drawLines(p1, p2, p3); if (!ref.hasSymbol) { ref.hasSymbol = true; if (arrow) { PointDouble upper = new PointDouble(ref.p.x, ref.p.y - ARROW_LENGTH); PointDouble lowerLeft = new PointDouble(ref.p.x - ARROW_LENGTH / 2, ref.p.y); PointDouble lowerRight = new PointDouble(ref.p.x + ARROW_LENGTH / 2, ref.p.y); drawer.drawLines(upper, lowerLeft, lowerRight, upper); } else { int dist = 2; double circleRadius = CIRCLE_DIAMETER / 2; drawer.drawCircle(ref.p.x, ref.p.y - circleRadius, circleRadius); drawer.drawLine(ref.p.x, ref.p.y - CIRCLE_DIAMETER + dist, ref.p.x, ref.p.y - dist); drawer.drawLine(ref.p.x - circleRadius + dist, ref.p.y - circleRadius, ref.p.x + circleRadius - dist, ref.p.y - circleRadius); } } } catch (IndexOutOfBoundsException e) { throw new RuntimeException("Too many tabs in line nr." + (cache.lineNr + 1) + ": " + lineWithoutTabs); } } } @Override public List<AutocompletionText> getAutocompletionStrings() { return Arrays.asList(new AutocompletionText(KEY + KeyValueFacet.SEP + HierarchyType.Actor, "draws hierarchy of actors"), new AutocompletionText(KEY + KeyValueFacet.SEP + HierarchyType.Package, "draws hierarchy of packages"), new AutocompletionText(KEY + KeyValueFacet.SEP + HierarchyType.WorkProcess, "draws hierarchy of work processes")); } }