diff --git a/src/net/sourceforge/plantuml/activitydiagram3/InstructionIf.java b/src/net/sourceforge/plantuml/activitydiagram3/InstructionIf.java index ee2b423b872..49e136308c3 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/InstructionIf.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/InstructionIf.java @@ -59,6 +59,7 @@ import net.sourceforge.plantuml.sequencediagram.NoteType; import net.sourceforge.plantuml.stereo.Stereotype; import net.sourceforge.plantuml.style.ISkinParam; +import net.sourceforge.plantuml.style.StyleBuilder; import net.sourceforge.plantuml.url.Url; public class InstructionIf extends WithNote implements Instruction, InstructionCollection { @@ -77,6 +78,7 @@ public class InstructionIf extends WithNote implements Instruction, InstructionC private final Stereotype stereotype; private final Swimlane swimlane; + private final StyleBuilder currentStyleBuilder; @Override public boolean containsBreak() { @@ -98,8 +100,9 @@ public InstructionIf(Swimlane swimlane, Instruction parent, Display labelTest, L this.skinParam = skinParam; this.topInlinkRendering = Objects.requireNonNull(inlinkRendering); this.swimlane = swimlane; - this.thens.add(new Branch(skinParam.getCurrentStyleBuilder(), swimlane, whenThen, labelTest, color, - LinkRendering.none(), stereotype)); + this.currentStyleBuilder = skinParam.getCurrentStyleBuilder(); + this.thens.add(new Branch(currentStyleBuilder, swimlane, whenThen, labelTest, color, LinkRendering.none(), + stereotype)); this.current = this.thens.get(0); } @@ -138,12 +141,12 @@ public Ftile createFtile(FtileFactory factory) { branch.updateFtile(factory); if (elseBranch == null) - this.elseBranch = new Branch(skinParam.getCurrentStyleBuilder(), swimlane, LinkRendering.none(), + this.elseBranch = new Branch(currentStyleBuilder, swimlane, LinkRendering.none(), Display.NULL, null, LinkRendering.none(), stereotype); elseBranch.updateFtile(factory); Ftile result = factory.createIf(swimlane, thens, elseBranch, outColor, topInlinkRendering, url, - getPositionedNotes(), stereotype); + getPositionedNotes(), stereotype, currentStyleBuilder); // if (getPositionedNotes().size() > 0) // result = FtileWithNoteOpale.create(result, getPositionedNotes(), false, VerticalAlignment.CENTER); diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactory.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactory.java index ada34034c5f..7b4f9a4bf43 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactory.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactory.java @@ -52,6 +52,7 @@ import net.sourceforge.plantuml.stereo.Stereotype; import net.sourceforge.plantuml.style.ISkinParam; import net.sourceforge.plantuml.style.Style; +import net.sourceforge.plantuml.style.StyleBuilder; import net.sourceforge.plantuml.url.Url; public interface FtileFactory { @@ -89,7 +90,8 @@ public Ftile createWhile(LinkRendering outColor, Swimlane swimlane, Ftile whileB HColor color, Instruction specialOut, Ftile backward, LinkRendering incoming1, LinkRendering incoming2); public Ftile createIf(Swimlane swimlane, List thens, Branch elseBranch, LinkRendering outColor, - LinkRendering topInlinkRendering, Url url, Collection notes, Stereotype stereotype); + LinkRendering topInlinkRendering, Url url, Collection notes, Stereotype stereotype, + StyleBuilder currentStyleBuilder); public Ftile createSwitch(Swimlane swimlane, List branches, LinkRendering afterEndwhile, LinkRendering topInlinkRendering, Display labelTest); diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactoryDelegator.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactoryDelegator.java index c95352ecb6a..4738d072565 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactoryDelegator.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactoryDelegator.java @@ -60,6 +60,7 @@ import net.sourceforge.plantuml.style.ISkinParam; import net.sourceforge.plantuml.style.SName; import net.sourceforge.plantuml.style.Style; +import net.sourceforge.plantuml.style.StyleBuilder; import net.sourceforge.plantuml.style.StyleSignatureBasic; import net.sourceforge.plantuml.url.Url; @@ -188,8 +189,10 @@ public Ftile createWhile(LinkRendering outColor, Swimlane swimlane, Ftile whileB @Override public Ftile createIf(Swimlane swimlane, List thens, Branch elseBranch, LinkRendering afterEndwhile, - LinkRendering topInlinkRendering, Url url, Collection notes, Stereotype stereotype) { - return factory.createIf(swimlane, thens, elseBranch, afterEndwhile, topInlinkRendering, url, notes, stereotype); + LinkRendering topInlinkRendering, Url url, Collection notes, Stereotype stereotype, + StyleBuilder currentStyleBuilder) { + return factory.createIf(swimlane, thens, elseBranch, afterEndwhile, topInlinkRendering, url, notes, stereotype, + currentStyleBuilder); } @Override diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileFactoryDelegatorIf.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileFactoryDelegatorIf.java index bd116dc3f07..92eda31feec 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileFactoryDelegatorIf.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileFactoryDelegatorIf.java @@ -51,6 +51,7 @@ import net.sourceforge.plantuml.stereo.Stereotype; import net.sourceforge.plantuml.style.PName; import net.sourceforge.plantuml.style.Style; +import net.sourceforge.plantuml.style.StyleBuilder; import net.sourceforge.plantuml.svek.ConditionEndStyle; import net.sourceforge.plantuml.svek.ConditionStyle; import net.sourceforge.plantuml.url.Url; @@ -66,15 +67,16 @@ public FtileFactoryDelegatorIf(FtileFactory factory, Pragma pragma) { @Override public Ftile createIf(Swimlane swimlane, List thens, Branch elseBranch, LinkRendering afterEndwhile, - LinkRendering topInlinkRendering, Url url, Collection notes, Stereotype stereotype) { + LinkRendering topInlinkRendering, Url url, Collection notes, Stereotype stereotype, + StyleBuilder currentStyleBuilder) { final ConditionStyle conditionStyle = skinParam().getConditionStyle(); final ConditionEndStyle conditionEndStyle = skinParam().getConditionEndStyle(); final Branch branch0 = thens.get(0); - final Style styleArrow = getDefaultStyleDefinitionArrow().getMergedStyle(skinParam().getCurrentStyleBuilder()); + final Style styleArrow = getDefaultStyleDefinitionArrow().getMergedStyle(currentStyleBuilder); final Style styleDiamond = getDefaultStyleDefinitionDiamond().withTOBECHANGED(stereotype) - .getMergedStyle(skinParam().getCurrentStyleBuilder()); + .getMergedStyle(currentStyleBuilder); final HColor backColor = branch0.getColor() == null ? styleDiamond.value(PName.BackGroundColor).asColor(skinParam().getIHtmlColorSet()) : branch0.getColor(); diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/VCompactFactory.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/VCompactFactory.java index e2ee4eaa6ff..340ed7a5788 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/VCompactFactory.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/VCompactFactory.java @@ -169,7 +169,8 @@ public Ftile createWhile(LinkRendering afterEndwhile, Swimlane swimlane, Ftile w @Override public Ftile createIf(Swimlane swimlane, List thens, Branch elseBranch, LinkRendering afterEndwhile, - LinkRendering topInlinkRendering, Url url, Collection notes, Stereotype stereotype) { + LinkRendering topInlinkRendering, Url url, Collection notes, Stereotype stereotype, + StyleBuilder currentStyleBuilder) { final List ftiles = new ArrayList<>(); for (Branch branch : thens) ftiles.add(branch.getFtile()); diff --git a/src/net/sourceforge/plantuml/ebnf/ETileNamedGroup.java b/src/net/sourceforge/plantuml/ebnf/ETileNamedGroup.java new file mode 100644 index 00000000000..60927fa4c49 --- /dev/null +++ b/src/net/sourceforge/plantuml/ebnf/ETileNamedGroup.java @@ -0,0 +1,143 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: https://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * https://plantuml.com/patreon (only 1$ per month!) + * https://plantuml.com/paypal + * + * This file is part of PlantUML. + * + * PlantUML is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * PlantUML distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + * License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * + * Original Author: Arnaud Roques + * + * + */ +package net.sourceforge.plantuml.ebnf; + +import net.sourceforge.plantuml.klimt.UTranslate; +import net.sourceforge.plantuml.klimt.color.HColor; +import net.sourceforge.plantuml.klimt.color.HColorSet; +import net.sourceforge.plantuml.klimt.color.NoSuchColorException; +import net.sourceforge.plantuml.klimt.drawing.UGraphic; +import net.sourceforge.plantuml.klimt.font.FontConfiguration; +import net.sourceforge.plantuml.klimt.font.StringBounder; +import net.sourceforge.plantuml.klimt.geom.XDimension2D; +import net.sourceforge.plantuml.klimt.shape.URectangle; +import net.sourceforge.plantuml.klimt.shape.UText; +import net.sourceforge.plantuml.style.ISkinParam; + +public class ETileNamedGroup extends ETile { + + private final ETile orig; + private final ISkinParam skinParam; + private String commentAbove; + private String commentBelow; + private final HColorSet colorSet; + private final double deltax = 10; + private final double deltay1 = 10; + private final double deltay2 = 10; + private final UText groupName; + + private final FontConfiguration fc; + + public ETileNamedGroup(ETile orig, FontConfiguration fc, HColorSet colorSet, ISkinParam skinParam, String name) { + this.skinParam = skinParam; + this.orig = orig; + this.fc = fc; + this.colorSet = colorSet; + this.groupName = UText.build(name, fc); + + } + + @Override + public double getH1(StringBounder stringBounder) { + // final TextBlock note = getNoteAbove(stringBounder); + return deltay1 + orig.getH1(stringBounder); + } + + @Override + public double getH2(StringBounder stringBounder) { + // final TextBlock note = getNoteBelow(stringBounder); + return orig.getH2(stringBounder) + deltay2; + } + + @Override + public double getWidth(StringBounder stringBounder) { + return orig.getWidth(stringBounder) + 2 * deltax; + } + + @Override + public void drawU(UGraphic ug) { + final StringBounder stringBounder = ug.getStringBounder(); + final XDimension2D dim = calculateDimension(stringBounder); + + try { + final HColor background = colorSet.getColor("#E8E8FF"); + final UGraphic ugBack = ug.apply(background).apply(background.bg()); + ugBack.draw(URectangle.build(dim)); + final XDimension2D dimText = stringBounder.calculateDimension(fc.getFont(), groupName.getText()); + ugBack.apply(UTranslate.dy(-dimText.getHeight())).draw(URectangle.build(dimText.delta(10, 0))); + + } catch (NoSuchColorException e) { + e.printStackTrace(); + } + final double linePos = getH1(stringBounder); + drawHline(ug, linePos, 0, deltax); + drawHline(ug, linePos, dim.getWidth() - deltax, dim.getWidth()); + + orig.drawU(ug.apply(new UTranslate(deltax, deltay1))); + ug.apply(UTranslate.dx(5)).draw(groupName); + + } + + @Override + public void push(ETile tile) { + throw new UnsupportedOperationException(); + } + +// @Override +// protected void addCommentAbove(String comment) { +// this.commentAbove = comment; +// } +// +// @Override +// protected void addCommentBelow(String comment) { +// this.commentBelow = comment; +// } + +// private TextBlock getNoteAbove(StringBounder stringBounder) { +// if (commentAbove == null) +// return TextBlockUtils.EMPTY_TEXT_BLOCK; +// final FloatingNote note = FloatingNote.create(Display.getWithNewlines(commentAbove), skinParam, SName.ebnf); +// return TextBlockUtils.withMargin(note, 0, 0, 0, 10); +// } +// +// private TextBlock getNoteBelow(StringBounder stringBounder) { +// if (commentBelow == null) +// return TextBlockUtils.EMPTY_TEXT_BLOCK; +// final FloatingNote note = FloatingNote.create(Display.getWithNewlines(commentBelow), skinParam, SName.ebnf); +// return TextBlockUtils.withMargin(note, 0, 0, 10, 0); +// } + +} diff --git a/src/net/sourceforge/plantuml/regexdiagram/PSystemRegex.java b/src/net/sourceforge/plantuml/regexdiagram/PSystemRegex.java index e66975752d7..03e171ef1bb 100644 --- a/src/net/sourceforge/plantuml/regexdiagram/PSystemRegex.java +++ b/src/net/sourceforge/plantuml/regexdiagram/PSystemRegex.java @@ -52,11 +52,11 @@ import net.sourceforge.plantuml.ebnf.ETileAlternation; import net.sourceforge.plantuml.ebnf.ETileBox; import net.sourceforge.plantuml.ebnf.ETileConcatenation; +import net.sourceforge.plantuml.ebnf.ETileNamedGroup; import net.sourceforge.plantuml.ebnf.ETileOneOrMore; import net.sourceforge.plantuml.ebnf.ETileOptional; import net.sourceforge.plantuml.ebnf.ETileZeroOrMore; import net.sourceforge.plantuml.ebnf.Symbol; -import net.sourceforge.plantuml.ebnf.TextBlockable; import net.sourceforge.plantuml.klimt.color.HColor; import net.sourceforge.plantuml.klimt.color.HColorSet; import net.sourceforge.plantuml.klimt.color.HColors; @@ -76,8 +76,6 @@ public class PSystemRegex extends TitledDiagram { - private final List expressions = new ArrayList<>(); - public PSystemRegex(UmlSource source) { super(source, UmlDiagramType.REGEX, null); final ISkinParam skinParam = getSkinParam(); @@ -164,6 +162,8 @@ else if (token.getType() == ReTokenType.ESCAPED_CHAR) push(token, Symbol.TERMINAL_STRING1); else if (token.getType() == ReTokenType.GROUP) push(token, Symbol.SPECIAL_SEQUENCE); + else if (token.getType() == ReTokenType.NAMED_GROUP) + namedGroup(token.getData()); else if (token.getType() == ReTokenType.CLASS) push(token, Symbol.LITTERAL); else if (token.getType() == ReTokenType.ANCHOR) @@ -205,6 +205,11 @@ private void push(ReToken element, Symbol type) { stack.addFirst(new ETileBox(element.getData(), type, fontConfiguration, style, colorSet, getSkinParam())); } + private void namedGroup(String name) { + final ETile arg1 = stack.removeFirst(); + stack.addFirst(new ETileNamedGroup(arg1, fontConfiguration, colorSet, getSkinParam(), name)); + } + private void repetitionZeroOrMore(boolean isCompact) { final ETile arg1 = stack.removeFirst(); if (isCompact) diff --git a/src/net/sourceforge/plantuml/regexdiagram/ReTokenType.java b/src/net/sourceforge/plantuml/regexdiagram/ReTokenType.java index d723fcfed0b..11d68ff8854 100644 --- a/src/net/sourceforge/plantuml/regexdiagram/ReTokenType.java +++ b/src/net/sourceforge/plantuml/regexdiagram/ReTokenType.java @@ -43,12 +43,15 @@ public enum ReTokenType { QUANTIFIER, // ANCHOR, // GROUP, // + NAMED_GROUP, // ALTERNATIVE, // PARENTHESIS_OPEN, // PARENTHESIS_CLOSE, // CONCATENATION_IMPLICIT; static public boolean needImplicitConcatenation(ReTokenType token1, ReTokenType token2) { + if (token1 == NAMED_GROUP) + return false; if (token1 == ALTERNATIVE) return false; if (token2 == ALTERNATIVE) diff --git a/src/net/sourceforge/plantuml/regexdiagram/RegexExpression.java b/src/net/sourceforge/plantuml/regexdiagram/RegexExpression.java index 584ed4e6314..01bd39abd98 100644 --- a/src/net/sourceforge/plantuml/regexdiagram/RegexExpression.java +++ b/src/net/sourceforge/plantuml/regexdiagram/RegexExpression.java @@ -63,9 +63,15 @@ public static List parse(CharInspector it) throws RegexParsingException } else if (current == '[') { final String s = readGroup(it); result.add(new ReToken(ReTokenType.GROUP, s)); + } else if (isStartComment(it)) { + skipComment(it); + } else if (isStartNamedCapturingGroup(it)) { + final ReToken token = readNamedGroup(it); + result.add(token); + result.add(new ReToken(ReTokenType.PARENTHESIS_OPEN, "(")); } else if (isStartOpenParenthesis(it)) { - final String s = readOpenParenthesis(it); - result.add(new ReToken(ReTokenType.PARENTHESIS_OPEN, s)); + final ReToken token = readOpenParenthesis(it); + result.add(token); } else if (current == ')') { result.add(new ReToken(ReTokenType.PARENTHESIS_CLOSE, ")")); it.jump(); @@ -103,7 +109,55 @@ private static boolean isStartOpenParenthesis(CharInspector it) { return false; } - private static String readOpenParenthesis(CharInspector it) throws RegexParsingException { + private static boolean isStartNamedCapturingGroup(CharInspector it) { + final char current0 = it.peek(0); + if (current0 == '(' && it.peek(1) == '?' && it.peek(2) == '<') + return true; + return false; + } + + private static boolean isStartComment(CharInspector it) { + final char current0 = it.peek(0); + if (current0 == '(' && it.peek(1) == '?' && it.peek(2) == '#') + return true; + return false; + } + + private static void skipComment(CharInspector it) throws RegexParsingException { + it.jump(); + it.jump(); + it.jump(); + final StringBuilder comment = new StringBuilder(); + while (true) { + if (it.peek(0) == 0) + throw new RegexParsingException("Unclosed comment"); + if (it.peek(0) == ')') { + it.jump(); + return; + } + comment.append(it.peek(0)); + it.jump(); + } + } + + private static ReToken readNamedGroup(CharInspector it) throws RegexParsingException { + it.jump(); + it.jump(); + it.jump(); + final StringBuilder namedGroup = new StringBuilder(); + while (true) { + if (it.peek(0) == 0) + throw new RegexParsingException("Unclosed named capturing group"); + if (it.peek(0) == '>') { + it.jump(); + return new ReToken(ReTokenType.NAMED_GROUP, namedGroup.toString()); + } + namedGroup.append(it.peek(0)); + it.jump(); + } + } + + private static ReToken readOpenParenthesis(CharInspector it) { final char current0 = it.peek(0); it.jump(); final StringBuilder result = new StringBuilder(); @@ -118,18 +172,7 @@ private static String readOpenParenthesis(CharInspector it) throws RegexParsingE it.jump(); result.append("?!"); } - if (it.peek(0) == '?' && it.peek(1) == '<') { - while (true) { - if (it.peek(0) == 0) - throw new RegexParsingException("Unclosed named capturing group"); - if (it.peek(0) == '>') { - it.jump(); - return result.toString(); - } - it.jump(); - } - } - return result.toString(); + return new ReToken(ReTokenType.PARENTHESIS_OPEN, result.toString()); } private static boolean isStartQuantifier(CharInspector it) { diff --git a/src/net/sourceforge/plantuml/regexdiagram/ShuntingYard.java b/src/net/sourceforge/plantuml/regexdiagram/ShuntingYard.java index c29e837028e..8b45b62748e 100644 --- a/src/net/sourceforge/plantuml/regexdiagram/ShuntingYard.java +++ b/src/net/sourceforge/plantuml/regexdiagram/ShuntingYard.java @@ -59,6 +59,8 @@ public ShuntingYard(Iterator it) { ouputQueue.add(token); } else if (token.getType() == ReTokenType.GROUP) { ouputQueue.add(token); + } else if (token.getType() == ReTokenType.NAMED_GROUP) { + operatorStack.addFirst(token); } else if (token.getType() == ReTokenType.CLASS) { ouputQueue.add(token); } else if (token.getType() == ReTokenType.ANCHOR) { @@ -80,7 +82,10 @@ public ShuntingYard(Iterator it) { && operatorStack.peekFirst().getType() != ReTokenType.PARENTHESIS_OPEN) ouputQueue.add(operatorStack.removeFirst()); final ReToken first = operatorStack.removeFirst(); -// ouputQueue.add(first); + if (operatorStack.peekFirst() != null + && operatorStack.peekFirst().getType() == ReTokenType.NAMED_GROUP) + ouputQueue.add(operatorStack.removeFirst()); + } else { throw new UnsupportedOperationException(token.toString());