From 5ec10372021db00c8b9ea5109ef189b91ff06ebd Mon Sep 17 00:00:00 2001 From: Arnaud Roques Date: Sun, 10 Jan 2021 21:52:19 +0100 Subject: [PATCH] Import version 1.2021.0 --- build.xml | 1 + pom.xml | 2 +- src/net/sourceforge/plantuml/FileFormat.java | 20 +- src/net/sourceforge/plantuml/ISkinParam.java | 4 + .../sourceforge/plantuml/PSystemBuilder.java | 2 + src/net/sourceforge/plantuml/SkinParam.java | 18 ++ .../plantuml/SkinParamDelegator.java | 5 + src/net/sourceforge/plantuml/StringUtils.java | 3 + .../sourceforge/plantuml/TitledDiagram.java | 14 +- src/net/sourceforge/plantuml/UmlDiagram.java | 8 +- .../sourceforge/plantuml/UmlDiagramType.java | 2 +- .../activitydiagram/ActivityDiagram.java | 7 +- .../activitydiagram3/ActivityDiagram3.java | 7 +- .../ftile/UGraphicInterceptorUDrawable2.java | 6 +- .../plantuml/board/BoardDiagram.java | 5 +- .../sourceforge/plantuml/bpm/BpmDiagram.java | 5 +- .../classdiagram/AbstractEntityDiagram.java | 5 +- .../plantuml/classdiagram/ClassDiagram.java | 7 +- .../plantuml/command/CommandCaption.java | 10 +- .../plantuml/command/CommandFooter.java | 8 +- .../plantuml/command/CommandHeader.java | 8 +- .../plantuml/command/CommandLegend.java | 73 +++++ .../plantuml/command/CommandTitle.java | 10 +- .../command/PSystemCommandFactory.java | 1 + .../compositediagram/CompositeDiagram.java | 7 +- .../plantuml/core/DiagramType.java | 5 +- .../creole/command/CommandCreoleStyle.java | 2 +- .../creole/command/CommandCreoleStyle2.java | 89 ++++++ .../plantuml/creole/legacy/StripeSimple.java | 16 +- .../plantuml/cucadiagram/CucaDiagram.java | 4 +- .../cucadiagram/dot/AbstractGraphviz.java | 8 +- .../cucadiagram/dot/GraphvizWindowsLite.java | 4 +- .../descdiagram/DescriptionDiagram.java | 9 +- .../plantuml/flowdiagram/FlowDiagram.java | 5 +- .../plantuml/gitlog/GitDiagram.java | 5 +- .../plantuml/graphic/StyledString.java | 92 ++++++ src/net/sourceforge/plantuml/help/Help.java | 5 +- .../plantuml/jsondiagram/JsonDiagram.java | 14 +- .../jsondiagram/JsonDiagramFactory.java | 5 +- .../plantuml/jsondiagram/SmetanaForJson.java | 12 +- .../plantuml/mindmap/CommandMindMapPlus.java | 10 +- .../plantuml/mindmap/MindMapDiagram.java | 5 +- .../plantuml/nwdiag/NwDiagram.java | 5 +- .../AbstractClassOrObjectDiagram.java | 57 ++-- .../plantuml/postit/PostItDiagram.java | 5 +- .../plantuml/project/GanttDiagram.java | 43 +-- .../project/draw/TimeHeaderDaily.java | 2 +- .../project/draw/TimeHeaderWeekly.java | 2 +- .../plantuml/project/lang/SubjectTask.java | 5 +- .../plantuml/project/solver/SolverImpl.java | 10 + .../plantuml/salt/PSystemSalt.java | 6 +- .../sequencediagram/SequenceDiagram.java | 7 +- .../sequencediagram/command/CommandArrow.java | 4 +- .../plantuml/skin/ArrowDirection.java | 1 - .../plantuml/sprite/ListSpriteDiagram.java | 7 +- .../plantuml/sprite/StdlibDiagram.java | 7 +- .../plantuml/statediagram/StateDiagram.java | 7 +- .../plantuml/sudoku/GraphicsSudoku.java | 3 +- .../plantuml/svek/GraphvizCrash.java | 5 +- .../plantuml/svg/LengthAdjust.java | 44 +++ .../sourceforge/plantuml/svg/SvgGraphics.java | 19 +- src/net/sourceforge/plantuml/tim/Eater.java | 6 + .../plantuml/tim/EaterFunctionCall.java | 38 +-- .../plantuml/tim/expression/TokenStack.java | 53 ++-- .../plantuml/timingdiagram/TimingDiagram.java | 5 +- .../plantuml/ugraphic/FontChecker.java | 4 +- .../plantuml/ugraphic/ImageBuilder.java | 13 +- .../plantuml/ugraphic/ImageParameter.java | 8 + .../plantuml/ugraphic/UGraphicUtils.java | 4 +- .../sourceforge/plantuml/ugraphic/UText.java | 16 +- .../plantuml/ugraphic/g2d/DriverTextG2d.java | 154 ++++++---- .../plantuml/ugraphic/svg/UGraphicSvg.java | 18 +- .../sourceforge/plantuml/version/Version.java | 4 +- .../sourceforge/plantuml/wbs/WBSDiagram.java | 5 +- .../plantuml/wire/CommandComponent.java | 17 +- .../plantuml/wire/CommandPrint.java | 78 +++++ .../plantuml/wire/CommandSpot.java | 2 +- .../plantuml/wire/CommandWLink.java | 29 +- .../plantuml/wire/WArrowDirection.java | 54 ++++ src/net/sourceforge/plantuml/wire/WBlock.java | 107 ++++--- .../plantuml/wire/WLinkHorizontal.java | 197 +++++++++++++ .../sourceforge/plantuml/wire/WLinkType.java | 6 +- .../plantuml/wire/WLinkVertical.java | 164 +++++++++++ .../plantuml/wire/WOrientation.java | 48 +++ .../plantuml/wire/{WLink.java => WPrint.java} | 68 ++--- .../plantuml/wire/WireDiagram.java | 72 ++++- .../plantuml/wire/WireDiagramFactory.java | 1 + .../plantuml/yaml/SimpleYamlParser.java | 273 ++++++++++++++++++ .../plantuml/yaml/YamlDiagramFactory.java | 79 +++++ stdlib/c4-abx.repx | Bin 1817 -> 3262 bytes stdlib/c4-dex.repx | Bin 1 -> 201 bytes stdlib/cloudogu-abx.repx | Bin 2161 -> 2197 bytes stdlib/office-abx.repx | Bin 22878 -> 22962 bytes 93 files changed, 1854 insertions(+), 436 deletions(-) create mode 100644 src/net/sourceforge/plantuml/command/CommandLegend.java create mode 100644 src/net/sourceforge/plantuml/creole/command/CommandCreoleStyle2.java create mode 100644 src/net/sourceforge/plantuml/graphic/StyledString.java create mode 100644 src/net/sourceforge/plantuml/svg/LengthAdjust.java create mode 100644 src/net/sourceforge/plantuml/wire/CommandPrint.java create mode 100644 src/net/sourceforge/plantuml/wire/WArrowDirection.java create mode 100644 src/net/sourceforge/plantuml/wire/WLinkHorizontal.java create mode 100644 src/net/sourceforge/plantuml/wire/WLinkVertical.java create mode 100644 src/net/sourceforge/plantuml/wire/WOrientation.java rename src/net/sourceforge/plantuml/wire/{WLink.java => WPrint.java} (58%) create mode 100644 src/net/sourceforge/plantuml/yaml/SimpleYamlParser.java create mode 100644 src/net/sourceforge/plantuml/yaml/YamlDiagramFactory.java diff --git a/build.xml b/build.xml index 5a42a7c21..e96a52bed 100644 --- a/build.xml +++ b/build.xml @@ -83,6 +83,7 @@ + diff --git a/pom.xml b/pom.xml index 3f9841f86..1f08503ae 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ net.sourceforge.plantuml plantuml - 1.2020.27-SNAPSHOT + 1.2021.1-SNAPSHOT jar PlantUML diff --git a/src/net/sourceforge/plantuml/FileFormat.java b/src/net/sourceforge/plantuml/FileFormat.java index ff00bdf25..624d1394f 100644 --- a/src/net/sourceforge/plantuml/FileFormat.java +++ b/src/net/sourceforge/plantuml/FileFormat.java @@ -46,7 +46,9 @@ import java.io.IOException; import net.sourceforge.plantuml.braille.BrailleCharFactory; import net.sourceforge.plantuml.braille.UGraphicBraille; +import net.sourceforge.plantuml.graphic.FontStyle; import net.sourceforge.plantuml.graphic.StringBounder; +import net.sourceforge.plantuml.graphic.StyledString; import net.sourceforge.plantuml.png.MetadataTag; import net.sourceforge.plantuml.security.SFile; import net.sourceforge.plantuml.svg.SvgGraphics; @@ -142,10 +144,20 @@ public enum FileFormat { } static private Dimension2DDouble getJavaDimension(UFont font, String text) { - final Font javaFont = font.getFont(); - final FontMetrics fm = gg.getFontMetrics(javaFont); - final Rectangle2D rect = fm.getStringBounds(text, gg); - return new Dimension2DDouble(rect.getWidth(), rect.getHeight()); + double width = 0; + double height = 0; + for (StyledString styledString : StyledString.build(text)) { + final Font javaFont; + if (styledString.getStyle() == FontStyle.BOLD) + javaFont = font.bold().getFont(); + else + javaFont = font.getFont(); + final FontMetrics fm = gg.getFontMetrics(javaFont); + final Rectangle2D rect = fm.getStringBounds(styledString.getText(), gg); + width += rect.getWidth(); + height = Math.max(height, rect.getHeight()); + } + return new Dimension2DDouble(width, height); } private StringBounder getBrailleStringBounder() { diff --git a/src/net/sourceforge/plantuml/ISkinParam.java b/src/net/sourceforge/plantuml/ISkinParam.java index a69c50498..c1d69a15d 100644 --- a/src/net/sourceforge/plantuml/ISkinParam.java +++ b/src/net/sourceforge/plantuml/ISkinParam.java @@ -51,6 +51,7 @@ import net.sourceforge.plantuml.style.StyleBuilder; import net.sourceforge.plantuml.svek.ConditionEndStyle; import net.sourceforge.plantuml.svek.ConditionStyle; import net.sourceforge.plantuml.svek.PackageStyle; +import net.sourceforge.plantuml.svg.LengthAdjust; import net.sourceforge.plantuml.ugraphic.UFont; import net.sourceforge.plantuml.ugraphic.UStroke; import net.sourceforge.plantuml.ugraphic.color.HColor; @@ -189,5 +190,8 @@ public interface ISkinParam extends ISkinSimple { public ActorStyle actorStyle(); public void setSvgSize(String origin, String sizeToUse); + + public LengthAdjust getlengthAdjust(); + } \ No newline at end of file diff --git a/src/net/sourceforge/plantuml/PSystemBuilder.java b/src/net/sourceforge/plantuml/PSystemBuilder.java index 96b4f71af..1689482c4 100644 --- a/src/net/sourceforge/plantuml/PSystemBuilder.java +++ b/src/net/sourceforge/plantuml/PSystemBuilder.java @@ -97,6 +97,7 @@ import net.sourceforge.plantuml.version.PSystemLicenseFactory; import net.sourceforge.plantuml.version.PSystemVersionFactory; import net.sourceforge.plantuml.wbs.WBSDiagramFactory; import net.sourceforge.plantuml.wire.WireDiagramFactory; +import net.sourceforge.plantuml.yaml.YamlDiagramFactory; public class PSystemBuilder { @@ -217,6 +218,7 @@ public class PSystemBuilder { factories.add(new JsonDiagramFactory()); factories.add(new GitDiagramFactory()); factories.add(new BoardDiagramFactory()); + factories.add(new YamlDiagramFactory()); return factories; } diff --git a/src/net/sourceforge/plantuml/SkinParam.java b/src/net/sourceforge/plantuml/SkinParam.java index ba152d34f..2dbeff312 100644 --- a/src/net/sourceforge/plantuml/SkinParam.java +++ b/src/net/sourceforge/plantuml/SkinParam.java @@ -73,6 +73,7 @@ import net.sourceforge.plantuml.style.StyleLoader; import net.sourceforge.plantuml.svek.ConditionEndStyle; import net.sourceforge.plantuml.svek.ConditionStyle; import net.sourceforge.plantuml.svek.PackageStyle; +import net.sourceforge.plantuml.svg.LengthAdjust; import net.sourceforge.plantuml.ugraphic.UFont; import net.sourceforge.plantuml.ugraphic.UStroke; import net.sourceforge.plantuml.ugraphic.color.ColorMapper; @@ -113,6 +114,9 @@ public class SkinParam implements ISkinParam { if (type == UmlDiagramType.BOARD) { UseStyle.setBetaStyle(true); } + if (type == UmlDiagramType.YAML) { + UseStyle.setBetaStyle(true); + } if (type == UmlDiagramType.SEQUENCE) { // skin = "debug.skin"; // USE_STYLE2.set(true); @@ -1258,4 +1262,18 @@ public class SkinParam implements ISkinParam { return s; } + public LengthAdjust getlengthAdjust() { + final String value = getValue("lengthAdjust"); + if ("spacingAndGlyphs".equalsIgnoreCase(value)) { + return LengthAdjust.SPACING_AND_GLYPHS; + } + if ("spacing".equalsIgnoreCase(value)) { + return LengthAdjust.SPACING; + } + if ("none".equalsIgnoreCase(value)) { + return LengthAdjust.NONE; + } + return LengthAdjust.defaultValue(); + } + } diff --git a/src/net/sourceforge/plantuml/SkinParamDelegator.java b/src/net/sourceforge/plantuml/SkinParamDelegator.java index c1771f81b..b9292535c 100644 --- a/src/net/sourceforge/plantuml/SkinParamDelegator.java +++ b/src/net/sourceforge/plantuml/SkinParamDelegator.java @@ -53,6 +53,7 @@ import net.sourceforge.plantuml.style.StyleBuilder; import net.sourceforge.plantuml.svek.ConditionEndStyle; import net.sourceforge.plantuml.svek.ConditionStyle; import net.sourceforge.plantuml.svek.PackageStyle; +import net.sourceforge.plantuml.svg.LengthAdjust; import net.sourceforge.plantuml.ugraphic.UFont; import net.sourceforge.plantuml.ugraphic.UStroke; import net.sourceforge.plantuml.ugraphic.color.ColorMapper; @@ -372,4 +373,8 @@ public class SkinParamDelegator implements ISkinParam { return skinParam.transformStringForSizeHack(s); } + public LengthAdjust getlengthAdjust() { + return skinParam.getlengthAdjust(); + } + } diff --git a/src/net/sourceforge/plantuml/StringUtils.java b/src/net/sourceforge/plantuml/StringUtils.java index cbcb4b4d7..f194c4152 100644 --- a/src/net/sourceforge/plantuml/StringUtils.java +++ b/src/net/sourceforge/plantuml/StringUtils.java @@ -68,6 +68,9 @@ public class StringUtils { public static final char PACKAGE_PRIVATE_METHOD = '\uEEF5'; public static final char PUBLIC_METHOD = '\uEEF4'; public static final char IE_MANDATORY = '\uEEF3'; + + public static final char BOLD_START = '\uEEF2'; + public static final char BOLD_END = '\uEEF1'; // Used in BackSlash public static final char PRIVATE_BLOCK = '\uE000'; diff --git a/src/net/sourceforge/plantuml/TitledDiagram.java b/src/net/sourceforge/plantuml/TitledDiagram.java index 8c32b482b..a44e55377 100644 --- a/src/net/sourceforge/plantuml/TitledDiagram.java +++ b/src/net/sourceforge/plantuml/TitledDiagram.java @@ -58,6 +58,7 @@ public abstract class TitledDiagram extends AbstractPSystem implements Diagram, private final DisplaySection header = DisplaySection.none(); private final DisplaySection footer = DisplaySection.none(); private Display mainFrame; + private final UmlDiagramType type; private final SkinParam skinParam; @@ -67,22 +68,25 @@ public abstract class TitledDiagram extends AbstractPSystem implements Diagram, return pragma; } - public TitledDiagram() { - this.skinParam = SkinParam.create(getUmlDiagramType()); + public TitledDiagram(UmlDiagramType type) { + this.type = type; + this.skinParam = SkinParam.create(type); } public final StyleBuilder getCurrentStyleBuilder() { return skinParam.getCurrentStyleBuilder(); } - public TitledDiagram(ISkinSimple orig) { - this(); + public TitledDiagram(UmlDiagramType type, ISkinSimple orig) { + this(type); if (orig != null) { this.skinParam.copyAllFrom(orig); } } - abstract public UmlDiagramType getUmlDiagramType(); + final public UmlDiagramType getUmlDiagramType() { + return type; + } public final ISkinParam getSkinParam() { return skinParam; diff --git a/src/net/sourceforge/plantuml/UmlDiagram.java b/src/net/sourceforge/plantuml/UmlDiagram.java index 0ffd3b4c8..6e39b76fc 100644 --- a/src/net/sourceforge/plantuml/UmlDiagram.java +++ b/src/net/sourceforge/plantuml/UmlDiagram.java @@ -98,12 +98,12 @@ public abstract class UmlDiagram extends TitledDiagram implements Diagram, Annot private Animation animation; - public UmlDiagram() { - super(); + public UmlDiagram(UmlDiagramType type) { + super(type); } - public UmlDiagram(ISkinSimple orig) { - super(orig); + public UmlDiagram(UmlDiagramType type, ISkinSimple orig) { + super(type, orig); } final public int getMinwidth() { diff --git a/src/net/sourceforge/plantuml/UmlDiagramType.java b/src/net/sourceforge/plantuml/UmlDiagramType.java index 353491dd9..3311a9d73 100644 --- a/src/net/sourceforge/plantuml/UmlDiagramType.java +++ b/src/net/sourceforge/plantuml/UmlDiagramType.java @@ -39,7 +39,7 @@ import net.sourceforge.plantuml.style.SName; public enum UmlDiagramType { SEQUENCE, STATE, CLASS, OBJECT, ACTIVITY, DESCRIPTION, COMPOSITE, FLOW, TIMING, BPM, NWDIAG, MINDMAP, WBS, WIRE, - HELP, GANTT, SALT, JSON, GIT, BOARD; + HELP, GANTT, SALT, JSON, GIT, BOARD, YAML; public SName getStyleName() { if (this == SEQUENCE) { diff --git a/src/net/sourceforge/plantuml/activitydiagram/ActivityDiagram.java b/src/net/sourceforge/plantuml/activitydiagram/ActivityDiagram.java index 061199041..922a20389 100644 --- a/src/net/sourceforge/plantuml/activitydiagram/ActivityDiagram.java +++ b/src/net/sourceforge/plantuml/activitydiagram/ActivityDiagram.java @@ -61,7 +61,7 @@ public class ActivityDiagram extends CucaDiagram { private ConditionalContext currentContext; public ActivityDiagram(ISkinSimple skinParam) { - super(skinParam); + super(UmlDiagramType.ACTIVITY, skinParam); setNamespaceSeparator(null); } @@ -163,11 +163,6 @@ public class ActivityDiagram extends CucaDiagram { return lastEntityBrancheConsulted; } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.ACTIVITY; - } - public final ConditionalContext getCurrentContext() { return currentContext; } diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagram3.java b/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagram3.java index 13ad44103..94559c6f7 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagram3.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagram3.java @@ -82,7 +82,7 @@ public class ActivityDiagram3 extends UmlDiagram { private final Swimlanes swinlanes = new Swimlanes(getSkinParam(), getPragma()); public ActivityDiagram3(ISkinSimple skinParam) { - super(skinParam); + super(UmlDiagramType.ACTIVITY, skinParam); } private void manageSwimlaneStrategy() { @@ -199,11 +199,6 @@ public class ActivityDiagram3 extends UmlDiagram { return new DiagramDescription("activity3"); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.ACTIVITY; - } - @Override protected ImageData exportDiagramInternal(OutputStream os, int index, FileFormatOption fileFormatOption) throws IOException { diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/UGraphicInterceptorUDrawable2.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/UGraphicInterceptorUDrawable2.java index d399cdacd..be7257e84 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/UGraphicInterceptorUDrawable2.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/UGraphicInterceptorUDrawable2.java @@ -47,6 +47,7 @@ import net.sourceforge.plantuml.ugraphic.UGraphic; import net.sourceforge.plantuml.ugraphic.ULine; import net.sourceforge.plantuml.ugraphic.UShape; import net.sourceforge.plantuml.ugraphic.UTranslate; +import net.sourceforge.plantuml.ugraphic.color.HColor; import net.sourceforge.plantuml.ugraphic.color.HColorUtils; public class UGraphicInterceptorUDrawable2 extends UGraphicDelegator { @@ -85,10 +86,11 @@ public class UGraphicInterceptorUDrawable2 extends UGraphicDelegator { } private void drawGoto(FtileGoto ftile) { + final HColor gotoColor = HColorUtils.MY_RED; + final FtileGeometry geom = ftile.calculateDimension(getStringBounder()); final Point2D pt = geom.getPointIn(); - UGraphic ugGoto = getUg().apply(HColorUtils.GREEN).apply( - HColorUtils.GREEN.bg()); + UGraphic ugGoto = getUg().apply(gotoColor).apply(gotoColor.bg()); ugGoto = ugGoto.apply(new UTranslate(pt)); final UTranslate posNow = getPosition(); final UTranslate dest = positions.get(ftile.getName()); diff --git a/src/net/sourceforge/plantuml/board/BoardDiagram.java b/src/net/sourceforge/plantuml/board/BoardDiagram.java index 2371508c7..e4a1942ea 100644 --- a/src/net/sourceforge/plantuml/board/BoardDiagram.java +++ b/src/net/sourceforge/plantuml/board/BoardDiagram.java @@ -80,9 +80,8 @@ public class BoardDiagram extends UmlDiagram { return new DiagramDescription("Board"); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.BOARD; + public BoardDiagram() { + super(UmlDiagramType.BOARD); } @Override diff --git a/src/net/sourceforge/plantuml/bpm/BpmDiagram.java b/src/net/sourceforge/plantuml/bpm/BpmDiagram.java index 8b71abbfd..e2d1ca9e7 100644 --- a/src/net/sourceforge/plantuml/bpm/BpmDiagram.java +++ b/src/net/sourceforge/plantuml/bpm/BpmDiagram.java @@ -78,9 +78,8 @@ public class BpmDiagram extends UmlDiagram { return new DiagramDescription("(Bpm Diagram)"); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.BPM; + public BpmDiagram() { + super(UmlDiagramType.BPM); } @Override diff --git a/src/net/sourceforge/plantuml/classdiagram/AbstractEntityDiagram.java b/src/net/sourceforge/plantuml/classdiagram/AbstractEntityDiagram.java index a219e41ed..0baa074ed 100644 --- a/src/net/sourceforge/plantuml/classdiagram/AbstractEntityDiagram.java +++ b/src/net/sourceforge/plantuml/classdiagram/AbstractEntityDiagram.java @@ -41,13 +41,14 @@ import java.util.Collections; import java.util.List; import net.sourceforge.plantuml.ISkinSimple; +import net.sourceforge.plantuml.UmlDiagramType; import net.sourceforge.plantuml.core.DiagramDescription; import net.sourceforge.plantuml.cucadiagram.CucaDiagram; public abstract class AbstractEntityDiagram extends CucaDiagram { - public AbstractEntityDiagram(ISkinSimple orig) { - super(orig); + public AbstractEntityDiagram(UmlDiagramType type, ISkinSimple orig) { + super(type, orig); } final protected List getDotStrings() { diff --git a/src/net/sourceforge/plantuml/classdiagram/ClassDiagram.java b/src/net/sourceforge/plantuml/classdiagram/ClassDiagram.java index 9ab5beede..6a1f7ca1b 100644 --- a/src/net/sourceforge/plantuml/classdiagram/ClassDiagram.java +++ b/src/net/sourceforge/plantuml/classdiagram/ClassDiagram.java @@ -67,7 +67,7 @@ import net.sourceforge.plantuml.ugraphic.color.HColor; public class ClassDiagram extends AbstractClassOrObjectDiagram { public ClassDiagram(ISkinSimple skinParam) { - super(skinParam); + super(UmlDiagramType.CLASS, skinParam); } private Code getShortName1972(Code code) { @@ -170,11 +170,6 @@ public class ClassDiagram extends AbstractClassOrObjectDiagram { return super.leafExist(getFullyQualifiedCode1972(code)); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.CLASS; - } - private boolean allowMixing; public void setAllowMixing(boolean allowMixing) { diff --git a/src/net/sourceforge/plantuml/command/CommandCaption.java b/src/net/sourceforge/plantuml/command/CommandCaption.java index e05583db6..b8b2c5634 100644 --- a/src/net/sourceforge/plantuml/command/CommandCaption.java +++ b/src/net/sourceforge/plantuml/command/CommandCaption.java @@ -40,6 +40,7 @@ import net.sourceforge.plantuml.TitledDiagram; import net.sourceforge.plantuml.command.regex.IRegex; import net.sourceforge.plantuml.command.regex.RegexConcat; import net.sourceforge.plantuml.command.regex.RegexLeaf; +import net.sourceforge.plantuml.command.regex.RegexOr; import net.sourceforge.plantuml.command.regex.RegexResult; import net.sourceforge.plantuml.cucadiagram.Display; import net.sourceforge.plantuml.cucadiagram.DisplayPositionned; @@ -57,13 +58,16 @@ public class CommandCaption extends SingleLineCommand2 { RegexLeaf.start(), // new RegexLeaf("caption"), // new RegexLeaf("(?:[%s]*:[%s]*|[%s]+)"), // - new RegexLeaf("DISPLAY", "(.*[\\p{L}0-9_.].*)"), RegexLeaf.end()); // + new RegexOr(// + new RegexLeaf("DISPLAY1", "[%g](.*)[%g]"), // + new RegexLeaf("DISPLAY2", "(.*[\\p{L}0-9_.].*)")), // + RegexLeaf.end()); // } @Override protected CommandExecutionResult executeArg(TitledDiagram diagram, LineLocation location, RegexResult arg) { - diagram.setCaption(DisplayPositionned.single(Display.getWithNewlines(arg.get("DISPLAY", 0)), - HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM)); + final Display s = Display.getWithNewlines(arg.getLazzy("DISPLAY", 0)); + diagram.setCaption(DisplayPositionned.single(s, HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM)); return CommandExecutionResult.ok(); } diff --git a/src/net/sourceforge/plantuml/command/CommandFooter.java b/src/net/sourceforge/plantuml/command/CommandFooter.java index c628f8c71..c33b3d925 100644 --- a/src/net/sourceforge/plantuml/command/CommandFooter.java +++ b/src/net/sourceforge/plantuml/command/CommandFooter.java @@ -63,7 +63,10 @@ public class CommandFooter extends SingleLineCommand2 { new RegexOr( // new RegexConcat(RegexLeaf.spaceZeroOrMore(), new RegexLeaf(":"), RegexLeaf.spaceZeroOrMore()), // RegexLeaf.spaceOneOrMore()), // - new RegexLeaf("LABEL", "(.*[\\p{L}0-9_.].*)"), RegexLeaf.end()); // + new RegexOr(// + new RegexLeaf("LABEL1", "[%g](.*)[%g]"), // + new RegexLeaf("LABEL2", "(.*[\\p{L}0-9_.].*)")), // + RegexLeaf.end()); // } @Override @@ -74,7 +77,8 @@ public class CommandFooter extends SingleLineCommand2 { ha = FontParam.FOOTER.getStyleDefinition(null).getMergedStyle(diagram.getCurrentStyleBuilder()) .getHorizontalAlignment(); } - diagram.getFooter().putDisplay(Display.getWithNewlines(arg.get("LABEL", 0)), ha); + final Display s = Display.getWithNewlines(arg.getLazzy("LABEL", 0)); + diagram.getFooter().putDisplay(s, ha); return CommandExecutionResult.ok(); } diff --git a/src/net/sourceforge/plantuml/command/CommandHeader.java b/src/net/sourceforge/plantuml/command/CommandHeader.java index 8a49f0377..29e7e2029 100644 --- a/src/net/sourceforge/plantuml/command/CommandHeader.java +++ b/src/net/sourceforge/plantuml/command/CommandHeader.java @@ -65,7 +65,10 @@ public class CommandHeader extends SingleLineCommand2 { new RegexLeaf(":"), // RegexLeaf.spaceZeroOrMore()), // RegexLeaf.spaceOneOrMore()), // - new RegexLeaf("LABEL", "(.*[\\p{L}0-9_.].*)"), RegexLeaf.end()); // + new RegexOr(// + new RegexLeaf("LABEL1", "[%g](.*)[%g]"), // + new RegexLeaf("LABEL2", "(.*[\\p{L}0-9_.].*)")), // + RegexLeaf.end()); // } @Override @@ -76,7 +79,8 @@ public class CommandHeader extends SingleLineCommand2 { ha = FontParam.HEADER.getStyleDefinition(null).getMergedStyle(diagram.getCurrentStyleBuilder()) .getHorizontalAlignment(); } - diagram.getHeader().putDisplay(Display.getWithNewlines(arg.get("LABEL", 0)), ha); + final Display s = Display.getWithNewlines(arg.getLazzy("LABEL", 0)); + diagram.getHeader().putDisplay(s, ha); return CommandExecutionResult.ok(); } } diff --git a/src/net/sourceforge/plantuml/command/CommandLegend.java b/src/net/sourceforge/plantuml/command/CommandLegend.java new file mode 100644 index 000000000..5bec424b6 --- /dev/null +++ b/src/net/sourceforge/plantuml/command/CommandLegend.java @@ -0,0 +1,73 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://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.command; + +import net.sourceforge.plantuml.LineLocation; +import net.sourceforge.plantuml.TitledDiagram; +import net.sourceforge.plantuml.command.regex.IRegex; +import net.sourceforge.plantuml.command.regex.RegexConcat; +import net.sourceforge.plantuml.command.regex.RegexLeaf; +import net.sourceforge.plantuml.command.regex.RegexOr; +import net.sourceforge.plantuml.command.regex.RegexResult; +import net.sourceforge.plantuml.cucadiagram.Display; +import net.sourceforge.plantuml.cucadiagram.DisplayPositionned; +import net.sourceforge.plantuml.graphic.HorizontalAlignment; +import net.sourceforge.plantuml.graphic.VerticalAlignment; + +public class CommandLegend extends SingleLineCommand2 { + + public CommandLegend() { + super(getRegexConcat()); + } + + static IRegex getRegexConcat() { + return RegexConcat.build(CommandLegend.class.getName(), // + RegexLeaf.start(), // + new RegexLeaf("legend"), // + new RegexLeaf("(?:[%s]*:[%s]*|[%s]+)"), // + new RegexOr(// + new RegexLeaf("LEGEND1", "[%g](.*)[%g]"), // + new RegexLeaf("LEGEND2", "(.*[\\p{L}0-9_.].*)")), // + RegexLeaf.end()); // + } + + @Override + protected CommandExecutionResult executeArg(TitledDiagram diagram, LineLocation location, RegexResult arg) { + final Display s = Display.getWithNewlines(arg.getLazzy("LEGEND", 0)); + diagram.setLegend(DisplayPositionned.single(s, HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM)); + return CommandExecutionResult.ok(); + } +} diff --git a/src/net/sourceforge/plantuml/command/CommandTitle.java b/src/net/sourceforge/plantuml/command/CommandTitle.java index 864af6c44..a69825010 100644 --- a/src/net/sourceforge/plantuml/command/CommandTitle.java +++ b/src/net/sourceforge/plantuml/command/CommandTitle.java @@ -40,6 +40,7 @@ import net.sourceforge.plantuml.TitledDiagram; import net.sourceforge.plantuml.command.regex.IRegex; import net.sourceforge.plantuml.command.regex.RegexConcat; import net.sourceforge.plantuml.command.regex.RegexLeaf; +import net.sourceforge.plantuml.command.regex.RegexOr; import net.sourceforge.plantuml.command.regex.RegexResult; import net.sourceforge.plantuml.cucadiagram.Display; import net.sourceforge.plantuml.cucadiagram.DisplayPositionned; @@ -57,13 +58,16 @@ public class CommandTitle extends SingleLineCommand2 { RegexLeaf.start(), // new RegexLeaf("title"), // new RegexLeaf("(?:[%s]*:[%s]*|[%s]+)"), // - new RegexLeaf("TITLE", "(.*[\\p{L}0-9_.].*)"), RegexLeaf.end()); // + new RegexOr(// + new RegexLeaf("TITLE1", "[%g](.*)[%g]"), // + new RegexLeaf("TITLE2", "(.*[\\p{L}0-9_.].*)")), // + RegexLeaf.end()); // } @Override protected CommandExecutionResult executeArg(TitledDiagram diagram, LineLocation location, RegexResult arg) { - diagram.setTitle(DisplayPositionned.single(Display.getWithNewlines(arg.get("TITLE", 0)), - HorizontalAlignment.CENTER, VerticalAlignment.TOP)); + final Display s = Display.getWithNewlines(arg.getLazzy("TITLE", 0)); + diagram.setTitle(DisplayPositionned.single(s, HorizontalAlignment.CENTER, VerticalAlignment.TOP)); return CommandExecutionResult.ok(); } } diff --git a/src/net/sourceforge/plantuml/command/PSystemCommandFactory.java b/src/net/sourceforge/plantuml/command/PSystemCommandFactory.java index 801c1658c..31826e56d 100644 --- a/src/net/sourceforge/plantuml/command/PSystemCommandFactory.java +++ b/src/net/sourceforge/plantuml/command/PSystemCommandFactory.java @@ -262,6 +262,7 @@ public abstract class PSystemCommandFactory extends PSystemAbstractFactory { cmds.add(new CommandMultilinesCaption()); cmds.add(new CommandMultilinesTitle()); cmds.add(new CommandMultilinesLegend()); + cmds.add(new CommandLegend()); cmds.add(new CommandFooter()); cmds.add(new CommandMultilinesFooter()); diff --git a/src/net/sourceforge/plantuml/compositediagram/CompositeDiagram.java b/src/net/sourceforge/plantuml/compositediagram/CompositeDiagram.java index 25a80f879..b438035f8 100644 --- a/src/net/sourceforge/plantuml/compositediagram/CompositeDiagram.java +++ b/src/net/sourceforge/plantuml/compositediagram/CompositeDiagram.java @@ -47,7 +47,7 @@ import net.sourceforge.plantuml.graphic.USymbol; public class CompositeDiagram extends AbstractEntityDiagram { public CompositeDiagram(ISkinSimple skinParam) { - super(skinParam); + super(UmlDiagramType.COMPOSITE, skinParam); } @Override @@ -63,9 +63,4 @@ public class CompositeDiagram extends AbstractEntityDiagram { return getOrCreateLeafDefault(ident, code, type, symbol); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.COMPOSITE; - } - } diff --git a/src/net/sourceforge/plantuml/core/DiagramType.java b/src/net/sourceforge/plantuml/core/DiagramType.java index d5984d0fb..8a0435518 100644 --- a/src/net/sourceforge/plantuml/core/DiagramType.java +++ b/src/net/sourceforge/plantuml/core/DiagramType.java @@ -39,7 +39,7 @@ import net.sourceforge.plantuml.utils.StartUtils; public enum DiagramType { UML, BPM, DITAA, DOT, PROJECT, JCCKIT, SALT, FLOW, CREOLE, JUNGLE, CUTE, MATH, LATEX, - DEFINITION, GANTT, NW, MINDMAP, WBS, WIRE, JSON, GIT, BOARD, + DEFINITION, GANTT, NW, MINDMAP, WBS, WIRE, JSON, GIT, BOARD, YAML, UNKNOWN; static public DiagramType getTypeFromArobaseStart(String s) { @@ -113,6 +113,9 @@ public enum DiagramType { if (StartUtils.startsWithSymbolAnd("startboard", s)) { return BOARD; } + if (StartUtils.startsWithSymbolAnd("startyaml", s)) { + return YAML; + } return UNKNOWN; } } diff --git a/src/net/sourceforge/plantuml/creole/command/CommandCreoleStyle.java b/src/net/sourceforge/plantuml/creole/command/CommandCreoleStyle.java index 3a03ab8bc..f5972488f 100644 --- a/src/net/sourceforge/plantuml/creole/command/CommandCreoleStyle.java +++ b/src/net/sourceforge/plantuml/creole/command/CommandCreoleStyle.java @@ -50,7 +50,7 @@ public class CommandCreoleStyle implements Command { private final FontStyle style; private final boolean tryExtendedColor; - public static CommandCreoleStyle createCreole(FontStyle style) { + public static Command createCreole(FontStyle style) { return new CommandCreoleStyle("^(" + style.getCreoleSyntax() + "(.+?)" + style.getCreoleSyntax() + ")", style, false); } diff --git a/src/net/sourceforge/plantuml/creole/command/CommandCreoleStyle2.java b/src/net/sourceforge/plantuml/creole/command/CommandCreoleStyle2.java new file mode 100644 index 000000000..93e6550e9 --- /dev/null +++ b/src/net/sourceforge/plantuml/creole/command/CommandCreoleStyle2.java @@ -0,0 +1,89 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://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.creole.command; + +import net.sourceforge.plantuml.StringUtils; +import net.sourceforge.plantuml.command.regex.Matcher2; +import net.sourceforge.plantuml.command.regex.MyPattern; +import net.sourceforge.plantuml.command.regex.Pattern2; +import net.sourceforge.plantuml.creole.legacy.StripeSimple; +import net.sourceforge.plantuml.graphic.FontStyle; + +public class CommandCreoleStyle2 implements Command { + + private final Pattern2 p; + private final FontStyle style; + + public static Command createCreole(FontStyle style) { + return new CommandCreoleStyle2("^(" + style.getCreoleSyntax() + "(.+?)" + style.getCreoleSyntax() + ")", style); + } + + public static Command createLegacy(FontStyle style) { + return new CommandCreoleStyle2( + "^((" + style.getActivationPattern() + ")(.+?)" + style.getDeactivationPattern() + ")", style); + } + + public static Command createLegacyEol(FontStyle style) { + return new CommandCreoleStyle2("^((" + style.getActivationPattern() + ")(.+))$", style); + } + + private CommandCreoleStyle2(String p, FontStyle style) { + this.p = MyPattern.cmpile(p); + this.style = style; + } + + public String executeAndGetRemaining(final String line, StripeSimple stripe) { + final Matcher2 m = p.matcher(line); + if (m.find() == false) { + throw new IllegalStateException(); + } + + final int groupCount = m.groupCount(); + final String part1 = m.group(groupCount); + final String part2 = line.substring(m.group(1).length()); + return StringUtils.BOLD_START + part1 + StringUtils.BOLD_END + part2; + + } + + public int matchingSize(String line) { + final Matcher2 m = p.matcher(line); + if (m.find() == false) { + return 0; + } + return m.group(1).length(); + } + +} diff --git a/src/net/sourceforge/plantuml/creole/legacy/StripeSimple.java b/src/net/sourceforge/plantuml/creole/legacy/StripeSimple.java index 5b7ab8883..a6848290f 100644 --- a/src/net/sourceforge/plantuml/creole/legacy/StripeSimple.java +++ b/src/net/sourceforge/plantuml/creole/legacy/StripeSimple.java @@ -69,6 +69,7 @@ import net.sourceforge.plantuml.creole.command.CommandCreoleSizeChange; import net.sourceforge.plantuml.creole.command.CommandCreoleSpace; import net.sourceforge.plantuml.creole.command.CommandCreoleSprite; import net.sourceforge.plantuml.creole.command.CommandCreoleStyle; +import net.sourceforge.plantuml.creole.command.CommandCreoleStyle2; import net.sourceforge.plantuml.creole.command.CommandCreoleSvgAttributeChange; import net.sourceforge.plantuml.creole.command.CommandCreoleUrl; import net.sourceforge.plantuml.graphic.FontConfiguration; @@ -113,6 +114,8 @@ public class StripeSimple implements Stripe { return header; } + public final static boolean TSPAN = false; + public StripeSimple(FontConfiguration fontConfiguration, StripeStyle style, CreoleContext context, ISkinSimple skinParam, CreoleMode modeSimpleLine) { this.fontConfiguration = fontConfiguration; @@ -120,9 +123,16 @@ public class StripeSimple implements Stripe { this.skinParam = skinParam; // class Splitter - this.commands.add(CommandCreoleStyle.createCreole(FontStyle.BOLD)); - this.commands.add(CommandCreoleStyle.createLegacy(FontStyle.BOLD)); - this.commands.add(CommandCreoleStyle.createLegacyEol(FontStyle.BOLD)); + if (TSPAN) { + this.commands.add(CommandCreoleStyle2.createCreole(FontStyle.BOLD)); + this.commands.add(CommandCreoleStyle2.createLegacy(FontStyle.BOLD)); + this.commands.add(CommandCreoleStyle2.createLegacyEol(FontStyle.BOLD)); + } else { + this.commands.add(CommandCreoleStyle.createCreole(FontStyle.BOLD)); + this.commands.add(CommandCreoleStyle.createLegacy(FontStyle.BOLD)); + this.commands.add(CommandCreoleStyle.createLegacyEol(FontStyle.BOLD)); + } + this.commands.add(CommandCreoleStyle.createCreole(FontStyle.ITALIC)); this.commands.add(CommandCreoleStyle.createLegacy(FontStyle.ITALIC)); this.commands.add(CommandCreoleStyle.createLegacyEol(FontStyle.ITALIC)); diff --git a/src/net/sourceforge/plantuml/cucadiagram/CucaDiagram.java b/src/net/sourceforge/plantuml/cucadiagram/CucaDiagram.java index 289c4c790..5ab89226d 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/CucaDiagram.java +++ b/src/net/sourceforge/plantuml/cucadiagram/CucaDiagram.java @@ -118,8 +118,8 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy, return ident; } - public CucaDiagram(ISkinSimple orig) { - super(orig); + public CucaDiagram(UmlDiagramType type, ISkinSimple orig) { + super(type, orig); this.stacks2.add(Ident.empty()); } diff --git a/src/net/sourceforge/plantuml/cucadiagram/dot/AbstractGraphviz.java b/src/net/sourceforge/plantuml/cucadiagram/dot/AbstractGraphviz.java index 095a8d319..8328e8a1d 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/dot/AbstractGraphviz.java +++ b/src/net/sourceforge/plantuml/cucadiagram/dot/AbstractGraphviz.java @@ -77,10 +77,14 @@ abstract class AbstractGraphviz implements Graphviz { this.dotString = dotString; this.type = type; } + + protected boolean findExecutableOnPath() { + return true; + } - protected File searchDotExe() { + final protected File searchDotExe() { String getenv = GraphvizUtils.getenvGraphvizDot(); - if (getenv == null) { + if (findExecutableOnPath() && getenv == null) { getenv = findExecutableOnPath(getExeName()); } if (getenv == null) { diff --git a/src/net/sourceforge/plantuml/cucadiagram/dot/GraphvizWindowsLite.java b/src/net/sourceforge/plantuml/cucadiagram/dot/GraphvizWindowsLite.java index 7b55b5bab..7a12b1b6c 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/dot/GraphvizWindowsLite.java +++ b/src/net/sourceforge/plantuml/cucadiagram/dot/GraphvizWindowsLite.java @@ -46,8 +46,8 @@ class GraphvizWindowsLite extends AbstractGraphviz { static private File specificDotExe; @Override - protected File searchDotExe() { - return specificDotExe(); + protected boolean findExecutableOnPath() { + return false; } @Override diff --git a/src/net/sourceforge/plantuml/descdiagram/DescriptionDiagram.java b/src/net/sourceforge/plantuml/descdiagram/DescriptionDiagram.java index 944527f12..f9475f900 100644 --- a/src/net/sourceforge/plantuml/descdiagram/DescriptionDiagram.java +++ b/src/net/sourceforge/plantuml/descdiagram/DescriptionDiagram.java @@ -48,7 +48,7 @@ import net.sourceforge.plantuml.graphic.USymbol; public class DescriptionDiagram extends AbstractEntityDiagram { public DescriptionDiagram(ISkinSimple skinParam) { - super(skinParam); + super(UmlDiagramType.DESCRIPTION, skinParam); } @Override @@ -74,7 +74,7 @@ public class DescriptionDiagram extends AbstractEntityDiagram { if (type == null) { String codeString = code.getName(); if (codeString.startsWith("[") && codeString.endsWith("]")) { - final USymbol sym = getSkinParam().componentStyle().toUSymbol() ; + final USymbol sym = getSkinParam().componentStyle().toUSymbol(); final Ident idNewLong = ident.eventuallyRemoveStartingAndEndingDoubleQuote("\"([:"); return getOrCreateLeafDefault(idNewLong, idNewLong.toCode(this), LeafType.DESCRIPTION, sym); } @@ -127,9 +127,4 @@ public class DescriptionDiagram extends AbstractEntityDiagram { return super.checkFinalError(); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.DESCRIPTION; - } - } diff --git a/src/net/sourceforge/plantuml/flowdiagram/FlowDiagram.java b/src/net/sourceforge/plantuml/flowdiagram/FlowDiagram.java index e3792f028..724fdfe66 100644 --- a/src/net/sourceforge/plantuml/flowdiagram/FlowDiagram.java +++ b/src/net/sourceforge/plantuml/flowdiagram/FlowDiagram.java @@ -87,9 +87,8 @@ public class FlowDiagram extends UmlDiagram implements TextBlock { return new DiagramDescription("Flow Diagram"); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.FLOW; + public FlowDiagram() { + super(UmlDiagramType.FLOW); } public void lineSimple(TileGeometry orientation, String idDest, String label) { diff --git a/src/net/sourceforge/plantuml/gitlog/GitDiagram.java b/src/net/sourceforge/plantuml/gitlog/GitDiagram.java index 7e8c1663f..18d5eda0e 100644 --- a/src/net/sourceforge/plantuml/gitlog/GitDiagram.java +++ b/src/net/sourceforge/plantuml/gitlog/GitDiagram.java @@ -67,6 +67,7 @@ public class GitDiagram extends UmlDiagram { private final Collection gnodes; public GitDiagram(GitTextArea textArea) { + super(UmlDiagramType.GIT); this.gnodes = new GNodeBuilder(textArea.getAllCommits()).getAllNodes(); new GNodeBuilder(textArea.getAllCommits()); } @@ -75,10 +76,6 @@ public class GitDiagram extends UmlDiagram { return new DiagramDescription("(Git)"); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.GIT; - } @Override protected ImageData exportDiagramInternal(OutputStream os, int index, FileFormatOption fileFormatOption) diff --git a/src/net/sourceforge/plantuml/graphic/StyledString.java b/src/net/sourceforge/plantuml/graphic/StyledString.java new file mode 100644 index 000000000..fba2d7255 --- /dev/null +++ b/src/net/sourceforge/plantuml/graphic/StyledString.java @@ -0,0 +1,92 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://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.graphic; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.sourceforge.plantuml.StringUtils; + +public class StyledString { + + private final String text; + private final FontStyle style; + + private StyledString(String text, FontStyle style) { + this.text = text; + this.style = style; + } + + @Override + public String toString() { + return style + "[" + text + "]"; + } + + public final String getText() { + return text; + } + + public final FontStyle getStyle() { + return style; + } + + public static List build(String s) { + final List result = new ArrayList(); + while (s.length() > 0) { + final int i1 = s.indexOf(StringUtils.BOLD_START); + if (i1 == -1) { + result.add(new StyledString(s, FontStyle.PLAIN)); + s = ""; + break; + } + final int i2 = s.indexOf(StringUtils.BOLD_END); + if (i1 > 0) + result.add(new StyledString(s.substring(0, i1), FontStyle.PLAIN)); + + if (i2 == -1) { + result.add(new StyledString(s.substring(i1 + 1), FontStyle.BOLD)); + s = ""; + } else { + result.add(new StyledString(s.substring(i1 + 1, i2), FontStyle.BOLD)); + s = s.substring(i2 + 1); + } + } + return Collections.unmodifiableList(result); + + } + +} diff --git a/src/net/sourceforge/plantuml/help/Help.java b/src/net/sourceforge/plantuml/help/Help.java index a9b961677..024b03fc8 100644 --- a/src/net/sourceforge/plantuml/help/Help.java +++ b/src/net/sourceforge/plantuml/help/Help.java @@ -66,9 +66,8 @@ public class Help extends UmlDiagram { return new DiagramDescription("(Help)"); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.HELP; + public Help() { + super(UmlDiagramType.HELP); } @Override diff --git a/src/net/sourceforge/plantuml/jsondiagram/JsonDiagram.java b/src/net/sourceforge/plantuml/jsondiagram/JsonDiagram.java index e917257b2..fa20bca87 100644 --- a/src/net/sourceforge/plantuml/jsondiagram/JsonDiagram.java +++ b/src/net/sourceforge/plantuml/jsondiagram/JsonDiagram.java @@ -74,7 +74,8 @@ public class JsonDiagram extends UmlDiagram { private final JsonValue root; private final List highlighted; - public JsonDiagram(JsonValue json, List highlighted) { + public JsonDiagram(UmlDiagramType type, JsonValue json, List highlighted) { + super(UmlDiagramType.JSON); if (json != null && (json.isString() || json.isBoolean() || json.isNumber())) { this.root = new JsonArray(); ((JsonArray) this.root).add(json); @@ -85,14 +86,12 @@ public class JsonDiagram extends UmlDiagram { } public DiagramDescription getDescription() { + if (getUmlDiagramType() == UmlDiagramType.YAML) { + return new DiagramDescription("(Yaml)"); + } return new DiagramDescription("(Json)"); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.JSON; - } - @Override protected ImageData exportDiagramInternal(OutputStream os, int index, FileFormatOption fileFormatOption) throws IOException { @@ -124,7 +123,8 @@ public class JsonDiagram extends UmlDiagram { private void drawInternal(UGraphic ug) { if (root == null) { - final Display display = Display.getWithNewlines("Your data does not sound like JSON data"); + final Display display = Display + .getWithNewlines("Your data does not sound like " + getUmlDiagramType() + " data"); final FontConfiguration fontConfiguration = FontConfiguration.blackBlueTrue(UFont.courier(14)); TextBlock result = display.create(fontConfiguration, HorizontalAlignment.LEFT, getSkinParam()); result = TextBlockUtils.withMargin(result, 5, 2); diff --git a/src/net/sourceforge/plantuml/jsondiagram/JsonDiagramFactory.java b/src/net/sourceforge/plantuml/jsondiagram/JsonDiagramFactory.java index 5b741ab71..a4c55391b 100644 --- a/src/net/sourceforge/plantuml/jsondiagram/JsonDiagramFactory.java +++ b/src/net/sourceforge/plantuml/jsondiagram/JsonDiagramFactory.java @@ -41,6 +41,7 @@ import java.util.List; import net.sourceforge.plantuml.BackSlash; import net.sourceforge.plantuml.StringLocated; +import net.sourceforge.plantuml.UmlDiagramType; import net.sourceforge.plantuml.command.PSystemAbstractFactory; import net.sourceforge.plantuml.core.Diagram; import net.sourceforge.plantuml.core.DiagramType; @@ -81,7 +82,9 @@ public class JsonDiagramFactory extends PSystemAbstractFactory { } catch (ParseException e) { json = null; } - return new JsonDiagram(json, highlighted); + final JsonDiagram result = new JsonDiagram(UmlDiagramType.JSON, json, highlighted); + result.setSource(source); + return result; } } diff --git a/src/net/sourceforge/plantuml/jsondiagram/SmetanaForJson.java b/src/net/sourceforge/plantuml/jsondiagram/SmetanaForJson.java index decd89c01..74277b3f2 100644 --- a/src/net/sourceforge/plantuml/jsondiagram/SmetanaForJson.java +++ b/src/net/sourceforge/plantuml/jsondiagram/SmetanaForJson.java @@ -80,16 +80,16 @@ public class SmetanaForJson { private ST_Agraph_s g; private StringBounder stringBounder; - private final List nodes = new ArrayList(); + private final List nodes = new ArrayList(); private final List edges = new ArrayList(); private Mirror xMirror; - static class Node { + static class InternalNode { private final TextBlockJson block; private final ST_Agnode_s node; - public Node(TextBlockJson block, ST_Agnode_s node) { + public InternalNode(TextBlockJson block, ST_Agnode_s node) { this.block = block; this.node = node; } @@ -118,7 +118,7 @@ public class SmetanaForJson { final TextBlockJson block = new TextBlockJson(skinParam, current, highlighted); final ST_Agnode_s node1 = createNode(block.calculateDimension(stringBounder), block.size(), current.isArray(), (int) block.getWidthColA(stringBounder), (int) block.getWidthColB(stringBounder)); - nodes.add(new Node(block, node1)); + nodes.add(new InternalNode(block, node1)); final List children = block.children(); final List keys = block.keys(); for (int i = 0; i < children.size(); i++) { @@ -151,12 +151,12 @@ public class SmetanaForJson { public void drawMe(JsonValue root, List highlighted) { initGraph(root, highlighted); double max = 0; - for (Node node : nodes) { + for (InternalNode node : nodes) { max = Math.max(max, node.getMaxX()); } xMirror = new Mirror(max); - for (Node node : nodes) { + for (InternalNode node : nodes) { node.block.drawU(ug.apply(getPosition(node.node))); } final HColor color = getStyle().value(PName.LineColor).asColor(skinParam.getIHtmlColorSet()); diff --git a/src/net/sourceforge/plantuml/mindmap/CommandMindMapPlus.java b/src/net/sourceforge/plantuml/mindmap/CommandMindMapPlus.java index df5275f89..42fbdebdc 100644 --- a/src/net/sourceforge/plantuml/mindmap/CommandMindMapPlus.java +++ b/src/net/sourceforge/plantuml/mindmap/CommandMindMapPlus.java @@ -42,8 +42,10 @@ import net.sourceforge.plantuml.command.SingleLineCommand2; import net.sourceforge.plantuml.command.regex.IRegex; import net.sourceforge.plantuml.command.regex.RegexConcat; import net.sourceforge.plantuml.command.regex.RegexLeaf; +import net.sourceforge.plantuml.command.regex.RegexOptional; import net.sourceforge.plantuml.command.regex.RegexResult; import net.sourceforge.plantuml.cucadiagram.Display; +import net.sourceforge.plantuml.ugraphic.color.HColor; public class CommandMindMapPlus extends SingleLineCommand2 { @@ -54,6 +56,7 @@ public class CommandMindMapPlus extends SingleLineCommand2 { static IRegex getRegexConcat() { return RegexConcat.build(CommandMindMapPlus.class.getName(), RegexLeaf.start(), // new RegexLeaf("TYPE", "([+-]+)"), // + new RegexOptional(new RegexLeaf("BACKCOLOR", "\\[(#\\w+)\\]")), // new RegexLeaf("SHAPE", "(_)?"), // RegexLeaf.spaceOneOrMore(), // new RegexLeaf("LABEL", "([^%s].*)"), RegexLeaf.end()); @@ -63,8 +66,13 @@ public class CommandMindMapPlus extends SingleLineCommand2 { protected CommandExecutionResult executeArg(MindMapDiagram diagram, LineLocation location, RegexResult arg) { final String type = arg.get("TYPE", 0); final String label = arg.get("LABEL", 0); + final String stringColor = arg.get("BACKCOLOR", 0); + HColor backColor = null; + if (stringColor != null) { + backColor = diagram.getSkinParam().getIHtmlColorSet().getColorIfValid(stringColor); + } final Direction direction = type.contains("-") ? Direction.LEFT : Direction.RIGHT; - return diagram.addIdea(null, type.length() - 1, Display.getWithNewlines(label), + return diagram.addIdea(backColor, type.length() - 1, Display.getWithNewlines(label), IdeaShape.fromDesc(arg.get("SHAPE", 0)), direction); } diff --git a/src/net/sourceforge/plantuml/mindmap/MindMapDiagram.java b/src/net/sourceforge/plantuml/mindmap/MindMapDiagram.java index aa884ec4d..555760385 100644 --- a/src/net/sourceforge/plantuml/mindmap/MindMapDiagram.java +++ b/src/net/sourceforge/plantuml/mindmap/MindMapDiagram.java @@ -87,9 +87,8 @@ public class MindMapDiagram extends UmlDiagram { return new DiagramDescription("MindMap"); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.MINDMAP; + public MindMapDiagram() { + super(UmlDiagramType.MINDMAP); } @Override diff --git a/src/net/sourceforge/plantuml/nwdiag/NwDiagram.java b/src/net/sourceforge/plantuml/nwdiag/NwDiagram.java index 230f9e285..0c208971d 100644 --- a/src/net/sourceforge/plantuml/nwdiag/NwDiagram.java +++ b/src/net/sourceforge/plantuml/nwdiag/NwDiagram.java @@ -92,9 +92,8 @@ public class NwDiagram extends UmlDiagram { return new DiagramDescription("(Nwdiag)"); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.NWDIAG; + public NwDiagram() { + super(UmlDiagramType.NWDIAG); } public void init() { diff --git a/src/net/sourceforge/plantuml/objectdiagram/AbstractClassOrObjectDiagram.java b/src/net/sourceforge/plantuml/objectdiagram/AbstractClassOrObjectDiagram.java index 875fdc996..5635324b1 100644 --- a/src/net/sourceforge/plantuml/objectdiagram/AbstractClassOrObjectDiagram.java +++ b/src/net/sourceforge/plantuml/objectdiagram/AbstractClassOrObjectDiagram.java @@ -39,6 +39,7 @@ import java.util.ArrayList; import java.util.List; import net.sourceforge.plantuml.ISkinSimple; +import net.sourceforge.plantuml.UmlDiagramType; import net.sourceforge.plantuml.classdiagram.AbstractEntityDiagram; import net.sourceforge.plantuml.command.CommandExecutionResult; import net.sourceforge.plantuml.cucadiagram.Code; @@ -57,10 +58,10 @@ import net.sourceforge.plantuml.utils.UniqueSequence; public abstract class AbstractClassOrObjectDiagram extends AbstractEntityDiagram { - public AbstractClassOrObjectDiagram(ISkinSimple orig) { - super(orig); + public AbstractClassOrObjectDiagram(UmlDiagramType type, ISkinSimple orig) { + super(type, orig); } - + @Override public Ident cleanIdent(Ident ident) { String codeString = ident.getName(); @@ -70,18 +71,16 @@ public abstract class AbstractClassOrObjectDiagram extends AbstractEntityDiagram return ident; } - final public boolean insertBetween(IEntity entity1, IEntity entity2, IEntity node) { final Link link = foundLink(entity1, entity2); if (link == null) { return false; } - final Link l1 = new Link(entity1, node, link.getType(), link.getLabel(), link.getLength(), - link.getQualifier1(), null, link.getLabeldistance(), link.getLabelangle(), getSkinParam() - .getCurrentStyleBuilder()); + final Link l1 = new Link(entity1, node, link.getType(), link.getLabel(), link.getLength(), link.getQualifier1(), + null, link.getLabeldistance(), link.getLabelangle(), getSkinParam().getCurrentStyleBuilder()); final Link l2 = new Link(node, entity2, link.getType(), link.getLabel(), link.getLength(), null, - link.getQualifier2(), link.getLabeldistance(), link.getLabelangle(), getSkinParam() - .getCurrentStyleBuilder()); + link.getQualifier2(), link.getLabeldistance(), link.getLabelangle(), + getSkinParam().getCurrentStyleBuilder()); addLink(l1); addLink(l2); removeLink(link); @@ -146,8 +145,8 @@ public abstract class AbstractClassOrObjectDiagram extends AbstractEntityDiagram insertPointBetween(entity2A, entity2B, point2); final int length = 1; - final Link point1ToPoint2 = new Link(point1, point2, linkType, label, length, getSkinParam() - .getCurrentStyleBuilder()); + final Link point1ToPoint2 = new Link(point1, point2, linkType, label, length, + getSkinParam().getCurrentStyleBuilder()); addLink(point1ToPoint2); return CommandExecutionResult.ok(); @@ -164,15 +163,15 @@ public abstract class AbstractClassOrObjectDiagram extends AbstractEntityDiagram removeLink(existingLink1); } - final IEntity entity1real = existingLink1.isInverted() ? existingLink1.getEntity2() : existingLink1 - .getEntity1(); - final IEntity entity2real = existingLink1.isInverted() ? existingLink1.getEntity1() : existingLink1 - .getEntity2(); + final IEntity entity1real = existingLink1.isInverted() ? existingLink1.getEntity2() + : existingLink1.getEntity1(); + final IEntity entity2real = existingLink1.isInverted() ? existingLink1.getEntity1() + : existingLink1.getEntity2(); final Link entity1ToPoint = new Link(entity1real, point1, existingLink1.getType().getPart2(), existingLink1.getLabel(), existingLink1.getLength(), existingLink1.getQualifier1(), null, - existingLink1.getLabeldistance(), existingLink1.getLabelangle(), getSkinParam() - .getCurrentStyleBuilder()); + existingLink1.getLabeldistance(), existingLink1.getLabelangle(), + getSkinParam().getCurrentStyleBuilder()); entity1ToPoint.setLinkArrow(existingLink1.getLinkArrow()); final Link pointToEntity2 = new Link(point1, entity2real, existingLink1.getType().getPart1(), Display.NULL, existingLink1.getLength(), null, existingLink1.getQualifier2(), existingLink1.getLabeldistance(), @@ -267,16 +266,16 @@ public abstract class AbstractClassOrObjectDiagram extends AbstractEntityDiagram void createNew(int mode, LinkType linkType, Display label) { existingLink = foundLink(entity1, entity2); if (existingLink == null) { - existingLink = new Link(entity1, entity2, new LinkType(LinkDecor.NONE, LinkDecor.NONE), Display.NULL, - 2, getSkinParam().getCurrentStyleBuilder()); + existingLink = new Link(entity1, entity2, new LinkType(LinkDecor.NONE, LinkDecor.NONE), Display.NULL, 2, + getSkinParam().getCurrentStyleBuilder()); } else { removeLink(existingLink); } - final IEntity entity1real = existingLink.isInverted() ? existingLink.getEntity2() : existingLink - .getEntity1(); - final IEntity entity2real = existingLink.isInverted() ? existingLink.getEntity1() : existingLink - .getEntity2(); + final IEntity entity1real = existingLink.isInverted() ? existingLink.getEntity2() + : existingLink.getEntity1(); + final IEntity entity2real = existingLink.isInverted() ? existingLink.getEntity1() + : existingLink.getEntity2(); entity1ToPoint = new Link(entity1real, point, existingLink.getType().getPart2(), existingLink.getLabel(), existingLink.getLength(), existingLink.getQualifier1(), null, existingLink.getLabeldistance(), @@ -303,11 +302,11 @@ public abstract class AbstractClassOrObjectDiagram extends AbstractEntityDiagram addLink(pointToEntity2); if (mode == 1) { - pointToAssocied = new Link(point, associed, linkType, label, length, getSkinParam() - .getCurrentStyleBuilder()); + pointToAssocied = new Link(point, associed, linkType, label, length, + getSkinParam().getCurrentStyleBuilder()); } else { - pointToAssocied = new Link(associed, point, linkType, label, length, getSkinParam() - .getCurrentStyleBuilder()); + pointToAssocied = new Link(associed, point, linkType, label, length, + getSkinParam().getCurrentStyleBuilder()); } addLink(pointToAssocied); } @@ -315,8 +314,8 @@ public abstract class AbstractClassOrObjectDiagram extends AbstractEntityDiagram void createInSecond(LinkType linkType, Display label) { existingLink = foundLink(entity1, entity2); if (existingLink == null) { - existingLink = new Link(entity1, entity2, new LinkType(LinkDecor.NONE, LinkDecor.NONE), Display.NULL, - 2, getSkinParam().getCurrentStyleBuilder()); + existingLink = new Link(entity1, entity2, new LinkType(LinkDecor.NONE, LinkDecor.NONE), Display.NULL, 2, + getSkinParam().getCurrentStyleBuilder()); } else { removeLink(existingLink); } diff --git a/src/net/sourceforge/plantuml/postit/PostItDiagram.java b/src/net/sourceforge/plantuml/postit/PostItDiagram.java index 9821265da..c65f503b8 100644 --- a/src/net/sourceforge/plantuml/postit/PostItDiagram.java +++ b/src/net/sourceforge/plantuml/postit/PostItDiagram.java @@ -65,9 +65,8 @@ public class PostItDiagram extends UmlDiagram { private final Map postIts = new HashMap(); - @Override - public UmlDiagramType getUmlDiagramType() { - return null; + public PostItDiagram() { + super(UmlDiagramType.TIMING); } @Override diff --git a/src/net/sourceforge/plantuml/project/GanttDiagram.java b/src/net/sourceforge/plantuml/project/GanttDiagram.java index f6fd4f1fe..2e264b8e0 100644 --- a/src/net/sourceforge/plantuml/project/GanttDiagram.java +++ b/src/net/sourceforge/plantuml/project/GanttDiagram.java @@ -130,15 +130,14 @@ public class GanttDiagram extends TitledDiagram implements ToTaskDraw, WithSprit private Day printStart; private Day printEnd; - private HColor linksColor = HColorUtils.RED_DARK; + private HColor linksColor = null; public DiagramDescription getDescription() { return new DiagramDescription("(Project)"); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.GANTT; + public GanttDiagram() { + super(UmlDiagramType.GANTT); } private int horizontalPages = 1; @@ -261,12 +260,14 @@ public class GanttDiagram extends TitledDiagram implements ToTaskDraw, WithSprit if (openClose.getCalendar() == null) { return new TimeHeaderSimple(min, max); } else if (printScale == PrintScale.WEEKLY) { - return new TimeHeaderWeekly(openClose.getCalendar(), min, max, openClose, colorDays, colorDaysOfWeek, nameDays); + return new TimeHeaderWeekly(openClose.getCalendar(), min, max, openClose, colorDays, colorDaysOfWeek, + nameDays); } else if (printScale == PrintScale.MONTHLY) { - return new TimeHeaderMonthly(openClose.getCalendar(), min, max, openClose, colorDays, colorDaysOfWeek, nameDays); + return new TimeHeaderMonthly(openClose.getCalendar(), min, max, openClose, colorDays, colorDaysOfWeek, + nameDays); } else { - return new TimeHeaderDaily(openClose.getCalendar(), min, max, openClose, colorDays, colorDaysOfWeek, nameDays, - printStart, printEnd); + return new TimeHeaderDaily(openClose.getCalendar(), min, max, openClose, colorDays, colorDaysOfWeek, + nameDays, printStart, printEnd); } } @@ -293,19 +294,17 @@ public class GanttDiagram extends TitledDiagram implements ToTaskDraw, WithSprit if (printStart != null && constraint.isHidden(min, max)) { continue; } - - // If the linksColor is the default color, we should try to get the arrow color (default is RED_DARK) - if (linksColor == HColorUtils.RED_DARK) { - constraint.getUDrawable(timeScale, getLinkColor(), this).drawU(ug); - } else { - constraint.getUDrawable(timeScale, linksColor, this).drawU(ug); - } + constraint.getUDrawable(timeScale, getLinkColor(), this).drawU(ug); } + } private HColor getLinkColor() { - final Style styleArrow = getDefaultStyleDefinitionArrow().getMergedStyle(getCurrentStyleBuilder()); - return styleArrow.value(PName.LineColor).asColor(colorSet); + if (linksColor == null) { + final Style styleArrow = getDefaultStyleDefinitionArrow().getMergedStyle(getCurrentStyleBuilder()); + return styleArrow.value(PName.LineColor).asColor(colorSet); + } + return linksColor; } public StyleSignature getDefaultStyleDefinitionArrow() { @@ -596,7 +595,7 @@ public class GanttDiagram extends TitledDiagram implements ToTaskDraw, WithSprit return openClose.getLoadAt(day) > 0; } - public void affectResource(Task result, String description) { + public boolean affectResource(Task result, String description) { final Pattern p = Pattern.compile("([^:]+)(:(\\d+))?"); final Matcher m = p.matcher(description); if (m.find() == false) { @@ -607,7 +606,11 @@ public class GanttDiagram extends TitledDiagram implements ToTaskDraw, WithSprit if (m.group(3) != null) { percentage = Integer.parseInt(m.group(3)); } + if (percentage == 0) { + return false; + } result.addResource(resource, percentage); + return true; } public Resource getResource(String resourceName) { @@ -677,7 +680,7 @@ public class GanttDiagram extends TitledDiagram implements ToTaskDraw, WithSprit public void colorDay(DayOfWeek day, HColor color) { colorDaysOfWeek.put(day, color); } - + public void nameDay(Day day, String name) { nameDays.put(day, name); } @@ -734,4 +737,4 @@ public class GanttDiagram extends TitledDiagram implements ToTaskDraw, WithSprit } -} +} \ No newline at end of file diff --git a/src/net/sourceforge/plantuml/project/draw/TimeHeaderDaily.java b/src/net/sourceforge/plantuml/project/draw/TimeHeaderDaily.java index d679e65f8..dc24abe9d 100644 --- a/src/net/sourceforge/plantuml/project/draw/TimeHeaderDaily.java +++ b/src/net/sourceforge/plantuml/project/draw/TimeHeaderDaily.java @@ -103,7 +103,7 @@ public class TimeHeaderDaily extends TimeHeader { final double x2 = getTimeScale().getEndingPosition(wink); HColor back = colorDays.get(wink); // Day of week should be stronger than period of time (back color). - HColor backDoW = colorDaysOfWeek.get(wink.getDayOfWeek()); + final HColor backDoW = colorDaysOfWeek.get(wink.getDayOfWeek()); if (backDoW != null) { back = backDoW; } diff --git a/src/net/sourceforge/plantuml/project/draw/TimeHeaderWeekly.java b/src/net/sourceforge/plantuml/project/draw/TimeHeaderWeekly.java index 22f3723be..fca6daeb3 100644 --- a/src/net/sourceforge/plantuml/project/draw/TimeHeaderWeekly.java +++ b/src/net/sourceforge/plantuml/project/draw/TimeHeaderWeekly.java @@ -102,7 +102,7 @@ public class TimeHeaderWeekly extends TimeHeader { for (Day wink = min; wink.compareTo(max) <= 0; wink = wink.increment()) { HColor back = colorDays.get(wink); // Day of week should be stronger than period of time (back color). - HColor backDoW = colorDaysOfWeek.get(wink.getDayOfWeek()); + final HColor backDoW = colorDaysOfWeek.get(wink.getDayOfWeek()); if (backDoW != null) { back = backDoW; } diff --git a/src/net/sourceforge/plantuml/project/lang/SubjectTask.java b/src/net/sourceforge/plantuml/project/lang/SubjectTask.java index 148f83b2b..aa1da4393 100644 --- a/src/net/sourceforge/plantuml/project/lang/SubjectTask.java +++ b/src/net/sourceforge/plantuml/project/lang/SubjectTask.java @@ -63,7 +63,10 @@ public class SubjectTask implements Subject { for (final StringTokenizer st = new StringTokenizer(resource, "{}"); st.hasMoreTokens();) { final String part = st.nextToken().trim(); if (part.length() > 0) { - project.affectResource(result, part); + final boolean ok = project.affectResource(result, part); + if (ok == false) { + return Failable.error("Bad argument for resource"); + } } } diff --git a/src/net/sourceforge/plantuml/project/solver/SolverImpl.java b/src/net/sourceforge/plantuml/project/solver/SolverImpl.java index b25021aa2..83da16e1e 100644 --- a/src/net/sourceforge/plantuml/project/solver/SolverImpl.java +++ b/src/net/sourceforge/plantuml/project/solver/SolverImpl.java @@ -52,9 +52,14 @@ public class SolverImpl extends AbstractSolver implements Solver { protected Day computeEnd() { Day current = (Day) values.get(TaskAttribute.START); int fullLoad = ((Load) values.get(TaskAttribute.LOAD)).getFullLoad(); + int cpt = 0; while (fullLoad > 0) { fullLoad -= loadPlanable.getLoadAt(current); current = current.increment(); + cpt++; + if (cpt > 100000) { + throw new IllegalStateException(); + } } return current.decrement(); } @@ -63,12 +68,17 @@ public class SolverImpl extends AbstractSolver implements Solver { protected Day computeStart() { Day current = (Day) values.get(TaskAttribute.END); int fullLoad = ((Load) values.get(TaskAttribute.LOAD)).getFullLoad(); + int cpt = 0; while (fullLoad > 0) { fullLoad -= loadPlanable.getLoadAt(current); current = current.decrement(); if (current.getMillis() <= 0) { return current; } + cpt++; + if (cpt > 100000) { + throw new IllegalStateException(); + } } return current.increment(); } diff --git a/src/net/sourceforge/plantuml/salt/PSystemSalt.java b/src/net/sourceforge/plantuml/salt/PSystemSalt.java index 5ae8c52fb..5721dffd1 100644 --- a/src/net/sourceforge/plantuml/salt/PSystemSalt.java +++ b/src/net/sourceforge/plantuml/salt/PSystemSalt.java @@ -104,6 +104,7 @@ public class PSystemSalt extends TitledDiagram implements WithSprite { @Deprecated public PSystemSalt(List data) { + super(UmlDiagramType.SALT); this.data = data; } @@ -280,11 +281,6 @@ public class PSystemSalt extends TitledDiagram implements WithSprite { this.iamSalt = true; } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.SALT; - } - public final boolean isIamSalt() { return iamSalt; } diff --git a/src/net/sourceforge/plantuml/sequencediagram/SequenceDiagram.java b/src/net/sourceforge/plantuml/sequencediagram/SequenceDiagram.java index 8e6543524..d431cc1e0 100644 --- a/src/net/sourceforge/plantuml/sequencediagram/SequenceDiagram.java +++ b/src/net/sourceforge/plantuml/sequencediagram/SequenceDiagram.java @@ -81,7 +81,7 @@ public class SequenceDiagram extends UmlDiagram { private final Rose skin2 = new Rose(); public SequenceDiagram(ISkinSimple skinParam) { - super(skinParam); + super(UmlDiagramType.SEQUENCE, skinParam); } @Deprecated @@ -388,11 +388,6 @@ public class SequenceDiagram extends UmlDiagram { } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.SEQUENCE; - } - private ParticipantEnglober participantEnglober; public void boxStart(Display comment, HColor color, Stereotype stereotype) { diff --git a/src/net/sourceforge/plantuml/sequencediagram/command/CommandArrow.java b/src/net/sourceforge/plantuml/sequencediagram/command/CommandArrow.java index e3d98577d..142d6d55b 100644 --- a/src/net/sourceforge/plantuml/sequencediagram/command/CommandArrow.java +++ b/src/net/sourceforge/plantuml/sequencediagram/command/CommandArrow.java @@ -112,7 +112,9 @@ public class CommandArrow extends SingleLineCommand2 { new RegexLeaf("LIFECOLOR", "(?:(#\\w+)?)"), // RegexLeaf.spaceZeroOrMore(), // new RegexLeaf("URL", "(" + UrlBuilder.getRegexp() + ")?"), // - RegexLeaf.spaceZeroOrMore(), new RegexLeaf("MESSAGE", "(?::[%s]*(.*))?"), RegexLeaf.end()); + RegexLeaf.spaceZeroOrMore(), // + new RegexLeaf("MESSAGE", "(?::[%s]*(.*))?"), // + RegexLeaf.end()); } private Participant getOrCreateParticipant(SequenceDiagram system, RegexResult arg2, String n) { diff --git a/src/net/sourceforge/plantuml/skin/ArrowDirection.java b/src/net/sourceforge/plantuml/skin/ArrowDirection.java index 00701a50e..5a00d80c2 100644 --- a/src/net/sourceforge/plantuml/skin/ArrowDirection.java +++ b/src/net/sourceforge/plantuml/skin/ArrowDirection.java @@ -35,7 +35,6 @@ */ package net.sourceforge.plantuml.skin; - public enum ArrowDirection { LEFT_TO_RIGHT_NORMAL, RIGHT_TO_LEFT_REVERSE, SELF, BOTH_DIRECTION; diff --git a/src/net/sourceforge/plantuml/sprite/ListSpriteDiagram.java b/src/net/sourceforge/plantuml/sprite/ListSpriteDiagram.java index 011dcbcb2..a957956e8 100644 --- a/src/net/sourceforge/plantuml/sprite/ListSpriteDiagram.java +++ b/src/net/sourceforge/plantuml/sprite/ListSpriteDiagram.java @@ -68,18 +68,13 @@ import net.sourceforge.plantuml.ugraphic.color.HColorUtils; public class ListSpriteDiagram extends UmlDiagram { public ListSpriteDiagram(ISkinSimple skinParam) { - super(skinParam); + super(UmlDiagramType.HELP, skinParam); } public DiagramDescription getDescription() { return new DiagramDescription("(Sprites)"); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.HELP; - } - @Override protected ImageData exportDiagramInternal(OutputStream os, int index, FileFormatOption fileFormatOption) throws IOException { diff --git a/src/net/sourceforge/plantuml/sprite/StdlibDiagram.java b/src/net/sourceforge/plantuml/sprite/StdlibDiagram.java index 277b703f5..5e0a3e5db 100644 --- a/src/net/sourceforge/plantuml/sprite/StdlibDiagram.java +++ b/src/net/sourceforge/plantuml/sprite/StdlibDiagram.java @@ -77,18 +77,13 @@ public class StdlibDiagram extends UmlDiagram { private String name; public StdlibDiagram(ISkinSimple skinParam) { - super(skinParam); + super(UmlDiagramType.HELP, skinParam); } public DiagramDescription getDescription() { return new DiagramDescription("(Sprites)"); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.HELP; - } - @Override protected ImageData exportDiagramInternal(OutputStream os, int index, FileFormatOption fileFormatOption) throws IOException { diff --git a/src/net/sourceforge/plantuml/statediagram/StateDiagram.java b/src/net/sourceforge/plantuml/statediagram/StateDiagram.java index 6f57711cf..5a745310d 100644 --- a/src/net/sourceforge/plantuml/statediagram/StateDiagram.java +++ b/src/net/sourceforge/plantuml/statediagram/StateDiagram.java @@ -57,7 +57,7 @@ public class StateDiagram extends AbstractEntityDiagram { private static final String CONCURRENT_PREFIX = "CONC"; public StateDiagram(ISkinSimple skinParam) { - super(skinParam); + super(UmlDiagramType.STATE, skinParam); // setNamespaceSeparator(null); } @@ -260,11 +260,6 @@ public class StateDiagram extends AbstractEntityDiagram { super.endGroup(); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.STATE; - } - private boolean hideEmptyDescription = false; @Override diff --git a/src/net/sourceforge/plantuml/sudoku/GraphicsSudoku.java b/src/net/sourceforge/plantuml/sudoku/GraphicsSudoku.java index 30d7e0f0b..d3dfda28d 100644 --- a/src/net/sourceforge/plantuml/sudoku/GraphicsSudoku.java +++ b/src/net/sourceforge/plantuml/sudoku/GraphicsSudoku.java @@ -57,6 +57,7 @@ import net.sourceforge.plantuml.graphic.FontConfiguration; import net.sourceforge.plantuml.graphic.HorizontalAlignment; import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.png.PngIO; +import net.sourceforge.plantuml.svg.LengthAdjust; import net.sourceforge.plantuml.ugraphic.UFont; import net.sourceforge.plantuml.ugraphic.UGraphic; import net.sourceforge.plantuml.ugraphic.URectangle; @@ -88,7 +89,7 @@ public class GraphicsSudoku { public ImageData writeImageSvg(OutputStream os) throws IOException { final UGraphicSvg ug = new UGraphicSvg(true, new Dimension2DDouble(0, 0), new ColorMapperIdentity(), - (String) null, false, 1.0, null, null, 0, "none", SvgCharSizeHack.NO_HACK); + (String) null, false, 1.0, null, null, 0, "none", SvgCharSizeHack.NO_HACK, LengthAdjust.defaultValue()); drawInternal(ug); ug.createXml(os, null); return ImageDataSimple.ok(); diff --git a/src/net/sourceforge/plantuml/svek/GraphvizCrash.java b/src/net/sourceforge/plantuml/svek/GraphvizCrash.java index 2845d8713..87ce7f06a 100644 --- a/src/net/sourceforge/plantuml/svek/GraphvizCrash.java +++ b/src/net/sourceforge/plantuml/svek/GraphvizCrash.java @@ -110,8 +110,9 @@ public class GraphvizCrash extends AbstractTextBlock implements IEntityImage { public static void checkOldVersionWarning(List strings) { final long days = (System.currentTimeMillis() - Version.compileTime()) / 1000L / 3600 / 24; if (days >= 90) { - strings.add("This version of PlantUML is " + days + " days old, so you should"); - strings.add(" consider upgrading from https://plantuml.com/download"); + strings.add(" "); + strings.add("This version of PlantUML is " + days + " days old, so you should"); + strings.add("consider upgrading from https://plantuml.com/download"); } } diff --git a/src/net/sourceforge/plantuml/svg/LengthAdjust.java b/src/net/sourceforge/plantuml/svg/LengthAdjust.java new file mode 100644 index 000000000..0c8fb1d0e --- /dev/null +++ b/src/net/sourceforge/plantuml/svg/LengthAdjust.java @@ -0,0 +1,44 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://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.svg; + +public enum LengthAdjust { + NONE, SPACING, SPACING_AND_GLYPHS; + + public static LengthAdjust defaultValue() { + return SPACING; + } +} diff --git a/src/net/sourceforge/plantuml/svg/SvgGraphics.java b/src/net/sourceforge/plantuml/svg/SvgGraphics.java index 007884099..d34f9c65c 100644 --- a/src/net/sourceforge/plantuml/svg/SvgGraphics.java +++ b/src/net/sourceforge/plantuml/svg/SvgGraphics.java @@ -118,6 +118,7 @@ public class SvgGraphics { private final String shadowId; private final String gradientId; private final boolean svgDimensionStyle; + private final LengthAdjust lengthAdjust; final protected void ensureVisible(double x, double y) { if (x > maxX) { @@ -129,13 +130,14 @@ public class SvgGraphics { } public SvgGraphics(boolean svgDimensionStyle, Dimension2D minDim, double scale, String hover, long seed, - String preserveAspectRatio) { - this(svgDimensionStyle, minDim, null, scale, hover, seed, preserveAspectRatio); + String preserveAspectRatio, LengthAdjust lengthAdjust) { + this(svgDimensionStyle, minDim, null, scale, hover, seed, preserveAspectRatio, lengthAdjust); } public SvgGraphics(boolean svgDimensionStyle, Dimension2D minDim, String backcolor, double scale, String hover, - long seed, String preserveAspectRatio) { + long seed, String preserveAspectRatio, LengthAdjust lengthAdjust) { try { + this.lengthAdjust = lengthAdjust; this.svgDimensionStyle = svgDimensionStyle; this.scale = scale; this.document = getDocument(); @@ -436,8 +438,15 @@ public class SvgGraphics { fillMe(elt); elt.setAttribute("font-size", format(fontSize)); // elt.setAttribute("text-anchor", "middle"); - elt.setAttribute("lengthAdjust", "spacingAndGlyphs"); - elt.setAttribute("textLength", format(textLength)); + + if (lengthAdjust == LengthAdjust.SPACING) { + elt.setAttribute("lengthAdjust", "spacing"); + elt.setAttribute("textLength", format(textLength)); + } else if (lengthAdjust == LengthAdjust.SPACING_AND_GLYPHS) { + elt.setAttribute("lengthAdjust", "spacingAndGlyphs"); + elt.setAttribute("textLength", format(textLength)); + } + if (fontWeight != null) { elt.setAttribute("font-weight", fontWeight); } diff --git a/src/net/sourceforge/plantuml/tim/Eater.java b/src/net/sourceforge/plantuml/tim/Eater.java index bb6fa985d..4268d917b 100644 --- a/src/net/sourceforge/plantuml/tim/Eater.java +++ b/src/net/sourceforge/plantuml/tim/Eater.java @@ -218,6 +218,12 @@ public abstract class Eater { return s.charAt(i); } + final public boolean matchAffectation() { + final String tmp = s.substring(i); + final boolean result = tmp.matches("^\\$?[_\\p{L}][_\\p{L}0-9]*\\s*=.*"); + return result; + } + final public char peekCharN2() { if (i + 1 >= s.length()) { return 0; diff --git a/src/net/sourceforge/plantuml/tim/EaterFunctionCall.java b/src/net/sourceforge/plantuml/tim/EaterFunctionCall.java index c1c844a2c..0777d642d 100644 --- a/src/net/sourceforge/plantuml/tim/EaterFunctionCall.java +++ b/src/net/sourceforge/plantuml/tim/EaterFunctionCall.java @@ -74,20 +74,34 @@ public class EaterFunctionCall extends Eater { final TValue result = TValue.fromString(value); values.add(result); } else if (unquoted) { - final String read = eatAndGetOptionalQuotedString(); - if (TokenStack.isSpecialAffectationWhenFunctionCall(read)) { - updateNamedArguments(read, context, memory); + if (matchAffectation()) { + final String varname = eatAndGetVarname(); + skipSpaces(); + checkAndEatChar('='); + skipSpaces(); + final String read = eatAndGetOptionalQuotedString(); + final String value = context.applyFunctionsAndVariables(memory, getLineLocation(), read); + final TValue result = TValue.fromString(value); + namedArguments.put(varname, result); } else { + final String read = eatAndGetOptionalQuotedString(); final String value = context.applyFunctionsAndVariables(memory, getLineLocation(), read); final TValue result = TValue.fromString(value); values.add(result); } +// } } else { - final TokenStack tokens = TokenStack.eatUntilCloseParenthesisOrComma(this).withoutSpace(); - if (tokens.isSpecialAffectationWhenFunctionCall()) { - final String special = tokens.tokenIterator().nextToken().getSurface(); - updateNamedArguments(special, context, memory); + if (matchAffectation()) { + final String varname = eatAndGetVarname(); + skipSpaces(); + checkAndEatChar('='); + skipSpaces(); + final TokenStack tokens = TokenStack.eatUntilCloseParenthesisOrComma(this).withoutSpace(); + tokens.guessFunctions(); + final TValue result = tokens.getResult(getLineLocation(), context, memory); + namedArguments.put(varname, result); } else { + final TokenStack tokens = TokenStack.eatUntilCloseParenthesisOrComma(this).withoutSpace(); tokens.guessFunctions(); final TValue result = tokens.getResult(getLineLocation(), context, memory); values.add(result); @@ -108,16 +122,6 @@ public class EaterFunctionCall extends Eater { } } - private void updateNamedArguments(String special, TContext context, TMemory memory) - throws EaterException, EaterExceptionLocated { - assert special.contains("="); - final StringEater stringEater = new StringEater(special); - final String varname = stringEater.eatAndGetVarname(); - stringEater.checkAndEatChar('='); - final TValue expr = stringEater.eatExpression(context, memory); - namedArguments.put(varname, expr); - } - public final List getValues() { return Collections.unmodifiableList(values); } diff --git a/src/net/sourceforge/plantuml/tim/expression/TokenStack.java b/src/net/sourceforge/plantuml/tim/expression/TokenStack.java index 071a71014..421487cc5 100644 --- a/src/net/sourceforge/plantuml/tim/expression/TokenStack.java +++ b/src/net/sourceforge/plantuml/tim/expression/TokenStack.java @@ -47,39 +47,38 @@ import net.sourceforge.plantuml.tim.Eater; import net.sourceforge.plantuml.tim.EaterException; import net.sourceforge.plantuml.tim.EaterExceptionLocated; import net.sourceforge.plantuml.tim.TContext; -import net.sourceforge.plantuml.tim.TLineType; import net.sourceforge.plantuml.tim.TMemory; public class TokenStack { final private List tokens; - public boolean isSpecialAffectationWhenFunctionCall() { - if (tokens.size() != 1) { - return false; - } - final Token single = tokens.get(0); - if (single.getTokenType() != TokenType.PLAIN_TEXT) { - return false; - } - return isSpecialAffectationWhenFunctionCall(single.getSurface()); - } - - public static boolean isSpecialAffectationWhenFunctionCall(String surface) { - final int idx = surface.indexOf('='); - if (idx <= 0) { - return false; - } - if (TLineType.isLetterOrUnderscoreOrDollar(surface.charAt(0)) == false) { - return false; - } - for (int i = 1; i < idx; i++) { - if (TLineType.isLetterOrUnderscoreOrDigit(surface.charAt(i)) == false) { - return false; - } - } - return true; - } +// public boolean isSpecialAffectationWhenFunctionCall() { +// if (tokens.size() != 1) { +// return false; +// } +// final Token single = tokens.get(0); +// if (single.getTokenType() != TokenType.PLAIN_TEXT) { +// return false; +// } +// return isSpecialAffectationWhenFunctionCall(single.getSurface()); +// } +// +// public static boolean isSpecialAffectationWhenFunctionCall(String surface) { +// final int idx = surface.indexOf('='); +// if (idx <= 0) { +// return false; +// } +// if (TLineType.isLetterOrUnderscoreOrDollar(surface.charAt(0)) == false) { +// return false; +// } +// for (int i = 1; i < idx; i++) { +// if (TLineType.isLetterOrUnderscoreOrDigit(surface.charAt(i)) == false) { +// return false; +// } +// } +// return true; +// } public TokenStack() { this(new ArrayList()); diff --git a/src/net/sourceforge/plantuml/timingdiagram/TimingDiagram.java b/src/net/sourceforge/plantuml/timingdiagram/TimingDiagram.java index 6ca095bd0..2d3eb4510 100644 --- a/src/net/sourceforge/plantuml/timingdiagram/TimingDiagram.java +++ b/src/net/sourceforge/plantuml/timingdiagram/TimingDiagram.java @@ -95,9 +95,8 @@ public class TimingDiagram extends UmlDiagram implements Clocks { return new DiagramDescription("(Timing Diagram)"); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.TIMING; + public TimingDiagram() { + super(UmlDiagramType.TIMING); } @Override diff --git a/src/net/sourceforge/plantuml/ugraphic/FontChecker.java b/src/net/sourceforge/plantuml/ugraphic/FontChecker.java index 4e29cc886..c9df3eab4 100644 --- a/src/net/sourceforge/plantuml/ugraphic/FontChecker.java +++ b/src/net/sourceforge/plantuml/ugraphic/FontChecker.java @@ -60,6 +60,7 @@ import net.sourceforge.plantuml.graphic.UDrawable; import net.sourceforge.plantuml.security.ImageIO; import net.sourceforge.plantuml.security.SFile; import net.sourceforge.plantuml.style.ClockwiseTopRightBottomLeft; +import net.sourceforge.plantuml.svg.LengthAdjust; import net.sourceforge.plantuml.svg.SvgGraphics; import net.sourceforge.plantuml.ugraphic.color.ColorMapperIdentity; import net.sourceforge.plantuml.ugraphic.color.HColorUtils; @@ -159,7 +160,8 @@ public class FontChecker { } private String getSvgImage(char c) throws IOException, TransformerException { - final SvgGraphics svg = new SvgGraphics(true, new Dimension2DDouble(0, 0), 1.0, null, 42, "none"); + final SvgGraphics svg = new SvgGraphics(true, new Dimension2DDouble(0, 0), 1.0, null, 42, "none", + LengthAdjust.defaultValue()); svg.setStrokeColor("black"); svg.svgImage(getBufferedImage(c), 0, 0); final ByteArrayOutputStream os = new ByteArrayOutputStream(); diff --git a/src/net/sourceforge/plantuml/ugraphic/ImageBuilder.java b/src/net/sourceforge/plantuml/ugraphic/ImageBuilder.java index e7fc2acf4..26587344a 100644 --- a/src/net/sourceforge/plantuml/ugraphic/ImageBuilder.java +++ b/src/net/sourceforge/plantuml/ugraphic/ImageBuilder.java @@ -71,6 +71,7 @@ import net.sourceforge.plantuml.graphic.UDrawable; import net.sourceforge.plantuml.mjpeg.MJPEGGenerator; import net.sourceforge.plantuml.security.ImageIO; import net.sourceforge.plantuml.security.SFile; +import net.sourceforge.plantuml.svg.LengthAdjust; import net.sourceforge.plantuml.ugraphic.color.ColorMapper; import net.sourceforge.plantuml.ugraphic.color.HColor; import net.sourceforge.plantuml.ugraphic.color.HColorBackground; @@ -283,7 +284,8 @@ public class ImageBuilder { option.getWatermark()); case SVG: return createUGraphicSVG(colorMapper, param.getDpiFactor(), dim, param.getBackcolor(), - option.getSvgLinkTarget(), option.getHoverColor(), seed, option.getPreserveAspectRatio()); + option.getSvgLinkTarget(), option.getHoverColor(), seed, option.getPreserveAspectRatio(), + param.getlengthAdjust()); case EPS: return new UGraphicEps(colorMapper, EpsStrategy.getDefault2()); case EPS_TEXT: @@ -307,7 +309,7 @@ public class ImageBuilder { } private UGraphic2 createUGraphicSVG(ColorMapper colorMapper, double scale, Dimension2D dim, final HColor suggested, - String svgLinkTarget, String hover, long seed, String preserveAspectRatio) { + String svgLinkTarget, String hover, long seed, String preserveAspectRatio, LengthAdjust lengthAdjust) { HColor backColor = HColorUtils.WHITE; if (suggested instanceof HColorSimple) { backColor = suggested; @@ -316,14 +318,15 @@ public class ImageBuilder { final UGraphicSvg ug; if (suggested instanceof HColorGradient) { ug = new UGraphicSvg(dimensionStyle, dim, colorMapper, (HColorGradient) suggested, false, scale, - svgLinkTarget, hover, seed, preserveAspectRatio, param.getSvgCharSizeHack()); + svgLinkTarget, hover, seed, preserveAspectRatio, param.getSvgCharSizeHack(), + param.getlengthAdjust()); } else if (backColor == null || colorMapper.toColor(backColor).equals(Color.WHITE)) { ug = new UGraphicSvg(dimensionStyle, dim, colorMapper, false, scale, svgLinkTarget, hover, seed, - preserveAspectRatio, param.getSvgCharSizeHack()); + preserveAspectRatio, param.getSvgCharSizeHack(), param.getlengthAdjust()); } else { final String tmp = colorMapper.toSvg(backColor); ug = new UGraphicSvg(dimensionStyle, dim, colorMapper, tmp, false, scale, svgLinkTarget, hover, seed, - preserveAspectRatio, param.getSvgCharSizeHack()); + preserveAspectRatio, param.getSvgCharSizeHack(), param.getlengthAdjust()); } return ug; diff --git a/src/net/sourceforge/plantuml/ugraphic/ImageParameter.java b/src/net/sourceforge/plantuml/ugraphic/ImageParameter.java index f9777fedd..9260ed8e1 100644 --- a/src/net/sourceforge/plantuml/ugraphic/ImageParameter.java +++ b/src/net/sourceforge/plantuml/ugraphic/ImageParameter.java @@ -43,6 +43,7 @@ import net.sourceforge.plantuml.SvgCharSizeHack; import net.sourceforge.plantuml.anim.Animation; import net.sourceforge.plantuml.skin.rose.Rose; import net.sourceforge.plantuml.style.ClockwiseTopRightBottomLeft; +import net.sourceforge.plantuml.svg.LengthAdjust; import net.sourceforge.plantuml.ugraphic.color.ColorMapper; import net.sourceforge.plantuml.ugraphic.color.HColor; @@ -58,6 +59,7 @@ public class ImageParameter { private final HColor backcolor; private final boolean svgDimensionStyle; private final SvgCharSizeHack svgCharSizeHack; + private final LengthAdjust lengthAdjust; private final UStroke borderStroke; private final HColor borderColor; @@ -79,6 +81,7 @@ public class ImageParameter { this.borderCorner = 0; this.borderStroke = null; this.svgCharSizeHack = SvgCharSizeHack.NO_HACK; + this.lengthAdjust = LengthAdjust.defaultValue(); } public ImageParameter(ISkinParam skinParam, Animation animation, double dpiFactor, String metadata, @@ -104,6 +107,7 @@ public class ImageParameter { } this.svgCharSizeHack = skinParam; + this.lengthAdjust = skinParam.getlengthAdjust(); } @@ -159,4 +163,8 @@ public class ImageParameter { return svgCharSizeHack; } + public final LengthAdjust getlengthAdjust() { + return lengthAdjust; + } + } diff --git a/src/net/sourceforge/plantuml/ugraphic/UGraphicUtils.java b/src/net/sourceforge/plantuml/ugraphic/UGraphicUtils.java index 6e6288ad4..52564fa00 100644 --- a/src/net/sourceforge/plantuml/ugraphic/UGraphicUtils.java +++ b/src/net/sourceforge/plantuml/ugraphic/UGraphicUtils.java @@ -48,6 +48,7 @@ import net.sourceforge.plantuml.SvgCharSizeHack; import net.sourceforge.plantuml.eps.EpsStrategy; import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.png.PngIO; +import net.sourceforge.plantuml.svg.LengthAdjust; import net.sourceforge.plantuml.ugraphic.color.ColorMapper; import net.sourceforge.plantuml.ugraphic.color.HColor; import net.sourceforge.plantuml.ugraphic.eps.UGraphicEps; @@ -66,7 +67,8 @@ public abstract class UGraphicUtils { final Dimension2D size = computeSize(colorMapper, background, image); final UGraphicSvg svg = new UGraphicSvg(true, size, colorMapper, colorMapper.toRGB(background), false, 1.0, fileFormatOption.getSvgLinkTarget(), fileFormatOption.getHoverColor(), seed, - fileFormatOption.getPreserveAspectRatio(), SvgCharSizeHack.NO_HACK); + fileFormatOption.getPreserveAspectRatio(), SvgCharSizeHack.NO_HACK, + LengthAdjust.defaultValue()); image.drawU(svg); svg.createXml(os, fileFormatOption.isWithMetadata() ? metadata : null); } else if (fileFormat == FileFormat.EPS) { diff --git a/src/net/sourceforge/plantuml/ugraphic/UText.java b/src/net/sourceforge/plantuml/ugraphic/UText.java index 214c74056..29decbf13 100644 --- a/src/net/sourceforge/plantuml/ugraphic/UText.java +++ b/src/net/sourceforge/plantuml/ugraphic/UText.java @@ -44,16 +44,26 @@ public class UText implements UShape { private final String text; private final FontConfiguration font; + private final int orientation; @Override public String toString() { return "UText[" + text + "]"; } - public UText(String text, FontConfiguration font) { + private UText(String text, FontConfiguration font, int orientation) { assert text.indexOf('\t') == -1; this.text = text; this.font = font; + this.orientation = orientation; + } + + public UText(String text, FontConfiguration font) { + this(text, font, 0); + } + + public UText withOrientation(int orientation) { + return new UText(text, font, orientation); } public String getText() { @@ -70,4 +80,8 @@ public class UText implements UShape { return descent; } + public final int getOrientation() { + return orientation; + } + } diff --git a/src/net/sourceforge/plantuml/ugraphic/g2d/DriverTextG2d.java b/src/net/sourceforge/plantuml/ugraphic/g2d/DriverTextG2d.java index 075ea0a00..f84a061d0 100644 --- a/src/net/sourceforge/plantuml/ugraphic/g2d/DriverTextG2d.java +++ b/src/net/sourceforge/plantuml/ugraphic/g2d/DriverTextG2d.java @@ -40,19 +40,20 @@ import java.awt.Color; import java.awt.FontMetrics; import java.awt.GradientPaint; import java.awt.Graphics2D; -import java.awt.GraphicsEnvironment; import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; import java.awt.geom.Dimension2D; import java.awt.geom.Rectangle2D; +import java.util.List; import net.sourceforge.plantuml.Dimension2DDouble; import net.sourceforge.plantuml.EnsureVisible; import net.sourceforge.plantuml.FileFormat; -import net.sourceforge.plantuml.Log; import net.sourceforge.plantuml.TikzFontDistortion; import net.sourceforge.plantuml.graphic.FontConfiguration; import net.sourceforge.plantuml.graphic.FontStyle; import net.sourceforge.plantuml.graphic.StringBounder; +import net.sourceforge.plantuml.graphic.StyledString; import net.sourceforge.plantuml.ugraphic.UDriver; import net.sourceforge.plantuml.ugraphic.UFont; import net.sourceforge.plantuml.ugraphic.UParam; @@ -70,83 +71,108 @@ public class DriverTextG2d implements UDriver { this.visible = visible; } - private static void printFont() { - final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - final String fontNames[] = ge.getAvailableFontFamilyNames(); - final int j = fontNames.length; - for (int i = 0; i < j; i++) { - Log.info("Available fonts: " + fontNames[i]); - } - } - public void draw(UShape ushape, double x, double y, ColorMapper mapper, UParam param, Graphics2D g2d) { final UText shape = (UText) ushape; final FontConfiguration fontConfiguration = shape.getFontConfiguration(); + final String text = shape.getText(); + + final List strings = StyledString.build(text); final UFont font = fontConfiguration.getFont().scaled(param.getScale()); - final Dimension2D dimBack = calculateDimension( - FileFormat.PNG.getDefaultStringBounder(TikzFontDistortion.getDefault()), font, shape.getText()); + + for (StyledString styledString : strings) { + final FontConfiguration fc = styledString.getStyle() == FontStyle.BOLD ? fontConfiguration.bold() + : fontConfiguration; + final Dimension2D dim = calculateDimension( + FileFormat.PNG.getDefaultStringBounder(TikzFontDistortion.getDefault()), fc.getFont(), + styledString.getText()); + printSingleText(g2d, fc, styledString.getText(), x, y, mapper, param); + x += dim.getWidth(); + } + } + + private void printSingleText(Graphics2D g2d, final FontConfiguration fontConfiguration, final String text, double x, + double y, ColorMapper mapper, UParam param) { + final UFont font = fontConfiguration.getFont().scaled(param.getScale()); final HColor extended = fontConfiguration.getExtendedColor(); - if (fontConfiguration.containsStyle(FontStyle.BACKCOLOR)) { - final Rectangle2D.Double area = new Rectangle2D.Double(x, y - dimBack.getHeight() + 1.5, dimBack.getWidth(), - dimBack.getHeight()); - if (extended instanceof HColorGradient) { - final GradientPaint paint = DriverRectangleG2d.getPaintGradient(x, y, mapper, dimBack.getWidth(), - dimBack.getHeight(), extended); - g2d.setPaint(paint); - g2d.fill(area); - } else { - final Color backColor = mapper.toColor(extended); - if (backColor != null) { - g2d.setColor(backColor); - g2d.setBackground(backColor); + + final int orientation = 0; + + if (orientation == 90) { + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g2d.setFont(font.getFont()); + g2d.setColor(mapper.toColor(fontConfiguration.getColor())); + final AffineTransform orig = g2d.getTransform(); + g2d.translate(x, y); + g2d.rotate(Math.PI / 2); + g2d.drawString(text, 0, 0); + g2d.setTransform(orig); + + } else if (orientation == 0) { + + final Dimension2D dimBack = calculateDimension( + FileFormat.PNG.getDefaultStringBounder(TikzFontDistortion.getDefault()), font, text); + if (fontConfiguration.containsStyle(FontStyle.BACKCOLOR)) { + final Rectangle2D.Double area = new Rectangle2D.Double(x, y - dimBack.getHeight() + 1.5, + dimBack.getWidth(), dimBack.getHeight()); + if (extended instanceof HColorGradient) { + final GradientPaint paint = DriverRectangleG2d.getPaintGradient(x, y, mapper, dimBack.getWidth(), + dimBack.getHeight(), extended); + g2d.setPaint(paint); g2d.fill(area); + } else { + final Color backColor = mapper.toColor(extended); + if (backColor != null) { + g2d.setColor(backColor); + g2d.setBackground(backColor); + g2d.fill(area); + } } } - } - visible.ensureVisible(x, y - dimBack.getHeight() + 1.5); - visible.ensureVisible(x + dimBack.getWidth(), y + 1.5); + visible.ensureVisible(x, y - dimBack.getHeight() + 1.5); + visible.ensureVisible(x + dimBack.getWidth(), y + 1.5); - g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - g2d.setFont(font.getFont()); - g2d.setColor(mapper.toColor(fontConfiguration.getColor())); - g2d.drawString(shape.getText(), (float) x, (float) y); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g2d.setFont(font.getFont()); + g2d.setColor(mapper.toColor(fontConfiguration.getColor())); + g2d.drawString(text, (float) x, (float) y); - if (fontConfiguration.containsStyle(FontStyle.UNDERLINE)) { - if (extended != null) { - g2d.setColor(mapper.toColor(extended)); + if (fontConfiguration.containsStyle(FontStyle.UNDERLINE)) { + if (extended != null) { + g2d.setColor(mapper.toColor(extended)); + } + final Dimension2D dim = calculateDimension( + FileFormat.PNG.getDefaultStringBounder(TikzFontDistortion.getDefault()), font, text); + final int ypos = (int) (y + 2.5); + g2d.setStroke(new BasicStroke((float) 1)); + g2d.drawLine((int) x, ypos, (int) (x + dim.getWidth()), ypos); + g2d.setStroke(new BasicStroke()); } - final Dimension2D dim = calculateDimension( - FileFormat.PNG.getDefaultStringBounder(TikzFontDistortion.getDefault()), font, shape.getText()); - final int ypos = (int) (y + 2.5); - g2d.setStroke(new BasicStroke((float) 1)); - g2d.drawLine((int) x, ypos, (int) (x + dim.getWidth()), ypos); - g2d.setStroke(new BasicStroke()); - } - if (fontConfiguration.containsStyle(FontStyle.WAVE)) { - final Dimension2D dim = calculateDimension( - FileFormat.PNG.getDefaultStringBounder(TikzFontDistortion.getDefault()), font, shape.getText()); - final int ypos = (int) (y + 2.5) - 1; - if (extended != null) { - g2d.setColor(mapper.toColor(extended)); + if (fontConfiguration.containsStyle(FontStyle.WAVE)) { + final Dimension2D dim = calculateDimension( + FileFormat.PNG.getDefaultStringBounder(TikzFontDistortion.getDefault()), font, text); + final int ypos = (int) (y + 2.5) - 1; + if (extended != null) { + g2d.setColor(mapper.toColor(extended)); + } + for (int i = (int) x; i < x + dim.getWidth() - 5; i += 6) { + g2d.drawLine(i, ypos - 0, i + 3, ypos + 1); + g2d.drawLine(i + 3, ypos + 1, i + 6, ypos - 0); + } } - for (int i = (int) x; i < x + dim.getWidth() - 5; i += 6) { - g2d.drawLine(i, ypos - 0, i + 3, ypos + 1); - g2d.drawLine(i + 3, ypos + 1, i + 6, ypos - 0); + if (fontConfiguration.containsStyle(FontStyle.STRIKE)) { + final Dimension2D dim = calculateDimension( + FileFormat.PNG.getDefaultStringBounder(TikzFontDistortion.getDefault()), font, text); + final FontMetrics fm = g2d.getFontMetrics(font.getFont()); + final int ypos = (int) (y - fm.getDescent() - 0.5); + if (extended != null) { + g2d.setColor(mapper.toColor(extended)); + } + g2d.setStroke(new BasicStroke((float) 1.5)); + g2d.drawLine((int) x, ypos, (int) (x + dim.getWidth()), ypos); + g2d.setStroke(new BasicStroke()); } } - if (fontConfiguration.containsStyle(FontStyle.STRIKE)) { - final Dimension2D dim = calculateDimension( - FileFormat.PNG.getDefaultStringBounder(TikzFontDistortion.getDefault()), font, shape.getText()); - final FontMetrics fm = g2d.getFontMetrics(font.getFont()); - final int ypos = (int) (y - fm.getDescent() - 0.5); - if (extended != null) { - g2d.setColor(mapper.toColor(extended)); - } - g2d.setStroke(new BasicStroke((float) 1.5)); - g2d.drawLine((int) x, ypos, (int) (x + dim.getWidth()), ypos); - g2d.setStroke(new BasicStroke()); - } } static public Dimension2D calculateDimension(StringBounder stringBounder, UFont font, String text) { diff --git a/src/net/sourceforge/plantuml/ugraphic/svg/UGraphicSvg.java b/src/net/sourceforge/plantuml/ugraphic/svg/UGraphicSvg.java index 56450367b..851a57d77 100644 --- a/src/net/sourceforge/plantuml/ugraphic/svg/UGraphicSvg.java +++ b/src/net/sourceforge/plantuml/ugraphic/svg/UGraphicSvg.java @@ -47,6 +47,7 @@ import net.sourceforge.plantuml.Url; import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.TextBlockUtils; import net.sourceforge.plantuml.posimo.DotPath; +import net.sourceforge.plantuml.svg.LengthAdjust; import net.sourceforge.plantuml.svg.SvgGraphics; import net.sourceforge.plantuml.ugraphic.AbstractCommonUGraphic; import net.sourceforge.plantuml.ugraphic.AbstractUGraphic; @@ -90,23 +91,24 @@ public class UGraphicSvg extends AbstractUGraphic implements ClipCo public UGraphicSvg(boolean svgDimensionStyle, Dimension2D minDim, ColorMapper colorMapper, String backcolor, boolean textAsPath, double scale, String linkTarget, String hover, long seed, String preserveAspectRatio, - SvgCharSizeHack charSizeHack) { - this(minDim, colorMapper, - new SvgGraphics(svgDimensionStyle, minDim, backcolor, scale, hover, seed, preserveAspectRatio), - textAsPath, linkTarget, charSizeHack); + SvgCharSizeHack charSizeHack, LengthAdjust lengthAdjust) { + this(minDim, colorMapper, new SvgGraphics(svgDimensionStyle, minDim, backcolor, scale, hover, seed, + preserveAspectRatio, lengthAdjust), textAsPath, linkTarget, charSizeHack); } public UGraphicSvg(boolean svgDimensionStyle, Dimension2D minDim, ColorMapper colorMapper, boolean textAsPath, double scale, String linkTarget, String hover, long seed, String preserveAspectRatio, - SvgCharSizeHack charSizeHack) { - this(minDim, colorMapper, new SvgGraphics(svgDimensionStyle, minDim, scale, hover, seed, preserveAspectRatio), + SvgCharSizeHack charSizeHack, LengthAdjust lengthAdjust) { + this(minDim, colorMapper, + new SvgGraphics(svgDimensionStyle, minDim, scale, hover, seed, preserveAspectRatio, lengthAdjust), textAsPath, linkTarget, charSizeHack); } public UGraphicSvg(boolean svgDimensionStyle, Dimension2D minDim, ColorMapper mapper, HColorGradient gr, boolean textAsPath, double scale, String linkTarget, String hover, long seed, String preserveAspectRatio, - SvgCharSizeHack charSizeHack) { - this(minDim, mapper, new SvgGraphics(svgDimensionStyle, minDim, scale, hover, seed, preserveAspectRatio), + SvgCharSizeHack charSizeHack, LengthAdjust lengthAdjust) { + this(minDim, mapper, + new SvgGraphics(svgDimensionStyle, minDim, scale, hover, seed, preserveAspectRatio, lengthAdjust), textAsPath, linkTarget, charSizeHack); final SvgGraphics svg = getGraphicObject(); diff --git a/src/net/sourceforge/plantuml/version/Version.java b/src/net/sourceforge/plantuml/version/Version.java index 7f1582918..809d1a4c9 100644 --- a/src/net/sourceforge/plantuml/version/Version.java +++ b/src/net/sourceforge/plantuml/version/Version.java @@ -44,7 +44,7 @@ public class Version { private static final int MAJOR_SEPARATOR = 1000000; public static int version() { - return 1202026; + return 1202100; } public static int versionPatched() { @@ -93,7 +93,7 @@ public class Version { } public static long compileTime() { - return 1608572707669L; + return 1610274305536L; } public static String compileTimeString() { diff --git a/src/net/sourceforge/plantuml/wbs/WBSDiagram.java b/src/net/sourceforge/plantuml/wbs/WBSDiagram.java index a6094981c..589d6bd6c 100644 --- a/src/net/sourceforge/plantuml/wbs/WBSDiagram.java +++ b/src/net/sourceforge/plantuml/wbs/WBSDiagram.java @@ -75,9 +75,8 @@ public class WBSDiagram extends UmlDiagram { return new DiagramDescription("Work Breakdown Structure"); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.WBS; + public WBSDiagram() { + super(UmlDiagramType.WBS); } @Override diff --git a/src/net/sourceforge/plantuml/wire/CommandComponent.java b/src/net/sourceforge/plantuml/wire/CommandComponent.java index aa5db1581..65ca8d28d 100644 --- a/src/net/sourceforge/plantuml/wire/CommandComponent.java +++ b/src/net/sourceforge/plantuml/wire/CommandComponent.java @@ -43,6 +43,8 @@ import net.sourceforge.plantuml.command.regex.RegexConcat; import net.sourceforge.plantuml.command.regex.RegexLeaf; import net.sourceforge.plantuml.command.regex.RegexOptional; import net.sourceforge.plantuml.command.regex.RegexResult; +import net.sourceforge.plantuml.ugraphic.color.HColor; +import net.sourceforge.plantuml.ugraphic.color.HColorSet; public class CommandComponent extends SingleLineCommand2 { @@ -53,7 +55,9 @@ public class CommandComponent extends SingleLineCommand2 { static IRegex getRegexConcat() { return RegexConcat.build(CommandComponent.class.getName(), RegexLeaf.start(), // new RegexLeaf("INDENT", "([\\s\\t]*)"), // - new RegexLeaf("NAME", "\\$([\\w]+)"), // + new RegexLeaf("\\*"), // + RegexLeaf.spaceZeroOrMore(), // + new RegexLeaf("NAME", "([\\w]+)"), // new RegexOptional(new RegexConcat( // RegexLeaf.spaceOneOrMore(), // new RegexLeaf("\\["), // @@ -62,6 +66,9 @@ public class CommandComponent extends SingleLineCommand2 { new RegexLeaf("HEIGHT", "([\\d]+)"), // new RegexLeaf("\\]")) // ), // + new RegexOptional(new RegexConcat( // + RegexLeaf.spaceZeroOrMore(), // + new RegexLeaf("COLOR", "(#\\w+)?"))), // RegexLeaf.spaceZeroOrMore(), // RegexLeaf.end()); } @@ -80,7 +87,13 @@ public class CommandComponent extends SingleLineCommand2 { height = Integer.parseInt(heightString); } - return diagram.addComponent(indent, name, width, height); + final String stringColor = arg.get("COLOR", 0); + HColor color = null; + if (stringColor != null) { + color = HColorSet.instance().getColorIfValid(stringColor); + } + + return diagram.addComponent(indent, name, width, height, color); } } diff --git a/src/net/sourceforge/plantuml/wire/CommandPrint.java b/src/net/sourceforge/plantuml/wire/CommandPrint.java new file mode 100644 index 000000000..cb4a4480b --- /dev/null +++ b/src/net/sourceforge/plantuml/wire/CommandPrint.java @@ -0,0 +1,78 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://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.wire; + +import net.sourceforge.plantuml.LineLocation; +import net.sourceforge.plantuml.command.CommandExecutionResult; +import net.sourceforge.plantuml.command.SingleLineCommand2; +import net.sourceforge.plantuml.command.regex.IRegex; +import net.sourceforge.plantuml.command.regex.RegexConcat; +import net.sourceforge.plantuml.command.regex.RegexLeaf; +import net.sourceforge.plantuml.command.regex.RegexResult; + +public class CommandPrint extends SingleLineCommand2 { + + public CommandPrint() { + super(false, getRegexConcat()); + } + + static IRegex getRegexConcat() { + return RegexConcat.build(CommandPrint.class.getName(), RegexLeaf.start(), // + new RegexLeaf("INDENT", "([\\s\\t]*)"), // + new RegexLeaf("print"), // + RegexLeaf.spaceZeroOrMore(), // + new RegexLeaf("\\("), // + RegexLeaf.spaceZeroOrMore(), // + new RegexLeaf("[%g]"), // + RegexLeaf.spaceZeroOrMore(), // + new RegexLeaf("TEXT", "(.*)"), // + RegexLeaf.spaceZeroOrMore(), // + new RegexLeaf("[%g]"), // + RegexLeaf.spaceZeroOrMore(), // + new RegexLeaf("\\)"), // + RegexLeaf.spaceZeroOrMore(), // + RegexLeaf.end()); + } + + @Override + protected CommandExecutionResult executeArg(WireDiagram diagram, LineLocation location, RegexResult arg) { + final String indent = arg.get("INDENT", 0); + final String text = arg.get("TEXT", 0); + return diagram.print(indent, text); + + } + +} diff --git a/src/net/sourceforge/plantuml/wire/CommandSpot.java b/src/net/sourceforge/plantuml/wire/CommandSpot.java index c637054d4..446dec8d5 100644 --- a/src/net/sourceforge/plantuml/wire/CommandSpot.java +++ b/src/net/sourceforge/plantuml/wire/CommandSpot.java @@ -56,7 +56,7 @@ public class CommandSpot extends SingleLineCommand2 { return RegexConcat.build(CommandSpot.class.getName(), RegexLeaf.start(), // new RegexLeaf("spot"), // RegexLeaf.spaceOneOrMore(), // - new RegexLeaf("NAME", "\\$([\\w][.\\w]*)"), // + new RegexLeaf("NAME", "([\\w][.\\w]*)"), // new RegexOptional(new RegexConcat(// new RegexLeaf("\\("), // RegexLeaf.spaceZeroOrMore(), // diff --git a/src/net/sourceforge/plantuml/wire/CommandWLink.java b/src/net/sourceforge/plantuml/wire/CommandWLink.java index 49e914c8b..528e2211f 100644 --- a/src/net/sourceforge/plantuml/wire/CommandWLink.java +++ b/src/net/sourceforge/plantuml/wire/CommandWLink.java @@ -43,6 +43,7 @@ import net.sourceforge.plantuml.command.regex.RegexConcat; import net.sourceforge.plantuml.command.regex.RegexLeaf; import net.sourceforge.plantuml.command.regex.RegexOptional; import net.sourceforge.plantuml.command.regex.RegexResult; +import net.sourceforge.plantuml.cucadiagram.Display; import net.sourceforge.plantuml.ugraphic.color.HColor; import net.sourceforge.plantuml.ugraphic.color.HColorSet; @@ -54,7 +55,7 @@ public class CommandWLink extends SingleLineCommand2 { static IRegex getRegexConcat() { return RegexConcat.build(CommandWLink.class.getName(), RegexLeaf.start(), // - new RegexLeaf("NAME1", "\\$([\\w][.\\w]*)"), // + new RegexLeaf("NAME1", "([\\w][.\\w]*)"), // new RegexOptional(new RegexConcat(// new RegexLeaf("\\("), // RegexLeaf.spaceZeroOrMore(), // @@ -67,13 +68,15 @@ public class CommandWLink extends SingleLineCommand2 { new RegexLeaf("\\)") // )), // RegexLeaf.spaceZeroOrMore(), // - new RegexLeaf("STYLE", "([-=])\\>"), // + new RegexLeaf("STYLE", "(\\|\\ { final String name1 = arg.get("NAME1", 0); final String x1 = arg.get("X1", 0); final String y1 = arg.get("Y1", 0); - + final String name2 = arg.get("NAME2", 0); - final WLinkType type = WLinkType.from(arg.get("STYLE", 0)); + final String style = arg.get("STYLE", 0); + final WLinkType type = WLinkType.from(style); + final WArrowDirection direction = WArrowDirection.from(style); + final WOrientation orientation = WOrientation.from(style); final String stringColor = arg.get("COLOR", 0); HColor color = null; @@ -93,7 +99,18 @@ public class CommandWLink extends SingleLineCommand2 { color = HColorSet.instance().getColorIfValid(stringColor); } - return diagram.link(name1, x1, y1, name2, type, color); + final Display label; + if (arg.get("MESSAGE", 0) == null) { + label = Display.NULL; + } else { + final String message = arg.get("MESSAGE", 0); + label = Display.getWithNewlines(message); + } + + if (orientation == WOrientation.VERTICAL) { + return diagram.vlink(name1, x1, y1, name2, type, direction, color, label); + } + return diagram.hlink(name1, x1, y1, name2, type, direction, color, label); } } diff --git a/src/net/sourceforge/plantuml/wire/WArrowDirection.java b/src/net/sourceforge/plantuml/wire/WArrowDirection.java new file mode 100644 index 000000000..dc3d0990e --- /dev/null +++ b/src/net/sourceforge/plantuml/wire/WArrowDirection.java @@ -0,0 +1,54 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://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.wire; + +public enum WArrowDirection { + NORMAL, REVERSE, BOTH, NONE; + + public static WArrowDirection from(String type) { + if (type.contains("<") && type.contains(">")) { + return BOTH; + } + if (type.contains(">")) { + return NORMAL; + } + if (type.contains("<")) { + return REVERSE; + } + return NONE; + } + +} diff --git a/src/net/sourceforge/plantuml/wire/WBlock.java b/src/net/sourceforge/plantuml/wire/WBlock.java index 26d018c38..4aea0460a 100644 --- a/src/net/sourceforge/plantuml/wire/WBlock.java +++ b/src/net/sourceforge/plantuml/wire/WBlock.java @@ -41,30 +41,41 @@ import java.util.List; import java.util.StringTokenizer; import net.sourceforge.plantuml.Dimension2DDouble; +import net.sourceforge.plantuml.ISkinParam; import net.sourceforge.plantuml.SpriteContainerEmpty; import net.sourceforge.plantuml.command.CommandExecutionResult; import net.sourceforge.plantuml.cucadiagram.Display; import net.sourceforge.plantuml.graphic.FontConfiguration; import net.sourceforge.plantuml.graphic.HorizontalAlignment; +import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.ugraphic.UFont; import net.sourceforge.plantuml.ugraphic.UGraphic; import net.sourceforge.plantuml.ugraphic.URectangle; import net.sourceforge.plantuml.ugraphic.UTranslate; +import net.sourceforge.plantuml.ugraphic.color.HColor; import net.sourceforge.plantuml.ugraphic.color.HColorUtils; public class WBlock { + private final static int STARTING_Y = 10; + private final String name; private final double forcedWidth; private final double forcedHeight; + private final HColor color; private final List children = new ArrayList(); - private UTranslate position = new UTranslate(); + private final UTranslate position; private WBlock parent; - private UTranslate futureGoto; - private UTranslate futureMove = new UTranslate(0, 20); + private UTranslate cursor = new UTranslate(10, STARTING_Y); + private WBlock addedToCursor = null; + + private UTranslate futureOutHorizontal; + private UTranslate futureOutVertical; + + private final List prints = new ArrayList(); public UTranslate getAbsolutePosition(String supx, String supy) { if (parent == null) { @@ -113,10 +124,12 @@ public class WBlock { return name + " " + position; } - public WBlock(String name, double width, double height) { + public WBlock(String name, UTranslate position, double width, double height, HColor color) { this.name = name; this.forcedWidth = width; this.forcedHeight = height; + this.color = color; + this.position = position; } private WBlock getChildByName(String name) { @@ -147,8 +160,8 @@ public class WBlock { public CommandExecutionResult newColumn(int level) { if (level == 0) { final Dimension2D max = getNaturalDimension(); - futureGoto = new UTranslate(max.getWidth() + 10, 20); - futureMove = new UTranslate(); + this.cursor = new UTranslate(max.getWidth() + 10, STARTING_Y); + this.addedToCursor = null; return CommandExecutionResult.ok(); } return getLastChild().newColumn(level - 1); @@ -156,8 +169,8 @@ public class WBlock { public CommandExecutionResult wgoto(int level, double x, double y) { if (level == 0) { - futureGoto = new UTranslate(x, y); - futureMove = new UTranslate(); + this.cursor = new UTranslate(x, y); + this.addedToCursor = null; return CommandExecutionResult.ok(); } return getLastChild().wgoto(level - 1, x, y); @@ -165,13 +178,24 @@ public class WBlock { public CommandExecutionResult wmove(int level, double x, double y) { if (level == 0) { - futureMove = futureMove.compose(new UTranslate(x, y)); + this.cursor = this.cursor.compose(new UTranslate(x, y)); return CommandExecutionResult.ok(); } return getLastChild().wmove(level - 1, x, y); } - public CommandExecutionResult addComponent(int level, String name, double width, double height) { + public CommandExecutionResult print(StringBounder stringBounder, ISkinParam skinParam, int level, String text) { + if (level == 0) { + final WPrint print = new WPrint(skinParam, getNextPosition(), null, Display.getWithNewlines(text)); + this.prints.add(print); + this.cursor = this.cursor.compose(UTranslate.dy(print.getHeight(stringBounder))); + + return CommandExecutionResult.ok(); + } + return getLastChild().print(stringBounder, skinParam, level - 1, text); + } + + public CommandExecutionResult addBlock(int level, String name, double width, double height, HColor color) { if (name.contains(".")) { throw new IllegalArgumentException(); } @@ -179,8 +203,10 @@ public class WBlock { return CommandExecutionResult.error("Component exists already"); } if (level == 0) { - final WBlock newBlock = new WBlock(name, width, height); - newBlock.position = getNextPosition(); + this.cursor = this.cursor.compose(UTranslate.dy(10)); + final WBlock newBlock = new WBlock(name, getNextPosition(), width, height, color); + this.cursor = this.cursor.compose(UTranslate.dy(10)); + this.addedToCursor = newBlock; children.add(newBlock); newBlock.parent = this; @@ -188,25 +214,16 @@ public class WBlock { } final WBlock last = getLastChild(); - return last.addComponent(level - 1, name, width, height); + return last.addBlock(level - 1, name, width, height, color); } private UTranslate getNextPosition() { - final UTranslate result; - if (futureGoto != null) { - result = futureGoto.compose(futureMove); - } else { - final WBlock last = getLastChild(); - if (last == null) { - result = futureMove.compose(UTranslate.dx(10)); - } else { - final Dimension2D dim = last.getMaxDimension(); - result = last.position.compose(UTranslate.dy(dim.getHeight())).compose(futureMove); - } + if (this.addedToCursor != null) { + final Dimension2D dim = this.addedToCursor.getMaxDimension(); + this.cursor = this.cursor.compose(UTranslate.dy(dim.getHeight())); } - futureGoto = null; - futureMove = new UTranslate(0, 20); - return result; + this.addedToCursor = null; + return this.cursor; } private WBlock getLastChild() { @@ -230,11 +247,18 @@ public class WBlock { ug = ug.apply(HColorUtils.BLACK); if (name.length() > 0) { final URectangle rect = new URectangle(getMaxDimension()); - ug.draw(rect); + UGraphic ugRect = ug; + if (color != null) { + ugRect = ugRect.apply(color.bg()); + } + ugRect.draw(rect); } for (WBlock child : children) { child.drawMe(ug.apply(child.position)); } + for (WPrint print : prints) { + print.drawMe(ug.apply(print.getPosition())); + } } private Dimension2D getMaxDimension() { @@ -260,20 +284,29 @@ public class WBlock { return new Dimension2DDouble(x, y); } - private UTranslate futureOut; - - public UTranslate getNextOut(String x1, String y1, WLinkType type) { + public UTranslate getNextOutHorizontal(String x, String y, WLinkType type) { final UTranslate result; - if (x1 != null && y1 != null) { - result = getAbsolutePosition(x1, y1); - } else if (futureOut == null) { + if (x != null && y != null) { + result = getAbsolutePosition(x, y); + } else if (futureOutHorizontal == null) { result = getAbsolutePosition("100%", "5"); } else { - result = futureOut; + result = futureOutHorizontal; } + futureOutHorizontal = result.compose(UTranslate.dy(type.spaceForNext())); + return result; + } - futureOut = result.compose(UTranslate.dy(type.ySpaceForNext())); - + public UTranslate getNextOutVertical(String x, String y, WLinkType type) { + final UTranslate result; + if (x != null && y != null) { + result = getAbsolutePosition(x, y); + } else if (futureOutVertical == null) { + result = getAbsolutePosition("5", "100%"); + } else { + result = futureOutVertical; + } + futureOutVertical = result.compose(UTranslate.dx(type.spaceForNext())); return result; } diff --git a/src/net/sourceforge/plantuml/wire/WLinkHorizontal.java b/src/net/sourceforge/plantuml/wire/WLinkHorizontal.java new file mode 100644 index 000000000..77b5de15b --- /dev/null +++ b/src/net/sourceforge/plantuml/wire/WLinkHorizontal.java @@ -0,0 +1,197 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://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.wire; + +import java.awt.geom.Dimension2D; + +import net.sourceforge.plantuml.ISkinParam; +import net.sourceforge.plantuml.cucadiagram.Display; +import net.sourceforge.plantuml.graphic.FontConfiguration; +import net.sourceforge.plantuml.graphic.HorizontalAlignment; +import net.sourceforge.plantuml.graphic.TextBlock; +import net.sourceforge.plantuml.ugraphic.UFont; +import net.sourceforge.plantuml.ugraphic.UGraphic; +import net.sourceforge.plantuml.ugraphic.ULine; +import net.sourceforge.plantuml.ugraphic.UPath; +import net.sourceforge.plantuml.ugraphic.URectangle; +import net.sourceforge.plantuml.ugraphic.UTranslate; +import net.sourceforge.plantuml.ugraphic.color.HColor; +import net.sourceforge.plantuml.ugraphic.color.HColorUtils; + +public class WLinkHorizontal { + + private final UTranslate start; + private final double destination; + private final WLinkType type; + private final WArrowDirection direction; + private final HColor color; + private final Display label; + private final ISkinParam skinParam; + + public WLinkHorizontal(ISkinParam skinParam, UTranslate start, double destination, WLinkType type, + WArrowDirection direction, HColor color, Display label) { + this.start = start; + this.destination = destination; + this.skinParam = skinParam; + this.direction = direction; + this.type = type; + this.label = label; + this.color = color == null ? HColorUtils.BLACK : color; + } + + private TextBlock getTextBlock() { + final FontConfiguration fontConfiguration = FontConfiguration.blackBlueTrue(UFont.sansSerif(10)) + .changeColor(color); + return label.create(fontConfiguration, HorizontalAlignment.LEFT, skinParam); + } + + public void drawMe(UGraphic ug) { + ug = ug.apply(color); + final TextBlock textBlock = getTextBlock(); + final Dimension2D dimText = textBlock.calculateDimension(ug.getStringBounder()); + + UGraphic ugText = ug.apply(start); + final double len = destination - start.getDx(); + + if (type == WLinkType.NORMAL) { + ug = ug.apply(color.bg()); + drawNormalArrow(ug); + + ugText = ugText.apply(UTranslate.dy(-dimText.getHeight() / 2)); + + } else if (type == WLinkType.BUS) { + ug = ug.apply(HColorUtils.WHITE.bg()); + drawBusArrow(ug); + ugText = ugText.apply(UTranslate.dy((20 - dimText.getHeight()) / 2 - 5)); + } + + if (dimText.getHeight() > 0) { + switch (direction) { + case NORMAL: + ugText = ugText.apply(UTranslate.dx(4)); + break; + case REVERSE: + ugText = ugText.apply(UTranslate.dx(len - dimText.getWidth() - 4)); + break; + default: + ugText = ugText.apply(UTranslate.dx((len - dimText.getWidth()) / 2)); + break; + } + if (type == WLinkType.NORMAL) { + ugText.apply(HColorUtils.WHITE).apply(HColorUtils.WHITE.bg()).draw(new URectangle(dimText)); + } + textBlock.drawU(ugText); + } + + } + + private void drawBusArrow(UGraphic ug) { + final double dx = destination - start.getDx() - 2; + final UPath path = new UPath(); + if (direction == WArrowDirection.NONE) { + path.moveTo(0, 0); + path.lineTo(dx, 0); + path.lineTo(dx, 10); + path.lineTo(0, 10); + path.lineTo(0, 0); + path.closePath(); + ug.apply(start.compose(UTranslate.dx(1))).draw(path); + } + if (direction == WArrowDirection.NORMAL) { + path.moveTo(0, 0); + path.lineTo(dx - 15, 0); + path.lineTo(dx - 15, -5); + path.lineTo(dx, 5); + path.lineTo(dx - 15, 15); + path.lineTo(dx - 15, 10); + path.lineTo(0, 10); + path.lineTo(0, 0); + path.closePath(); + ug.apply(start.compose(UTranslate.dx(1))).draw(path); + } + if (direction == WArrowDirection.BOTH) { + path.moveTo(0, 5); + path.lineTo(15, -5); + path.lineTo(15, 0); + path.lineTo(dx - 15, 0); + path.lineTo(dx - 15, -5); + path.lineTo(dx, 5); + path.lineTo(dx - 15, 15); + path.lineTo(dx - 15, 10); + path.lineTo(15, 10); + path.lineTo(15, 15); + path.lineTo(0, 5); + path.closePath(); + ug.apply(start.compose(UTranslate.dx(1))).draw(path); + } + if (direction == WArrowDirection.REVERSE) { + path.moveTo(0, 5); + path.lineTo(15, -5); + path.lineTo(15, 0); + path.lineTo(dx, 0); + path.lineTo(dx, 10); + path.lineTo(15, 10); + path.lineTo(15, 15); + path.lineTo(0, 5); + path.closePath(); + ug.apply(start.compose(UTranslate.dx(1))).draw(path); + } + } + + private void drawNormalArrow(UGraphic ug) { + final double dx = destination - start.getDx() - 2; + if (direction == WArrowDirection.BOTH || direction == WArrowDirection.NORMAL) { + final UPath path = new UPath(); + path.moveTo(0, 0); + path.lineTo(-5, -5); + path.lineTo(-5, 5); + path.lineTo(0, 0); + path.closePath(); + ug.apply(start.compose(UTranslate.dx(dx))).draw(path); + } + if (direction == WArrowDirection.BOTH || direction == WArrowDirection.REVERSE) { + final UPath path = new UPath(); + path.moveTo(0, 0); + path.lineTo(5, -5); + path.lineTo(5, 5); + path.lineTo(0, 0); + path.closePath(); + ug.apply(start.compose(UTranslate.dx(1))).draw(path); + } + ug.apply(start.compose(UTranslate.dx(1))).draw(ULine.hline(dx)); + } + +} diff --git a/src/net/sourceforge/plantuml/wire/WLinkType.java b/src/net/sourceforge/plantuml/wire/WLinkType.java index 6c1df7926..3a08379eb 100644 --- a/src/net/sourceforge/plantuml/wire/WLinkType.java +++ b/src/net/sourceforge/plantuml/wire/WLinkType.java @@ -40,16 +40,16 @@ public enum WLinkType { NORMAL, BUS; static public WLinkType from(String arg) { - if (arg.equals("-")) { + if (arg.contains("-")) { return WLinkType.NORMAL; } - if (arg.equals("=")) { + if (arg.contains("=")) { return WLinkType.BUS; } throw new IllegalArgumentException(); } - public double ySpaceForNext() { + public double spaceForNext() { switch (this) { case NORMAL: return 15; diff --git a/src/net/sourceforge/plantuml/wire/WLinkVertical.java b/src/net/sourceforge/plantuml/wire/WLinkVertical.java new file mode 100644 index 000000000..945d796ee --- /dev/null +++ b/src/net/sourceforge/plantuml/wire/WLinkVertical.java @@ -0,0 +1,164 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://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.wire; + +import net.sourceforge.plantuml.ISkinParam; +import net.sourceforge.plantuml.cucadiagram.Display; +import net.sourceforge.plantuml.graphic.FontConfiguration; +import net.sourceforge.plantuml.graphic.HorizontalAlignment; +import net.sourceforge.plantuml.graphic.TextBlock; +import net.sourceforge.plantuml.ugraphic.UFont; +import net.sourceforge.plantuml.ugraphic.UGraphic; +import net.sourceforge.plantuml.ugraphic.ULine; +import net.sourceforge.plantuml.ugraphic.UPath; +import net.sourceforge.plantuml.ugraphic.UTranslate; +import net.sourceforge.plantuml.ugraphic.color.HColor; +import net.sourceforge.plantuml.ugraphic.color.HColorUtils; + +public class WLinkVertical { + + private final UTranslate start; + private final double destination; + private final WLinkType type; + private final WArrowDirection direction; + private final HColor color; + private final Display label; + private final ISkinParam skinParam; + + public WLinkVertical(ISkinParam skinParam, UTranslate start, double destination, WLinkType type, + WArrowDirection direction, HColor color, Display label) { + this.start = start; + this.destination = destination; + this.skinParam = skinParam; + this.direction = direction; + this.type = type; + this.label = label; + this.color = color == null ? HColorUtils.BLACK : color; + } + + private TextBlock getTextBlock() { + final FontConfiguration fontConfiguration = FontConfiguration.blackBlueTrue(UFont.sansSerif(10)) + .changeColor(color); + return label.create(fontConfiguration, HorizontalAlignment.LEFT, skinParam); + } + + public void drawMe(UGraphic ug) { + ug = ug.apply(color); + if (type == WLinkType.NORMAL) { + ug = ug.apply(color.bg()); + drawNormalArrow(ug); + } else if (type == WLinkType.BUS) { + ug = ug.apply(HColorUtils.WHITE.bg()); + drawBusArrow(ug); + } + } + + private void drawBusArrow(UGraphic ug) { + final double dy = destination - start.getDy() - 2; + final UPath path = new UPath(); + if (direction == WArrowDirection.NONE) { + path.moveTo(0, 0); + path.lineTo(0, dy); + path.lineTo(10, dy); + path.lineTo(10, 0); + path.lineTo(0, 0); + path.closePath(); + ug.apply(start.compose(UTranslate.dy(1))).draw(path); + } + if (direction == WArrowDirection.NORMAL) { + path.moveTo(0, 0); + path.lineTo(0, dy - 15); + path.lineTo(-5, dy - 15); + path.lineTo(5, dy); + path.lineTo(15, dy - 15); + path.lineTo(10, dy - 15); + path.lineTo(10, 0); + path.lineTo(0, 0); + path.closePath(); + ug.apply(start.compose(UTranslate.dy(1))).draw(path); + } + if (direction == WArrowDirection.BOTH) { + path.moveTo(5, 0); + path.lineTo(-5, 15); + path.lineTo(0, 15); + path.lineTo(0, dy - 15); + path.lineTo(-5, dy - 15); + path.lineTo(5, dy); + path.lineTo(15, dy - 15); + path.lineTo(10, dy - 15); + path.lineTo(10, 15); + path.lineTo(15, 15); + path.lineTo(5, 0); + path.closePath(); + ug.apply(start.compose(UTranslate.dy(1))).draw(path); + } + if (direction == WArrowDirection.REVERSE) { + path.moveTo(5, 0); + path.lineTo(-5, 15); + path.lineTo(0, 15); + path.lineTo(0, dy); + path.lineTo(10, dy); + path.lineTo(10, 15); + path.lineTo(15, 15); + path.lineTo(5, 0); + path.closePath(); + ug.apply(start.compose(UTranslate.dy(1))).draw(path); + } } + + private void drawNormalArrow(UGraphic ug) { + final double dy = destination - start.getDy() - 2; + if (direction == WArrowDirection.BOTH || direction == WArrowDirection.NORMAL) { + final UPath path = new UPath(); + path.moveTo(0, 0); + path.lineTo(5, -5); + path.lineTo(-5, -5); + path.lineTo(0, 0); + path.closePath(); + ug.apply(start.compose(UTranslate.dy(dy))).draw(path); + } + if (direction == WArrowDirection.BOTH || direction == WArrowDirection.REVERSE) { + final UPath path = new UPath(); + path.moveTo(0, 0); + path.lineTo(5, 5); + path.lineTo(-5, 5); + path.lineTo(0, 0); + path.closePath(); + ug.apply(start.compose(UTranslate.dy(1))).draw(path); + } + ug.apply(start.compose(UTranslate.dy(1))).draw(ULine.vline(dy)); + } + +} diff --git a/src/net/sourceforge/plantuml/wire/WOrientation.java b/src/net/sourceforge/plantuml/wire/WOrientation.java new file mode 100644 index 000000000..56f97d0f6 --- /dev/null +++ b/src/net/sourceforge/plantuml/wire/WOrientation.java @@ -0,0 +1,48 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://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.wire; + +public enum WOrientation { + + HORIZONTAL, VERTICAL; + + public static WOrientation from(String style) { + if (style.contains("==") || style.contains("--")) { + return WOrientation.VERTICAL; + } + return HORIZONTAL; + } +} diff --git a/src/net/sourceforge/plantuml/wire/WLink.java b/src/net/sourceforge/plantuml/wire/WPrint.java similarity index 58% rename from src/net/sourceforge/plantuml/wire/WLink.java rename to src/net/sourceforge/plantuml/wire/WPrint.java index 618c05933..4f53de05f 100644 --- a/src/net/sourceforge/plantuml/wire/WLink.java +++ b/src/net/sourceforge/plantuml/wire/WPrint.java @@ -35,57 +35,49 @@ */ package net.sourceforge.plantuml.wire; +import net.sourceforge.plantuml.ISkinParam; +import net.sourceforge.plantuml.cucadiagram.Display; +import net.sourceforge.plantuml.graphic.FontConfiguration; +import net.sourceforge.plantuml.graphic.HorizontalAlignment; +import net.sourceforge.plantuml.graphic.StringBounder; +import net.sourceforge.plantuml.graphic.TextBlock; +import net.sourceforge.plantuml.ugraphic.UChange; +import net.sourceforge.plantuml.ugraphic.UFont; import net.sourceforge.plantuml.ugraphic.UGraphic; -import net.sourceforge.plantuml.ugraphic.ULine; -import net.sourceforge.plantuml.ugraphic.UPath; import net.sourceforge.plantuml.ugraphic.UTranslate; import net.sourceforge.plantuml.ugraphic.color.HColor; import net.sourceforge.plantuml.ugraphic.color.HColorUtils; -public class WLink { +public class WPrint { - private final UTranslate pos1; - private final double pos2x; - private final WLinkType type; + private final UTranslate position; private final HColor color; + private final Display label; + private final ISkinParam skinParam; - public WLink(WBlock block1, String x1, String y1, WBlock block2, WLinkType type, HColor color) { - pos1 = block1.getNextOut(x1, y1, type); - pos2x = block2.getAbsolutePosition("0", "0").getDx(); - this.type = type; + public WPrint(ISkinParam skinParam, UTranslate position, HColor color, Display label) { + this.position = position; + this.skinParam = skinParam; + this.label = label; this.color = color == null ? HColorUtils.BLACK : color; } + private TextBlock getTextBlock() { + final FontConfiguration fontConfiguration = FontConfiguration.blackBlueTrue(UFont.sansSerif(10)) + .changeColor(color); + return label.create(fontConfiguration, HorizontalAlignment.LEFT, skinParam); + } + + public UChange getPosition() { + return position; + } + public void drawMe(UGraphic ug) { + getTextBlock().drawU(ug); + } - final double dx = pos2x - pos1.getDx() - 2; - - ug = ug.apply(color).apply(color.bg()); - - if (type == WLinkType.NORMAL) { - final UPath path = new UPath(); - path.moveTo(0, 0); - path.lineTo(-5, -5); - path.lineTo(-5, 5); - path.lineTo(0, 0); - path.closePath(); - ug.apply(pos1.compose(UTranslate.dx(dx))).draw(path); - ug.apply(pos1.compose(UTranslate.dx(1))).draw(ULine.hline(dx)); - - } else if (type == WLinkType.BUS) { - final UPath path = new UPath(); - path.moveTo(0, 0); - path.lineTo(dx - 15, 0); - path.lineTo(dx - 15, -5); - path.lineTo(dx, 5); - path.lineTo(dx - 15, 15); - path.lineTo(dx - 15, 10); - path.lineTo(0, 10); - path.lineTo(0, 0); - path.closePath(); - ug.apply(pos1.compose(UTranslate.dx(1))).draw(path); - } - + public double getHeight(StringBounder stringBounder) { + return getTextBlock().calculateDimension(stringBounder).getHeight(); } } diff --git a/src/net/sourceforge/plantuml/wire/WireDiagram.java b/src/net/sourceforge/plantuml/wire/WireDiagram.java index 2623bdda3..0df0ad217 100644 --- a/src/net/sourceforge/plantuml/wire/WireDiagram.java +++ b/src/net/sourceforge/plantuml/wire/WireDiagram.java @@ -43,16 +43,19 @@ import java.util.ArrayList; import java.util.List; import net.sourceforge.plantuml.AnnotatedWorker; +import net.sourceforge.plantuml.FileFormat; import net.sourceforge.plantuml.FileFormatOption; import net.sourceforge.plantuml.ISkinParam; import net.sourceforge.plantuml.Scale; import net.sourceforge.plantuml.SkinParam; +import net.sourceforge.plantuml.TikzFontDistortion; import net.sourceforge.plantuml.UmlDiagram; import net.sourceforge.plantuml.UmlDiagramType; import net.sourceforge.plantuml.UseStyle; import net.sourceforge.plantuml.command.CommandExecutionResult; import net.sourceforge.plantuml.core.DiagramDescription; import net.sourceforge.plantuml.core.ImageData; +import net.sourceforge.plantuml.cucadiagram.Display; import net.sourceforge.plantuml.graphic.InnerStrategy; import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.TextBlock; @@ -62,21 +65,22 @@ import net.sourceforge.plantuml.ugraphic.ImageBuilder; import net.sourceforge.plantuml.ugraphic.ImageParameter; import net.sourceforge.plantuml.ugraphic.MinMax; import net.sourceforge.plantuml.ugraphic.UGraphic; +import net.sourceforge.plantuml.ugraphic.UTranslate; import net.sourceforge.plantuml.ugraphic.color.HColor; public class WireDiagram extends UmlDiagram { - private final WBlock root = new WBlock("", 0, 0); + private final WBlock root = new WBlock("", new UTranslate(), 0, 0, null); private final List spots = new ArrayList(); - private final List links = new ArrayList(); + private final List hlinks = new ArrayList(); + private final List vlinks = new ArrayList(); public DiagramDescription getDescription() { return new DiagramDescription("Wire Diagram"); } - @Override - public UmlDiagramType getUmlDiagramType() { - return UmlDiagramType.WIRE; + public WireDiagram() { + super(UmlDiagramType.WIRE); } @Override @@ -145,19 +149,22 @@ public class WireDiagram extends UmlDiagram { for (Spot spot : spots) { spot.drawMe(ug); } - for (WLink link : links) { + for (WLinkHorizontal link : hlinks) { + link.drawMe(ug); + } + for (WLinkVertical link : vlinks) { link.drawMe(ug); } } - public CommandExecutionResult addComponent(String indent, String name, int width, int height) { - final int level = indent.replace(" ", "\t").length(); - return this.root.addComponent(level, name, width, height); + public CommandExecutionResult addComponent(String indent, String name, int width, int height, HColor color) { + final int level = computeIndentationLevel(indent); + return this.root.addBlock(level, name, width, height, color); } public CommandExecutionResult newColumn(String indent) { - final int level = indent.replace(" ", "\t").length(); + final int level = computeIndentationLevel(indent); return this.root.newColumn(level); } @@ -172,16 +179,29 @@ public class WireDiagram extends UmlDiagram { } public CommandExecutionResult wgoto(String indent, double x, double y) { - final int level = indent.replace(" ", "\t").length(); + final int level = computeIndentationLevel(indent); return this.root.wgoto(level, x, y); } public CommandExecutionResult wmove(String indent, double x, double y) { - final int level = indent.replace(" ", "\t").length(); + final int level = computeIndentationLevel(indent); return this.root.wmove(level, x, y); } - public CommandExecutionResult link(String name1, String x1, String y1, String name2, WLinkType type, HColor color) { + public CommandExecutionResult print(String indent, String text) { + final int level = computeIndentationLevel(indent); + + final StringBounder stringBounder = FileFormat.PNG.getDefaultStringBounder(TikzFontDistortion.getDefault()); + return this.root.print(stringBounder, getSkinParam(), level, text); + } + + private int computeIndentationLevel(String indent) { + final int level = indent.replace(" ", "\t").length(); + return level; + } + + public CommandExecutionResult vlink(String name1, String x1, String y1, String name2, WLinkType type, + WArrowDirection direction, HColor color, Display label) { final WBlock block1 = this.root.getBlock(name1); if (block1 == null) { return CommandExecutionResult.error("No such element " + name1); @@ -190,8 +210,30 @@ public class WireDiagram extends UmlDiagram { if (block2 == null) { return CommandExecutionResult.error("No such element " + name2); } - final WLink link = new WLink(block1, x1, y1, block2, type, color); - this.links.add(link); + + final UTranslate start = block1.getNextOutVertical(x1, y1, type); + final double destination = block2.getAbsolutePosition("0", "0").getDy(); + + this.vlinks.add(new WLinkVertical(getSkinParam(), start, destination, type, direction, color, label)); + + return CommandExecutionResult.ok(); + } + + public CommandExecutionResult hlink(String name1, String x1, String y1, String name2, WLinkType type, + WArrowDirection direction, HColor color, Display label) { + final WBlock block1 = this.root.getBlock(name1); + if (block1 == null) { + return CommandExecutionResult.error("No such element " + name1); + } + final WBlock block2 = this.root.getBlock(name2); + if (block2 == null) { + return CommandExecutionResult.error("No such element " + name2); + } + + final UTranslate start = block1.getNextOutHorizontal(x1, y1, type); + final double destination = block2.getAbsolutePosition("0", "0").getDx(); + + this.hlinks.add(new WLinkHorizontal(getSkinParam(), start, destination, type, direction, color, label)); return CommandExecutionResult.ok(); } diff --git a/src/net/sourceforge/plantuml/wire/WireDiagramFactory.java b/src/net/sourceforge/plantuml/wire/WireDiagramFactory.java index 3dcd90ca1..32d34775f 100644 --- a/src/net/sourceforge/plantuml/wire/WireDiagramFactory.java +++ b/src/net/sourceforge/plantuml/wire/WireDiagramFactory.java @@ -59,6 +59,7 @@ public class WireDiagramFactory extends PSystemCommandFactory { cmds.add(new CommandMove()); cmds.add(new CommandWLink()); cmds.add(new CommandNewColumn()); + cmds.add(new CommandPrint()); return cmds; } diff --git a/src/net/sourceforge/plantuml/yaml/SimpleYamlParser.java b/src/net/sourceforge/plantuml/yaml/SimpleYamlParser.java new file mode 100644 index 000000000..e844baaf0 --- /dev/null +++ b/src/net/sourceforge/plantuml/yaml/SimpleYamlParser.java @@ -0,0 +1,273 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://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.yaml; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import net.sourceforge.plantuml.StringUtils; +import net.sourceforge.plantuml.json.JsonArray; +import net.sourceforge.plantuml.json.JsonObject; +import net.sourceforge.plantuml.json.JsonValue; + +public class SimpleYamlParser { + + private static final String KEY = "([_0-9\\w][- _0-9\\w]*)"; + + private JsonObject result; + private final List pendingIndents = new ArrayList(); + + public JsonObject parse(List lines) { + List tmp = new ArrayList(); + for (String s : lines) { + if (s.trim().length() == 0) + continue; + if (s.trim().startsWith("#")) + continue; + tmp.add(s); + } + tmp = mergeMultiline(tmp); + result = new JsonObject(); + pendingIndents.clear(); + pendingIndents.add(0); + for (String s : tmp) { + parseSingleLine(s); + } + return result; + + } + + private List mergeMultiline(List tmp) { + final List result = new ArrayList(); + for (int i = 0; i < tmp.size(); i++) { + if (nameOnly(tmp.get(i)) != null && textOnly(tmp.get(i + 1))) { + final StringBuilder sb = new StringBuilder(tmp.get(i)); + while (textOnly(tmp.get(i + 1))) { + sb.append(" " + tmp.get(i + 1).trim()); + i++; + } + result.add(sb.toString()); + } else { + result.add(tmp.get(i)); + } + } + return result; + } + + private String[] dashNameAndValue(String s) { + final Pattern p1 = Pattern.compile("^\\s*[-]\\s*" + KEY + "\\s*:\\s*(\\S.*)$"); + final Matcher m1 = p1.matcher(s); + if (m1.matches()) { + final String name = m1.group(1); + final String data = m1.group(2).trim(); + return new String[] { name, data }; + } + return null; + } + + private String[] nameAndValue(String s) { + final Pattern p1 = Pattern.compile("^\\s*" + KEY + "\\s*:\\s*(\\S.*)$"); + final Matcher m1 = p1.matcher(s); + if (m1.matches()) { + final String name = m1.group(1); + final String data = m1.group(2).trim(); + return new String[] { name, data }; + } + return null; + } + + private String nameOnly(String s) { + final Pattern p1 = Pattern.compile("^\\s*" + KEY + "\\s*:\\s*\\|?\\s*$"); + final Matcher m1 = p1.matcher(s); + if (m1.matches()) { + final String name = m1.group(1); + return name; + } + return null; + } + + private String listedValue(String s) { + final Pattern p1 = Pattern.compile("^\\s*[-]\\s*(\\S.*)$"); + final Matcher m1 = p1.matcher(s); + if (m1.matches()) { + final String name = m1.group(1).trim(); + return name; + } + return null; + } + + private boolean textOnly(String s) { + if (isList(s)) + return false; + return s.indexOf(':') == -1; + } + + private void parseSingleLine(String s) { + // System.err.println("s=" + s); + final int indent = getIndent(s); + + if (isListStrict(s)) { + muteToArray(indent); + return; + } + + final String[] dashNameAndValue = dashNameAndValue(s); + if (dashNameAndValue != null) { + muteToArray(indent); + parseSingleLine(s.replaceFirst("[-]", " ")); + return; + } + + final String listedValue = listedValue(s); + if (listedValue != null) { + final JsonArray array = getForceArray(indent); + array.add(listedValue); + return; + } + + final JsonObject working = getWorking(indent); + if (working == null) { + System.err.println("ERROR: ignoring " + s); + return; + } + + final String[] nameAndValue = nameAndValue(s); + if (nameAndValue != null) { + final String name = nameAndValue[0]; + final String data = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(nameAndValue[1], "\""); + working.add(name, data); + return; + } + + final String nameOnly = nameOnly(s); + if (nameOnly != null) { + working.add(nameOnly, new JsonObject()); + return; + } + + throw new UnsupportedOperationException(s); + } + + private JsonArray getForceArray(int indent) { + while (getLastIndent() > indent - 1) + pendingIndents.remove(pendingIndents.size() - 1); + + final JsonObject last = search(result, pendingIndents.size()); + final String field = last.names().get(last.size() - 1); + if (last.get(field) instanceof JsonArray == false) { + last.set(field, new JsonArray()); + } + return (JsonArray) last.get(field); + } + + private void muteToArray(int indent) { + while (getLastIndent() > indent) + pendingIndents.remove(pendingIndents.size() - 1); + + final JsonObject last = search(result, pendingIndents.size()); + final String field = last.names().get(last.size() - 1); + if (last.get(field) instanceof JsonArray == false) { + last.set(field, new JsonArray()); + } else { + ((JsonArray) last.get(field)).add(new JsonObject()); + } + } + + private boolean isList(String s) { + return s.trim().startsWith("-"); + } + + private boolean isListStrict(String s) { + return s.trim().equals("-"); + } + + private int getLastIndent() { + return pendingIndents.get(pendingIndents.size() - 1); + } + + private JsonObject getWorking(int indent) { + if (indent > getLastIndent()) { + pendingIndents.add(indent); + return search(result, pendingIndents.size()); + } + if (indent == getLastIndent()) { + return search(result, pendingIndents.size()); + } + final int idx = pendingIndents.indexOf(indent); + if (idx == -1) { + return null; + } + + while (pendingIndents.size() > idx + 1) + pendingIndents.remove(pendingIndents.size() - 1); + + return search(result, pendingIndents.size()); + } + + private static JsonObject search(JsonObject current, int size) { + if (size <= 1) { + return current; + } + final String last = current.names().get(current.size() - 1); + // System.err.println("last=" + last); + JsonValue tmp = current.get(last); + if (tmp instanceof JsonArray) { + JsonArray array = (JsonArray) tmp; + if (array.size() == 0) { + tmp = new JsonObject(); + array.add(tmp); + } else { + tmp = array.get(array.size() - 1); + } + } + return search((JsonObject) tmp, size - 1); + } + + private int getIndent(String s) { + int indent = 0; + for (int i = 0; i < s.length(); i++) { + final char ch = s.charAt(i); + if (ch == ' ' || ch == '\t') { + indent++; + } else { + return indent; + } + } + return 0; + } + +} \ No newline at end of file diff --git a/src/net/sourceforge/plantuml/yaml/YamlDiagramFactory.java b/src/net/sourceforge/plantuml/yaml/YamlDiagramFactory.java new file mode 100644 index 000000000..f351ab708 --- /dev/null +++ b/src/net/sourceforge/plantuml/yaml/YamlDiagramFactory.java @@ -0,0 +1,79 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2020, Arnaud Roques + * + * Project Info: http://plantuml.com + * + * If you like this project or if you find it useful, you can support us at: + * + * http://plantuml.com/patreon (only 1$ per month!) + * http://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.yaml; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import net.sourceforge.plantuml.StringLocated; +import net.sourceforge.plantuml.UmlDiagramType; +import net.sourceforge.plantuml.command.PSystemAbstractFactory; +import net.sourceforge.plantuml.core.Diagram; +import net.sourceforge.plantuml.core.DiagramType; +import net.sourceforge.plantuml.core.UmlSource; +import net.sourceforge.plantuml.json.JsonObject; +import net.sourceforge.plantuml.jsondiagram.JsonDiagram; + +public class YamlDiagramFactory extends PSystemAbstractFactory { + + public YamlDiagramFactory() { + super(DiagramType.YAML); + } + + public Diagram createSystem(UmlSource source) { + JsonObject yaml = null; + try { + final List list = new ArrayList(); + final Iterator it = source.iterator2(); + it.next(); + while (true) { + final String line = it.next().getString(); + if (it.hasNext() == false) { + break; + } + list.add(line); + } + yaml = new SimpleYamlParser().parse(list); + } catch (Exception e) { + e.printStackTrace(); + } + final JsonDiagram result = new JsonDiagram(UmlDiagramType.YAML, yaml, new ArrayList()); + result.setSource(source); + return result; + } + +} diff --git a/stdlib/c4-abx.repx b/stdlib/c4-abx.repx index 5f36249ad6a19b7229dd8f71f4c5402892b7aea6..86f12fdf50fbf070eef11bfd53349d87a6d31c9c 100644 GIT binary patch literal 3262 zcmV;v3_IH~jZ#ge|0lry*2D$BDF%m$mDf?e6>x#>@Qb+q|<~!bp&m1OpA= z@c_WGFH^s#40s=-Cnd>igLg?=TfWB$|=wnoHHpq_f3x*IvD0q$*8pD_H zNRhwWgr1j`PVcD)S$6hqQ1t_#*S}oVSsqE3Oc*i$w<)_H$75ZWlFCfX9X$zs2!(|& zH98QN^0thV+M>|6@qhM;)98z)@q#LYJS?n}(6&d@FaW3+=8L2H<*LsWCx>fAC5a;V zDnnw!vtyKt>BV)8y*v!oimSz?w?T3@bgb)ad7}LWXyc+Rr;_2x@Izh0IgFzif3i8_rW;=|_$F)#{gNzJ%$E5>K&@!yq$7HgYk=V>RaDstN zB~JnZvgp`(Rp?0o(elY3ke{Rra}Oa8@z_Ha#U3wAUnO_Pz+M1D73J_I0JPZ5a_&{0 z)J)4R*uzD3SyUI?K+_a2KbxURbo_(AFqlp#eiHl}#lhoB@n{=sV)Ob0YB^LjkCaActlh+&4vfr^F0fCMsjE`5i@bbtwV z{#rOPYqn6qYEZ=Sa2oOpVZ7Y#2k6Z87Kh7J7ZqHU#fcaGb*BgRsL=Nhdy(#(^V@WR zzE(txrH61fu>waESK*#M|6tAkzyF{$Z->?w_Cv^(kmtpZM;{Lls|Zq*cIpyKd8PN3 zy7iq|&t+HVc0Z++Y_|S+OL(PYpSk;MAWwQpi!E3wLRd)Yaw7MRuCYDaaIpQou|z}YOrMKkSkZ*;Hno0yh1eaYRwdPSklOMR8G zH}q^$txRUrS}1g#Z(rKAeT=ccxyvxpOrBhHe$uIVoj0tu&|VQ4d%^QxK%9tp%0B9Y zDA6q=&KKv(%_ipLa-~#Ro+OXi4e?l(6NLdeP7c?RqVi-?^v?&RFw#owMP;N|65O{rT41~|x^MU8vaaP- zN(MaPElsx%mCp}4lQ@Mnw z{KS-##iIf*#%P^IRdP`c5wNN+ zlKryYk3KppOW$a};49LkqSw#%;<7Z>8+`d=P1gVX=-rNr;7WrJ9zs@i#e3J;)|dFd z0xOE+eQ0kePvm#ZgTd^Be;@3@YN3;xlwpZPeW&1}%#Al;$>+jg9*Xq?^I{;IJy_0F zK?)`y^$!|p%JXg1(FGK2y@0q)Yd^x8(3W5m(vqiKnngxNijB<&I`+nb^2iGqEB6zt zsS1YJ7Aq8-64^14S@MIeyjRNWnKCvgrgPSqzS-iM&K|dTZaBjwfMjzU2>ebs!i|LH*{u9pY7cJ=IPyXbU)ndnQa@XVftRrmy!a&dirSl(7^n!FS7#9kk3BZmS`nVMVYn<; zASY=jMjZJs=@V)VJH9@RFP1O0J^od?XXV^dS}8zC*vHC20PP)FnP%Csg7gU$aF&Ps zQ4MWD*-?7=d%{0T^Ltm+J;7H5~?bwpccUpd{xJ zkXX*gmNHCpja@ogJ~OI@SJ+xx$5Aq|#F|l3-dLnW*S7sPP>7~(y!x#=7?L3AW5TK; zyK%3!KB8bcJKE=kK9JTD)x+1?48*7msv9rXo|}#K})gX9-FKZ95Dpv0N zO;~O!_(YmFv?j0h5#?zdWyEV&6pX9Rs_4zZ@4C1{v*JAN2_k;$F`f{UlYF}2>Yy=Z zWxnT^oo+ajyDaaq>kiz?t~hGR($ wlfhAbSuj{3<(CImC^h^&Xl6mvMBln9RuIBfT0>=k|>3yT67lOV(_THE9=5xWGO z|04G44cWw!Ze<$t*SDHXh{;y)fafr?YFaua9d{(i0DjWz0|QR@W>?#vQeciN?MiE( z

u8r$&Y-Q+{`j?+?G%eg7kc?Gj_tjADNQ_ubS%9;$<`EA}jz>7wQOvbs(=Z0M|6 zTuL_9bfBG`4fdt3zPR5u0f?mlY8(FUHbTTuKQMR&qMZUjx2?7E^MP5Q^0E$^j9(&| z+eyOhoHakNJ`!nEGFXRBhQN6}Zp@mO$P-FhY~`x%K%FnLz|{aO@qzuDyM9Uqtypn6 zHVc(IQBN8!Aaj=oo#}iY@uY-$VE2CVh`6NPX?LBpTtM=?u7F@XeLBheX@h`ir5Y9gvKbORA@vz# z@!%Fi8)e}3lvu3p`>AIlfp3}$>&B7uklG=k(!Ml62E2YOpMeFWOe9z|pPW}TCz0YM zuV5>0EH(WV^UgW`3VwBYuBJ;|uq=(0E=^_W%Og<|Luri0bXWAJ(frKdh<(H?t5Nck zS-D@&ush*1*1IcM#YC>nWpXB)d`!nrG*47zRvT%K(Opky_D3YmVJwTSVuJaqIdBbs zo?nIm@Ubq~9$1&e+b8ckx4R!6mN5swc!U0`hNo}AI{`N1Pr`a+RY1A9lvv2{7}o5VET z-xfOtG22MY1QE9ZTwA;D;dqF3Ll|mCq_?;R_G3LR(0M#PLPZOnfw0?v5gx_O;3%XmpX$f)lSC{1X`{Ofw-=m>>dwz^H2u zs4oMW9!Nd+$ARNe(CHnH>*kng9_JdLnp}N@aN#nbb*ya6jCsSS^5m_wi{2nK@4b9i z=nyU$@MhY@KI1j--F#Wk4$G^X2>6H zwG!_)_FD(rqSX69Z_md#5US0@dBPBjOkrR){NrGW*XWO40i|4n1O7m>J>;}FHQ)&h z#jM$yCEj6j5_{U#rQeZVu$M?2)1X+JoAZYWBAL|S{EPs=7tU#EH%=5K9IX)Hkfs_H zxUW_x#9e5PP_6?`)2zt_Y^fI-s3^Ze+y5h=|pTljy>f8Y@zEm_d>)Z5kOUlj z1M)Knz(;V1&?Y{z*d@Y7be6pQ(kqG6E_sn?^Ts>FDul+_^|>9-0b{UqfmbS1J97nK zaePpt{NqA8Mi`|75!ZuJKJ?v`!+@8D&nq4;lmLtd0nVracO#&5Ajb7zext!qAstmh z4au^=fpC0Bl#1kD$f1yO>5}>mN5Z>A-r_gKiPBN?X{^Lls+82oUc!>Rrs#Tt5xg9O HNVU-iz3OS+ diff --git a/stdlib/c4-dex.repx b/stdlib/c4-dex.repx index 1c8a0e7976207fb9f03ed7e260950b62b8b9d396..82d2be92e976b48ccbe13fbd067251e6828bd501 100644 GIT binary patch literal 201 zcmV;)05<;{e*^%==egW(tZiw5h2aGl-^uTP z`?eMs7D*tQP-0R80dn1o-KDn+lZ`CTq?APiSrwP*myECsGM%Cdil5*F+L~AvO=2x* zTVgfXr9aUFgc@i#f;@_2tfuls50gcl69R8blZGd7c6tI=o;7DsV7 z6orIE3u6Z1z>Mj~8G$JLg*YM7h74zwsejTVJ(@^k`s8nhvw)=-fXdCJ_ zBEWzBGWMFPWsz8%UEWGUR1`u@ts>9Nol6o1=eD=sX-7yZ^J zaAlD;FBzq-ij&4bj|FV57x3zLPcandx^%_5BQsrA$q!=+p*T6Xp44#_+rzQUTh;fr zwkkQ=1=iJO=%gls>-Y8>rSZ`lbH#Y|cBKFDoa1~ZFf0++$^WJTBFZxKug4wIo*CkjyQ88IK|Khm^H<*b8Vaex5qEFd z*Z8C92Xv8Xa5_{6killoVxHoK|*61JM3M*O+{Y0%2UXmbl%Ga807Z~D_huul0{I+TuY1O-fW z5k4iM!ldXBpg_^#JB@Hc@eu(!I07n;G+r!|QdBFCb@Z?bi0c)KIrO8nl^H94l7Wzj ztF;`vF38H>7{|}=Rwcwn_=Q-O4-2d$DauU9j!;`XiKfM(0NBrtK7wTF-uRPHJ`@b@ zuO7bMg!xHd!+`#y+PL|?@S&>(ZZx&&?I>wwrD1wVxv?!7ZEG}Fa!Gm zP8z(MNL0*3@>7-c^a(_4%uiFycnt3^SEo!XuxB-aiKZp}IurB6=!<`#0oj4;oO0Uf zVf5=wIh~EDm<<#lm`p@b{UnOvNuNN+qWOT_(VS3xi77zjKYz{k9Wz+<_GR`Va2d-S zsQ>pzgq74`R~d{Km+*NPpN%iU;!sdN_X^!h4UswZ&Rv|#i{#Pg#`4qVshA>>m2F@Xt z6k_Xkq7e-g-wpG*&kq5|C{3qS$=uZ?>`WpJn53?cOLf&+=bxAC6e}~H=Oy56$uign z5El5j ze>0h-@`d4aU-FhF)ZKVh8|xpu^RdO7XTW86L+-?;?Z~cX$TCS*Vx)1&QaPpP+|mTD zuBwgocqiwX9xLfn#^3R39Zp-5+vfV43C{XzN{hE?^o?YY$Jc;z)0^@e)0(r-OB&ny z^A6h{mp|v4K({Mb;@@d2b5+A6=ad#rqmlDC<>r*%oN~NG01mcsngHJ=yk=RE=K|}p z19F3OgRI_cOJ=kLNEXbwAmt(f5Z)#b$7``T1eVA08#Hpu4#@Pc?{ry}*CjJr0wfFO zT##~+00?grh~u?b90K1%9KMSAo_@6*88=$HtT3HQj5ID;DyQ_ETbjVtRkg7m@8moK z*28ecvJj)TBfCoQJPDK-XaQiFLlePqHpH zANt>R@eNJUv^Jc4Xni>5?a8r$uM;=(ZOzIMA`{raive{6Lf#krofv=ZKI zdHIRwJ9@}vWyj}RUhY_vkc25Qp=2|T#tT4!9!mRzZzqP^f>34Fc4SwXVFq257-?Lx zRL<#=w={vHRmZM2*5gka=b0WW?e9z&^f)GbwNAZ9a@)NBUs=VZSS?;THQpTY)?}US zLy9|m81t?z4t#~w7H;vVUnfP)k14N}I0b$Xdd`=enI0?g6ZBkO zt%FfBxos}K5sLG#Mz`W^wD<`=enI0?Y+uLCBU#On8_R$l3e?D7WWJ-(c zU>f!Fn8!I+vi#gQDd7U(~Ne!yY(b+)%lazAhs|GzCMql~C0+PMeL_g=``lU=+I zCa=*^?5f)a`I4ya+~!@sPRhz3S6MZ2416!|%12zrsEJ%v1lQ56XtXFXj8kYz;SzDY zp#kMc6{mq7EA5e^KA$JLtW)oi+&Ax!3exjsX=10wyCd||!+AF64$Hd)0E!9);!2va z15@DVeFl$du#b5&vZHFNlse}{^RUH8&e17V2mr7iAvjvdc?5hHLVroKpPb}3NW;(Y zeed%T{a?wtEUV{UZnPI7ees5CEUyy)sOuDpt19Q>82Gwl@M$@N;=CEz(awiSnDe4} X*kUB-=#(l109cO@94+KL(qZ}tJE%;= literal 2161 zcmV-%2#)s~flMMQH|dD#O#!eNup5^Hp`6XLdl8GaSaA}yyvIm9*MQ62k2!4XS2)I? z#L@yyB!UoIvhzBD!z4{FRTzy&TBlM2fbjU-rYUomi8Xhm|1(WF85AP0B}WoTTVLAt zq)24(H_?-K=r+!fgjLICKM*Vy_ezqj*q%U0xqMP_myjI7C1J=yJraxcNY_^=B<_WM zhXnYqpZem=_&YE(495R?f=Wl30HiTWmvpshtTcwa+IKdsBRi#`pjIu$-&Y^JbDFS0 zPekwIQccSzr)Ndu@4qb7wWu}`d`C&{h)SZJ{kvgt?dwneP zRvrcjeQiVV*Od z5KL^h(a;(M_d6s+rsG4(nUM~Mg%>z?54n=!D)WTeqq6uLcmJD-Yj{QEch(wZEN6#1 z{p#-iE^Wgn9UmVsz37x)*;`MHl^bW zK>;-#+~*u3fop{T2}**WHNxqLk2>h=2xxq!{&H+m>ekZZgdVm5aZx3iLp>_a%tHFp zA8?5tt>vEUfUN9~alC$Kl@KeD1F>wE5-UlHQUdH}M?JurZLj@FDDUn?3vUWB+l2X! zUE`I0OgC1(zI&0?qTs`Xcq1?Xe}E`jh!7dzjW?BZ&VQ8RW_A@g?IGI?r8iQvmcp?g zFWvAv5b=YQH+3wbB}HsvX-SXt*2K2%98SO{rj+CPZ#}jAf|NJWE4sd$y-+_;0pKTk z?-8VDI*^z?#44$AF07~i<0!Ci;G`k7Gg5YG^yw)*KY{2NdQMj33Ep3>PMKCe@iRoGV>>ubrcHlaPTs$j_Ki{-bB%G{1a6q)}kfOGdcsnCMfmTKL0lA}PA^Fmo zH}s#sX8(>EEPMMj`w+N{We(K;`y+xSb=Xw~YXa-@n+!f0H^JgmP(JqxeUs+>2>~#z zknXNF`+7N(ySi}yrW)R_2}e&lK8R}>EY-Y&wtSm*9nv%Da*DNVV4@mc#!a4gENxxG zs3CMh{A*_zms4y+7=`I&0M2bJDO^~W6US)a>U&oGrSG@AW-Lr6>d>a@0(P1V3WCtx zkPX`-96S$M2+WUdQrEiTK-)=8+RQ)JNm*#b&p5<}MjVZsl z0UjkhW?7Nv0_$)9a)NY)EZ(d`rZ)sg7|dCLiHZb3c$-iduf_ZjSRTWV zi--*eAl=`Z>98s!G<~S%^{lkwqnV9tBGDG%ispXXz5R6oJdDDq}w0 z$=OVeiQ)e24^N1$i5+q~CRi7<5B+ahe55Iw)`lgItq+S?pBx+bIBBZnMTcD%pk<%~9QNthA?N;Zb0@d8kwhtl!j z;Y4#=5UR}Dk1Q%POs|U)J&jA0%2~R^Ek)pH)z+(w`S`8I*-VXz_B#^>J&p_8069T5BXQ4V$n=H)69*ewAYqXJ2yYXJ@ z4FyO)*Qn}2xwEVRaOd8C*f<=tk|O&*LmtqWt2L<0S%cs0~}1sBuv^2VVpZK-$d^QN=REKHb&^*8pvtQ8W8ks8BOh@Y z?M~#VBDl6$UOOm0j8kYx;o@<;p#bSf6{mq36YYkh-uDw-rl}nf+|1r@6r}rMX<|$D zHx{91569V@b(pwI03fMQATFgD8<_&n_h~$)!9M!k$bzb^Qfi;)^~1(Tauyv@g#ZBS z5rU(IoQ;4-A@nPW9ek4Cr9%+?S705M)nh5wTMCi7c!O(9TqgjK*C`a2RnGY_aG#^` nX*q*pzZ+T5#=A-A^Spl8_(;y8L#hw}U_C-`w2-rr3f&g~`?NR8 diff --git a/stdlib/office-abx.repx b/stdlib/office-abx.repx index aab96209edbc0217cb7bf336dc92effb227526f8..f0069b29f550d72a157a2bbbf91271ba79243358 100644 GIT binary patch literal 22962 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 bpbBRL5S*w!wiCD|PKj-w`|;}~Bj7&(eGcNZ literal 22878 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