From e1e6bcc5c8a4724238ebc9ef1ffd6b5ed0951f91 Mon Sep 17 00:00:00 2001 From: Arnaud Roques Date: Tue, 20 Apr 2021 22:19:49 +0200 Subject: [PATCH] Improve ELK support --- .../sourceforge/plantuml/TitledDiagram.java | 3 + .../cucadiagram/dot/Neighborhood.java | 6 +- .../plantuml/elk/CucaDiagramFileMakerElk.java | 217 +++++++++++++---- src/net/sourceforge/plantuml/elk/ElkPath.java | 69 +++++- .../plantuml/elk/proxy/EnumProxy.java | 7 + .../plantuml/elk/proxy/Reflect.java | 227 ++++++++++++++++++ .../core/RecursiveGraphLayoutEngine.java | 15 ++ .../elk/proxy/core/options/CoreOptions.java | 16 ++ .../elk/proxy/core/options/Direction.java | 9 + .../core/options/EdgeLabelPlacement.java | 15 ++ .../proxy/core/options/HierarchyHandling.java | 9 + .../core/options/NodeLabelPlacement.java | 15 ++ .../proxy/core/options/SizeConstraint.java | 15 ++ .../core/util/NullElkProgressMonitor.java | 10 + .../elk/proxy/graph/ElkBendPoint.java | 34 +++ .../plantuml/elk/proxy/graph/ElkEdge.java | 41 ++++ .../elk/proxy/graph/ElkEdgeSection.java | 55 +++++ .../plantuml/elk/proxy/graph/ElkLabel.java | 31 +++ .../plantuml/elk/proxy/graph/ElkNode.java | 51 ++++ .../elk/proxy/graph/ElkWithProperty.java | 49 ++++ .../elk/proxy/graph/util/ElkGraphUtil.java | 31 +++ .../plantuml/svek/Bibliotekon.java | 30 +-- .../sourceforge/plantuml/svek/Cluster.java | 45 ++-- .../plantuml/svek/ClusterDecoration.java | 11 +- .../plantuml/svek/DotStringFactory.java | 14 +- .../plantuml/svek/GeneralImageBuilder.java | 25 +- .../svek/{Line.java => SvekLine.java} | 6 +- .../sourceforge/plantuml/svek/SvekResult.java | 2 +- .../EntityImageLollipopInterfaceEye1.java | 6 +- .../plantuml/svek/image/EntityImageNote.java | 6 +- 30 files changed, 934 insertions(+), 136 deletions(-) create mode 100644 src/net/sourceforge/plantuml/elk/proxy/EnumProxy.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/Reflect.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/core/RecursiveGraphLayoutEngine.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/core/options/CoreOptions.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/core/options/Direction.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/core/options/EdgeLabelPlacement.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/core/options/HierarchyHandling.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/core/options/NodeLabelPlacement.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/core/options/SizeConstraint.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/core/util/NullElkProgressMonitor.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/graph/ElkBendPoint.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/graph/ElkEdge.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/graph/ElkEdgeSection.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/graph/ElkLabel.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/graph/ElkNode.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/graph/ElkWithProperty.java create mode 100644 src/net/sourceforge/plantuml/elk/proxy/graph/util/ElkGraphUtil.java rename src/net/sourceforge/plantuml/svek/{Line.java => SvekLine.java} (99%) diff --git a/src/net/sourceforge/plantuml/TitledDiagram.java b/src/net/sourceforge/plantuml/TitledDiagram.java index cdd5eb22a..6e0eae99f 100644 --- a/src/net/sourceforge/plantuml/TitledDiagram.java +++ b/src/net/sourceforge/plantuml/TitledDiagram.java @@ -56,6 +56,7 @@ import net.sourceforge.plantuml.ugraphic.ImageBuilder; public abstract class TitledDiagram extends AbstractPSystem implements Diagram, Annotated { public static boolean FORCE_SMETANA = false; + public static boolean FORCE_ELK = false; private DisplayPositionned title = DisplayPositionned.none(HorizontalAlignment.CENTER, VerticalAlignment.TOP); @@ -213,6 +214,8 @@ public abstract class TitledDiagram extends AbstractPSystem implements Diagram, } public boolean isUseElk() { + if (FORCE_ELK) + return true; return this.useElk; } diff --git a/src/net/sourceforge/plantuml/cucadiagram/dot/Neighborhood.java b/src/net/sourceforge/plantuml/cucadiagram/dot/Neighborhood.java index a4d440a76..9ae6fdfcf 100644 --- a/src/net/sourceforge/plantuml/cucadiagram/dot/Neighborhood.java +++ b/src/net/sourceforge/plantuml/cucadiagram/dot/Neighborhood.java @@ -46,7 +46,7 @@ import java.util.Set; import net.sourceforge.plantuml.cucadiagram.ILeaf; import net.sourceforge.plantuml.cucadiagram.Link; import net.sourceforge.plantuml.svek.Bibliotekon; -import net.sourceforge.plantuml.svek.Line; +import net.sourceforge.plantuml.svek.SvekLine; import net.sourceforge.plantuml.ugraphic.UGraphic; import net.sourceforge.plantuml.ugraphic.ULine; import net.sourceforge.plantuml.ugraphic.UPolygon; @@ -68,7 +68,7 @@ public class Neighborhood { public void drawU(UGraphic ug, double minX, double minY, Bibliotekon bibliotekon, Dimension2D shapeDim) { final Set contactPoints = new HashSet(); for (Link link : sametailLinks) { - final Line line = bibliotekon.getLine(link); + final SvekLine line = bibliotekon.getLine(link); final Point2D contact = line.getStartContactPoint(); contactPoints.add(contact); } @@ -90,7 +90,7 @@ public class Neighborhood { } for (Link link : allButSametails) { - final Line line = bibliotekon.getLine(link); + final SvekLine line = bibliotekon.getLine(link); final Point2D contact = link.getEntity1() == leaf ? line.getStartContactPoint() : line.getEndContactPoint(); if (contact == null) { assert false; diff --git a/src/net/sourceforge/plantuml/elk/CucaDiagramFileMakerElk.java b/src/net/sourceforge/plantuml/elk/CucaDiagramFileMakerElk.java index 323a33f3a..5ad393890 100644 --- a/src/net/sourceforge/plantuml/elk/CucaDiagramFileMakerElk.java +++ b/src/net/sourceforge/plantuml/elk/CucaDiagramFileMakerElk.java @@ -47,34 +47,61 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import org.eclipse.elk.core.RecursiveGraphLayoutEngine; -import org.eclipse.elk.core.options.CoreOptions; -import org.eclipse.elk.core.options.Direction; -import org.eclipse.elk.core.options.EdgeLabelPlacement; -import org.eclipse.elk.core.options.EdgeType; -import org.eclipse.elk.core.options.NodeLabelPlacement; -import org.eclipse.elk.core.options.SizeConstraint; -import org.eclipse.elk.core.options.SizeOptions; -import org.eclipse.elk.core.util.NullElkProgressMonitor; -import org.eclipse.elk.graph.ElkEdge; -import org.eclipse.elk.graph.ElkLabel; -import org.eclipse.elk.graph.ElkNode; -import org.eclipse.elk.graph.util.ElkGraphUtil; - +import net.sourceforge.plantuml.AlignmentParam; import net.sourceforge.plantuml.FileFormatOption; import net.sourceforge.plantuml.FontParam; import net.sourceforge.plantuml.ISkinParam; import net.sourceforge.plantuml.StringUtils; import net.sourceforge.plantuml.UmlDiagram; +import net.sourceforge.plantuml.UmlDiagramType; +import net.sourceforge.plantuml.UseStyle; import net.sourceforge.plantuml.api.ImageDataSimple; import net.sourceforge.plantuml.core.ImageData; import net.sourceforge.plantuml.cucadiagram.CucaDiagram; import net.sourceforge.plantuml.cucadiagram.Display; import net.sourceforge.plantuml.cucadiagram.GroupType; +import net.sourceforge.plantuml.cucadiagram.IEntity; import net.sourceforge.plantuml.cucadiagram.IGroup; import net.sourceforge.plantuml.cucadiagram.ILeaf; import net.sourceforge.plantuml.cucadiagram.Link; import net.sourceforge.plantuml.cucadiagram.entity.EntityFactory; + +/* + * You can choose between real "org.eclipse.elk..." classes or proxied "net.sourceforge.plantuml.elk.proxy..." + * + * Using proxied classes allows to compile PlantUML without having ELK available on the classpath. + * Since GraphViz is the default layout engine up to now, we do not want to enforce the use of ELK just for compilation. + * (for people not using maven) + * + * If you are debugging, you should probably switch to "org.eclipse.elk..." classes + * + */ + +/* +import org.eclipse.elk.core.RecursiveGraphLayoutEngine; +import org.eclipse.elk.core.options.CoreOptions; +import org.eclipse.elk.core.options.Direction; +import org.eclipse.elk.core.options.EdgeLabelPlacement; +import org.eclipse.elk.core.options.HierarchyHandling; +import org.eclipse.elk.core.options.NodeLabelPlacement; +import org.eclipse.elk.core.util.NullElkProgressMonitor; +import org.eclipse.elk.graph.ElkEdge; +import org.eclipse.elk.graph.ElkLabel; +import org.eclipse.elk.graph.ElkNode; +import org.eclipse.elk.graph.util.ElkGraphUtil; +*/ + +import net.sourceforge.plantuml.elk.proxy.core.RecursiveGraphLayoutEngine; +import net.sourceforge.plantuml.elk.proxy.core.options.CoreOptions; +import net.sourceforge.plantuml.elk.proxy.core.options.Direction; +import net.sourceforge.plantuml.elk.proxy.core.options.EdgeLabelPlacement; +import net.sourceforge.plantuml.elk.proxy.core.options.HierarchyHandling; +import net.sourceforge.plantuml.elk.proxy.core.options.NodeLabelPlacement; +import net.sourceforge.plantuml.elk.proxy.core.util.NullElkProgressMonitor; +import net.sourceforge.plantuml.elk.proxy.graph.ElkEdge; +import net.sourceforge.plantuml.elk.proxy.graph.ElkLabel; +import net.sourceforge.plantuml.elk.proxy.graph.ElkNode; +import net.sourceforge.plantuml.elk.proxy.graph.util.ElkGraphUtil; import net.sourceforge.plantuml.graphic.AbstractTextBlock; import net.sourceforge.plantuml.graphic.FontConfiguration; import net.sourceforge.plantuml.graphic.HorizontalAlignment; @@ -82,12 +109,19 @@ import net.sourceforge.plantuml.graphic.QuoteUtils; import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.graphic.TextBlockUtils; +import net.sourceforge.plantuml.graphic.USymbol; +import net.sourceforge.plantuml.style.PName; +import net.sourceforge.plantuml.style.SName; +import net.sourceforge.plantuml.style.Style; import net.sourceforge.plantuml.svek.Bibliotekon; +import net.sourceforge.plantuml.svek.Cluster; +import net.sourceforge.plantuml.svek.ClusterDecoration; import net.sourceforge.plantuml.svek.CucaDiagramFileMaker; import net.sourceforge.plantuml.svek.DotStringFactory; import net.sourceforge.plantuml.svek.GeneralImageBuilder; import net.sourceforge.plantuml.svek.GraphvizCrash; import net.sourceforge.plantuml.svek.IEntityImage; +import net.sourceforge.plantuml.svek.PackageStyle; import net.sourceforge.plantuml.svek.TextBlockBackcolored; import net.sourceforge.plantuml.ugraphic.MinMax; import net.sourceforge.plantuml.ugraphic.UGraphic; @@ -183,51 +217,114 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { } public void drawU(UGraphic ug) { + drawAllClusters(ug); + drawAllNodes(ug); + drawAllEdges(ug); + } - // Draw all clusters + private void drawAllClusters(UGraphic ug) { for (Entry ent : clusters.entrySet()) { - final IGroup group = ent.getKey(); - final ElkNode elkNode = ent.getValue(); - - final Point2D corner = getPosition(elkNode); - - final URectangle rect = new URectangle(elkNode.getWidth(), elkNode.getHeight()); - - // Print a simple rectangle right now - ug.apply(HColorUtils.BLACK).apply(new UStroke(1.5)).apply(new UTranslate(corner)).draw(rect); + drawSingleCluster(ug, ent.getKey(), ent.getValue()); } + } - // Draw all nodes + private void drawAllNodes(UGraphic ug) { for (Entry ent : nodes.entrySet()) { - final ILeaf leaf = ent.getKey(); - final ElkNode elkNode = ent.getValue(); - - final IEntityImage image = printEntityInternal(leaf); - - // Retrieve coord from ELK - final Point2D corner = getPosition(elkNode); - - // Print the node image at right coord - image.drawU(ug.apply(new UTranslate(corner))); + drawSingleNode(ug, ent.getKey(), ent.getValue()); } + } - // Draw all edges + private void drawAllEdges(UGraphic ug) { for (Entry ent : edges.entrySet()) { final Link link = ent.getKey(); if (link.isInvis()) { continue; } - final ElkEdge edge = ent.getValue(); + drawSingleEdge(ug, link, ent.getValue()); + } + } - // Unfortunately, we have to translate "edge" in its own "cluster" coordonate - final Point2D translate = getPosition(edge.getContainingNode()); - final UGraphic ugTranslated = ug.apply(new UTranslate(translate)); - - new ElkPath(link, edge, diagram, getLabel(link), getQualifier(link, 1), getQualifier(link, 2)) - .drawU(ugTranslated); + private void drawSingleCluster(UGraphic ug, IGroup group, ElkNode elkNode) { + final Point2D corner = getPosition(elkNode); + final URectangle rect = new URectangle(elkNode.getWidth(), elkNode.getHeight()); + PackageStyle packageStyle = group.getPackageStyle(); + final ISkinParam skinParam = diagram.getSkinParam(); + if (packageStyle == null) { + packageStyle = skinParam.packageStyle(); } + final UmlDiagramType umlDiagramType = diagram.getUmlDiagramType(); + + final double shadowing; + final UStroke stroke; + if (UseStyle.useBetaStyle()) { + final Style style = Cluster.getDefaultStyleDefinition(umlDiagramType.getStyleName()) + .getMergedStyle(skinParam.getCurrentStyleBuilder()); + shadowing = style.value(PName.Shadowing).asDouble(); + stroke = style.getStroke(); + } else { + if (group.getUSymbol() == null) { + shadowing = skinParam.shadowing2(group.getStereotype(), USymbol.PACKAGE.getSkinParameter()) ? 3 : 0; + } else { + shadowing = skinParam.shadowing2(group.getStereotype(), group.getUSymbol().getSkinParameter()) ? 3 + : 0; + } + stroke = Cluster.getStrokeInternal(group, skinParam); + } + HColor backColor = getBackColor(umlDiagramType); + backColor = Cluster.getBackColor(backColor, skinParam, group.getStereotype(), + umlDiagramType.getStyleName()); + + final double roundCorner = group.getUSymbol() == null ? 0 + : group.getUSymbol().getSkinParameter().getRoundCorner(skinParam, group.getStereotype()); + + final TextBlock ztitle = getTitleBlock(group); + final TextBlock zstereo = TextBlockUtils.empty(0, 0); + + final ClusterDecoration decoration = new ClusterDecoration(packageStyle, group.getUSymbol(), ztitle, + zstereo, 0, 0, elkNode.getWidth(), elkNode.getHeight(), stroke); + + final HColor borderColor = HColorUtils.BLACK; + decoration.drawU(ug.apply(new UTranslate(corner)), backColor, borderColor, shadowing, roundCorner, + skinParam.getHorizontalAlignment(AlignmentParam.packageTitleAlignment, null, false), + skinParam.getStereotypeAlignment()); + +// // Print a simple rectangle right now +// ug.apply(HColorUtils.BLACK).apply(new UStroke(1.5)).apply(new UTranslate(corner)).draw(rect); + } + + private TextBlock getTitleBlock(IGroup g) { + final Display label = g.getDisplay(); + if (label == null) { + return TextBlockUtils.empty(0, 0); + } + + final ISkinParam skinParam = diagram.getSkinParam(); + final FontConfiguration fontConfiguration = g.getFontConfigurationForTitle(skinParam); + return label.create(fontConfiguration, HorizontalAlignment.CENTER, skinParam); + } + + private HColor getBackColor(UmlDiagramType umlDiagramType) { + return null; + } + + private void drawSingleNode(UGraphic ug, ILeaf leaf, ElkNode elkNode) { + final IEntityImage image = printEntityInternal(leaf); + // Retrieve coord from ELK + final Point2D corner = getPosition(elkNode); + + // Print the node image at right coord + image.drawU(ug.apply(new UTranslate(corner))); + } + + private void drawSingleEdge(UGraphic ug, Link link, ElkEdge edge) { + // Unfortunately, we have to translate "edge" in its own "cluster" coordinate + final Point2D translate = getPosition(edge.getContainingNode()); + + final ElkPath elkPath = new ElkPath(diagram, SName.classDiagram, link, edge, getLabel(link), + getQualifier(link, 1), getQualifier(link, 2)); + elkPath.drawU(ug.apply(new UTranslate(translate))); } public Dimension2D calculateDimension(StringBounder stringBounder) { @@ -253,6 +350,14 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { return result; } + private ElkNode getElkNode(final IEntity entity) { + ElkNode node = nodes.get(entity); + if (node == null) { + node = clusters.get(entity); + } + return node; + } + @Override public ImageData createFile(OutputStream os, List dotStrings, FileFormatOption fileFormatOption) throws IOException { @@ -261,6 +366,7 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { try { final ElkNode root = ElkGraphUtil.createGraph(); root.setProperty(CoreOptions.DIRECTION, Direction.DOWN); + root.setProperty(CoreOptions.HIERARCHY_HANDLING, HierarchyHandling.INCLUDE_CHILDREN); printAllSubgroups(root, diagram.getRootGroup()); printEntities(root, getUnpackagedEntities()); @@ -360,20 +466,26 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { // No idea of what we are doing here :-) label.setProperty(CoreOptions.NODE_LABELS_PLACEMENT, EnumSet.of(NodeLabelPlacement.INSIDE, NodeLabelPlacement.H_CENTER, NodeLabelPlacement.V_CENTER)); + // This padding setting have no impact ? // label.setProperty(CoreOptions.NODE_LABELS_PADDING, new ElkPadding(100.0)); - node.setProperty(CoreOptions.NODE_SIZE_CONSTRAINTS, EnumSet.of(SizeConstraint.NODE_LABELS)); - node.setProperty(CoreOptions.NODE_SIZE_OPTIONS, EnumSet.noneOf(SizeOptions.class)); + + // final EnumSet constraints = + // EnumSet.of(SizeConstraint.NODE_LABELS); + // node.setProperty(CoreOptions.NODE_SIZE_CONSTRAINTS, constraints); + + // node.setProperty(CoreOptions.NODE_SIZE_OPTIONS, + // EnumSet.noneOf(SizeOptions.class)); // Let's store this nodes.put(leaf, node); } private void manageSingleEdge(final Link link) { - final ElkNode node1 = nodes.get(link.getEntity1()); - final ElkNode node2 = nodes.get(link.getEntity2()); + final ElkNode node1 = getElkNode(link.getEntity1()); + final ElkNode node2 = getElkNode(link.getEntity2()); - final ElkEdge edge = ElkGraphUtil.createEdge(node1.getParent()); + final ElkEdge edge = ElkGraphUtil.createSimpleEdge(node1, node2); final TextBlock labelLink = getLabel(link); if (labelLink != null) { @@ -383,7 +495,7 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { edgeLabel.setDimensions(dim.getWidth(), dim.getHeight()); // Duplicated, with qualifier, but who cares? edge.setProperty(CoreOptions.EDGE_LABELS_INLINE, true); - edge.setProperty(CoreOptions.EDGE_TYPE, EdgeType.ASSOCIATION); + // edge.setProperty(CoreOptions.EDGE_TYPE, EdgeType.ASSOCIATION); } if (link.getQualifier1() != null) { final ElkLabel edgeLabel = ElkGraphUtil.createLabel(edge); @@ -394,7 +506,7 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { edgeLabel.setProperty(CoreOptions.EDGE_LABELS_PLACEMENT, EdgeLabelPlacement.TAIL); // Duplicated, with main label, but who cares? edge.setProperty(CoreOptions.EDGE_LABELS_INLINE, true); - edge.setProperty(CoreOptions.EDGE_TYPE, EdgeType.ASSOCIATION); + // edge.setProperty(CoreOptions.EDGE_TYPE, EdgeType.ASSOCIATION); } if (link.getQualifier2() != null) { final ElkLabel edgeLabel = ElkGraphUtil.createLabel(edge); @@ -405,10 +517,9 @@ public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker { edgeLabel.setProperty(CoreOptions.EDGE_LABELS_PLACEMENT, EdgeLabelPlacement.HEAD); // Duplicated, with main label, but who cares? edge.setProperty(CoreOptions.EDGE_LABELS_INLINE, true); - edge.setProperty(CoreOptions.EDGE_TYPE, EdgeType.ASSOCIATION); + // edge.setProperty(CoreOptions.EDGE_TYPE, EdgeType.ASSOCIATION); } - edge.getSources().add(node1); - edge.getTargets().add(node2); + edges.put(link, edge); } diff --git a/src/net/sourceforge/plantuml/elk/ElkPath.java b/src/net/sourceforge/plantuml/elk/ElkPath.java index d4ee757ee..42456db70 100644 --- a/src/net/sourceforge/plantuml/elk/ElkPath.java +++ b/src/net/sourceforge/plantuml/elk/ElkPath.java @@ -35,27 +35,53 @@ */ package net.sourceforge.plantuml.elk; +import java.awt.geom.Point2D; +import java.util.Collection; +import java.util.List; + +/* + * You can choose between real "org.eclipse.elk..." classes or proxied "net.sourceforge.plantuml.elk.proxy..." + * + * Using proxied classes allows to compile PlantUML without having ELK available on the classpath. + * Since GraphViz is the default layout engine up to now, we do not want to enforce the use of ELK just for compilation. + * (for people not using maven) + * + * If you are debugging, you should probably switch to "org.eclipse.elk..." classes + * + */ + +/* import org.eclipse.elk.graph.ElkBendPoint; import org.eclipse.elk.graph.ElkEdge; import org.eclipse.elk.graph.ElkEdgeSection; import org.eclipse.elk.graph.ElkLabel; -import org.eclipse.emf.common.util.EList; +*/ + +import net.sourceforge.plantuml.elk.proxy.graph.ElkBendPoint; +import net.sourceforge.plantuml.elk.proxy.graph.ElkEdge; +import net.sourceforge.plantuml.elk.proxy.graph.ElkEdgeSection; +import net.sourceforge.plantuml.elk.proxy.graph.ElkLabel; import net.sourceforge.plantuml.ColorParam; import net.sourceforge.plantuml.LineParam; import net.sourceforge.plantuml.UmlDiagramType; import net.sourceforge.plantuml.cucadiagram.CucaDiagram; import net.sourceforge.plantuml.cucadiagram.Link; +import net.sourceforge.plantuml.cucadiagram.LinkDecor; import net.sourceforge.plantuml.cucadiagram.LinkType; import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.graphic.UDrawable; import net.sourceforge.plantuml.graphic.color.ColorType; import net.sourceforge.plantuml.skin.rose.Rose; +import net.sourceforge.plantuml.style.SName; +import net.sourceforge.plantuml.svek.extremity.ExtremityFactory; +import net.sourceforge.plantuml.svek.extremity.ExtremityFactoryExtends; import net.sourceforge.plantuml.ugraphic.UGraphic; import net.sourceforge.plantuml.ugraphic.ULine; import net.sourceforge.plantuml.ugraphic.UStroke; import net.sourceforge.plantuml.ugraphic.UTranslate; import net.sourceforge.plantuml.ugraphic.color.HColor; +import net.sourceforge.plantuml.ugraphic.color.HColorUtils; public class ElkPath implements UDrawable { @@ -67,9 +93,10 @@ public class ElkPath implements UDrawable { private final TextBlock headLabel; private final TextBlock tailLabel; private final Rose rose = new Rose(); + private final SName styleName; - public ElkPath(Link link, ElkEdge edge, CucaDiagram diagram, TextBlock centerLabel, TextBlock tailLabel, - TextBlock headLabel) { + public ElkPath(CucaDiagram diagram, SName styleName, Link link, ElkEdge edge, TextBlock centerLabel, + TextBlock tailLabel, TextBlock headLabel) { this.link = link; this.edge = edge; @@ -77,6 +104,8 @@ public class ElkPath implements UDrawable { this.centerLabel = centerLabel; this.tailLabel = tailLabel; this.headLabel = headLabel; + this.styleName = styleName; + } private ColorParam getArrowColorParam() { @@ -119,18 +148,46 @@ public class ElkPath implements UDrawable { } ug = ug.apply(stroke).apply(color); - final EList sections = edge.getSections(); + final List sections = edge.getSections(); if (sections.size() == 0) { System.err.println("Strange: no section?"); System.err.println("Maybe a 'Long hierarchical edge' " + edge.isHierarchical()); + return; } else { drawSections(ug, sections); } + final UDrawable extremityFactory1 = getDecors(link.getType().getDecor1(), Math.PI / 2, HColorUtils.WHITE); + final UDrawable extremityFactory2 = getDecors(link.getType().getDecor2(), -Math.PI / 2, HColorUtils.WHITE); + + if (extremityFactory1 != null) { + final double x = sections.get(0).getEndX(); + final double y = sections.get(0).getEndY(); + extremityFactory1.drawU(ug.apply(new UTranslate(x, y))); + } + + if (extremityFactory2 != null) { + final double x = sections.get(0).getStartX(); + final double y = sections.get(0).getStartY(); + extremityFactory2.drawU(ug.apply(new UTranslate(x, y))); + } + drawLabels(ug); } + private UDrawable getDecors(LinkDecor decors, double angle, HColor backColor) { + // For legacy reason, extends are treated differently + if (decors == LinkDecor.EXTENDS) { + return new ExtremityFactoryExtends(backColor).createUDrawable(new Point2D.Double(), angle, null); + } + final ExtremityFactory extremityFactory = decors.getExtremityFactory(backColor); + if (extremityFactory == null) { + return null; + } + return extremityFactory.createUDrawable(new Point2D.Double(), angle, null); + } + private void drawLabels(UGraphic ug) { for (ElkLabel label : edge.getLabels()) { final double x = label.getX(); @@ -151,9 +208,9 @@ public class ElkPath implements UDrawable { } } - private void drawSections(UGraphic ug, final EList sections) { + private void drawSections(UGraphic ug, final Collection sections) { for (ElkEdgeSection section : sections) { - final EList points = section.getBendPoints(); + final Collection points = section.getBendPoints(); double x1 = section.getStartX(); double y1 = section.getStartY(); diff --git a/src/net/sourceforge/plantuml/elk/proxy/EnumProxy.java b/src/net/sourceforge/plantuml/elk/proxy/EnumProxy.java new file mode 100644 index 000000000..fccda5454 --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/EnumProxy.java @@ -0,0 +1,7 @@ +package net.sourceforge.plantuml.elk.proxy; + +public interface EnumProxy { + + public Enum getTrueObject(); + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/Reflect.java b/src/net/sourceforge/plantuml/elk/proxy/Reflect.java new file mode 100644 index 000000000..a47754a60 --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/Reflect.java @@ -0,0 +1,227 @@ +/* ======================================================================== + * 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.elk.proxy; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/* + * Various methods to do Java introspection + */ +public class Reflect { + + public static Class clazz(String className) { + try { + return Class.forName(className); + } catch (Throwable t) { + t.printStackTrace(); + throw new IllegalArgumentException(t); + } + } + + public static Enum getEnum(String clazz, String name) { + try { + final Class cl = clazz(clazz); + for (Object en : cl.getEnumConstants()) { + if (en.toString().equals(name)) { + return (Enum) en; + } + } + throw new UnsupportedOperationException(name); + } catch (Throwable t) { + t.printStackTrace(); + throw new IllegalArgumentException(t); + } + } + + public static Object opt(String className, String fieldname) { + try { + final Class cl = Class.forName(className); + final Field field = cl.getField(fieldname); + return field.get(null); + } catch (Throwable t) { + t.printStackTrace(); + throw new IllegalArgumentException(t); + } + } + + public static Object newInstance(String className) { + try { + final Class cl = Class.forName(className); + return cl.newInstance(); + } catch (Throwable t) { + t.printStackTrace(); + throw new IllegalArgumentException(t); + } + } + + public static Object newInstance(String className, Object arg1) { + try { + final Class cl = Class.forName(className); + final Constructor m = cl.getConstructor(arg1.getClass()); + return m.newInstance(arg1); + } catch (Throwable t) { + t.printStackTrace(); + throw new IllegalArgumentException(t); + } + } + + public static Object field(String className, String fieldName) { + try { + final Class cl = Class.forName(className); + final Field f = cl.getField(fieldName); + return f.get(null); + } catch (Throwable t) { + t.printStackTrace(); + throw new IllegalArgumentException(t); + } + } + + public static Object callStatic(String className, String method) { + try { + final Class cl = Class.forName(className); + final Method m = cl.getMethod(method); + return m.invoke(null); + } catch (Throwable t) { + t.printStackTrace(); + throw new IllegalArgumentException(t); + } + } + + public static Object callStatic(String className, String method, Object arg1) { + try { + final Class cl = Class.forName(className); + final Method m = cl.getMethod(method, arg1.getClass()); + return m.invoke(null, arg1); + } catch (Throwable t) { + t.printStackTrace(); + throw new IllegalArgumentException(t); + } + } + + public static Object callStatic(String className, String method, Object arg1, Object arg2) { + try { + final Class cl = Class.forName(className); + final Method m = cl.getMethod(method, arg1.getClass(), arg2.getClass()); + return m.invoke(null, arg1, arg2); + } catch (Throwable t) { + t.printStackTrace(); + throw new IllegalArgumentException(t); + } + } + + public static Object callStatic2(String className, String method, Object arg1) { + try { + final Class cl = Class.forName(className); + final Method m = getStaticMethod(cl, method, 1); + return m.invoke(null, arg1); + } catch (Throwable t) { + t.printStackTrace(); + throw new IllegalArgumentException(t); + } + } + + public static Object callStatic2(String className, String method, Object arg1, Object arg2) { + try { + final Class cl = Class.forName(className); + final Method m = getStaticMethod(cl, method, 2); + return m.invoke(null, arg1, arg2); + } catch (Throwable t) { + t.printStackTrace(); + throw new IllegalArgumentException(t); + } + } + + public static Method getStaticMethod(Class cl, String method, int nbArgs) { + for (Method m : cl.getMethods()) { + if (m.getName().equals(method) && m.getParameters().length == nbArgs) { + return m; + } + } + throw new IllegalArgumentException(); + } + + public static Object call(Object instance, String method) { + try { + final Method m = instance.getClass().getMethod(method); + return m.invoke(instance); + } catch (Throwable t) { + t.printStackTrace(); + throw new IllegalArgumentException(t); + } + } + + public static Object call(Object instance, String method, Object arg1) { + try { + final Method m = instance.getClass().getMethod(method, arg1.getClass()); + return m.invoke(instance, arg1); + } catch (Throwable t) { + t.printStackTrace(); + throw new IllegalArgumentException(t); + } + } + + public static Object call(Object instance, String method, Object arg1, Object arg2) { + try { + final Method m = instance.getClass().getMethod(method, arg1.getClass(), arg2.getClass()); + return m.invoke(instance, arg1, arg2); + } catch (Throwable t) { + t.printStackTrace(); + throw new IllegalArgumentException(t); + } + } + + public static Object call2(Object instance, String method, Object arg1, Object arg2) { + try { + final Method m = getMethod(instance, method, 2); + return m.invoke(instance, arg1, arg2); + } catch (Throwable t) { + t.printStackTrace(); + throw new IllegalArgumentException(t); + } + } + + public static Method getMethod(Object instance, String method, int nbArgs) { + for (Method m : instance.getClass().getMethods()) { + if (m.getName().equals(method) && m.getParameters().length == nbArgs) { + return m; + } + } + throw new IllegalArgumentException(); + } + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/core/RecursiveGraphLayoutEngine.java b/src/net/sourceforge/plantuml/elk/proxy/core/RecursiveGraphLayoutEngine.java new file mode 100644 index 000000000..1bce5655c --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/core/RecursiveGraphLayoutEngine.java @@ -0,0 +1,15 @@ +package net.sourceforge.plantuml.elk.proxy.core; + +import net.sourceforge.plantuml.elk.proxy.Reflect; +import net.sourceforge.plantuml.elk.proxy.core.util.NullElkProgressMonitor; +import net.sourceforge.plantuml.elk.proxy.graph.ElkNode; + +public class RecursiveGraphLayoutEngine { + + private final Object obj = Reflect.newInstance("org.eclipse.elk.core.RecursiveGraphLayoutEngine"); + + public void layout(ElkNode root, NullElkProgressMonitor monitor) { + Reflect.call2(obj, "layout", root.obj, monitor.obj); + } + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/core/options/CoreOptions.java b/src/net/sourceforge/plantuml/elk/proxy/core/options/CoreOptions.java new file mode 100644 index 000000000..f830fe15a --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/core/options/CoreOptions.java @@ -0,0 +1,16 @@ +package net.sourceforge.plantuml.elk.proxy.core.options; + +import net.sourceforge.plantuml.elk.proxy.Reflect; + +public class CoreOptions { + + public static final Object DIRECTION = Reflect.field("org.eclipse.elk.core.options.CoreOptions", "DIRECTION"); + public static final Object EDGE_LABELS_INLINE = Reflect.field("org.eclipse.elk.core.options.CoreOptions", "EDGE_LABELS_INLINE"); + public static final Object NODE_SIZE_CONSTRAINTS = Reflect.field("org.eclipse.elk.core.options.CoreOptions", "NODE_SIZE_CONSTRAINTS"); + public static final Object HIERARCHY_HANDLING = Reflect.field("org.eclipse.elk.core.options.CoreOptions", "HIERARCHY_HANDLING"); + public static final Object EDGE_LABELS_PLACEMENT = Reflect.field("org.eclipse.elk.core.options.CoreOptions", "EDGE_LABELS_PLACEMENT"); + public static final Object EDGE_TYPE = Reflect.field("org.eclipse.elk.core.options.CoreOptions", "EDGE_TYPE"); + public static final Object NODE_LABELS_PLACEMENT = Reflect.field("org.eclipse.elk.core.options.CoreOptions", "NODE_LABELS_PLACEMENT"); + public static final Object NODE_SIZE_OPTIONS = Reflect.field("org.eclipse.elk.core.options.CoreOptions", "NODE_SIZE_OPTIONS"); + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/core/options/Direction.java b/src/net/sourceforge/plantuml/elk/proxy/core/options/Direction.java new file mode 100644 index 000000000..6b42a96c3 --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/core/options/Direction.java @@ -0,0 +1,9 @@ +package net.sourceforge.plantuml.elk.proxy.core.options; + +import net.sourceforge.plantuml.elk.proxy.Reflect; + +public class Direction { + + public static final Object DOWN = Reflect.field("org.eclipse.elk.core.options.Direction", "DOWN"); + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/core/options/EdgeLabelPlacement.java b/src/net/sourceforge/plantuml/elk/proxy/core/options/EdgeLabelPlacement.java new file mode 100644 index 000000000..fced3ac14 --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/core/options/EdgeLabelPlacement.java @@ -0,0 +1,15 @@ +package net.sourceforge.plantuml.elk.proxy.core.options; + +import net.sourceforge.plantuml.elk.proxy.EnumProxy; +import net.sourceforge.plantuml.elk.proxy.Reflect; + +public enum EdgeLabelPlacement implements EnumProxy { + + TAIL, HEAD; + + @Override + public Enum getTrueObject() { + return Reflect.getEnum("org.eclipse.elk.core.options.EdgeLabelPlacement", name()); + } + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/core/options/HierarchyHandling.java b/src/net/sourceforge/plantuml/elk/proxy/core/options/HierarchyHandling.java new file mode 100644 index 000000000..c6383c37c --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/core/options/HierarchyHandling.java @@ -0,0 +1,9 @@ +package net.sourceforge.plantuml.elk.proxy.core.options; + +import net.sourceforge.plantuml.elk.proxy.Reflect; + +public class HierarchyHandling { + + public static final Object INCLUDE_CHILDREN = Reflect.field("org.eclipse.elk.core.options.HierarchyHandling", "INCLUDE_CHILDREN"); + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/core/options/NodeLabelPlacement.java b/src/net/sourceforge/plantuml/elk/proxy/core/options/NodeLabelPlacement.java new file mode 100644 index 000000000..9691162e8 --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/core/options/NodeLabelPlacement.java @@ -0,0 +1,15 @@ +package net.sourceforge.plantuml.elk.proxy.core.options; + +import net.sourceforge.plantuml.elk.proxy.EnumProxy; +import net.sourceforge.plantuml.elk.proxy.Reflect; + +public enum NodeLabelPlacement implements EnumProxy { + + INSIDE, V_CENTER, H_CENTER; + + @Override + public Enum getTrueObject() { + return Reflect.getEnum("org.eclipse.elk.core.options.NodeLabelPlacement", name()); + } + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/core/options/SizeConstraint.java b/src/net/sourceforge/plantuml/elk/proxy/core/options/SizeConstraint.java new file mode 100644 index 000000000..4d1715403 --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/core/options/SizeConstraint.java @@ -0,0 +1,15 @@ +package net.sourceforge.plantuml.elk.proxy.core.options; + +import net.sourceforge.plantuml.elk.proxy.EnumProxy; +import net.sourceforge.plantuml.elk.proxy.Reflect; + +public enum SizeConstraint implements EnumProxy { + NODE_LABELS; + + @Override + public Enum getTrueObject() { + return Reflect.getEnum("org.eclipse.elk.core.options.SizeConstraint", name()); + } + + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/core/util/NullElkProgressMonitor.java b/src/net/sourceforge/plantuml/elk/proxy/core/util/NullElkProgressMonitor.java new file mode 100644 index 000000000..a938da8d7 --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/core/util/NullElkProgressMonitor.java @@ -0,0 +1,10 @@ +package net.sourceforge.plantuml.elk.proxy.core.util; + +import net.sourceforge.plantuml.elk.proxy.Reflect; + +public class NullElkProgressMonitor { + + public final Object obj = Reflect.newInstance("org.eclipse.elk.core.util.NullElkProgressMonitor"); + + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/graph/ElkBendPoint.java b/src/net/sourceforge/plantuml/elk/proxy/graph/ElkBendPoint.java new file mode 100644 index 000000000..d1539648f --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/graph/ElkBendPoint.java @@ -0,0 +1,34 @@ +package net.sourceforge.plantuml.elk.proxy.graph; + +import net.sourceforge.plantuml.elk.proxy.Reflect; + +public class ElkBendPoint { + + public final Object obj; + + public ElkBendPoint(Object obj) { + if (obj == null) { + throw new IllegalArgumentException(); + } + this.obj = obj; + } + + @Override + public int hashCode() { + return this.obj.hashCode(); + } + + @Override + public boolean equals(Object other) { + return this.obj.equals(((ElkBendPoint) other).obj); + } + + public double getX() { + return (Double) Reflect.call(obj, "getX"); + } + + public double getY() { + return (Double) Reflect.call(obj, "getY"); + } + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/graph/ElkEdge.java b/src/net/sourceforge/plantuml/elk/proxy/graph/ElkEdge.java new file mode 100644 index 000000000..a5ac77060 --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/graph/ElkEdge.java @@ -0,0 +1,41 @@ +package net.sourceforge.plantuml.elk.proxy.graph; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import net.sourceforge.plantuml.elk.proxy.Reflect; + +public class ElkEdge extends ElkWithProperty { + + public ElkEdge(Object obj) { + super(obj); + } + + public ElkNode getContainingNode() { + return new ElkNode(Reflect.call(obj, "getContainingNode")); + } + + public Collection getLabels() { + final List result = new ArrayList(); + Collection internal = (Collection) Reflect.call(obj, "getLabels"); + for (Object element : internal) { + result.add(new ElkLabel(element)); + } + return result; + } + + public List getSections() { + final List result = new ArrayList(); + Collection internal = (Collection) Reflect.call(obj, "getSections"); + for (Object element : internal) { + result.add(new ElkEdgeSection(element)); + } + return result; + } + + public boolean isHierarchical() { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/graph/ElkEdgeSection.java b/src/net/sourceforge/plantuml/elk/proxy/graph/ElkEdgeSection.java new file mode 100644 index 000000000..367db65f2 --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/graph/ElkEdgeSection.java @@ -0,0 +1,55 @@ +package net.sourceforge.plantuml.elk.proxy.graph; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import net.sourceforge.plantuml.elk.proxy.Reflect; + +public class ElkEdgeSection { + + public final Object obj; + + public ElkEdgeSection(Object obj) { + if (obj == null) { + throw new IllegalArgumentException(); + } + this.obj = obj; + } + + @Override + public int hashCode() { + return this.obj.hashCode(); + } + + @Override + public boolean equals(Object other) { + return this.obj.equals(((ElkEdgeSection) other).obj); + } + + public double getStartX() { + return (Double) Reflect.call(obj, "getStartX"); + } + + public double getStartY() { + return (Double) Reflect.call(obj, "getStartY"); + } + + public double getEndX() { + return (Double) Reflect.call(obj, "getEndX"); + } + + public double getEndY() { + return (Double) Reflect.call(obj, "getEndY"); + } + + public Collection getBendPoints() { + final List result = new ArrayList(); + Collection internal = (Collection) Reflect.call(obj, "getBendPoints"); + for (Object element : internal) { + result.add(new ElkBendPoint(element)); + } + return result; + } + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/graph/ElkLabel.java b/src/net/sourceforge/plantuml/elk/proxy/graph/ElkLabel.java new file mode 100644 index 000000000..92a8e738a --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/graph/ElkLabel.java @@ -0,0 +1,31 @@ +package net.sourceforge.plantuml.elk.proxy.graph; + +import net.sourceforge.plantuml.elk.proxy.Reflect; + +public class ElkLabel extends ElkWithProperty { + + public ElkLabel(Object obj) { + super(obj); + } + + public void setText(String text) { + Reflect.call(obj, "setText", text); + } + + public void setDimensions(double width, double height) { + Reflect.call2(obj, "setDimensions", width, height); + } + + public String getText() { + return (String) Reflect.call(obj, "getText"); + } + + public double getX() { + return (Double) Reflect.call(obj, "getX"); + } + + public double getY() { + return (Double) Reflect.call(obj, "getY"); + } + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/graph/ElkNode.java b/src/net/sourceforge/plantuml/elk/proxy/graph/ElkNode.java new file mode 100644 index 000000000..e7f9f53eb --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/graph/ElkNode.java @@ -0,0 +1,51 @@ +package net.sourceforge.plantuml.elk.proxy.graph; + +import java.util.ArrayList; +import java.util.Collection; + +import net.sourceforge.plantuml.elk.proxy.Reflect; + +public class ElkNode extends ElkWithProperty { + + public ElkNode(Object obj) { + super(obj); + } + + public ElkNode getParent() { + final Object tmp = Reflect.call(obj, "getParent"); + if (tmp == null) { + return null; + } + return new ElkNode(tmp); + } + + public double getX() { + return (Double) Reflect.call(obj, "getX"); + } + + public double getY() { + return (Double) Reflect.call(obj, "getY"); + } + + public Collection getLabels() { + final Collection result = new ArrayList(); + final Collection internal = (Collection) Reflect.call(obj, "getLabels"); + for (Object element : internal) { + result.add(new ElkLabel(element)); + } + return result; + } + + public double getWidth() { + return (Double) Reflect.call(obj, "getWidth"); + } + + public double getHeight() { + return (Double) Reflect.call(obj, "getHeight"); + } + + public void setDimensions(double width, double height) { + Reflect.call2(obj, "setDimensions", width, height); + } + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/graph/ElkWithProperty.java b/src/net/sourceforge/plantuml/elk/proxy/graph/ElkWithProperty.java new file mode 100644 index 000000000..1b74c0d71 --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/graph/ElkWithProperty.java @@ -0,0 +1,49 @@ +package net.sourceforge.plantuml.elk.proxy.graph; + +import java.util.Collection; +import java.util.EnumSet; + +import net.sourceforge.plantuml.elk.proxy.EnumProxy; +import net.sourceforge.plantuml.elk.proxy.Reflect; + +public class ElkWithProperty { + + public final Object obj; + + public ElkWithProperty(Object obj) { + if (obj == null) { + throw new IllegalArgumentException(); + } + this.obj = obj; + } + + @Override + final public int hashCode() { + return this.obj.hashCode(); + } + + @Override + final public boolean equals(Object other) { + return this.obj.equals(((ElkWithProperty) other).obj); + } + + final public void setProperty(Object key, Object value) { + if (value instanceof EnumSet) { + EnumSet result = null; + for (Object foo : (Collection) value) { + final EnumProxy elk = (EnumProxy) foo; + if (result == null) { + result = EnumSet.noneOf((Class) elk.getClass()); + } + result.add(elk); + } + Reflect.call2(obj, "setProperty", key, result); + } else if (value instanceof EnumProxy) { + final Enum elk = ((EnumProxy) value).getTrueObject(); + Reflect.call2(obj, "setProperty", key, elk); + } else { + Reflect.call2(obj, "setProperty", key, value); + } + } + +} diff --git a/src/net/sourceforge/plantuml/elk/proxy/graph/util/ElkGraphUtil.java b/src/net/sourceforge/plantuml/elk/proxy/graph/util/ElkGraphUtil.java new file mode 100644 index 000000000..df299d550 --- /dev/null +++ b/src/net/sourceforge/plantuml/elk/proxy/graph/util/ElkGraphUtil.java @@ -0,0 +1,31 @@ +package net.sourceforge.plantuml.elk.proxy.graph.util; + +import net.sourceforge.plantuml.elk.proxy.Reflect; +import net.sourceforge.plantuml.elk.proxy.graph.ElkEdge; +import net.sourceforge.plantuml.elk.proxy.graph.ElkLabel; +import net.sourceforge.plantuml.elk.proxy.graph.ElkNode; + +public class ElkGraphUtil { + + public static ElkLabel createLabel(ElkEdge edge) { + return new ElkLabel(Reflect.callStatic2("org.eclipse.elk.graph.util.ElkGraphUtil", "createLabel", edge.obj)); + } + + public static ElkLabel createLabel(ElkNode node) { + return new ElkLabel(Reflect.callStatic2("org.eclipse.elk.graph.util.ElkGraphUtil", "createLabel", node.obj)); + } + + public static ElkNode createNode(ElkNode root) { + return new ElkNode(Reflect.callStatic2("org.eclipse.elk.graph.util.ElkGraphUtil", "createNode", root.obj)); + } + + public static ElkEdge createSimpleEdge(ElkNode node1, ElkNode node2) { + return new ElkEdge(Reflect.callStatic2("org.eclipse.elk.graph.util.ElkGraphUtil", "createSimpleEdge", node1.obj, + node2.obj)); + } + + public static ElkNode createGraph() { + return new ElkNode(Reflect.callStatic("org.eclipse.elk.graph.util.ElkGraphUtil", "createGraph")); + } + +} diff --git a/src/net/sourceforge/plantuml/svek/Bibliotekon.java b/src/net/sourceforge/plantuml/svek/Bibliotekon.java index 63db03b9c..c1d9c9926 100644 --- a/src/net/sourceforge/plantuml/svek/Bibliotekon.java +++ b/src/net/sourceforge/plantuml/svek/Bibliotekon.java @@ -55,9 +55,9 @@ public class Bibliotekon { private final Map nodeMap = new LinkedHashMap();; - private final List lines0 = new ArrayList(); - private final List lines1 = new ArrayList(); - private final List allLines = new ArrayList(); + private final List lines0 = new ArrayList(); + private final List lines1 = new ArrayList(); + private final List allLines = new ArrayList(); public SvekNode createNode(ILeaf ent, IEntityImage image, ColorSequence colorSequence, StringBounder stringBounder) { final SvekNode node = new SvekNode(ent, image, colorSequence, stringBounder); @@ -74,13 +74,13 @@ public class Bibliotekon { return null; } - public void addLine(Line line) { + public void addLine(SvekLine line) { allLines.add(line); if (first(line)) { if (line.hasNoteLabelText()) { // lines0.add(0, line); for (int i = 0; i < lines0.size(); i++) { - final Line other = lines0.get(i); + final SvekLine other = lines0.get(i); if (other.hasNoteLabelText() == false && line.sameConnections(other)) { lines0.add(i, line); return; @@ -95,7 +95,7 @@ public class Bibliotekon { } } - private static boolean first(Line line) { + private static boolean first(SvekLine line) { final int length = line.getLength(); if (length == 1) { return true; @@ -158,15 +158,15 @@ public class Bibliotekon { return Collections.unmodifiableMap(result); } - public List allLines() { + public List allLines() { return Collections.unmodifiableList(allLines); } - public List lines0() { + public List lines0() { return Collections.unmodifiableList(lines0); } - public List lines1() { + public List lines1() { return Collections.unmodifiableList(lines1); } @@ -178,9 +178,9 @@ public class Bibliotekon { return Collections.unmodifiableCollection(nodeMap.values()); } - public List getAllLineConnectedTo(IEntity leaf) { - final List result = new ArrayList(); - for (Line line : allLines) { + public List getAllLineConnectedTo(IEntity leaf) { + final List result = new ArrayList(); + for (SvekLine line : allLines) { if (line.isLinkFromOrTo(leaf)) { result.add(line); } @@ -188,8 +188,8 @@ public class Bibliotekon { return Collections.unmodifiableList(result); } - public Line getLine(Link link) { - for (Line line : allLines) { + public SvekLine getLine(Link link) { + for (SvekLine line : allLines) { if (line.isLink(link)) { return line; } @@ -198,7 +198,7 @@ public class Bibliotekon { } public IEntity getOnlyOther(IEntity entity) { - for (Line line : allLines) { + for (SvekLine line : allLines) { final IEntity other = line.getOther(entity); if (other != null) { return other; diff --git a/src/net/sourceforge/plantuml/svek/Cluster.java b/src/net/sourceforge/plantuml/svek/Cluster.java index a95279e7e..cdc400de4 100644 --- a/src/net/sourceforge/plantuml/svek/Cluster.java +++ b/src/net/sourceforge/plantuml/svek/Cluster.java @@ -178,7 +178,7 @@ public class Cluster implements Moveable { return Collections.unmodifiableList(nodes); } - private List getNodesOrderedTop(Collection lines) { + private List getNodesOrderedTop(Collection lines) { final List firsts = new ArrayList(); final Set tops = new HashSet(); final Map shs = new HashMap(); @@ -192,7 +192,7 @@ public class Cluster implements Moveable { } } - for (Line l : lines) { + for (SvekLine l : lines) { if (tops.contains(l.getStartUidPrefix())) { final SvekNode sh = shs.get(l.getEndUidPrefix()); if (sh != null && sh.getEntityPosition() == EntityPosition.NORMAL) { @@ -211,7 +211,7 @@ public class Cluster implements Moveable { return firsts; } - private List getNodesOrderedWithoutTop(Collection lines) { + private List getNodesOrderedWithoutTop(Collection lines) { final List all = new ArrayList(nodes); final Set tops = new HashSet(); final Map shs = new HashMap(); @@ -229,7 +229,7 @@ public class Cluster implements Moveable { } } - for (Line l : lines) { + for (SvekLine l : lines) { if (tops.contains(l.getStartUidPrefix())) { final SvekNode sh = shs.get(l.getEndUidPrefix()); if (sh != null) { @@ -308,7 +308,6 @@ public class Cluster implements Moveable { if (fullName.startsWith("##") == false) { ug.draw(new UComment("cluster " + fullName)); } - final Stereotype stereotype = group.getStereotype(); HColor borderColor; if (UseStyle.useBetaStyle()) { final Style style = getDefaultStyleDefinition(umlDiagramType.getStyleName()) @@ -317,11 +316,11 @@ public class Cluster implements Moveable { } else { if (umlDiagramType == UmlDiagramType.STATE) { - borderColor = getColor(ColorParam.stateBorder, skinParam, stereotype); + borderColor = getColor(ColorParam.stateBorder, skinParam, group.getStereotype()); } else if (umlDiagramType == UmlDiagramType.ACTIVITY) { - borderColor = getColor(ColorParam.packageBorder, skinParam, stereotype); + borderColor = getColor(ColorParam.packageBorder, skinParam, group.getStereotype()); } else { - borderColor = getColor(ColorParam.packageBorder, skinParam, stereotype); + borderColor = getColor(ColorParam.packageBorder, skinParam, group.getStereotype()); } } @@ -374,13 +373,13 @@ public class Cluster implements Moveable { shadowing = skinParam2.shadowing2(group.getStereotype(), group.getUSymbol().getSkinParameter()) ? 3 : 0; } - stroke = getStrokeInternal(skinParam2); + stroke = getStrokeInternal(group, skinParam2); } HColor backColor = getBackColor(umlDiagramType); backColor = getBackColor(backColor, skinParam2, group.getStereotype(), umlDiagramType.getStyleName()); if (ztitle != null || zstereo != null) { final double roundCorner = group.getUSymbol() == null ? 0 - : group.getUSymbol().getSkinParameter().getRoundCorner(skinParam, stereotype); + : group.getUSymbol().getSkinParameter().getRoundCorner(skinParam, group.getStereotype()); final ClusterDecoration decoration = new ClusterDecoration(packageStyle, group.getUSymbol(), ztitle, zstereo, minX, minY, maxX, maxY, stroke); @@ -402,7 +401,7 @@ public class Cluster implements Moveable { } - private UStroke getStrokeInternal(ISkinParam skinParam) { + static public UStroke getStrokeInternal(IGroup group, ISkinParam skinParam) { final Colors colors = group.getColors(skinParam); if (colors.getSpecificLineStroke() != null) { return colors.getSpecificLineStroke(); @@ -517,8 +516,8 @@ public class Cluster implements Moveable { return fontParam.getStyleDefinition(SName.stateDiagram).getMergedStyle(skinParam.getCurrentStyleBuilder()); } - private boolean isThereALinkFromOrToGroup(Collection lines) { - for (Line line : lines) { + private boolean isThereALinkFromOrToGroup(Collection lines) { + for (SvekLine line : lines) { if (line.isLinkFromOrTo(group)) { return true; } @@ -526,7 +525,7 @@ public class Cluster implements Moveable { return false; } - public void printCluster1(StringBuilder sb, Collection lines, StringBounder stringBounder) { + public void printCluster1(StringBuilder sb, Collection lines, StringBounder stringBounder) { for (SvekNode node : getNodesOrderedTop(lines)) { node.appendShape(sb, stringBounder); } @@ -618,8 +617,8 @@ public class Cluster implements Moveable { printRanks(RANK_SINK, withPositionProtected(stringBounder, EntityPosition.getOutputs()), sb, stringBounder); } - public SvekNode printCluster2(StringBuilder sb, Collection lines, StringBounder stringBounder, DotMode dotMode, - GraphvizVersion graphvizVersion, UmlDiagramType type) { + public SvekNode printCluster2(StringBuilder sb, Collection lines, StringBounder stringBounder, + DotMode dotMode, GraphvizVersion graphvizVersion, UmlDiagramType type) { SvekNode added = null; for (SvekNode node : getNodesOrderedWithoutTop(lines)) { @@ -639,16 +638,16 @@ public class Cluster implements Moveable { return added; } - private void appendRankSame(StringBuilder sb, Collection lines) { + private void appendRankSame(StringBuilder sb, Collection lines) { for (String same : getRankSame(lines)) { sb.append(same); SvekUtils.println(sb); } } - private Set getRankSame(Collection lines) { + private Set getRankSame(Collection lines) { final Set rankSame = new HashSet(); - for (Line l : lines) { + for (SvekLine l : lines) { if (l.hasEntryPoint()) { continue; } @@ -738,8 +737,8 @@ public class Cluster implements Moveable { return null; } - private void printInternal(StringBuilder sb, Collection lines, StringBounder stringBounder, DotMode dotMode, - GraphvizVersion graphvizVersion, UmlDiagramType type) { + private void printInternal(StringBuilder sb, Collection lines, StringBounder stringBounder, + DotMode dotMode, GraphvizVersion graphvizVersion, UmlDiagramType type) { final boolean thereALinkFromOrToGroup2 = isThereALinkFromOrToGroup(lines); boolean thereALinkFromOrToGroup1 = thereALinkFromOrToGroup2; final boolean useProtectionWhenThereALinkFromOrToGroup = graphvizVersion @@ -753,7 +752,7 @@ public class Cluster implements Moveable { } final Set entityPositionsExceptNormal = entityPositionsExceptNormal(); if (entityPositionsExceptNormal.size() > 0) { - for (Line line : lines) { + for (SvekLine line : lines) { if (line.isLinkFromOrTo(group)) { line.setProjectionCluster(this); } @@ -779,7 +778,7 @@ public class Cluster implements Moveable { final String label; if (isLabel()) { final StringBuilder sblabel = new StringBuilder("<"); - Line.appendTable(sblabel, getTitleAndAttributeWidth(), getTitleAndAttributeHeight() - 5, colorTitle); + SvekLine.appendTable(sblabel, getTitleAndAttributeWidth(), getTitleAndAttributeHeight() - 5, colorTitle); sblabel.append(">"); label = sblabel.toString(); final HorizontalAlignment align = skinParam.getHorizontalAlignment(AlignmentParam.packageTitleAlignment, diff --git a/src/net/sourceforge/plantuml/svek/ClusterDecoration.java b/src/net/sourceforge/plantuml/svek/ClusterDecoration.java index c8d233580..07f9956c0 100644 --- a/src/net/sourceforge/plantuml/svek/ClusterDecoration.java +++ b/src/net/sourceforge/plantuml/svek/ClusterDecoration.java @@ -76,13 +76,6 @@ public class ClusterDecoration { return style.toUSymbol(); } - public final static int marginTitleX1 = 3; - public final static int marginTitleX2 = 3; - public final static int marginTitleX3 = 7; - public final static int marginTitleY0 = 0; - public final static int marginTitleY1 = 3; - public final static int marginTitleY2 = 3; - public void drawU(UGraphic ug, HColor backColor, HColor borderColor, double shadowing, double roundCorner, HorizontalAlignment titleAlignment, HorizontalAlignment stereoAlignment) { final SymbolContext biColor = new SymbolContext(backColor, borderColor); @@ -91,8 +84,8 @@ public class ClusterDecoration { } final SymbolContext symbolContext = biColor.withShadow(shadowing).withStroke(defaultStroke) .withCorner(roundCorner, 0); - symbol.asBig(title, titleAlignment, stereo, maxX - minX, maxY - minY, symbolContext, stereoAlignment).drawU( - ug.apply(new UTranslate(minX, minY))); + symbol.asBig(title, titleAlignment, stereo, maxX - minX, maxY - minY, symbolContext, stereoAlignment) + .drawU(ug.apply(new UTranslate(minX, minY))); } } diff --git a/src/net/sourceforge/plantuml/svek/DotStringFactory.java b/src/net/sourceforge/plantuml/svek/DotStringFactory.java index f6d0e6e31..6c403c5fe 100644 --- a/src/net/sourceforge/plantuml/svek/DotStringFactory.java +++ b/src/net/sourceforge/plantuml/svek/DotStringFactory.java @@ -125,7 +125,7 @@ public class DotStringFactory implements Moveable { private double getHorizontalDzeta() { double max = 0; - for (Line l : bibliotekon.allLines()) { + for (SvekLine l : bibliotekon.allLines()) { final double c = l.getHorizontalDzeta(stringBounder); if (c > max) { max = c; @@ -136,7 +136,7 @@ public class DotStringFactory implements Moveable { private double getVerticalDzeta() { double max = 0; - for (Line l : bibliotekon.allLines()) { + for (SvekLine l : bibliotekon.allLines()) { final double c = l.getVerticalDzeta(stringBounder); if (c > max) { max = c; @@ -207,14 +207,14 @@ public class DotStringFactory implements Moveable { manageMinMaxCluster(sb); root.printCluster1(sb, bibliotekon.allLines(), stringBounder); - for (Line line : bibliotekon.lines0()) { + for (SvekLine line : bibliotekon.lines0()) { line.appendLine(getGraphvizVersion(), sb, dotMode); } root.fillRankMin(rankMin); root.printCluster2(sb, bibliotekon.allLines(), stringBounder, dotMode, getGraphvizVersion(), umlDiagramType); printMinRanking(sb); - for (Line line : bibliotekon.lines1()) { + for (SvekLine line : bibliotekon.lines1()) { line.appendLine(getGraphvizVersion(), sb, dotMode); } SvekUtils.println(sb); @@ -442,11 +442,11 @@ public class DotStringFactory implements Moveable { cluster.setTitlePosition(minXtitle, minYtitle); } - for (Line line : bibliotekon.allLines()) { + for (SvekLine line : bibliotekon.allLines()) { line.solveLine(svgResult); } - for (Line line : bibliotekon.allLines()) { + for (SvekLine line : bibliotekon.allLines()) { line.manageCollision(bibliotekon.allNodes()); } // corner1.manage(0, 0); @@ -493,7 +493,7 @@ public class DotStringFactory implements Moveable { for (SvekNode sh : bibliotekon.allNodes()) { sh.moveSvek(deltaX, deltaY); } - for (Line line : bibliotekon.allLines()) { + for (SvekLine line : bibliotekon.allLines()) { line.moveSvek(deltaX, deltaY); } for (Cluster cl : bibliotekon.allCluster()) { diff --git a/src/net/sourceforge/plantuml/svek/GeneralImageBuilder.java b/src/net/sourceforge/plantuml/svek/GeneralImageBuilder.java index 250e3c222..d5767568f 100644 --- a/src/net/sourceforge/plantuml/svek/GeneralImageBuilder.java +++ b/src/net/sourceforge/plantuml/svek/GeneralImageBuilder.java @@ -424,17 +424,10 @@ public final class GeneralImageBuilder { } try { final ISkinParam skinParam = dotData.getSkinParam(); - final FontConfiguration labelFont; - if (UseStyle.useBetaStyle()) { - final Style style = getDefaultStyleDefinitionArrow(link.getStereotype()) - .getMergedStyle(link.getStyleBuilder()); - labelFont = style.getFontConfiguration(skinParam.getIHtmlColorSet()); - } else { - labelFont = new FontConfiguration(skinParam, FontParam.ARROW, null); - } + final FontConfiguration labelFont = getFontForLink(link, skinParam); - final Line line = new Line(link, dotStringFactory.getColorSequence(), skinParam, stringBounder, - labelFont, dotStringFactory.getBibliotekon(), dotData.getPragma()); + final SvekLine line = new SvekLine(link, dotStringFactory.getColorSequence(), skinParam, stringBounder, + labelFont, dotStringFactory.getBibliotekon(), pragma); dotStringFactory.getBibliotekon().addLine(line); @@ -489,6 +482,18 @@ public final class GeneralImageBuilder { } + private FontConfiguration getFontForLink(Link link, final ISkinParam skinParam) { + final FontConfiguration labelFont; + if (UseStyle.useBetaStyle()) { + final Style style = getDefaultStyleDefinitionArrow(link.getStereotype()) + .getMergedStyle(link.getStyleBuilder()); + labelFont = style.getFontConfiguration(skinParam.getIHtmlColorSet()); + } else { + labelFont = new FontConfiguration(skinParam, FontParam.ARROW, null); + } + return labelFont; + } + private boolean isSvekTrace() { final String value = pragma.getValue("svek_trace"); return "true".equalsIgnoreCase(value) || "on".equalsIgnoreCase(value); diff --git a/src/net/sourceforge/plantuml/svek/Line.java b/src/net/sourceforge/plantuml/svek/SvekLine.java similarity index 99% rename from src/net/sourceforge/plantuml/svek/Line.java rename to src/net/sourceforge/plantuml/svek/SvekLine.java index fd6ca03ff..6ce4f2580 100644 --- a/src/net/sourceforge/plantuml/svek/Line.java +++ b/src/net/sourceforge/plantuml/svek/SvekLine.java @@ -101,7 +101,7 @@ import net.sourceforge.plantuml.ugraphic.color.HColor; import net.sourceforge.plantuml.ugraphic.color.HColorNone; import net.sourceforge.plantuml.ugraphic.color.HColorUtils; -public class Line implements Moveable, Hideable, GuideLine { +public class SvekLine implements Moveable, Hideable, GuideLine { private static final Dimension2DDouble CONSTRAINT_SPOT = new Dimension2DDouble(10, 10); @@ -205,7 +205,7 @@ public class Line implements Moveable, Hideable, GuideLine { throw new IllegalArgumentException(); } - public Line(Link link, ColorSequence colorSequence, ISkinParam skinParam, StringBounder stringBounder, + public SvekLine(Link link, ColorSequence colorSequence, ISkinParam skinParam, StringBounder stringBounder, FontConfiguration font, Bibliotekon bibliotekon, Pragma pragma) { if (link == null) { @@ -985,7 +985,7 @@ public class Line implements Moveable, Hideable, GuideLine { return link.isHidden(); } - public boolean sameConnections(Line other) { + public boolean sameConnections(SvekLine other) { return link.sameConnections(other.link); } diff --git a/src/net/sourceforge/plantuml/svek/SvekResult.java b/src/net/sourceforge/plantuml/svek/SvekResult.java index 13de93c54..9e58179da 100644 --- a/src/net/sourceforge/plantuml/svek/SvekResult.java +++ b/src/net/sourceforge/plantuml/svek/SvekResult.java @@ -98,7 +98,7 @@ public final class SvekResult extends AbstractTextBlock implements IEntityImage final Set ids = new HashSet(); - for (Line line : dotStringFactory.getBibliotekon().allLines()) { + for (SvekLine line : dotStringFactory.getBibliotekon().allLines()) { final UGraphic ug2 = line.isHidden() ? ug.apply(UHidden.HIDDEN) : ug; if (UseStyle.useBetaStyle()) { diff --git a/src/net/sourceforge/plantuml/svek/image/EntityImageLollipopInterfaceEye1.java b/src/net/sourceforge/plantuml/svek/image/EntityImageLollipopInterfaceEye1.java index 9ce7ad939..1c625eae4 100644 --- a/src/net/sourceforge/plantuml/svek/image/EntityImageLollipopInterfaceEye1.java +++ b/src/net/sourceforge/plantuml/svek/image/EntityImageLollipopInterfaceEye1.java @@ -53,7 +53,7 @@ import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.svek.AbstractEntityImage; import net.sourceforge.plantuml.svek.Bibliotekon; -import net.sourceforge.plantuml.svek.Line; +import net.sourceforge.plantuml.svek.SvekLine; import net.sourceforge.plantuml.svek.ShapeType; import net.sourceforge.plantuml.ugraphic.UEllipse; import net.sourceforge.plantuml.ugraphic.UGraphic; @@ -100,10 +100,10 @@ public class EntityImageLollipopInterfaceEye1 extends AbstractEntityImage { Point2D pos = bibliotekon.getNode(getEntity()).getPosition(); - final List lines = bibliotekon.getAllLineConnectedTo(getEntity()); + final List lines = bibliotekon.getAllLineConnectedTo(getEntity()); final UTranslate reverse = new UTranslate(pos).reverse(); final ConnectedCircle connectedCircle = new ConnectedCircle(SIZE / 2); - for (Line line : lines) { + for (SvekLine line : lines) { Point2D pt = line.getMyPoint(getEntity()); pt = reverse.getTranslated(pt); connectedCircle.addSecondaryConnection(pt); diff --git a/src/net/sourceforge/plantuml/svek/image/EntityImageNote.java b/src/net/sourceforge/plantuml/svek/image/EntityImageNote.java index 34f555521..79ad6eddc 100644 --- a/src/net/sourceforge/plantuml/svek/image/EntityImageNote.java +++ b/src/net/sourceforge/plantuml/svek/image/EntityImageNote.java @@ -71,7 +71,7 @@ import net.sourceforge.plantuml.style.SName; import net.sourceforge.plantuml.style.Style; import net.sourceforge.plantuml.style.StyleSignature; import net.sourceforge.plantuml.svek.AbstractEntityImage; -import net.sourceforge.plantuml.svek.Line; +import net.sourceforge.plantuml.svek.SvekLine; import net.sourceforge.plantuml.svek.ShapeType; import net.sourceforge.plantuml.svek.SvekNode; import net.sourceforge.plantuml.ugraphic.UGraphic; @@ -300,11 +300,11 @@ public class EntityImageNote extends AbstractEntityImage implements Stencil { return ShapeType.RECTANGLE; } - private Line opaleLine; + private SvekLine opaleLine; private SvekNode node; private SvekNode other; - public void setOpaleLine(Line line, SvekNode node, SvekNode other) { + public void setOpaleLine(SvekLine line, SvekNode node, SvekNode other) { if (other == null) { throw new IllegalArgumentException(); }