From 0fc2fad4327cb41c3aecd134e97dccaea61d985a Mon Sep 17 00:00:00 2001 From: Arnaud Roques Date: Tue, 26 Jul 2022 12:57:15 +0200 Subject: [PATCH] wip --- src/net/sourceforge/plantuml/Direction.java | 27 ++- .../ftile/vertical/FtileBoxOld.java | 40 ++-- .../plantuml/command/CommandRankDir.java | 6 +- .../plantuml/creole/SheetBlock1.java | 66 +++--- .../plantuml/cucadiagram/TextBlockMap.java | 7 +- .../sourceforge/plantuml/mindmap/Branch.java | 3 +- .../mindmap/CommandMindMapDirection.java | 12 +- .../plantuml/mindmap/CommandMindMapPlus.java | 3 +- .../plantuml/mindmap/CommandMindMapRoot.java | 2 +- .../plantuml/mindmap/FingerImpl.java | 80 +++++--- .../sourceforge/plantuml/mindmap/MindMap.java | 70 ++++--- .../plantuml/mindmap/MindMapDiagram.java | 13 +- .../mindmap/MindMapDiagramFactory.java | 4 +- .../sourceforge/plantuml/tim/TContext.java | 2 + .../plantuml/tim/stdlib/LoadJson.java | 14 +- .../plantuml/tim/stdlib/LoadJsonLegacy.java | 190 ++++++++++++++++++ 16 files changed, 405 insertions(+), 134 deletions(-) create mode 100644 src/net/sourceforge/plantuml/tim/stdlib/LoadJsonLegacy.java diff --git a/src/net/sourceforge/plantuml/Direction.java b/src/net/sourceforge/plantuml/Direction.java index e9fd58140..eac004e8a 100644 --- a/src/net/sourceforge/plantuml/Direction.java +++ b/src/net/sourceforge/plantuml/Direction.java @@ -41,21 +41,21 @@ public enum Direction { RIGHT, LEFT, DOWN, UP; public Direction getInv() { - if (this == RIGHT) { + if (this == RIGHT) return LEFT; - } - if (this == LEFT) { + + if (this == LEFT) return RIGHT; - } - if (this == DOWN) { + + if (this == DOWN) return UP; - } - if (this == UP) { + + if (this == UP) return DOWN; - } + throw new IllegalStateException(); } - + public String getShortCode() { return name().substring(0, 1); } @@ -122,4 +122,13 @@ public enum Direction { throw new IllegalArgumentException("Not a H or V line!"); } + + public static Direction lazzyValueOf(String s) { + s = s.toUpperCase(); + if ("TOP".equals(s)) + return Direction.UP; + if ("BOTTOM".equals(s)) + return Direction.DOWN; + return valueOf(s); + } } diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vertical/FtileBoxOld.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vertical/FtileBoxOld.java index d87d95cfd..c4e828a82 100644 --- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vertical/FtileBoxOld.java +++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vertical/FtileBoxOld.java @@ -77,7 +77,7 @@ import net.sourceforge.plantuml.ugraphic.color.HColorNone; public class FtileBoxOld extends AbstractFtile { - private final ClockwiseTopRightBottomLeft padding; + // private final ClockwiseTopRightBottomLeft paddingUnused; private final TextBlock tb; private double roundCorner = 25; @@ -123,12 +123,14 @@ public class FtileBoxOld extends AbstractFtile { class MyStencil implements Stencil { public double getStartingX(StringBounder stringBounder, double y) { - return -padding.getLeft(); + return 0; + // return -padding.getLeft(); } public double getEndingX(StringBounder stringBounder, double y) { final Dimension2D dim = calculateDimension(stringBounder); - return dim.getWidth() - padding.getRight(); + return dim.getWidth(); + // return dim.getWidth() - padding.getRight(); } } @@ -160,16 +162,19 @@ public class FtileBoxOld extends AbstractFtile { this.backColor = style.value(PName.BackGroundColor).asColor(skinParam.getThemeStyle(), getIHtmlColorSet()); final FontConfiguration fc = style.getFontConfiguration(skinParam.getThemeStyle(), getIHtmlColorSet()); this.horizontalAlignment = style.getHorizontalAlignment(); - this.padding = style.getPadding(); + // this.padding = style.getPadding(); this.roundCorner = style.value(PName.RoundCorner).asDouble(); this.shadowing = style.value(PName.Shadowing).asDouble(); final LineBreakStrategy wrapWidth = style.wrapWidth(); this.minimumWidth = style.value(PName.MinimumWidth).asDouble(); - final Sheet sheet = Parser - .build(fc, skinParam.getDefaultTextAlignment(horizontalAlignment), skinParam, CreoleMode.FULL) - .createSheet(label); - this.tb = new SheetBlock2(new SheetBlock1(sheet, wrapWidth, skinParam.getPadding()), new MyStencil(), + // final HorizontalAlignment alignment = + // skinParam.getDefaultTextAlignment(horizontalAlignment); + final Sheet sheet = Parser.build(fc, horizontalAlignment, skinParam, CreoleMode.FULL).createSheet(label); +// this.tb = new SheetBlock1(sheet, wrapWidth, 0, this.padding.getLeft(), this.padding.getRight()); +// this.tb = new SheetBlock2(new SheetBlock1(sheet, wrapWidth, 0, this.padding.getLeft(), this.padding.getRight()), +// new MyStencil(), new UStroke(1)); + this.tb = new SheetBlock2(new SheetBlock1(sheet, wrapWidth, style.getPadding()), new MyStencil(), new UStroke(1)); this.print = label.toString(); @@ -205,13 +210,20 @@ public class FtileBoxOld extends AbstractFtile { shape.drawU(ug); if (horizontalAlignment == HorizontalAlignment.LEFT) - tb.drawU(ug.apply(new UTranslate(padding.getLeft(), padding.getTop()))); + tb.drawU(ug); else if (horizontalAlignment == HorizontalAlignment.RIGHT) - tb.drawU(ug.apply(new UTranslate(dimTotal.getWidth() - tbWidth(stringBounder) - padding.getRight(), - padding.getBottom()))); + tb.drawU(ug.apply(new UTranslate(dimTotal.getWidth() - tbWidth(stringBounder), 0))); else if (horizontalAlignment == HorizontalAlignment.CENTER) - tb.drawU(ug.apply(new UTranslate((dimTotal.getWidth() - tbWidth(stringBounder)) / 2, padding.getBottom()))); + tb.drawU(ug.apply(new UTranslate((dimTotal.getWidth() - tbWidth(stringBounder)) / 2, 0))); +// if (horizontalAlignment == HorizontalAlignment.LEFT) +// tb.drawU(ug.apply(new UTranslate(padding.getLeft(), padding.getTop()))); +// else if (horizontalAlignment == HorizontalAlignment.RIGHT) +// tb.drawU(ug.apply(new UTranslate(dimTotal.getWidth() - tbWidth(stringBounder) - padding.getRight(), +// padding.getBottom()))); +// else if (horizontalAlignment == HorizontalAlignment.CENTER) +// tb.drawU(ug.apply(new UTranslate((dimTotal.getWidth() - tbWidth(stringBounder)) / 2, padding.getBottom()))); +// } private double tbWidth(final StringBounder stringBounder) { @@ -221,8 +233,8 @@ public class FtileBoxOld extends AbstractFtile { @Override protected FtileGeometry calculateDimensionFtile(StringBounder stringBounder) { Dimension2D dimRaw = tb.calculateDimension(stringBounder); - dimRaw = Dimension2DDouble.delta(dimRaw, padding.getLeft() + padding.getRight(), - padding.getBottom() + padding.getTop()); +// dimRaw = Dimension2DDouble.delta(dimRaw, padding.getLeft() + padding.getRight(), +// padding.getBottom() + padding.getTop()); dimRaw = Dimension2DDouble.atLeast(dimRaw, minimumWidth, 0); return new FtileGeometry(dimRaw.getWidth() + boxStyle.getShield(), dimRaw.getHeight(), dimRaw.getWidth() / 2, 0, dimRaw.getHeight()); diff --git a/src/net/sourceforge/plantuml/command/CommandRankDir.java b/src/net/sourceforge/plantuml/command/CommandRankDir.java index 2b52e539c..3f4141536 100644 --- a/src/net/sourceforge/plantuml/command/CommandRankDir.java +++ b/src/net/sourceforge/plantuml/command/CommandRankDir.java @@ -37,14 +37,14 @@ package net.sourceforge.plantuml.command; import net.sourceforge.plantuml.LineLocation; import net.sourceforge.plantuml.SkinParam; import net.sourceforge.plantuml.StringUtils; +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.RegexResult; -import net.sourceforge.plantuml.cucadiagram.CucaDiagram; import net.sourceforge.plantuml.cucadiagram.Rankdir; -public class CommandRankDir extends SingleLineCommand2 { +public class CommandRankDir extends SingleLineCommand2 { public CommandRankDir() { super(getRegexConcat()); @@ -59,7 +59,7 @@ public class CommandRankDir extends SingleLineCommand2 { } @Override - protected CommandExecutionResult executeArg(CucaDiagram diagram, LineLocation location, RegexResult arg) { + protected CommandExecutionResult executeArg(TitledDiagram diagram, LineLocation location, RegexResult arg) { final String s = StringUtils.goUpperCase(arg.get("DIRECTION", 0)).replace(' ', '_'); ((SkinParam) diagram.getSkinParam()).setRankdir(Rankdir.valueOf(s)); // diagram.setRankdir(Rankdir.valueOf(s)); diff --git a/src/net/sourceforge/plantuml/creole/SheetBlock1.java b/src/net/sourceforge/plantuml/creole/SheetBlock1.java index 032f37ef3..05aef814c 100644 --- a/src/net/sourceforge/plantuml/creole/SheetBlock1.java +++ b/src/net/sourceforge/plantuml/creole/SheetBlock1.java @@ -35,7 +35,6 @@ */ package net.sourceforge.plantuml.creole; -import net.sourceforge.plantuml.awt.geom.Dimension2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -46,6 +45,7 @@ import java.util.Objects; import net.sourceforge.plantuml.Dimension2DDouble; import net.sourceforge.plantuml.LineBreakStrategy; import net.sourceforge.plantuml.annotation.HaxeIgnored; +import net.sourceforge.plantuml.awt.geom.Dimension2D; import net.sourceforge.plantuml.creole.atom.Atom; import net.sourceforge.plantuml.creole.legacy.StripeSimple; import net.sourceforge.plantuml.graphic.AbstractTextBlock; @@ -53,6 +53,7 @@ import net.sourceforge.plantuml.graphic.HorizontalAlignment; import net.sourceforge.plantuml.graphic.InnerStrategy; import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.TextBlock; +import net.sourceforge.plantuml.style.ClockwiseTopRightBottomLeft; import net.sourceforge.plantuml.ugraphic.MinMax; import net.sourceforge.plantuml.ugraphic.UGraphic; import net.sourceforge.plantuml.ugraphic.UTranslate; @@ -70,16 +71,25 @@ public class SheetBlock1 extends AbstractTextBlock implements TextBlock, Atom, S private Map positions; private MinMax minMax; private final LineBreakStrategy maxWidth; - private final double padding; + private final ClockwiseTopRightBottomLeft padding; private final double marginX1; private final double marginX2; @HaxeIgnored public SheetBlock1(Sheet sheet, LineBreakStrategy maxWidth, double padding) { + this(sheet, maxWidth, ClockwiseTopRightBottomLeft.same(padding), 0, 0); + } + + public SheetBlock1(Sheet sheet, LineBreakStrategy maxWidth, ClockwiseTopRightBottomLeft padding) { this(sheet, maxWidth, padding, 0, 0); } public SheetBlock1(Sheet sheet, LineBreakStrategy maxWidth, double padding, double marginX1, double marginX2) { + this(sheet, maxWidth, ClockwiseTopRightBottomLeft.same(padding), marginX1, marginX2); + } + + public SheetBlock1(Sheet sheet, LineBreakStrategy maxWidth, ClockwiseTopRightBottomLeft padding, double marginX1, + double marginX2) { this.sheet = sheet; this.maxWidth = Objects.requireNonNull(maxWidth); this.padding = padding; @@ -93,37 +103,37 @@ public class SheetBlock1 extends AbstractTextBlock implements TextBlock, Atom, S } public HorizontalAlignment getCellAlignment() { - if (stripes.size() != 1) { + if (stripes.size() != 1) return HorizontalAlignment.LEFT; - } + final Stripe simple = stripes.get(0); - if (simple instanceof StripeSimple) { + if (simple instanceof StripeSimple) return ((StripeSimple) simple).getCellAlignment(); - } + return HorizontalAlignment.LEFT; } private void initMap(StringBounder stringBounder) { - if (positions != null) { + if (positions != null) return; - } + stripes = new ArrayList<>(); - for (Stripe stripe : sheet) { + for (Stripe stripe : sheet) stripes.addAll(new Fission(stripe, maxWidth).getSplitted(stringBounder)); - } + positions = new LinkedHashMap<>(); widths = new LinkedHashMap<>(); heights = new LinkedHashMap<>(); minMax = MinMax.getEmpty(true); double y = 0; for (Stripe stripe : stripes) { - if (stripe.getAtoms().size() == 0) { + if (stripe.getAtoms().size() == 0) continue; - } + final Sea sea = new Sea(stringBounder); - for (Atom atom : stripe.getAtoms()) { + for (Atom atom : stripe.getAtoms()) sea.add(atom); - } + sea.doAlign(); sea.translateMinYto(y); sea.exportAllPositions(positions); @@ -135,21 +145,22 @@ public class SheetBlock1 extends AbstractTextBlock implements TextBlock, Atom, S y += height; } final int coef; - if (sheet.getHorizontalAlignment() == HorizontalAlignment.CENTER) { + if (sheet.getHorizontalAlignment() == HorizontalAlignment.CENTER) coef = 2; - } else if (sheet.getHorizontalAlignment() == HorizontalAlignment.RIGHT) { + else if (sheet.getHorizontalAlignment() == HorizontalAlignment.RIGHT) coef = 1; - } else { + else coef = 0; - } + if (coef != 0) { double maxWidth = 0; - for (Double v : widths.values()) { - if (v > maxWidth) { + for (Double v : widths.values()) + if (v > maxWidth) maxWidth = v; - } - } + for (Map.Entry ent : widths.entrySet()) { + // final double diff = maxWidth - ent.getValue() + this.marginX1 + + // this.marginX2; final double diff = maxWidth - ent.getValue(); if (diff > 0) { for (Atom atom : ent.getKey().getAtoms()) { @@ -165,7 +176,7 @@ public class SheetBlock1 extends AbstractTextBlock implements TextBlock, Atom, S public Dimension2D calculateDimension(StringBounder stringBounder) { initMap(stringBounder); - return Dimension2DDouble.delta(minMax.getDimension(), 2 * padding); + return Dimension2DDouble.delta(minMax.getDimension(), padding.getBottom() + padding.getTop()); } @Override @@ -175,16 +186,15 @@ public class SheetBlock1 extends AbstractTextBlock implements TextBlock, Atom, S public void drawU(UGraphic ug) { initMap(ug.getStringBounder()); - if (padding > 0) { - ug = ug.apply(new UTranslate(padding, padding)); - } - for (Stripe stripe : stripes) { + if (padding.getLeft() > 0 || padding.getTop() > 0) + ug = ug.apply(new UTranslate(padding.getLeft(), padding.getTop())); + + for (Stripe stripe : stripes) for (Atom atom : stripe.getAtoms()) { final Position position = positions.get(atom); atom.drawU(position.translate(ug)); // position.drawDebug(ug); } - } } public double getStartingAltitude(StringBounder stringBounder) { diff --git a/src/net/sourceforge/plantuml/cucadiagram/TextBlockMap.java b/src/net/sourceforge/plantuml/cucadiagram/TextBlockMap.java index 16b44ad4a..0eb206dfa 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/TextBlockMap.java +++ b/src/net/sourceforge/plantuml/cucadiagram/TextBlockMap.java @@ -35,7 +35,6 @@ */ package net.sourceforge.plantuml.cucadiagram; -import net.sourceforge.plantuml.awt.geom.Dimension2D; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; @@ -45,12 +44,14 @@ import java.util.Map; import net.sourceforge.plantuml.Dimension2DDouble; import net.sourceforge.plantuml.FontParam; import net.sourceforge.plantuml.ISkinParam; +import net.sourceforge.plantuml.awt.geom.Dimension2D; import net.sourceforge.plantuml.graphic.AbstractTextBlock; 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.graphic.TextBlockUtils; +import net.sourceforge.plantuml.skin.VisibilityModifier; import net.sourceforge.plantuml.svek.Ports; import net.sourceforge.plantuml.svek.WithPorts; import net.sourceforge.plantuml.ugraphic.UEllipse; @@ -75,7 +76,9 @@ public class TextBlockMap extends AbstractTextBlock implements WithPorts { this.skinParam = skinParam; this.fontConfiguration = fontConfiguration; for (Map.Entry ent : map.entrySet()) { - final String key = ent.getKey(); + String key = ent.getKey(); + if (VisibilityModifier.isVisibilityCharacter(key)) + key = key.substring(1); this.keys.add(key); final String value = ent.getValue(); final TextBlock block1 = getTextBlock(key); diff --git a/src/net/sourceforge/plantuml/mindmap/Branch.java b/src/net/sourceforge/plantuml/mindmap/Branch.java index 8f3bdf28c..a31230000 100644 --- a/src/net/sourceforge/plantuml/mindmap/Branch.java +++ b/src/net/sourceforge/plantuml/mindmap/Branch.java @@ -35,7 +35,6 @@ */ package net.sourceforge.plantuml.mindmap; -import net.sourceforge.plantuml.Direction; import net.sourceforge.plantuml.ISkinParam; import net.sourceforge.plantuml.command.CommandExecutionResult; import net.sourceforge.plantuml.cucadiagram.Display; @@ -55,7 +54,7 @@ class Branch implements UDrawable { last = root; } - void initFinger(ISkinParam skinParam, Direction direction) { + void initFinger(ISkinParam skinParam, boolean direction) { finger = FingerImpl.build(root, skinParam, direction); } diff --git a/src/net/sourceforge/plantuml/mindmap/CommandMindMapDirection.java b/src/net/sourceforge/plantuml/mindmap/CommandMindMapDirection.java index 56b539acc..26f4c52e0 100644 --- a/src/net/sourceforge/plantuml/mindmap/CommandMindMapDirection.java +++ b/src/net/sourceforge/plantuml/mindmap/CommandMindMapDirection.java @@ -54,15 +54,19 @@ public class CommandMindMapDirection extends SingleLineCommand2 return RegexConcat.build(CommandMindMapDirection.class.getName(), RegexLeaf.start(), // new RegexLeaf("[^*]*"), // new RegexLeaf("\\b"), // - new RegexLeaf("DIRECTION", "(left|right)"), // + new RegexLeaf("DIRECTION", "(left|right|top|bottom)"), // new RegexLeaf("\\b"), // - new RegexLeaf("[^*]*"), RegexLeaf.end()); + new RegexLeaf("[^*]*"), // + new RegexLeaf("(side|direction)"), // + new RegexLeaf("[^*]*"), // + RegexLeaf.end()); } @Override protected CommandExecutionResult executeArg(MindMapDiagram diagram, LineLocation location, RegexResult arg) { - final String direction = arg.get("DIRECTION", 0); - diagram.setDefaultDirection(Direction.valueOf(direction.toUpperCase())); + final String dir = arg.get("DIRECTION", 0); + final Direction direction = Direction.lazzyValueOf(dir); + diagram.setDefaultDirection(direction); return CommandExecutionResult.ok(); } diff --git a/src/net/sourceforge/plantuml/mindmap/CommandMindMapPlus.java b/src/net/sourceforge/plantuml/mindmap/CommandMindMapPlus.java index 0ab5dd0af..340615850 100644 --- a/src/net/sourceforge/plantuml/mindmap/CommandMindMapPlus.java +++ b/src/net/sourceforge/plantuml/mindmap/CommandMindMapPlus.java @@ -35,7 +35,6 @@ */ package net.sourceforge.plantuml.mindmap; -import net.sourceforge.plantuml.Direction; import net.sourceforge.plantuml.LineLocation; import net.sourceforge.plantuml.command.CommandExecutionResult; import net.sourceforge.plantuml.command.SingleLineCommand2; @@ -74,7 +73,7 @@ public class CommandMindMapPlus extends SingleLineCommand2 { backColor = diagram.getSkinParam().getIHtmlColorSet().getColor(diagram.getSkinParam().getThemeStyle(), stringColor); } - final Direction direction = type.contains("-") ? Direction.LEFT : Direction.RIGHT; + final boolean direction = type.contains("-") ? false : true; 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/CommandMindMapRoot.java b/src/net/sourceforge/plantuml/mindmap/CommandMindMapRoot.java index 6d7c04c77..316b0f468 100644 --- a/src/net/sourceforge/plantuml/mindmap/CommandMindMapRoot.java +++ b/src/net/sourceforge/plantuml/mindmap/CommandMindMapRoot.java @@ -60,7 +60,7 @@ public class CommandMindMapRoot extends SingleLineCommand2 { @Override protected CommandExecutionResult executeArg(MindMapDiagram diagram, LineLocation location, RegexResult arg) { final String label = arg.get("LABEL", 0); - return diagram.addIdea(null, 0, Display.getWithNewlines(label), IdeaShape.BOX, null); + return diagram.addIdea(null, 0, Display.getWithNewlines(label), IdeaShape.BOX, true); } } diff --git a/src/net/sourceforge/plantuml/mindmap/FingerImpl.java b/src/net/sourceforge/plantuml/mindmap/FingerImpl.java index 015e46d39..c391d964c 100644 --- a/src/net/sourceforge/plantuml/mindmap/FingerImpl.java +++ b/src/net/sourceforge/plantuml/mindmap/FingerImpl.java @@ -39,12 +39,12 @@ import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.List; -import net.sourceforge.plantuml.Direction; import net.sourceforge.plantuml.ISkinParam; import net.sourceforge.plantuml.SkinParamColors; import net.sourceforge.plantuml.activitydiagram3.ftile.vertical.FtileBoxOld; import net.sourceforge.plantuml.awt.geom.Dimension2D; import net.sourceforge.plantuml.creole.CreoleMode; +import net.sourceforge.plantuml.cucadiagram.Rankdir; import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.graphic.TextBlockUtils; @@ -64,13 +64,13 @@ public class FingerImpl implements Finger, UDrawable { private final Idea idea; private final ISkinParam skinParam; - private final Direction direction; + private final int direction; private boolean drawPhalanx = true; private final List nail = new ArrayList<>(); private Tetris tetris = null; - public static FingerImpl build(Idea idea, ISkinParam skinParam, Direction direction) { + public static FingerImpl build(Idea idea, ISkinParam skinParam, boolean direction) { final FingerImpl result = new FingerImpl(idea, skinParam, direction); for (Idea child : idea.getChildren()) result.addInNail(build(child, skinParam, direction)); @@ -78,14 +78,18 @@ public class FingerImpl implements Finger, UDrawable { return result; } + private boolean isTopToBottom() { + return skinParam.getRankdir() == Rankdir.TOP_TO_BOTTOM; + } + public void addInNail(FingerImpl child) { nail.add(child); } - private FingerImpl(Idea idea, ISkinParam skinParam, Direction direction) { + private FingerImpl(Idea idea, ISkinParam skinParam, boolean direction) { this.idea = idea; this.skinParam = skinParam; - this.direction = direction; + this.direction = direction ? 1 : -1; } private ClockwiseTopRightBottomLeft getMargin() { @@ -97,19 +101,32 @@ public class FingerImpl implements Finger, UDrawable { final TextBlock phalanx = getPhalanx(); final Dimension2D dimPhalanx = phalanx.calculateDimension(stringBounder); if (drawPhalanx) { - final double posY = -getPhalanxThickness(stringBounder) / 2; - final double posX = direction == Direction.RIGHT ? 0 : -dimPhalanx.getWidth(); + final double posX; + final double posY; + if (isTopToBottom()) { + posX = -getPhalanxThickness(stringBounder) / 2; + posY = direction == 1 ? 0 : -dimPhalanx.getHeight(); + } else { + posX = direction == 1 ? 0 : -dimPhalanx.getWidth(); + posY = -getPhalanxThickness(stringBounder) / 2; + } phalanx.drawU(ug.apply(new UTranslate(posX, posY))); } - final Point2D p1 = new Point2D.Double( - direction == Direction.RIGHT ? dimPhalanx.getWidth() : -dimPhalanx.getWidth(), 0); + final Point2D p1; + if (isTopToBottom()) + p1 = new Point2D.Double(0, direction * dimPhalanx.getHeight()); + else + p1 = new Point2D.Double(direction * dimPhalanx.getWidth(), 0); for (int i = 0; i < nail.size(); i++) { final FingerImpl child = nail.get(i); final SymetricalTeePositioned stp = getTetris(stringBounder).getElements().get(i); - final double x = direction == Direction.RIGHT ? dimPhalanx.getWidth() + getX12() - : -dimPhalanx.getWidth() - getX12(); - final Point2D p2 = new Point2D.Double(x, stp.getY()); + final Point2D p2; + if (isTopToBottom()) + p2 = new Point2D.Double(stp.getY(), direction * (dimPhalanx.getHeight() + getX12())); + else + p2 = new Point2D.Double(direction * (dimPhalanx.getWidth() + getX12()), stp.getY()); + child.drawU(ug.apply(new UTranslate(p2))); drawLine(ug.apply(getLinkColor()).apply(getUStroke()), p1, p2); } @@ -127,14 +144,19 @@ public class FingerImpl implements Finger, UDrawable { } private void drawLine(UGraphic ug, Point2D p1, Point2D p2) { - // final ULine line = new ULine(p1, p2); - // ug.apply(new UTranslate(p1)).draw(line); final UPath path = new UPath(); - final double delta1 = direction == Direction.RIGHT ? 10 : -10; - final double delta2 = direction == Direction.RIGHT ? 25 : -25; path.moveTo(p1); - path.lineTo(p1.getX() + delta1, p1.getY()); - path.cubicTo(p1.getX() + delta2, p1.getY(), p2.getX() - delta2, p2.getY(), p2.getX() - delta1, p2.getY()); + if (isTopToBottom()) { + final double delta1 = direction * 3; + final double delta2 = direction * 10; + path.lineTo(p1.getX(), p1.getY() + delta1); + path.cubicTo(p1.getX(), p1.getY() + delta2, p2.getX(), p2.getY() - delta2, p2.getX(), p2.getY() - delta1); + } else { + final double delta1 = direction * 10; + final double delta2 = direction * 25; + path.lineTo(p1.getX() + delta1, p1.getY()); + path.cubicTo(p1.getX() + delta2, p1.getY(), p2.getX() - delta2, p2.getY(), p2.getX() - delta1, p2.getY()); + } path.lineTo(p2); ug.draw(path); } @@ -162,11 +184,17 @@ public class FingerImpl implements Finger, UDrawable { } private double getX1() { - return getMargin().getLeft(); + if (isTopToBottom()) + return getMargin().getTop(); + else + return getMargin().getLeft(); } private double getX2() { - return getMargin().getRight() + 30; + if (isTopToBottom()) + return getMargin().getBottom() + 5; + else + return getMargin().getRight() + 30; } public double getX12() { @@ -174,10 +202,14 @@ public class FingerImpl implements Finger, UDrawable { } public double getPhalanxThickness(StringBounder stringBounder) { + if (isTopToBottom()) + return getPhalanx().calculateDimension(stringBounder).getWidth(); return getPhalanx().calculateDimension(stringBounder).getHeight(); } public double getPhalanxElongation(StringBounder stringBounder) { + if (isTopToBottom()) + return getPhalanx().calculateDimension(stringBounder).getHeight(); return getPhalanx().calculateDimension(stringBounder).getWidth(); } @@ -192,14 +224,17 @@ public class FingerImpl implements Finger, UDrawable { Colors.empty().add(ColorType.BACK, idea.getBackColor())); final TextBlock box = FtileBoxOld.createMindMap(style, foo, idea.getLabel()); final ClockwiseTopRightBottomLeft margin = getMargin(); - return TextBlockUtils.withMargin(box, 0, 0, margin.getTop(), margin.getBottom()); + if (isTopToBottom()) + return TextBlockUtils.withMargin(box, margin.getLeft(), margin.getRight(), 0, 0); + else + return TextBlockUtils.withMargin(box, 0, 0, margin.getTop(), margin.getBottom()); } assert idea.getShape() == IdeaShape.NONE; final TextBlock text = idea.getLabel().create0( style.getFontConfiguration(skinParam.getThemeStyle(), skinParam.getIHtmlColorSet()), style.getHorizontalAlignment(), skinParam, style.wrapWidth(), CreoleMode.FULL, null, null); - if (direction == Direction.RIGHT) + if (direction == 1) return TextBlockUtils.withMargin(text, 3, 0, 1, 1); return TextBlockUtils.withMargin(text, 0, 3, 1, 1); @@ -227,7 +262,6 @@ public class FingerImpl implements Finger, UDrawable { public double getFullThickness(StringBounder stringBounder) { final double thickness1 = getPhalanxThickness(stringBounder); final double thickness2 = getNailThickness(stringBounder); - // System.err.println("thickness1=" + thickness1 + " thickness2=" + thickness2); return Math.max(thickness1, thickness2); } diff --git a/src/net/sourceforge/plantuml/mindmap/MindMap.java b/src/net/sourceforge/plantuml/mindmap/MindMap.java index 03094aa09..c34424213 100644 --- a/src/net/sourceforge/plantuml/mindmap/MindMap.java +++ b/src/net/sourceforge/plantuml/mindmap/MindMap.java @@ -35,13 +35,12 @@ */ package net.sourceforge.plantuml.mindmap; -import net.sourceforge.plantuml.awt.geom.Dimension2D; - import net.sourceforge.plantuml.Dimension2DDouble; -import net.sourceforge.plantuml.Direction; import net.sourceforge.plantuml.ISkinParam; +import net.sourceforge.plantuml.awt.geom.Dimension2D; import net.sourceforge.plantuml.command.CommandExecutionResult; import net.sourceforge.plantuml.cucadiagram.Display; +import net.sourceforge.plantuml.cucadiagram.Rankdir; import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.UDrawable; import net.sourceforge.plantuml.style.NoStyleAvailableException; @@ -51,8 +50,8 @@ import net.sourceforge.plantuml.ugraphic.color.HColor; public class MindMap implements UDrawable { - private final Branch left = new Branch(); - private final Branch right = new Branch(); + private final Branch regular = new Branch(); + private final Branch reverse = new Branch(); private final ISkinParam skinParam; @@ -61,66 +60,71 @@ public class MindMap implements UDrawable { } private void computeFinger() { - if (this.left.hasFinger() == false && this.right.hasFinger() == false) { - if (this.left.hasChildren()) - left.initFinger(skinParam, Direction.LEFT); + if (this.reverse.hasFinger() == false && this.regular.hasFinger() == false) { + if (this.reverse.hasChildren()) + reverse.initFinger(skinParam, false); - if (this.left.hasFinger() == false || this.right.hasChildren()) - right.initFinger(skinParam, Direction.RIGHT); + if (this.reverse.hasFinger() == false || this.regular.hasChildren()) + regular.initFinger(skinParam, true); - if (this.left.hasFinger() && this.right.hasFinger()) - this.left.doNotDrawFirstPhalanx(); + if (this.reverse.hasFinger() && this.regular.hasFinger()) + this.reverse.doNotDrawFirstPhalanx(); } } Dimension2D calculateDimension(StringBounder stringBounder) { this.computeFinger(); - final double y1 = this.right.getHalfThickness(stringBounder); - final double y2 = this.left.getHalfThickness(stringBounder); + final double y1 = this.regular.getHalfThickness(stringBounder); + final double y2 = this.reverse.getHalfThickness(stringBounder); final double y = Math.max(y1, y2); - final double x = this.left.getFullElongation(stringBounder); - - final double width = x + this.right.getFullElongation(stringBounder); + final double width = this.reverse.getX12(stringBounder) + this.regular.getX12(stringBounder); final double height = y - + Math.max(this.left.getHalfThickness(stringBounder), this.right.getHalfThickness(stringBounder)); - return new Dimension2DDouble(width, height); + + Math.max(this.reverse.getHalfThickness(stringBounder), this.regular.getHalfThickness(stringBounder)); + if (skinParam.getRankdir() == Rankdir.TOP_TO_BOTTOM) + return new Dimension2DDouble(height, width); + else + return new Dimension2DDouble(width, height); } @Override public void drawU(UGraphic ug) { - if (this.left.hasRoot() == false && this.right.hasRoot() == false) + if (this.reverse.hasRoot() == false && this.regular.hasRoot() == false) return; this.computeFinger(); final StringBounder stringBounder = ug.getStringBounder(); - final double y1 = this.right.getHalfThickness(stringBounder); - final double y2 = this.left.getHalfThickness(stringBounder); + final double y1 = this.regular.getHalfThickness(stringBounder); + final double y2 = this.reverse.getHalfThickness(stringBounder); final double y = Math.max(y1, y2); - final double x = this.left.getX12(stringBounder); - this.right.drawU(ug.apply(new UTranslate(x, y))); - this.left.drawU(ug.apply(new UTranslate(x, y))); + final double x = this.reverse.getX12(stringBounder); + if (skinParam.getRankdir() == Rankdir.TOP_TO_BOTTOM) + ug = ug.apply(new UTranslate(y, x)); + else + ug = ug.apply(new UTranslate(x, y)); + this.regular.drawU(ug); + this.reverse.drawU(ug); } CommandExecutionResult addIdeaInternal(String stereotype, HColor backColor, int level, Display label, - IdeaShape shape, Direction direction) { + IdeaShape shape, boolean direction) { try { - if (this.left.hasRoot() == false && this.right.hasRoot() == false) + if (this.reverse.hasRoot() == false && this.regular.hasRoot() == false) level = 0; if (level == 0) { - this.right.initRoot(skinParam.getCurrentStyleBuilder(), backColor, label, shape, stereotype); - this.left.initRoot(skinParam.getCurrentStyleBuilder(), backColor, label, shape, stereotype); + this.regular.initRoot(skinParam.getCurrentStyleBuilder(), backColor, label, shape, stereotype); + this.reverse.initRoot(skinParam.getCurrentStyleBuilder(), backColor, label, shape, stereotype); return CommandExecutionResult.ok(); } - if (direction == Direction.LEFT) - return this.left.add(skinParam.getCurrentStyleBuilder(), backColor, level, label, shape, stereotype); + if (direction == false) + return this.reverse.add(skinParam.getCurrentStyleBuilder(), backColor, level, label, shape, stereotype); - return this.right.add(skinParam.getCurrentStyleBuilder(), backColor, level, label, shape, stereotype); + return this.regular.add(skinParam.getCurrentStyleBuilder(), backColor, level, label, shape, stereotype); } catch (NoStyleAvailableException e) { // e.printStackTrace(); return CommandExecutionResult.error("General failure: no style available."); @@ -128,7 +132,7 @@ public class MindMap implements UDrawable { } boolean isFull(int level) { - return level == 0 && this.right.hasRoot(); + return level == 0 && this.regular.hasRoot(); } } diff --git a/src/net/sourceforge/plantuml/mindmap/MindMapDiagram.java b/src/net/sourceforge/plantuml/mindmap/MindMapDiagram.java index 8a0e8271e..f08cc7cbd 100644 --- a/src/net/sourceforge/plantuml/mindmap/MindMapDiagram.java +++ b/src/net/sourceforge/plantuml/mindmap/MindMapDiagram.java @@ -35,7 +35,6 @@ */ package net.sourceforge.plantuml.mindmap; -import net.sourceforge.plantuml.awt.geom.Dimension2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.OutputStream; @@ -45,14 +44,17 @@ import java.util.List; import net.sourceforge.plantuml.Dimension2DDouble; import net.sourceforge.plantuml.Direction; import net.sourceforge.plantuml.FileFormatOption; +import net.sourceforge.plantuml.SkinParam; import net.sourceforge.plantuml.UmlDiagram; import net.sourceforge.plantuml.UmlDiagramType; import net.sourceforge.plantuml.api.ThemeStyle; +import net.sourceforge.plantuml.awt.geom.Dimension2D; import net.sourceforge.plantuml.command.CommandExecutionResult; import net.sourceforge.plantuml.core.DiagramDescription; import net.sourceforge.plantuml.core.ImageData; import net.sourceforge.plantuml.core.UmlSource; import net.sourceforge.plantuml.cucadiagram.Display; +import net.sourceforge.plantuml.cucadiagram.Rankdir; import net.sourceforge.plantuml.graphic.InnerStrategy; import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.svek.TextBlockBackcolored; @@ -65,10 +67,10 @@ public class MindMapDiagram extends UmlDiagram { private final List mindmaps = new ArrayList<>(); - private Direction defaultDirection = Direction.RIGHT; + private boolean defaultDirection = true; - public final void setDefaultDirection(Direction defaultDirection) { - this.defaultDirection = defaultDirection; + public final void setDefaultDirection(Direction direction) { + this.defaultDirection = direction == Direction.RIGHT || direction == Direction.DOWN; } public DiagramDescription getDescription() { @@ -77,6 +79,7 @@ public class MindMapDiagram extends UmlDiagram { public MindMapDiagram(ThemeStyle style, UmlSource source) { super(style, source, UmlDiagramType.MINDMAP, null); + ((SkinParam) getSkinParam()).setRankdir(Rankdir.LEFT_TO_RIGHT); this.mindmaps.add(new MindMap(getSkinParam())); } @@ -132,7 +135,7 @@ public class MindMapDiagram extends UmlDiagram { } public CommandExecutionResult addIdea(HColor backColor, int level, Display label, IdeaShape shape, - Direction direction) { + boolean direction) { String stereotype = label.getEndingStereotype(); if (stereotype != null) label = label.removeEndingStereotype(); diff --git a/src/net/sourceforge/plantuml/mindmap/MindMapDiagramFactory.java b/src/net/sourceforge/plantuml/mindmap/MindMapDiagramFactory.java index 62db0fb93..15b66005d 100644 --- a/src/net/sourceforge/plantuml/mindmap/MindMapDiagramFactory.java +++ b/src/net/sourceforge/plantuml/mindmap/MindMapDiagramFactory.java @@ -41,8 +41,9 @@ import java.util.List; import net.sourceforge.plantuml.ISkinSimple; import net.sourceforge.plantuml.api.ThemeStyle; import net.sourceforge.plantuml.command.Command; -import net.sourceforge.plantuml.command.PSystemCommandFactory; +import net.sourceforge.plantuml.command.CommandRankDir; import net.sourceforge.plantuml.command.CommonCommands; +import net.sourceforge.plantuml.command.PSystemCommandFactory; import net.sourceforge.plantuml.core.DiagramType; import net.sourceforge.plantuml.core.UmlSource; @@ -58,6 +59,7 @@ public class MindMapDiagramFactory extends PSystemCommandFactory { final List cmds = new ArrayList<>(); CommonCommands.addCommonCommands1(cmds); // cmds.add(new CommandMindMapTabulation()); + cmds.add(new CommandRankDir()); cmds.add(new CommandMindMapOrgmode()); cmds.add(new CommandMindMapOrgmodeMultiline()); cmds.add(new CommandMindMapRoot()); diff --git a/src/net/sourceforge/plantuml/tim/TContext.java b/src/net/sourceforge/plantuml/tim/TContext.java index 37c8a2efd..92ae88c72 100644 --- a/src/net/sourceforge/plantuml/tim/TContext.java +++ b/src/net/sourceforge/plantuml/tim/TContext.java @@ -110,6 +110,7 @@ import net.sourceforge.plantuml.tim.stdlib.IsLight; import net.sourceforge.plantuml.tim.stdlib.JsonKeyExists; import net.sourceforge.plantuml.tim.stdlib.Lighten; import net.sourceforge.plantuml.tim.stdlib.LoadJson; +import net.sourceforge.plantuml.tim.stdlib.LoadJsonLegacy; import net.sourceforge.plantuml.tim.stdlib.LogicalNot; import net.sourceforge.plantuml.tim.stdlib.Lower; import net.sourceforge.plantuml.tim.stdlib.Newline; @@ -184,6 +185,7 @@ public class TContext { functionsSet.addFunction(new Dec2hex()); functionsSet.addFunction(new HslColor()); functionsSet.addFunction(new LoadJson()); + functionsSet.addFunction(new LoadJsonLegacy()); functionsSet.addFunction(new Chr()); functionsSet.addFunction(new Size()); functionsSet.addFunction(new GetJsonKey()); diff --git a/src/net/sourceforge/plantuml/tim/stdlib/LoadJson.java b/src/net/sourceforge/plantuml/tim/stdlib/LoadJson.java index da0b7c10d..f2020ee39 100755 --- a/src/net/sourceforge/plantuml/tim/stdlib/LoadJson.java +++ b/src/net/sourceforge/plantuml/tim/stdlib/LoadJson.java @@ -67,24 +67,24 @@ import net.sourceforge.plantuml.tim.expression.TValue; *
  *     @ startuml
  *     ' loads a local file
- *     !$JSON_LOCAL_RELATIVE=%loadJSON("file.json")
+ *     !$JSON_LOCAL_RELATIVE=%load_json("file.json")
  *
  *     ' loads a local file from an absolute file path
- *     !$JSON_LOCAL_ABS=%loadJSON("c:/loaded/data/file.json")
+ *     !$JSON_LOCAL_ABS=%load_json("c:/loaded/data/file.json")
  *
  *     ' tries to load a local file and returns an empty JSON
- *     !$JSON_LOCAL_REL_EMPTY=%loadJSON("file-not-existing.json")
+ *     !$JSON_LOCAL_REL_EMPTY=%load_json("file-not-existing.json")
  *
  *     ' tries to load a local file and returns an default JSON
  *     !$DEF_JSON={"status":"No data found"}
- *     !$JSON_LOCAL_REL_DEF=%loadJSON("file-not-existing.json", $DEF_JSON)
+ *     !$JSON_LOCAL_REL_DEF=%load_json("file-not-existing.json", $DEF_JSON)
  *
  *     ' loads a local file with a specific charset (default is UTF-8)
- *     !$JSON_LOCAL_RELATIVE_CHARSET=%loadJSON("file.json", "{}", "iso-8859-1")
+ *     !$JSON_LOCAL_RELATIVE_CHARSET=%load_json("file.json", "{}", "iso-8859-1")
  *
  *     ' loads a remote JSON from an endpoint (and default, if not reachable)
  *     !$STATUS_NO_CONNECTION={"status": "No connection"}
- *     !$JSON_REMOTE_DEF=%loadJSON("https://localhost:7778/management/health", $STATUS_NO_CONNECTION)
+ *     !$JSON_REMOTE_DEF=%load_json("https://localhost:7778/management/health", $STATUS_NO_CONNECTION)
  *     status -> $JSON_REMOTE_DEF.status
  *     @ enduml
  * 
@@ -98,7 +98,7 @@ public class LoadJson extends SimpleReturnFunction { private static final String VALUE_DEFAULT_DEFAULT = "{}"; public TFunctionSignature getSignature() { - return new TFunctionSignature("%loadJSON", 3); + return new TFunctionSignature("%load_json", 3); } public boolean canCover(int nbArg, Set namedArgument) { diff --git a/src/net/sourceforge/plantuml/tim/stdlib/LoadJsonLegacy.java b/src/net/sourceforge/plantuml/tim/stdlib/LoadJsonLegacy.java new file mode 100644 index 000000000..09468824f --- /dev/null +++ b/src/net/sourceforge/plantuml/tim/stdlib/LoadJsonLegacy.java @@ -0,0 +1,190 @@ +/* ======================================================================== + * PlantUML : a free UML diagram generator + * ======================================================================== + * + * (C) Copyright 2009-2021, 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.tim.stdlib; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.sourceforge.plantuml.FileSystem; +import net.sourceforge.plantuml.FileUtils; +import net.sourceforge.plantuml.LineLocation; +import net.sourceforge.plantuml.json.Json; +import net.sourceforge.plantuml.json.JsonValue; +import net.sourceforge.plantuml.json.ParseException; +import net.sourceforge.plantuml.security.SFile; +import net.sourceforge.plantuml.security.SURL; +import net.sourceforge.plantuml.tim.EaterException; +import net.sourceforge.plantuml.tim.EaterExceptionLocated; +import net.sourceforge.plantuml.tim.TContext; +import net.sourceforge.plantuml.tim.TFunctionSignature; +import net.sourceforge.plantuml.tim.TMemory; +import net.sourceforge.plantuml.tim.expression.TValue; + +/** + * Loads JSON data from file or URL source. + *

+ * Supports three parameters for datasource, default JSON value and charset. The + * datasource will be checked against the security rules. + *

+ * Examples:
+ * + *

+ *     @ startuml
+ *     ' loads a local file
+ *     !$JSON_LOCAL_RELATIVE=%loadJSON("file.json")
+ *
+ *     ' loads a local file from an absolute file path
+ *     !$JSON_LOCAL_ABS=%loadJSON("c:/loaded/data/file.json")
+ *
+ *     ' tries to load a local file and returns an empty JSON
+ *     !$JSON_LOCAL_REL_EMPTY=%loadJSON("file-not-existing.json")
+ *
+ *     ' tries to load a local file and returns an default JSON
+ *     !$DEF_JSON={"status":"No data found"}
+ *     !$JSON_LOCAL_REL_DEF=%loadJSON("file-not-existing.json", $DEF_JSON)
+ *
+ *     ' loads a local file with a specific charset (default is UTF-8)
+ *     !$JSON_LOCAL_RELATIVE_CHARSET=%loadJSON("file.json", "{}", "iso-8859-1")
+ *
+ *     ' loads a remote JSON from an endpoint (and default, if not reachable)
+ *     !$STATUS_NO_CONNECTION={"status": "No connection"}
+ *     !$JSON_REMOTE_DEF=%loadJSON("https://localhost:7778/management/health", $STATUS_NO_CONNECTION)
+ *     status -> $JSON_REMOTE_DEF.status
+ *     @ enduml
+ * 
+ * + * @author Aljoscha Rittner + */ +public class LoadJsonLegacy extends SimpleReturnFunction { + + private static final String VALUE_CHARSET_DEFAULT = "UTF-8"; + + private static final String VALUE_DEFAULT_DEFAULT = "{}"; + + public TFunctionSignature getSignature() { + return new TFunctionSignature("%loadJSON", 3); + } + + public boolean canCover(int nbArg, Set namedArgument) { + return nbArg == 1 || nbArg == 2 || nbArg == 3; + } + + public TValue executeReturnFunction(TContext context, TMemory memory, LineLocation location, List values, + Map named) throws EaterException, EaterExceptionLocated { + final String path = values.get(0).toString(); + try { + String data = loadStringData(path, getCharset(values)); + if (data == null) + data = getDefaultJson(values); + + JsonValue jsonValue = Json.parse(data); + return TValue.fromJson(jsonValue); + } catch (ParseException pe) { + pe.printStackTrace(); + throw EaterException.unlocated("JSON parse issue in source " + path + " on location " + pe.getLocation()); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + throw EaterException.unlocated("JSON encoding issue in source " + path + ": " + e.getMessage()); + } + } + + /** + * Returns the JSON default, if the data source contains no data. + * + * @param values value parameters + * @return the defined default JSON or {@code "{}"} + */ + private String getDefaultJson(List values) { + if (values.size() > 1) + return values.get(1).toString(); + + return VALUE_DEFAULT_DEFAULT; + } + + /** + * Returns the charset name (if set) + * + * @param values value parameters + * @return defined charset or {@code "UTF-8"} + */ + private String getCharset(List values) { + if (values.size() == 3) + return values.get(2).toString(); + + return VALUE_CHARSET_DEFAULT; + } + + /** + * Loads String data from a data source {@code path} (file or URL) and expects + * the data encoded in {@code charset}. + * + * @param path path to data source (http(s)-URL or file). + * @param charset character set to encode the string data + * @return the decoded String from the data source + * @throws EaterException if something went wrong on reading data + */ + private String loadStringData(String path, String charset) throws EaterException, UnsupportedEncodingException { + + byte[] byteData = null; + if (path.startsWith("http://") || path.startsWith("https://")) { + final SURL url = SURL.create(path); + if (url == null) + throw EaterException.located("load JSON: Invalid URL " + path); + byteData = url.getBytes(); + } else { + try { + final SFile file = FileSystem.getInstance().getFile(path); + if (file != null && file.exists() && file.canRead() && !file.isDirectory()) { + final ByteArrayOutputStream out = new ByteArrayOutputStream(1024 * 8); + FileUtils.copyToStream(file, out); + byteData = out.toByteArray(); + } + } catch (IOException e) { + e.printStackTrace(); + throw EaterException.located("load JSON: Cannot read file " + path + ". " + e.getMessage()); + } + } + + if (byteData == null || byteData.length == 0) + return null; // no length, no data (we want the default) + + return new String(byteData, charset); + + } +}