package com.baselet.element.relation.facet; import java.util.Arrays; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.baselet.control.SharedUtils; import com.baselet.control.basics.geom.Line; import com.baselet.control.enums.LineType; import com.baselet.control.enums.RegexValueHolder; import com.baselet.diagram.draw.DrawHandler; import com.baselet.diagram.draw.helper.ColorOwn; import com.baselet.diagram.draw.helper.ColorOwn.Transparency; import com.baselet.diagram.draw.helper.StyleException; import com.baselet.element.facet.FirstRunKeyValueFacet; import com.baselet.element.facet.PropertiesParserState; import com.baselet.element.relation.helper.RelationPointHandler; import com.baselet.element.relation.helper.ResizableObject; import com.baselet.element.sticking.PointDoubleIndexed; /** * must be in first-run because it manipulates the relation position and size (in case of [text]-[text] line which can possibly exceed the old relation size) * must handle values in parsingFinished when drawer-setup is finished */ public class RelationLineTypeFacet extends FirstRunKeyValueFacet { static final String KEY = "lt"; private static class Match<T extends RegexValueHolder> { private final String text; private final T type; public Match(String matchedText, T matchedObject) { super(); this.text = matchedText; this.type = matchedObject; } } public static final RelationLineTypeFacet INSTANCE = new RelationLineTypeFacet(); private RelationLineTypeFacet() {} private final Logger log = LoggerFactory.getLogger(RelationLineTypeFacet.class); /** * all arrowtypes and linetypes to expect (order is important because eg << must be before < to be recognized correctly, therefore there are 2 shared lists. also linetype .. must be before .) */ private static final List<ArrowEnd> SHARED_ARROW_STRINGS_BEFORE = Arrays.asList(ArrowEnd.CIRCLE_CROSS, ArrowEnd.CIRCLE, ArrowEnd.DIAGONAL_CROSS); private static final List<ArrowEnd> SHARED_ARROW_STRINGS_AFTER = Arrays.asList(ArrowEnd.BOX); private static final List<ArrowEnd> LEFT_ARROW_STRINGS = SharedUtils.mergeLists(SHARED_ARROW_STRINGS_BEFORE, Arrays.asList(ArrowEnd.LEFT_BOX, ArrowEnd.LEFT_FILLED_DIAMOND, ArrowEnd.LEFT_DIAMOND, ArrowEnd.LEFT_FILLED_CLOSED, ArrowEnd.LEFT_CLOSED, ArrowEnd.LEFT_NORMAL, ArrowEnd.LEFT_INVERTED, ArrowEnd.LEFT_INTERFACE_OPEN, ArrowEnd.LEFT_MEASURE_NORMAL), SHARED_ARROW_STRINGS_AFTER); private static final List<ArrowEnd> RIGHT_ARROW_STRINGS = SharedUtils.mergeLists(SHARED_ARROW_STRINGS_BEFORE, Arrays.asList(ArrowEnd.RIGHT_BOX, ArrowEnd.RIGHT_FILLED_DIAMOND, ArrowEnd.RIGHT_DIAMOND, ArrowEnd.RIGHT_FILLED_CLOSED, ArrowEnd.RIGHT_CLOSED, ArrowEnd.RIGHT_MEASURE_NORMAL, ArrowEnd.RIGHT_NORMAL, ArrowEnd.RIGHT_INVERTED, ArrowEnd.RIGHT_INTERFACE_OPEN), SHARED_ARROW_STRINGS_AFTER); private static final List<LineType> LINE_TYPES = Arrays.asList(LineType.SOLID, LineType.DOTTED, LineType.DASHED); public RelationPointHandler getRelationPoints(PropertiesParserState state) { return ((SettingsRelation) state.getSettings()).getRelationPoints(); } private static class Remaining { String value; public Remaining(String value) { this.value = value; } @Override public String toString() { // is used in error message return value; } } @Override public void handleValue(String value, PropertiesParserState state) {} private <T extends RegexValueHolder> String listToString(List<T> valueHolderList) { StringBuilder sb = new StringBuilder(); for (RegexValueHolder r : valueHolderList) { String simpleRegex = r.getRegexValue().replace(ArrowEnd.BOX_REGEX, "[text]").replaceAll("\\\\", ""); sb.append(simpleRegex).append(','); } sb.setLength(sb.length() - 1); return sb.toString(); } /** * if no explicit linetype is set, draw a solid line without arrows */ public static void drawDefaultLineAndArrows(DrawHandler drawer, RelationPointHandler relationPoints) { drawLineAndArrows(drawer, relationPoints, new Match<LineType>("", LineType.SOLID), new Match<ArrowEnd>("", null), new Match<ArrowEnd>("", null)); } private static void drawLineAndArrows(DrawHandler drawer, RelationPointHandler relationPoints, Match<LineType> lineType, Match<ArrowEnd> leftArrow, Match<ArrowEnd> rightArrow) { drawLineBetweenPoints(drawer, relationPoints, lineType.type, leftArrow.type != null, rightArrow.type != null); drawArrowEnds(drawer, relationPoints, leftArrow, rightArrow); relationPoints.resizeRectAndReposPoints(); // line description and relation-endings can change the relation size, therefore recalc it now } private static void drawArrowEnds(DrawHandler drawer, RelationPointHandler relationPoints, Match<ArrowEnd> leftArrow, Match<ArrowEnd> rightArrow) { ColorOwn oldBgColor = drawer.getBackgroundColor(); drawer.setBackgroundColor(oldBgColor.transparency(Transparency.FOREGROUND)); // arrow background is not transparent print(drawer, relationPoints, leftArrow, relationPoints.getFirstLine(), true); print(drawer, relationPoints, rightArrow, relationPoints.getLastLine(), false); drawer.setBackgroundColor(oldBgColor); // reset background } private static void print(DrawHandler drawer, ResizableObject relationPoints, Match<ArrowEnd> match, Line line, boolean drawOnLineStart) { relationPoints.resetPointMinSize(((PointDoubleIndexed) line.getPoint(drawOnLineStart)).getIndex()); if (match.type != null) { match.type.print(drawer, line, drawOnLineStart, match.text, relationPoints); } } private static void drawLineBetweenPoints(DrawHandler drawer, RelationPointHandler relationPoints, LineType lineType, boolean shortLeftLine, boolean shortRightLine) { LineType oldLt = drawer.getLineType(); drawer.setLineType(lineType); relationPoints.drawLinesBetweenPoints(drawer, shortLeftLine, shortRightLine); drawer.setLineType(oldLt); } private <T extends RegexValueHolder> Match<T> extractPart(List<T> valueHolderList, Remaining remaining) { for (T valueHolder : valueHolderList) { String regex = "^" + valueHolder.getRegexValue(); // only match from start of the line (left to right) String newRemainingValue = remaining.value.replaceFirst(regex, ""); if (!remaining.value.equals(newRemainingValue)) { String removedPart = remaining.value.substring(0, remaining.value.length() - newRemainingValue.length()); remaining.value = newRemainingValue; return new Match<T>(removedPart, valueHolder); } } return new Match<T>("", null); } private String getValueNotNull(Match<? extends RegexValueHolder> valueHolder) { if (valueHolder.type == null) { return ""; } else { return valueHolder.type.getRegexValue(); } } @Override public void parsingFinished(PropertiesParserState state, List<String> handledLines) { if (handledLines.isEmpty()) { drawDefaultLineAndArrows(state.getDrawer(), getRelationPoints(state)); } else if (handledLines.size() == 1) { RelationPointHandler relationPoints = getRelationPoints(state); String value = extractValue(handledLines.get(0)); Remaining remaining = new Remaining(value); Match<ArrowEnd> leftArrow = extractPart(LEFT_ARROW_STRINGS, remaining); Match<LineType> lineType = extractPart(LINE_TYPES, remaining); if (leftArrow.type == null && lineType.type == null) { throw new StyleException("left arrow must be one of the following or empty:\n" + listToString(LEFT_ARROW_STRINGS)); } if (lineType.type == null) { throw new StyleException("lineType must be specified. One of: " + listToString(LINE_TYPES)); } Match<ArrowEnd> rightArrow = extractPart(RIGHT_ARROW_STRINGS, remaining); if (rightArrow.type == null && !remaining.value.isEmpty()) { throw new StyleException("right arrow must be one of the following or empty:\n" + listToString(RIGHT_ARROW_STRINGS)); } if (!remaining.value.isEmpty()) { throw new StyleException("Unknown part after rightArrow: " + remaining); } log.debug("Split Relation " + value + " into following parts: " + getValueNotNull(leftArrow) + " | " + getValueNotNull(lineType) + " | " + getValueNotNull(rightArrow)); drawLineAndArrows(state.getDrawer(), relationPoints, lineType, leftArrow, rightArrow); relationPoints.resizeRectAndReposPoints(); // apply the (possible) changes now to make sure the following facets use correct coordinates } else { throw new StyleException("Only one lineType allowed"); } } @Override public KeyValue getKeyValue() { return new KeyValue( KEY, new ValueInfo("<-", "left arrow", "iVBORw0KGgoAAAANSUhEUgAAAEkAAAAQCAAAAAB/mQ0/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAq0lEQVR42mL8z4AAX3gYyAdMCOabUsUf1DDpR4fqld0cFJjE8P////////+eL2Oy/z9FAGLSdgOV5b//U27SaQeR7u///1Ns0u0Inor3/ykHDLcZqAP+M/x/HuJw/T813PT////1Ks2/KTeJiYGBIeD8S8MTVPDf//////8/rpHzmQpuYmBgsLgsbLiFKm76////bZuQ55SncQiYrvCaApMYkUuVDwIUeA4wABeKjdP2NRIjAAAAAElFTkSuQmCC") , new ValueInfo("|<-", "left measure-arrow", "iVBORw0KGgoAAAANSUhEUgAAAEkAAAAQCAAAAAB/mQ0/AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQffBxQPHSyED19AAAAAp0lEQVQ4y2P8z/ifAQa+8DCQD5gQzDelij+oYdKPDtUruzkoMInhP8P//////54vY7L/P0UAYtJ2A5Xlv/9TbtJpB5Hu7///U25SBE/F+/+UA4bbDNQB/xn/M4a8ma5BFbMY/q9Xaf5Nue+YGBgYAs6/NDxBFTf9////uEbOZyq4iYGBweKysOEWqrjp////t21CnlOexiFgusJrCkxiRC5VPghQ4DkAWvhD8uO+gNEAAAAA") , new ValueInfo("<.", "left arrow with dashed line", "iVBORw0KGgoAAAANSUhEUgAAAEkAAAAQCAAAAAB/mQ0/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAsUlEQVR42mL8z4AAX3gYyAdMCOabUsUf1DDpR4fqld0cFJjE8P////////+eL2Oy/z9FAGLSdgOV5b//U27SaQeR7u///1Ns0u0Inor3/ykHDLdhAVb///////Xkcur/M/x/HuJw/T813PT////1Ks2/KTeJiYGBIeD8S8MTDBQDiIHHNXI+U8FNDAwMFpeFDbdQxU3///+/bRPynPI0DgHTFV5TYBIjcqnyQYACzwEGAD/RpzQKsM6sAAAAAElFTkSuQmCC") , new ValueInfo("<..", "left arrow with dotted line", "iVBORw0KGgoAAAANSUhEUgAAAEkAAAAQCAAAAAB/mQ0/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAArElEQVR42mL8z4AAX3gYyAdMCOabUsUf1DDpR4fqld0cFJjE8P////////+eL2Oy/z9FAGLSdgOV5b//U27SaQeR7u///1Ns0u0Inor3/ykHDLcZGB7X1/+nHDH8fx7icP0/Ndz0////9SrNvyk3iYmBgSHg/EvDEwwUA4iBxzVyPlPBTQwMDBaXhQ23UMVN////v20T8pzyNA4B0xVeU2ASI3Kp8kGAAs8BBgAP7rtyPzvx8QAAAABJRU5ErkJggg==") , new ValueInfo("<<-", "closed left arrow", "iVBORw0KGgoAAAANSUhEUgAAAEkAAAAQCAAAAAB/mQ0/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAArElEQVR42mL8z4AAd1QYyAdMCOabUlUGapj0o0P1CgNF4P////////89X8Zk/38ohzwA0bzdQGX57/+Um3TaQaT7O8JYsk26HcFT8R7ZgeSadJuB4TGcQ0l4M/x/HuJw/T813PT////1Ks2/KTeJiYGBIeD8S8MTDBQDiIHHNXI+U8FNDAwMFpeFDbdQ5iRGeITdSZRY858qOVjlsDPDG6q4iYGB4YMABSYBBgB/rAWtC0QJWQAAAABJRU5ErkJggg==") , new ValueInfo("<<<-", "filled closed left arrow", "iVBORw0KGgoAAAANSUhEUgAAAEkAAAAQCAAAAAB/mQ0/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAmklEQVR42mL8z4AAd1QYyAdMCOabCFUGSsB/KPhewsYI55ADoJp/z+JjQjKWfJO2y7KgOJBck04bs6J5lTyTbgeyMDFQw6TbDNQB/xn+P/cUZaaKm/7//79eVpJKJv3/nCUpRrFJjJDcciKB9cYfBob/lOcWiyvB0taUBTkj3Bl3Ev8d+0+NfPf////pDK8pDycI+CBAgZMAAwDOwWE1BFRLXwAAAABJRU5ErkJggg==") , new ValueInfo("<<<<-", "diamond left arrow", "iVBORw0KGgoAAAANSUhEUgAAAEkAAAAQCAAAAAB/mQ0/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAA3UlEQVR42mL8z4AAPzgYyAdMCOabUs7EO1Qw6UeH6pXVDLoUmPX/////////ni9jsv////+3EzgSbv8nC0BM2m6gsvw3RIBssxj+//9/2kGk+ztCiEyzGP7fjuCpeI8qSJZZDLcZGB5jCl8nPbwZ/j8PcbiObtBhg5jH5ITTepXm38hi92MsDpMZd59zdI7DRb43K8z+TXYq+H9cI+czhLVao+Q9Benp///f9Sqb////f93F5fr//xSZ9P//bZuQ6zkam///p9ik//+nMxR8J9ug/4zIpcobEQpKFcAAdWYW1hzT7fsAAAAASUVORK5CYII=") , new ValueInfo("<<<<<-", "filled diamond left arrow", "iVBORw0KGgoAAAANSUhEUgAAAEkAAAAQCAAAAAB/mQ0/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAyUlEQVR42mL8z4AAPzgYyAdMCOabCE6NSxQY9R8KvpewMTIwMJrc+E8mgJr0exYf1HVM3rcpMWm7LAuShwNvk2vSaWNWFA+zskTdJsek24EsTOiBx8GaQLJZDLcZqAP+M/x/7inKjC7MLx39mJxwWi8riWIOs7bJYTLj7nOWpBjCIHWp2b/JT0/H1XWg6UBCruQ9JSnz/+96eWsGBgZBVafrZKZxRngOvpP47wX7734fivPd////pzMUfP9PNmBELlXeiFCQoAADAGzWnc70WHTSAAAAAElFTkSuQmCC") , new ValueInfo("()-", "circle left arrow", "iVBORw0KGgoAAAANSUhEUgAAAEoAAAAXCAAAAACJq4aEAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAA/0lEQVR42mL8z0AtwARjXKi15WRkNS09QalRNzwD2Zvf//88WTzV9Qq5Zv3//////90S/b//Q8Fkic3/yQIM/////79b5TiS0GWFzWQbdV3iOIrYZYnL5Brl0Y8mONmFTKPOK/xGF9U5ToZRTAwMa5NZ0OMifi15ieGADYaozQEyjGL8z8D5ngNd9Afvb7KMYsSSdxjJSKAsDAwcPzBdxfKbrLAywcx2FwzIC3aHIxiiRxzIy4NUTFcGGlPQjJ8iYUFmyUDFPIheMqiQXzL8/79bhFrl1f//1z0Umvd////9eLeOy2XyTPoPT+oX1h4484PFwCHYgtzymJF6NQ5gAGG56ureP9K3AAAAAElFTkSuQmCC") , new ValueInfo("(+)-", "circle left arrow with plus symbol", "iVBORw0KGgoAAAANSUhEUgAAAEoAAAAXCAAAAACJq4aEAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAABD0lEQVR42mL8z0AtwARjXKi15WRkNS09QalRNzwD2Zvf//88WTzV9Qq5Zv3//////90S/b//Q8Fkic3/yQIM/////79b5TiS0GWFzWQbdV3iOIrYZYnL5Brl0Y/sxP///092IdOo8wq/0Yz6r3OcDKOYGBjWJrOgx0X8WvISwwEbDFGbA2QYxfifgfM9B4IDAT94f5NlFON/BgYGRqSUhsolOoGyMDBw/OCA6EdyFctvssLKBDPbXTAgL9gdjmCIHnEgLw9SMV0ZaExBM36KhAWZJQMV8yB6yaBCfsnw//9uEWqVV///X/dQaN7//f/34906LpfJM+k/PFVeWHvgzA8WA4dgC3LLY0bq1TiAAQAcj+vskkcbaQAAAABJRU5ErkJggg==") , new ValueInfo("x-", "diagonally crossed left arrow", "iVBORw0KGgoAAAANSUhEUgAAAEoAAAAXCAAAAACJq4aEAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAf0lEQVR42uzUwQ3AIAgFUHQCjq7gnB3J3uhOsgG9WErTS4vcWk6GxBejX5JAVGX4CsWmyVMU11V7W/VZMoqw3VbvSikVvJKhhuGWLCWEbUK6UEK4+CWBqI8jkiHoVCYMoXcV94JxuYpLey/nfirdQ6UjDIxmMqAnDukfyI9rHwBCzBVSiqTGJwAAAABJRU5ErkJggg==") , new ValueInfo("[txt]-", "box end with text", "iVBORw0KGgoAAAANSUhEUgAAAEoAAAAXCAAAAACJq4aEAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAAzUlEQVR42mL8z0AtwMLAiF3iPxlGYdfESLqrmKjmv1GjyDDqOQM6g2TA+J/xPwMDAwPPF6gAlMFIVrpiYGBguPB1jwibFsONX/++7nFiosiDhxhWfrN5+drx+yGGlf8gziIVwD3I+J+h/uoXtyKYz8jwIJJR//T4jzJQYBRSuNx6++AGRanhP8P//////2f4/Vd3xSrd3/8ZfkP4/0kGMKNsOFNC//8PLP1vw3mdTKNgYcXwB5osoAwKgh1TfJhkZxYcZS8jOUZRrcoBDABbMrpBXVzS8wAAAABJRU5ErkJggg==") , new ValueInfo("[txt]<-", "box end with text and arrow pointing to it", "iVBORw0KGgoAAAANSUhEUgAAAEoAAAAXCAAAAACJq4aEAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAABPklEQVR42qyVv0sDMRTHX6SDpQ4KDg4OCicUpKDgJDc4OStU8A+40UkouHVwEZw6dXRUXBwcHG5wEgsOh2Cp2KVT/VVRsGAhHB8HSe/AGy7HvS0v5JNvXr55UUheURCVPEEGVPIiZa9qIrfzpUEN80INaoujXFCjo6UHfzKlLISkEAB9Mr92TdowqL5J9CPU1YpzqrFGlUyiZPJ3G7PHP2CNCsQP2tC5D8QPAWR36uATMqAa4t3MvLzNtRriaaBrbysUChERhdTbw819+RuKqg6aZVuYmDKHy+vm6kC4cA611QFjvnr66D3G9tgKXldbmVTpsHJ2XtGIjnx1W977tjeDW/R2YLuGW+yMUei6c2mNYlyXmCqg61afbVFJDweA5sJ7OpQxw7/WF2W/ptNVPQXKoiGrnBpyQXL7cn4HACsYkiEdOFqWAAAAAElFTkSuQmCC")); } }