1
0
mirror of https://github.com/octoleo/plantuml.git synced 2024-12-22 19:09:03 +00:00

fix: improve local style management for activity diagrams

https://github.com/plantuml/plantuml/issues/1629
This commit is contained in:
Arnaud Roques 2023-12-19 19:08:24 +01:00
parent b65aaff80b
commit 26189b1e64
16 changed files with 249 additions and 29 deletions

View File

@ -1,4 +1,4 @@
# Warning, "version" should be the same in gradle.properties and Version.java
# Any idea anyone how to magically synchronize those :-) ?
version = 1.2023.14beta1
version = 1.2023.14beta2
org.gradle.workers.max = 3

View File

@ -59,6 +59,7 @@ import net.sourceforge.plantuml.sequencediagram.NotePosition;
import net.sourceforge.plantuml.sequencediagram.NoteType;
import net.sourceforge.plantuml.stereo.Stereotype;
import net.sourceforge.plantuml.style.ISkinParam;
import net.sourceforge.plantuml.style.StyleBuilder;
public class InstructionRepeat extends AbstractInstruction implements Instruction {
@ -87,6 +88,7 @@ public class InstructionRepeat extends AbstractInstruction implements Instructio
private LinkRendering endRepeatLinkRendering = LinkRendering.none();
private final Colors colors;
private final StyleBuilder currentStyleBuilder;
public boolean containsBreak() {
return repeatList.containsBreak();
@ -94,6 +96,7 @@ public class InstructionRepeat extends AbstractInstruction implements Instructio
public InstructionRepeat(Swimlanes swimlanes, Instruction parent, LinkRendering nextLinkRenderer, HColor color,
Display startLabel, BoxStyle boxStyleIn, Colors colors) {
this.currentStyleBuilder = swimlanes.getCurrentStyleBuilder();
this.swimlanes = swimlanes;
this.swimlane = swimlanes.getCurrentSwimlane();
this.repeatList = new InstructionList(this.swimlane);
@ -158,7 +161,7 @@ public class InstructionRepeat extends AbstractInstruction implements Instructio
if (this.testCalled == false && incoming1.isNone())
incoming1 = swimlanes.nextLinkRenderer();
final Ftile result = factory.repeat(boxStyleIn, swimlane, swimlaneOut, startLabel, decorateOut, test, yes, out,
colors, back, isLastOfTheParent(), incoming1, incoming2);
colors, back, isLastOfTheParent(), incoming1, incoming2, currentStyleBuilder);
if (killed)
return new FtileKilled(result);

View File

@ -56,6 +56,7 @@ import net.sourceforge.plantuml.klimt.geom.VerticalAlignment;
import net.sourceforge.plantuml.sequencediagram.NotePosition;
import net.sourceforge.plantuml.sequencediagram.NoteType;
import net.sourceforge.plantuml.style.ISkinParam;
import net.sourceforge.plantuml.style.StyleBuilder;
public class InstructionWhile extends WithNote implements Instruction, InstructionCollection {
@ -82,6 +83,7 @@ public class InstructionWhile extends WithNote implements Instruction, Instructi
private LinkRendering incoming1 = LinkRendering.none();
private LinkRendering incoming2 = LinkRendering.none();
private boolean backwardCalled;
private final StyleBuilder currentStyleBuilder;
public void overwriteYes(Display yes) {
this.yes = yes;
@ -94,6 +96,7 @@ public class InstructionWhile extends WithNote implements Instruction, Instructi
this.nextLinkRenderer = Objects.requireNonNull(nextLinkRenderer);
this.yes = Objects.requireNonNull(yes);
this.swimlane = swimlane;
this.currentStyleBuilder = skinParam.getCurrentStyleBuilder();
this.color = color;
this.skinParam = skinParam;
}
@ -118,7 +121,7 @@ public class InstructionWhile extends WithNote implements Instruction, Instructi
final Ftile back = Display.isNull(backward) ? null
: factory.activity(backward, swimlane, boxStyle, Colors.empty(), null);
Ftile tmp = repeatList.createFtile(factory);
tmp = factory.createWhile(outColor, swimlane, tmp, test, yes, color, specialOut, back, incoming1, incoming2);
tmp = factory.createWhile(outColor, swimlane, tmp, test, yes, color, specialOut, back, incoming1, incoming2, currentStyleBuilder);
if (getPositionedNotes().size() > 0)
tmp = FtileWithNoteOpale.create(tmp, getPositionedNotes(), false, VerticalAlignment.CENTER);

View File

@ -84,10 +84,11 @@ public interface FtileFactory {
public Ftile repeat(BoxStyle boxStyleIn, Swimlane swimlane, Swimlane swimlaneOut, Display startLabel, Ftile repeat,
Display test, Display yes, Display out, Colors colors, Ftile backward, boolean noOut,
LinkRendering incoming1, LinkRendering incoming2);
LinkRendering incoming1, LinkRendering incoming2, StyleBuilder currentStyleBuilder);
public Ftile createWhile(LinkRendering outColor, Swimlane swimlane, Ftile whileBlock, Display test, Display yes,
HColor color, Instruction specialOut, Ftile backward, LinkRendering incoming1, LinkRendering incoming2);
HColor color, Instruction specialOut, Ftile backward, LinkRendering incoming1, LinkRendering incoming2,
StyleBuilder currentStyleBuilder);
public Ftile createIf(Swimlane swimlane, List<Branch> thens, Branch elseBranch, LinkRendering outColor,
LinkRendering topInlinkRendering, Url url, Collection<PositionedNote> notes, Stereotype stereotype,

View File

@ -175,16 +175,17 @@ public class FtileFactoryDelegator implements FtileFactory {
@Override
public Ftile repeat(BoxStyle boxStyleIn, Swimlane swimlane, Swimlane swimlaneOut, Display startLabel, Ftile repeat,
Display test, Display yes, Display out, Colors colors, Ftile backward, boolean noOut,
LinkRendering incoming1, LinkRendering incoming2) {
LinkRendering incoming1, LinkRendering incoming2, StyleBuilder currentStyleBuilder) {
return factory.repeat(boxStyleIn, swimlane, swimlaneOut, startLabel, repeat, test, yes, out, colors, backward,
noOut, incoming1, incoming2);
noOut, incoming1, incoming2, currentStyleBuilder);
}
@Override
public Ftile createWhile(LinkRendering outColor, Swimlane swimlane, Ftile whileBlock, Display test, Display yes,
HColor color, Instruction specialOut, Ftile back, LinkRendering incoming1, LinkRendering incoming2) {
HColor color, Instruction specialOut, Ftile back, LinkRendering incoming1, LinkRendering incoming2,
StyleBuilder styleBuilder) {
return factory.createWhile(outColor, swimlane, whileBlock, test, yes, color, specialOut, back, incoming1,
incoming2);
incoming2, styleBuilder);
}
@Override

View File

@ -82,6 +82,7 @@ import net.sourceforge.plantuml.style.ISkinParam;
import net.sourceforge.plantuml.style.PName;
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.style.Styleable;
import net.sourceforge.plantuml.svek.UGraphicForSnake;
@ -127,9 +128,9 @@ public class Swimlanes extends AbstractTextBlock implements TextBlock, Styleable
}
protected Style getStyle() {
if (style == null) {
if (style == null)
this.style = getStyleSignature().getMergedStyle(skinParam.getCurrentStyleBuilder());
}
return style;
}
@ -455,4 +456,8 @@ public class Swimlanes extends AbstractTextBlock implements TextBlock, Styleable
return cachedMinMax;
}
public StyleBuilder getCurrentStyleBuilder() {
return skinParam.getCurrentStyleBuilder();
}
}

View File

@ -62,6 +62,7 @@ import net.sourceforge.plantuml.klimt.font.FontConfiguration;
import net.sourceforge.plantuml.klimt.geom.XDimension2D;
import net.sourceforge.plantuml.style.PName;
import net.sourceforge.plantuml.style.Style;
import net.sourceforge.plantuml.style.StyleBuilder;
import net.sourceforge.plantuml.svek.ConditionStyle;
public class FtileFactoryDelegatorRepeat extends FtileFactoryDelegator {
@ -73,13 +74,12 @@ public class FtileFactoryDelegatorRepeat extends FtileFactoryDelegator {
@Override
public Ftile repeat(BoxStyle boxStyleIn, Swimlane swimlane, Swimlane swimlaneOut, Display startLabel,
final Ftile repeat, Display test, Display yes, Display out, Colors colors, Ftile backward, boolean noOut,
LinkRendering incoming1, LinkRendering incoming2) {
LinkRendering incoming1, LinkRendering incoming2, StyleBuilder currentStyleBuilder) {
final ConditionStyle conditionStyle = skinParam().getConditionStyle();
final Style styleArrow = getDefaultStyleDefinitionArrow().getMergedStyle(skinParam().getCurrentStyleBuilder());
final Style styleDiamond = getDefaultStyleDefinitionDiamond()
.getMergedStyle(skinParam().getCurrentStyleBuilder());
final Style styleArrow = getDefaultStyleDefinitionArrow().getMergedStyle(currentStyleBuilder);
final Style styleDiamond = getDefaultStyleDefinitionDiamond().getMergedStyle(currentStyleBuilder);
final HColor borderColor = styleDiamond.value(PName.LineColor).asColor(skinParam().getIHtmlColorSet());
final HColor diamondColor = styleDiamond.value(PName.BackGroundColor).asColor(skinParam().getIHtmlColorSet());
final Rainbow arrowColor = Rainbow.build(styleArrow, skinParam().getIHtmlColorSet());

View File

@ -58,6 +58,7 @@ import net.sourceforge.plantuml.klimt.drawing.UGraphic;
import net.sourceforge.plantuml.klimt.font.FontConfiguration;
import net.sourceforge.plantuml.style.PName;
import net.sourceforge.plantuml.style.Style;
import net.sourceforge.plantuml.style.StyleBuilder;
import net.sourceforge.plantuml.svek.ConditionStyle;
public class FtileFactoryDelegatorWhile extends FtileFactoryDelegator {
@ -68,13 +69,13 @@ public class FtileFactoryDelegatorWhile extends FtileFactoryDelegator {
@Override
public Ftile createWhile(LinkRendering outColor, Swimlane swimlane, Ftile whileBlock, Display test, Display yes,
HColor color, Instruction specialOut, Ftile backward, LinkRendering incoming1, LinkRendering incoming2) {
HColor color, Instruction specialOut, Ftile backward, LinkRendering incoming1, LinkRendering incoming2,
StyleBuilder currentStyleBuilder) {
final ConditionStyle conditionStyle = skinParam().getConditionStyle();
final Style styleArrow = getDefaultStyleDefinitionArrow().getMergedStyle(skinParam().getCurrentStyleBuilder());
final Style styleDiamond = getDefaultStyleDefinitionDiamond()
.getMergedStyle(skinParam().getCurrentStyleBuilder());
final Style styleArrow = getDefaultStyleDefinitionArrow().getMergedStyle(currentStyleBuilder);
final Style styleDiamond = getDefaultStyleDefinitionDiamond().getMergedStyle(currentStyleBuilder);
final HColor borderColor = styleDiamond.value(PName.LineColor).asColor(skinParam().getIHtmlColorSet());
final HColor backColor = styleDiamond.value(PName.BackGroundColor).asColor(skinParam().getIHtmlColorSet());
final Rainbow arrowColor = Rainbow.build(styleArrow, skinParam().getIHtmlColorSet());

View File

@ -156,14 +156,14 @@ public class VCompactFactory implements FtileFactory {
@Override
public Ftile repeat(BoxStyle boxStyleIn, Swimlane swimlane, Swimlane swimlaneOut, Display startLabel, Ftile repeat,
Display test, Display yes, Display out, Colors colors, Ftile backward, boolean noOut,
LinkRendering incoming1, LinkRendering incoming2) {
LinkRendering incoming1, LinkRendering incoming2, StyleBuilder currentStyleBuilder) {
return repeat;
}
@Override
public Ftile createWhile(LinkRendering afterEndwhile, Swimlane swimlane, Ftile whileBlock, Display test,
Display yes, HColor color, Instruction specialOut, Ftile back, LinkRendering incoming1,
LinkRendering incoming2) {
LinkRendering incoming2, StyleBuilder styleBuilder) {
return whileBlock;
}

View File

@ -0,0 +1,110 @@
/* ========================================================================
* 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.UStroke;
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.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.PName;
import net.sourceforge.plantuml.style.Style;
public class ETileLookAheadOrBehind extends ETile {
private final ETile orig;
private final HColorSet colorSet;
private final double deltax1 = 4;
private final double deltax2 = 8;
private final double deltay = 6;
private final Style style;
private final UText supText;
private final FontConfiguration fc;
public ETileLookAheadOrBehind(ETile orig, FontConfiguration fc, Style style, HColorSet colorSet, String type) {
this.style = style;
this.orig = orig;
this.fc = fc;
this.colorSet = colorSet;
this.supText = UText.build(type, fc);
}
@Override
public double getH1(StringBounder stringBounder) {
return deltay + orig.getH1(stringBounder);
}
@Override
public double getH2(StringBounder stringBounder) {
return orig.getH2(stringBounder) + deltay;
}
@Override
public double getWidth(StringBounder stringBounder) {
return orig.getWidth(stringBounder) + deltax1 + deltax2 + supText.calculateDimension(stringBounder).getWidth();
}
@Override
public void drawU(UGraphic ug) {
final StringBounder stringBounder = ug.getStringBounder();
final XDimension2D dim = calculateDimension(stringBounder);
final HColor lineColor = style.value(PName.LineColor).asColor(colorSet);
final URectangle rect = URectangle.build(dim).rounded(30);
ug.apply(lineColor).apply(new UStroke(2, 3, 1)).draw(rect);
final double posText = getH1(stringBounder) + supText.getDescent(stringBounder);
ug.apply(new UTranslate(4, 2 + posText)).draw(supText);
orig.drawU(ug.apply(new UTranslate(deltax1 + supText.calculateDimension(stringBounder).getWidth(), deltay)));
}
@Override
public void push(ETile tile) {
throw new UnsupportedOperationException();
}
}

View File

@ -38,6 +38,7 @@ package net.sourceforge.plantuml.klimt.shape;
import net.sourceforge.plantuml.klimt.UShape;
import net.sourceforge.plantuml.klimt.font.FontConfiguration;
import net.sourceforge.plantuml.klimt.font.StringBounder;
import net.sourceforge.plantuml.klimt.geom.XDimension2D;
public class UText implements UShape {
@ -83,4 +84,8 @@ public class UText implements UShape {
return orientation;
}
public XDimension2D calculateDimension(StringBounder stringBounder) {
return stringBounder.calculateDimension(font.getFont(), text);
}
}

View File

@ -52,6 +52,7 @@ import net.sourceforge.plantuml.ebnf.ETile;
import net.sourceforge.plantuml.ebnf.ETileAlternation;
import net.sourceforge.plantuml.ebnf.ETileBox;
import net.sourceforge.plantuml.ebnf.ETileConcatenation;
import net.sourceforge.plantuml.ebnf.ETileLookAheadOrBehind;
import net.sourceforge.plantuml.ebnf.ETileNamedGroup;
import net.sourceforge.plantuml.ebnf.ETileOneOrMore;
import net.sourceforge.plantuml.ebnf.ETileOptional;
@ -162,6 +163,10 @@ public class PSystemRegex extends TitledDiagram {
push(token, Symbol.TERMINAL_STRING1);
else if (token.getType() == ReTokenType.GROUP)
push(token, Symbol.SPECIAL_SEQUENCE);
else if (token.getType() == ReTokenType.LOOK_AHEAD)
lookAheadOrBehind(token.getData());
else if (token.getType() == ReTokenType.LOOK_BEHIND)
lookAheadOrBehind(token.getData());
else if (token.getType() == ReTokenType.NAMED_GROUP)
namedGroup(token.getData());
else if (token.getType() == ReTokenType.CLASS)
@ -205,6 +210,11 @@ public class PSystemRegex extends TitledDiagram {
stack.addFirst(new ETileBox(element.getData(), type, fontConfiguration, style, colorSet, getSkinParam()));
}
private void lookAheadOrBehind(String name) {
final ETile arg1 = stack.removeFirst();
stack.addFirst(new ETileLookAheadOrBehind(arg1, fontConfiguration, style, colorSet, name));
}
private void namedGroup(String name) {
final ETile arg1 = stack.removeFirst();
stack.addFirst(new ETileNamedGroup(arg1, fontConfiguration, colorSet, getSkinParam(), name));

View File

@ -44,13 +44,19 @@ public enum ReTokenType {
ANCHOR, //
GROUP, //
NAMED_GROUP, //
LOOK_AHEAD, //
LOOK_BEHIND, //
ALTERNATIVE, //
PARENTHESIS_OPEN, //
PARENTHESIS_CLOSE, //
CONCATENATION_IMPLICIT;
public boolean isNamedGroupOrLookAheadOrLookBehind() {
return this == NAMED_GROUP || this == LOOK_AHEAD || this == LOOK_BEHIND;
}
static public boolean needImplicitConcatenation(ReTokenType token1, ReTokenType token2) {
if (token1 == NAMED_GROUP)
if (token1.isNamedGroupOrLookAheadOrLookBehind())
return false;
if (token1 == ALTERNATIVE)
return false;

View File

@ -60,11 +60,22 @@ public class RegexExpression {
} else if (current == '|') {
result.add(new ReToken(ReTokenType.ALTERNATIVE, "|"));
it.jump();
} else if (isStartPosixGroup(it)) {
final String s = readGroupPosix(it);
result.add(new ReToken(ReTokenType.CLASS, s));
} else if (current == '[') {
final String s = readGroup(it);
result.add(new ReToken(ReTokenType.GROUP, s));
} else if (isStartComment(it)) {
skipComment(it);
} else if (isStartLookAhead(it)) {
final ReToken token = readLookAhead(it);
result.add(token);
result.add(new ReToken(ReTokenType.PARENTHESIS_OPEN, "("));
} else if (isStartLookBehind(it)) {
final ReToken token = readLookBehind(it);
result.add(token);
result.add(new ReToken(ReTokenType.PARENTHESIS_OPEN, "("));
} else if (isStartNamedCapturingGroup(it)) {
final ReToken token = readNamedGroup(it);
result.add(token);
@ -102,6 +113,20 @@ public class RegexExpression {
}
private static boolean isStartLookAhead(CharInspector it) {
final char current0 = it.peek(0);
if (current0 == '(' && it.peek(1) == '?' && (it.peek(2) == '=' || it.peek(2) == '!'))
return true;
return false;
}
private static boolean isStartLookBehind(CharInspector it) {
final char current0 = it.peek(0);
if (current0 == '(' && it.peek(1) == '?' && it.peek(2) == '<' && (it.peek(3) == '=' || it.peek(3) == '!'))
return true;
return false;
}
private static boolean isStartOpenParenthesis(CharInspector it) {
final char current0 = it.peek(0);
if (current0 == '(')
@ -109,10 +134,28 @@ public class RegexExpression {
return false;
}
private static boolean isStartPosixGroup(CharInspector it) {
final char current0 = it.peek(0);
if (current0 == '[' && it.peek(1) == '[' && it.peek(2) == ':')
return true;
return false;
}
private static boolean isStartNamedCapturingGroup(CharInspector it) {
final char current0 = it.peek(0);
if (current0 == '(' && it.peek(1) == '?' && it.peek(2) == '<')
return true;
if (current0 == '(' && it.peek(1) == '?' && it.peek(2) == '<') {
int i = 3;
while (it.peek(i) != 0) {
if (it.peek(i) == '>' && i == 3)
return false;
if (it.peek(i) == '>')
return true;
if (Character.isLetter(it.peek(i)) == false)
return false;
i++;
}
}
return false;
}
@ -140,6 +183,23 @@ public class RegexExpression {
}
}
private static ReToken readLookAhead(CharInspector it) throws RegexParsingException {
it.jump();
it.jump();
final char ch = it.peek(0);
it.jump();
return new ReToken(ReTokenType.LOOK_AHEAD, "?" + ch);
}
private static ReToken readLookBehind(CharInspector it) throws RegexParsingException {
it.jump();
it.jump();
it.jump();
final char ch = it.peek(0);
it.jump();
return new ReToken(ReTokenType.LOOK_BEHIND, "?<" + ch);
}
private static ReToken readNamedGroup(CharInspector it) throws RegexParsingException {
it.jump();
it.jump();
@ -218,6 +278,23 @@ public class RegexExpression {
return false;
}
private static String readGroupPosix(CharInspector it) {
it.jump();
it.jump();
it.jump();
final StringBuilder result = new StringBuilder(":");
while (it.peek(0) != 0) {
char ch = it.peek(0);
it.jump();
result.append(ch);
if (ch == ':')
break;
}
it.jump();
it.jump();
return result.toString();
}
private static String readGroup(CharInspector it) {
final char current0 = it.peek(0);
if (current0 != '[')

View File

@ -59,7 +59,7 @@ public class ShuntingYard {
ouputQueue.add(token);
} else if (token.getType() == ReTokenType.GROUP) {
ouputQueue.add(token);
} else if (token.getType() == ReTokenType.NAMED_GROUP) {
} else if (token.getType().isNamedGroupOrLookAheadOrLookBehind()) {
operatorStack.addFirst(token);
} else if (token.getType() == ReTokenType.CLASS) {
ouputQueue.add(token);
@ -82,11 +82,9 @@ public class ShuntingYard {
&& operatorStack.peekFirst().getType() != ReTokenType.PARENTHESIS_OPEN)
ouputQueue.add(operatorStack.removeFirst());
final ReToken first = operatorStack.removeFirst();
if (operatorStack.peekFirst() != null
&& operatorStack.peekFirst().getType() == ReTokenType.NAMED_GROUP)
if (operatorStack.peekFirst() != null && operatorStack.peekFirst().getType().isNamedGroupOrLookAheadOrLookBehind())
ouputQueue.add(operatorStack.removeFirst());
} else {
throw new UnsupportedOperationException(token.toString());
}

View File

@ -46,7 +46,7 @@ public class Version {
// Warning, "version" should be the same in gradle.properties and Version.java
// Any idea anyone how to magically synchronize those :-) ?
private static final String version = "1.2023.14beta1";
private static final String version = "1.2023.14beta2";
public static String versionString() {
return version;