2016-03-06 16:47:34 +00:00
|
|
|
/* ========================================================================
|
|
|
|
* PlantUML : a free UML diagram generator
|
|
|
|
* ========================================================================
|
|
|
|
*
|
2019-01-16 18:34:41 +00:00
|
|
|
* (C) Copyright 2009-2020, Arnaud Roques
|
2016-03-06 16:47:34 +00:00
|
|
|
*
|
|
|
|
* Project Info: http://plantuml.com
|
|
|
|
*
|
2017-03-15 19:13:31 +00:00
|
|
|
* 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
|
|
|
|
*
|
2016-03-06 16:47:34 +00:00
|
|
|
* 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.jdot;
|
|
|
|
|
|
|
|
import static gen.lib.cgraph.attr__c.agsafeset;
|
|
|
|
import static gen.lib.cgraph.edge__c.agedge;
|
|
|
|
import static gen.lib.cgraph.graph__c.agopen;
|
|
|
|
import static gen.lib.cgraph.node__c.agnode;
|
2016-04-04 19:05:10 +00:00
|
|
|
import static gen.lib.cgraph.subg__c.agsubg;
|
2016-03-06 16:47:34 +00:00
|
|
|
import static gen.lib.gvc.gvc__c.gvContext;
|
|
|
|
import static gen.lib.gvc.gvlayout__c.gvLayoutJobs;
|
|
|
|
|
|
|
|
import java.awt.geom.Dimension2D;
|
|
|
|
import java.awt.geom.Point2D;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.OutputStream;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collection;
|
2016-04-04 19:05:10 +00:00
|
|
|
import java.util.LinkedHashMap;
|
2016-03-06 16:47:34 +00:00
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
2016-04-22 20:36:08 +00:00
|
|
|
import java.util.concurrent.locks.Lock;
|
|
|
|
import java.util.concurrent.locks.ReentrantLock;
|
2016-03-06 16:47:34 +00:00
|
|
|
|
2020-04-19 16:04:39 +00:00
|
|
|
import h.ST_Agedge_s;
|
|
|
|
import h.ST_Agnode_s;
|
|
|
|
import h.ST_Agnodeinfo_t;
|
|
|
|
import h.ST_Agraph_s;
|
|
|
|
import h.ST_Agraphinfo_t;
|
2020-12-01 21:39:27 +00:00
|
|
|
import h.ST_Agrec_s;
|
2020-04-19 16:04:39 +00:00
|
|
|
import h.ST_GVC_s;
|
|
|
|
import h.ST_boxf;
|
2020-10-12 20:56:58 +00:00
|
|
|
import net.sourceforge.plantuml.AnnotatedWorker;
|
2016-03-06 16:47:34 +00:00
|
|
|
import net.sourceforge.plantuml.FileFormatOption;
|
|
|
|
import net.sourceforge.plantuml.FontParam;
|
|
|
|
import net.sourceforge.plantuml.ISkinParam;
|
2016-12-21 22:10:29 +00:00
|
|
|
import net.sourceforge.plantuml.StringUtils;
|
2016-03-06 16:47:34 +00:00
|
|
|
import net.sourceforge.plantuml.UmlDiagram;
|
2020-12-01 21:39:27 +00:00
|
|
|
import net.sourceforge.plantuml.UseStyle;
|
2016-03-06 16:47:34 +00:00
|
|
|
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.EntityPortion;
|
|
|
|
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.Member;
|
|
|
|
import net.sourceforge.plantuml.cucadiagram.MethodsOrFieldsArea;
|
|
|
|
import net.sourceforge.plantuml.cucadiagram.Stereotype;
|
2020-02-18 21:24:31 +00:00
|
|
|
import net.sourceforge.plantuml.cucadiagram.entity.EntityFactory;
|
2020-10-12 20:56:58 +00:00
|
|
|
import net.sourceforge.plantuml.graphic.AbstractTextBlock;
|
2016-03-06 16:47:34 +00:00
|
|
|
import net.sourceforge.plantuml.graphic.FontConfiguration;
|
|
|
|
import net.sourceforge.plantuml.graphic.HorizontalAlignment;
|
|
|
|
import net.sourceforge.plantuml.graphic.QuoteUtils;
|
|
|
|
import net.sourceforge.plantuml.graphic.StringBounder;
|
|
|
|
import net.sourceforge.plantuml.graphic.TextBlock;
|
|
|
|
import net.sourceforge.plantuml.graphic.TextBlockEmpty;
|
|
|
|
import net.sourceforge.plantuml.graphic.TextBlockUtils;
|
|
|
|
import net.sourceforge.plantuml.graphic.TextBlockWidth;
|
|
|
|
import net.sourceforge.plantuml.graphic.USymbol;
|
2020-05-07 14:12:08 +00:00
|
|
|
import net.sourceforge.plantuml.style.ClockwiseTopRightBottomLeft;
|
2020-06-21 20:31:45 +00:00
|
|
|
import net.sourceforge.plantuml.style.SName;
|
2020-10-12 20:56:58 +00:00
|
|
|
import net.sourceforge.plantuml.style.Style;
|
|
|
|
import net.sourceforge.plantuml.style.StyleSignature;
|
2016-03-06 16:47:34 +00:00
|
|
|
import net.sourceforge.plantuml.svek.Bibliotekon;
|
|
|
|
import net.sourceforge.plantuml.svek.Cluster;
|
|
|
|
import net.sourceforge.plantuml.svek.CucaDiagramFileMaker;
|
|
|
|
import net.sourceforge.plantuml.svek.DotStringFactory;
|
2018-10-21 19:44:14 +00:00
|
|
|
import net.sourceforge.plantuml.svek.GeneralImageBuilder;
|
2016-03-06 16:47:34 +00:00
|
|
|
import net.sourceforge.plantuml.svek.GraphvizCrash;
|
|
|
|
import net.sourceforge.plantuml.svek.IEntityImage;
|
2020-02-18 21:24:31 +00:00
|
|
|
import net.sourceforge.plantuml.svek.Node;
|
2020-10-12 20:56:58 +00:00
|
|
|
import net.sourceforge.plantuml.svek.TextBlockBackcolored;
|
2016-03-06 16:47:34 +00:00
|
|
|
import net.sourceforge.plantuml.ugraphic.ImageBuilder;
|
|
|
|
import net.sourceforge.plantuml.ugraphic.UGraphic;
|
2016-04-04 19:05:10 +00:00
|
|
|
import net.sourceforge.plantuml.ugraphic.UStroke;
|
2016-03-06 16:47:34 +00:00
|
|
|
import net.sourceforge.plantuml.ugraphic.UTranslate;
|
2020-10-12 20:56:58 +00:00
|
|
|
import net.sourceforge.plantuml.ugraphic.color.HColor;
|
2016-03-06 16:47:34 +00:00
|
|
|
import smetana.core.CString;
|
2016-04-04 19:05:10 +00:00
|
|
|
import smetana.core.JUtils;
|
2018-10-21 19:44:14 +00:00
|
|
|
import smetana.core.JUtilsDebug;
|
2016-03-06 16:47:34 +00:00
|
|
|
import smetana.core.Macro;
|
|
|
|
import smetana.core.Z;
|
|
|
|
|
|
|
|
public class CucaDiagramFileMakerJDot implements CucaDiagramFileMaker {
|
|
|
|
|
|
|
|
private final CucaDiagram diagram;
|
2016-04-04 19:05:10 +00:00
|
|
|
|
2016-08-25 20:45:37 +00:00
|
|
|
private final StringBounder stringBounder;
|
2018-10-21 19:44:14 +00:00
|
|
|
private final Map<ILeaf, ST_Agnode_s> nodes = new LinkedHashMap<ILeaf, ST_Agnode_s>();
|
|
|
|
private final Map<Link, ST_Agedge_s> edges = new LinkedHashMap<Link, ST_Agedge_s>();
|
|
|
|
private final Map<IGroup, ST_Agraph_s> clusters = new LinkedHashMap<IGroup, ST_Agraph_s>();
|
2016-03-06 16:47:34 +00:00
|
|
|
|
|
|
|
private final DotStringFactory dotStringFactory;
|
|
|
|
|
2020-10-12 20:56:58 +00:00
|
|
|
class Drawing extends AbstractTextBlock implements TextBlockBackcolored {
|
2016-03-06 16:47:34 +00:00
|
|
|
|
|
|
|
private final YMirror ymirror;
|
2020-10-12 20:56:58 +00:00
|
|
|
private final Dimension2D dim;
|
2016-03-06 16:47:34 +00:00
|
|
|
|
2020-10-12 20:56:58 +00:00
|
|
|
public Drawing(YMirror ymirror, Dimension2D dim) {
|
2016-03-06 16:47:34 +00:00
|
|
|
this.ymirror = ymirror;
|
2020-10-12 20:56:58 +00:00
|
|
|
this.dim = dim;
|
2016-03-06 16:47:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void drawU(UGraphic ug) {
|
2016-04-04 19:05:10 +00:00
|
|
|
|
2018-10-21 19:44:14 +00:00
|
|
|
for (Map.Entry<IGroup, ST_Agraph_s> ent : clusters.entrySet()) {
|
2016-04-04 19:05:10 +00:00
|
|
|
drawGroup(ug, ymirror, ent.getKey(), ent.getValue());
|
|
|
|
}
|
|
|
|
|
2018-10-21 19:44:14 +00:00
|
|
|
for (Map.Entry<ILeaf, ST_Agnode_s> ent : nodes.entrySet()) {
|
2016-03-06 16:47:34 +00:00
|
|
|
final ILeaf leaf = ent.getKey();
|
2020-02-18 21:24:31 +00:00
|
|
|
final ST_Agnode_s agnode = ent.getValue();
|
|
|
|
final Point2D corner = getCorner(agnode);
|
2016-03-06 16:47:34 +00:00
|
|
|
|
2020-02-18 21:24:31 +00:00
|
|
|
final Node node = dotStringFactory.getBibliotekon().getNode(leaf);
|
|
|
|
final IEntityImage image = node.getImage();
|
2016-03-06 16:47:34 +00:00
|
|
|
image.drawU(ug.apply(new UTranslate(corner)));
|
|
|
|
}
|
|
|
|
|
2018-10-21 19:44:14 +00:00
|
|
|
for (Map.Entry<Link, ST_Agedge_s> ent : edges.entrySet()) {
|
2016-03-06 16:47:34 +00:00
|
|
|
final Link link = ent.getKey();
|
2020-10-12 20:56:58 +00:00
|
|
|
if (link.isInvis()) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-10-21 19:44:14 +00:00
|
|
|
final ST_Agedge_s edge = ent.getValue();
|
2019-03-01 22:16:29 +00:00
|
|
|
new JDotPath(link, edge, ymirror, diagram, getLabel(link), getQualifier(link, 1), getQualifier(link, 2))
|
|
|
|
.drawU(ug);
|
2016-03-06 16:47:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-12 20:56:58 +00:00
|
|
|
public Dimension2D calculateDimension(StringBounder stringBounder) {
|
|
|
|
if (dim == null) {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
|
|
|
return dim;
|
|
|
|
}
|
|
|
|
|
2018-10-21 19:44:14 +00:00
|
|
|
private Point2D getCorner(ST_Agnode_s n) {
|
2020-12-01 21:39:27 +00:00
|
|
|
final ST_Agnodeinfo_t data = (ST_Agnodeinfo_t) Macro.AGDATA(n);
|
2018-11-26 18:46:22 +00:00
|
|
|
final double width = data.width * 72;
|
|
|
|
final double height = data.height * 72;
|
|
|
|
final double x = data.coord.x;
|
|
|
|
final double y = data.coord.y;
|
2016-03-06 16:47:34 +00:00
|
|
|
|
|
|
|
if (ymirror == null) {
|
|
|
|
return new Point2D.Double(x - width / 2, y - height / 2);
|
|
|
|
}
|
|
|
|
return ymirror.getMirrored(new Point2D.Double(x - width / 2, y + height / 2));
|
|
|
|
}
|
|
|
|
|
2020-10-12 20:56:58 +00:00
|
|
|
public HColor getBackcolor() {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2016-03-06 16:47:34 +00:00
|
|
|
}
|
|
|
|
|
2016-08-25 20:45:37 +00:00
|
|
|
public CucaDiagramFileMakerJDot(CucaDiagram diagram, StringBounder stringBounder) {
|
2016-03-06 16:47:34 +00:00
|
|
|
this.diagram = diagram;
|
2016-08-25 20:45:37 +00:00
|
|
|
this.stringBounder = stringBounder;
|
2016-07-25 19:25:28 +00:00
|
|
|
this.dotStringFactory = new DotStringFactory(stringBounder, diagram);
|
2016-03-06 16:47:34 +00:00
|
|
|
|
2016-04-04 19:05:10 +00:00
|
|
|
printGroups(diagram.getRootGroup());
|
|
|
|
printEntities(getUnpackagedEntities());
|
2016-03-06 16:47:34 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-10-21 19:44:14 +00:00
|
|
|
public void drawGroup(UGraphic ug, YMirror ymirror, IGroup group, ST_Agraph_s gr) {
|
2016-04-04 19:05:10 +00:00
|
|
|
JUtils.LOG2("drawGroup");
|
2020-11-21 17:33:24 +00:00
|
|
|
try {
|
2020-12-01 21:39:27 +00:00
|
|
|
final ST_Agrec_s tmp1 = Macro.AGDATA(gr);
|
|
|
|
final ST_Agraphinfo_t data = (ST_Agraphinfo_t) tmp1;
|
2020-11-21 17:33:24 +00:00
|
|
|
final ST_boxf bb = (ST_boxf) data.bb;
|
|
|
|
final double llx = bb.LL.x;
|
|
|
|
double lly = bb.LL.y;
|
|
|
|
final double urx = bb.UR.x;
|
|
|
|
double ury = bb.UR.y;
|
|
|
|
if (ymirror != null) {
|
|
|
|
final double tmpUry = ury;
|
|
|
|
ury = ymirror.getMirrored(lly);
|
|
|
|
lly = ymirror.getMirrored(tmpUry);
|
|
|
|
}
|
|
|
|
|
|
|
|
final Cluster cluster = dotStringFactory.getBibliotekon().getCluster(group);
|
|
|
|
cluster.setPosition(llx, lly, urx, ury);
|
|
|
|
JUtils.LOG2("cluster=" + cluster);
|
|
|
|
// ug.apply(new UTranslate(llx, lly)).apply(new
|
|
|
|
// UChangeColor(HtmlColorUtils.BLUE))
|
|
|
|
// .draw(new URectangle(urx - llx, ury - lly));
|
|
|
|
cluster.drawU(ug, new UStroke(1.5), diagram.getUmlDiagramType(), diagram.getSkinParam());
|
2020-12-01 21:39:27 +00:00
|
|
|
} catch (Exception e) {
|
2020-11-21 17:33:24 +00:00
|
|
|
System.err.println("CANNOT DRAW GROUP");
|
|
|
|
}
|
2016-04-04 19:05:10 +00:00
|
|
|
}
|
|
|
|
|
2016-03-06 16:47:34 +00:00
|
|
|
private void printGroups(IGroup parent) {
|
|
|
|
for (IGroup g : diagram.getChildrenGroups(parent)) {
|
|
|
|
if (g.isRemoved()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (diagram.isEmpty(g) && g.getGroupType() == GroupType.PACKAGE) {
|
2020-02-18 21:24:31 +00:00
|
|
|
final ISkinParam skinParam = diagram.getSkinParam();
|
|
|
|
final EntityFactory entityFactory = diagram.getEntityFactory();
|
|
|
|
final ILeaf folder = entityFactory.createLeafForEmptyGroup(g, skinParam);
|
2016-03-06 16:47:34 +00:00
|
|
|
printEntityNew(folder);
|
|
|
|
} else {
|
|
|
|
printGroup(g);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void printGroup(IGroup g) {
|
|
|
|
if (g.getGroupType() == GroupType.CONCURRENT_STATE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int titleAndAttributeWidth = 0;
|
|
|
|
int titleAndAttributeHeight = 0;
|
|
|
|
|
|
|
|
final TextBlock title = getTitleBlock(g);
|
|
|
|
final TextBlock stereo = getStereoBlock(g);
|
|
|
|
final TextBlock stereoAndTitle = TextBlockUtils.mergeTB(stereo, title, HorizontalAlignment.CENTER);
|
|
|
|
final Dimension2D dimLabel = stereoAndTitle.calculateDimension(stringBounder);
|
|
|
|
if (dimLabel.getWidth() > 0) {
|
|
|
|
final List<Member> members = ((IEntity) g).getBodier().getFieldsToDisplay();
|
|
|
|
final TextBlockWidth attribute;
|
|
|
|
if (members.size() == 0) {
|
|
|
|
attribute = new TextBlockEmpty();
|
|
|
|
} else {
|
|
|
|
attribute = new MethodsOrFieldsArea(members, FontParam.STATE_ATTRIBUTE, diagram.getSkinParam(),
|
2020-12-01 21:39:27 +00:00
|
|
|
g.getStereotype(), null, getStyle(FontParam.STATE_ATTRIBUTE));
|
2016-03-06 16:47:34 +00:00
|
|
|
}
|
|
|
|
final Dimension2D dimAttribute = attribute.calculateDimension(stringBounder);
|
|
|
|
final double attributeHeight = dimAttribute.getHeight();
|
|
|
|
final double attributeWidth = dimAttribute.getWidth();
|
|
|
|
final double marginForFields = attributeHeight > 0 ? IEntityImage.MARGIN : 0;
|
|
|
|
final USymbol uSymbol = g.getUSymbol();
|
|
|
|
final int suppHeightBecauseOfShape = uSymbol == null ? 0 : uSymbol.suppHeightBecauseOfShape();
|
|
|
|
final int suppWidthBecauseOfShape = uSymbol == null ? 0 : uSymbol.suppWidthBecauseOfShape();
|
|
|
|
|
|
|
|
titleAndAttributeWidth = (int) Math.max(dimLabel.getWidth(), attributeWidth) + suppWidthBecauseOfShape;
|
2020-02-18 21:24:31 +00:00
|
|
|
titleAndAttributeHeight = (int) (dimLabel.getHeight() + attributeHeight + marginForFields
|
|
|
|
+ suppHeightBecauseOfShape);
|
2016-03-06 16:47:34 +00:00
|
|
|
}
|
|
|
|
|
2020-02-18 21:24:31 +00:00
|
|
|
dotStringFactory.openCluster(titleAndAttributeWidth, titleAndAttributeHeight, title, stereo, g);
|
2016-03-06 16:47:34 +00:00
|
|
|
this.printEntities(g.getLeafsDirect());
|
|
|
|
|
|
|
|
printGroups(g);
|
|
|
|
|
|
|
|
dotStringFactory.closeCluster();
|
|
|
|
}
|
|
|
|
|
2020-12-01 21:39:27 +00:00
|
|
|
private Style getStyle(FontParam fontParam) {
|
|
|
|
return fontParam.getStyleDefinition(SName.stateDiagram)
|
|
|
|
.getMergedStyle(diagram.getSkinParam().getCurrentStyleBuilder());
|
|
|
|
}
|
|
|
|
|
2016-03-06 16:47:34 +00:00
|
|
|
private void printEntities(Collection<ILeaf> entities2) {
|
|
|
|
for (ILeaf ent : entities2) {
|
|
|
|
if (ent.isRemoved()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
printEntity(ent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-21 19:44:14 +00:00
|
|
|
private void exportEntities(ST_Agraph_s g, Collection<ILeaf> entities2) {
|
2016-04-04 19:05:10 +00:00
|
|
|
for (ILeaf ent : entities2) {
|
|
|
|
if (ent.isRemoved()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
exportEntity(g, ent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-21 19:44:14 +00:00
|
|
|
private void exportEntity(ST_Agraph_s g, ILeaf leaf) {
|
2020-02-18 21:24:31 +00:00
|
|
|
final Node node = dotStringFactory.getBibliotekon().getNode(leaf);
|
2020-11-21 17:33:24 +00:00
|
|
|
if (node == null) {
|
|
|
|
System.err.println("CANNOT FIND NODE");
|
|
|
|
return;
|
|
|
|
}
|
2017-07-03 17:59:53 +00:00
|
|
|
// System.err.println("exportEntity " + leaf);
|
2020-02-18 21:24:31 +00:00
|
|
|
final ST_Agnode_s agnode = agnode(g, new CString(node.getUid()), true);
|
|
|
|
agsafeset(agnode, new CString("shape"), new CString("box"), new CString(""));
|
|
|
|
final String width = "" + (node.getWidth() / 72);
|
|
|
|
final String height = "" + (node.getHeight() / 72);
|
|
|
|
agsafeset(agnode, new CString("width"), new CString(width), new CString(""));
|
|
|
|
agsafeset(agnode, new CString("height"), new CString(height), new CString(""));
|
2016-04-22 20:36:08 +00:00
|
|
|
// System.err.println("NODE " + leaf.getUid() + " " + width + " " + height);
|
2020-02-18 21:24:31 +00:00
|
|
|
nodes.put(leaf, agnode);
|
2016-04-04 19:05:10 +00:00
|
|
|
}
|
|
|
|
|
2016-03-06 16:47:34 +00:00
|
|
|
private void printEntity(ILeaf ent) {
|
|
|
|
if (ent.isRemoved()) {
|
|
|
|
throw new IllegalStateException();
|
|
|
|
}
|
|
|
|
final IEntityImage image = printEntityInternal(ent);
|
2020-02-18 21:24:31 +00:00
|
|
|
final Node node = getBibliotekon().createNode(ent, image, dotStringFactory.getColorSequence(), stringBounder);
|
|
|
|
dotStringFactory.addNode(node);
|
2016-03-06 16:47:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 TextBlock getStereoBlock(IGroup g) {
|
|
|
|
final Stereotype stereotype = g.getStereotype();
|
|
|
|
if (stereotype == null) {
|
|
|
|
return TextBlockUtils.empty(0, 0);
|
|
|
|
}
|
2019-03-01 22:16:29 +00:00
|
|
|
final TextBlock tmp = stereotype.getSprite(diagram.getSkinParam());
|
|
|
|
if (tmp != null) {
|
|
|
|
return tmp;
|
2016-03-06 16:47:34 +00:00
|
|
|
}
|
2019-03-29 22:14:07 +00:00
|
|
|
final List<String> stereos = stereotype.getLabels(diagram.getSkinParam().guillemet());
|
2016-03-06 16:47:34 +00:00
|
|
|
if (stereos == null) {
|
|
|
|
return TextBlockUtils.empty(0, 0);
|
|
|
|
}
|
|
|
|
final boolean show = diagram.showPortion(EntityPortion.STEREOTYPE, g);
|
|
|
|
if (show == false) {
|
|
|
|
return TextBlockUtils.empty(0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
final FontParam fontParam = FontParam.PACKAGE_STEREOTYPE;
|
|
|
|
return Display.create(stereos).create(new FontConfiguration(diagram.getSkinParam(), fontParam, stereotype),
|
|
|
|
HorizontalAlignment.CENTER, diagram.getSkinParam());
|
|
|
|
}
|
|
|
|
|
|
|
|
private Collection<ILeaf> getUnpackagedEntities() {
|
|
|
|
final List<ILeaf> result = new ArrayList<ILeaf>();
|
|
|
|
for (ILeaf ent : diagram.getLeafsvalues()) {
|
|
|
|
if (diagram.getEntityFactory().getRootGroup() == ent.getParentContainer()) {
|
|
|
|
result.add(ent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-10-21 19:44:14 +00:00
|
|
|
private void printCluster(ST_Agraph_s g, Cluster cluster) {
|
2020-02-18 21:24:31 +00:00
|
|
|
for (Node node : cluster.getNodes()) {
|
|
|
|
final ST_Agnode_s agnode = agnode(g, new CString(node.getUid()), true);
|
|
|
|
agsafeset(agnode, new CString("shape"), new CString("box"), new CString(""));
|
|
|
|
final String width = "" + (node.getWidth() / 72);
|
|
|
|
final String height = "" + (node.getHeight() / 72);
|
|
|
|
agsafeset(agnode, new CString("width"), new CString(width), new CString(""));
|
|
|
|
agsafeset(agnode, new CString("height"), new CString(height), new CString(""));
|
|
|
|
final ILeaf leaf = dotStringFactory.getBibliotekon().getLeaf(node);
|
|
|
|
nodes.put(leaf, agnode);
|
2016-03-06 16:47:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-04-22 20:36:08 +00:00
|
|
|
private static final Lock lock = new ReentrantLock();
|
|
|
|
|
2016-03-06 16:47:34 +00:00
|
|
|
public ImageData createFile(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
|
|
|
|
throws IOException {
|
2016-04-22 20:36:08 +00:00
|
|
|
lock.lock();
|
|
|
|
try {
|
|
|
|
return createFileLocked(os, dotStrings, fileFormatOption);
|
|
|
|
} finally {
|
|
|
|
lock.unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private ImageData createFileLocked(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
|
|
|
|
throws IOException {
|
2016-03-06 16:47:34 +00:00
|
|
|
|
|
|
|
for (ILeaf leaf : diagram.getLeafsvalues()) {
|
|
|
|
printEntityNew(leaf);
|
|
|
|
}
|
|
|
|
|
|
|
|
Z.open();
|
|
|
|
try {
|
2018-10-21 19:44:14 +00:00
|
|
|
final ST_Agraph_s g = agopen(new CString("g"), Z.z().Agdirected, null);
|
2016-03-06 16:47:34 +00:00
|
|
|
|
|
|
|
// printCluster(g, root);
|
2016-04-04 19:05:10 +00:00
|
|
|
exportEntities(g, getUnpackagedEntities());
|
|
|
|
exportGroups(g, diagram.getEntityFactory().getRootGroup());
|
|
|
|
|
|
|
|
// for (ILeaf leaf : diagram.getLeafsvalues()) {
|
|
|
|
// final Shape shape = bibliotekon.getShape(leaf);
|
|
|
|
// final Agnode_s node = agnode(g, new CString(shape.getUid()), true);
|
|
|
|
// agsafeset(node, new CString("shape"), new CString("box"), new CString(""));
|
|
|
|
// final String width = "" + (shape.getWidth() / 72);
|
|
|
|
// final String height = "" + (shape.getHeight() / 72);
|
|
|
|
// agsafeset(node, new CString("width"), new CString(width), new CString(""));
|
|
|
|
// agsafeset(node, new CString("height"), new CString(height), new CString(""));
|
|
|
|
// nodes.put(leaf, node);
|
|
|
|
// // System.err
|
2020-02-18 21:24:31 +00:00
|
|
|
// // .println("NODE " + leaf.getUid() + " [shape=box, width=" + width + ",
|
|
|
|
// height=" + height + "]");
|
2016-04-04 19:05:10 +00:00
|
|
|
// }
|
|
|
|
//
|
2016-03-06 16:47:34 +00:00
|
|
|
for (Link link : diagram.getLinks()) {
|
2016-04-22 20:36:08 +00:00
|
|
|
// System.err.println("link=" + link);
|
2018-10-21 19:44:14 +00:00
|
|
|
final ST_Agedge_s e = createEdge(g, link);
|
2016-04-22 20:36:08 +00:00
|
|
|
// System.err.println("Agedge_s=" + e);
|
2016-03-06 16:47:34 +00:00
|
|
|
if (e != null) {
|
|
|
|
edges.put(link, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-21 19:44:14 +00:00
|
|
|
final ST_GVC_s gvc = gvContext();
|
|
|
|
JUtilsDebug.reset();
|
2016-03-06 16:47:34 +00:00
|
|
|
gvLayoutJobs(gvc, g);
|
2018-10-21 19:44:14 +00:00
|
|
|
JUtilsDebug.printMe();
|
2016-03-06 16:47:34 +00:00
|
|
|
|
|
|
|
// for (Agedge_s e : edges.values()) {
|
|
|
|
// DebugUtils.printDebugEdge(e);
|
|
|
|
// }
|
|
|
|
|
|
|
|
final double scale = 1;
|
|
|
|
|
2020-10-12 20:56:58 +00:00
|
|
|
final ClockwiseTopRightBottomLeft margins;
|
2020-12-01 21:39:27 +00:00
|
|
|
if (UseStyle.useBetaStyle()) {
|
2020-10-12 20:56:58 +00:00
|
|
|
final Style style = StyleSignature.of(SName.root, SName.document)
|
|
|
|
.getMergedStyle(diagram.getSkinParam().getCurrentStyleBuilder());
|
|
|
|
margins = style.getMargin();
|
2020-05-07 14:12:08 +00:00
|
|
|
} else {
|
2020-10-12 20:56:58 +00:00
|
|
|
margins = ClockwiseTopRightBottomLeft.topRightBottomLeft(0, 5, 5, 0);
|
2020-05-07 14:12:08 +00:00
|
|
|
}
|
2016-03-06 16:47:34 +00:00
|
|
|
|
2020-10-12 20:56:58 +00:00
|
|
|
final ImageBuilder imageBuilder = ImageBuilder.buildD(diagram.getSkinParam(), margins,
|
|
|
|
diagram.getAnimation(), fileFormatOption.isWithMetadata() ? diagram.getMetadata() : null, null,
|
|
|
|
scale);
|
|
|
|
|
|
|
|
imageBuilder.setUDrawable(new Drawing(null, null));
|
2016-08-25 20:45:37 +00:00
|
|
|
final Dimension2D dim = imageBuilder.getFinalDimension(stringBounder);
|
2016-03-06 16:47:34 +00:00
|
|
|
|
2020-10-12 20:56:58 +00:00
|
|
|
final AnnotatedWorker annotatedWorker = new AnnotatedWorker(diagram, diagram.getSkinParam(),
|
|
|
|
fileFormatOption.getDefaultStringBounder());
|
|
|
|
|
|
|
|
// imageBuilder.setUDrawable(new Drawing(new YMirror(dim.getHeight())));
|
|
|
|
imageBuilder.setUDrawable(annotatedWorker.addAdd(new Drawing(new YMirror(dim.getHeight()), dim)));
|
2016-03-06 16:47:34 +00:00
|
|
|
|
2017-04-19 18:30:16 +00:00
|
|
|
return imageBuilder.writeImageTOBEMOVED(fileFormatOption, diagram.seed(), os);
|
2016-03-06 16:47:34 +00:00
|
|
|
} catch (Throwable e) {
|
2018-10-21 19:44:14 +00:00
|
|
|
JUtilsDebug.printMe();
|
2017-04-19 18:30:16 +00:00
|
|
|
UmlDiagram.exportDiagramError(os, e, fileFormatOption, diagram.seed(), diagram.getMetadata(),
|
|
|
|
diagram.getFlashData(), getFailureText3(e));
|
2018-05-01 17:26:04 +00:00
|
|
|
return ImageDataSimple.error();
|
2016-03-06 16:47:34 +00:00
|
|
|
} finally {
|
|
|
|
Z.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-21 19:44:14 +00:00
|
|
|
private void exportGroups(ST_Agraph_s graph, IGroup parent) {
|
2016-04-04 19:05:10 +00:00
|
|
|
for (IGroup g : diagram.getChildrenGroups(parent)) {
|
|
|
|
if (g.isRemoved()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (diagram.isEmpty(g) && g.getGroupType() == GroupType.PACKAGE) {
|
2020-02-18 21:24:31 +00:00
|
|
|
final EntityFactory entityFactory = diagram.getEntityFactory();
|
|
|
|
final ILeaf folder = entityFactory.getLeafForEmptyGroup(g);
|
2016-04-04 19:05:10 +00:00
|
|
|
exportEntity(graph, folder);
|
|
|
|
} else {
|
|
|
|
exportGroup(graph, g);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-10-21 19:44:14 +00:00
|
|
|
private void exportGroup(ST_Agraph_s graph, IGroup group) {
|
2016-04-04 19:05:10 +00:00
|
|
|
final Cluster cluster = getBibliotekon().getCluster(group);
|
2020-10-12 20:56:58 +00:00
|
|
|
if (cluster == null) {
|
|
|
|
System.err.println("CucaDiagramFileMakerJDot::exportGroup issue");
|
|
|
|
return;
|
|
|
|
}
|
2016-04-04 19:05:10 +00:00
|
|
|
JUtils.LOG2("cluster = " + cluster.getClusterId());
|
2018-10-21 19:44:14 +00:00
|
|
|
final ST_Agraph_s cluster1 = agsubg(graph, new CString(cluster.getClusterId()), true);
|
2016-04-04 19:05:10 +00:00
|
|
|
if (cluster.isLabel()) {
|
|
|
|
final double width = cluster.getTitleAndAttributeWidth();
|
|
|
|
final double height = cluster.getTitleAndAttributeHeight() - 5;
|
2020-02-18 21:24:31 +00:00
|
|
|
agsafeset(cluster1, new CString("label"), Macro.createHackInitDimensionFromLabel((int) width, (int) height),
|
|
|
|
new CString(""));
|
2016-04-04 19:05:10 +00:00
|
|
|
}
|
|
|
|
this.exportEntities(cluster1, group.getLeafsDirect());
|
|
|
|
this.clusters.put(group, cluster1);
|
|
|
|
this.exportGroups(cluster1, group);
|
|
|
|
}
|
|
|
|
|
2016-03-06 16:47:34 +00:00
|
|
|
private TextBlock getLabel(Link link) {
|
|
|
|
final double marginLabel = 1; // startUid.equals(endUid) ? 6 : 1;
|
|
|
|
ISkinParam skinParam = diagram.getSkinParam();
|
2017-02-15 21:34:36 +00:00
|
|
|
final FontConfiguration labelFont = new FontConfiguration(skinParam, FontParam.ARROW, null);
|
2016-07-04 19:06:50 +00:00
|
|
|
final TextBlock label = link.getLabel().create(labelFont,
|
|
|
|
skinParam.getDefaultTextAlignment(HorizontalAlignment.CENTER), skinParam);
|
2016-08-25 20:45:37 +00:00
|
|
|
if (TextBlockUtils.isEmpty(label, stringBounder)) {
|
2016-03-06 16:47:34 +00:00
|
|
|
return label;
|
|
|
|
}
|
|
|
|
return TextBlockUtils.withMargin(label, marginLabel, marginLabel);
|
2018-10-21 19:44:14 +00:00
|
|
|
}
|
2016-03-06 16:47:34 +00:00
|
|
|
|
2018-10-21 19:44:14 +00:00
|
|
|
private TextBlock getQualifier(Link link, int n) {
|
|
|
|
final String tmp = n == 1 ? link.getQualifier1() : link.getQualifier2();
|
|
|
|
if (tmp == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
final double marginLabel = 1; // startUid.equals(endUid) ? 6 : 1;
|
|
|
|
ISkinParam skinParam = diagram.getSkinParam();
|
|
|
|
final FontConfiguration labelFont = new FontConfiguration(skinParam, FontParam.ARROW, null);
|
|
|
|
final TextBlock label = Display.getWithNewlines(tmp).create(labelFont,
|
|
|
|
skinParam.getDefaultTextAlignment(HorizontalAlignment.CENTER), skinParam);
|
|
|
|
if (TextBlockUtils.isEmpty(label, stringBounder)) {
|
|
|
|
return label;
|
|
|
|
}
|
|
|
|
return TextBlockUtils.withMargin(label, marginLabel, marginLabel);
|
2016-03-06 16:47:34 +00:00
|
|
|
}
|
|
|
|
|
2018-10-21 19:44:14 +00:00
|
|
|
private ST_Agnode_s getAgnodeFromLeaf(IEntity entity) {
|
|
|
|
final ST_Agnode_s n = nodes.get(entity);
|
2016-04-04 19:05:10 +00:00
|
|
|
if (n != null) {
|
|
|
|
return n;
|
|
|
|
}
|
2020-11-21 17:33:24 +00:00
|
|
|
try {
|
|
|
|
final String id = getBibliotekon().getNodeUid((ILeaf) entity);
|
|
|
|
for (Map.Entry<ILeaf, ST_Agnode_s> ent : nodes.entrySet()) {
|
|
|
|
if (id.equals(getBibliotekon().getNodeUid(ent.getKey()))) {
|
|
|
|
return ent.getValue();
|
|
|
|
}
|
2016-04-04 19:05:10 +00:00
|
|
|
}
|
2020-11-21 17:33:24 +00:00
|
|
|
} catch (IllegalStateException e) {
|
|
|
|
System.err.println("UNKNOWN ENTITY");
|
2016-04-04 19:05:10 +00:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-10-21 19:44:14 +00:00
|
|
|
private ST_Agedge_s createEdge(final ST_Agraph_s g, Link link) {
|
|
|
|
final ST_Agnode_s n = getAgnodeFromLeaf(link.getEntity1());
|
|
|
|
final ST_Agnode_s m = getAgnodeFromLeaf(link.getEntity2());
|
2016-04-04 19:05:10 +00:00
|
|
|
if (n == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (m == null) {
|
2016-03-06 16:47:34 +00:00
|
|
|
return null;
|
|
|
|
}
|
2018-10-21 19:44:14 +00:00
|
|
|
final ST_Agedge_s e = agedge(g, n, m, null, true);
|
2017-07-03 17:59:53 +00:00
|
|
|
// System.err.println("createEdge " + link);
|
2016-03-06 16:47:34 +00:00
|
|
|
agsafeset(e, new CString("arrowtail"), new CString("none"), new CString(""));
|
|
|
|
agsafeset(e, new CString("arrowhead"), new CString("none"), new CString(""));
|
|
|
|
|
|
|
|
int length = link.getLength();
|
2016-04-04 19:05:10 +00:00
|
|
|
// System.err.println("length=" + length);
|
2020-02-18 21:24:31 +00:00
|
|
|
// if (/* pragma.horizontalLineBetweenDifferentPackageAllowed() ||
|
|
|
|
// */link.isInvis() || length != 1) {
|
2016-04-04 19:05:10 +00:00
|
|
|
agsafeset(e, new CString("minlen"), new CString("" + (length - 1)), new CString(""));
|
|
|
|
// }
|
2020-02-18 21:24:31 +00:00
|
|
|
// System.err.print("EDGE " + link.getEntity1().getUid() + "->" +
|
|
|
|
// link.getEntity2().getUid() + " minlen="
|
2016-04-22 20:36:08 +00:00
|
|
|
// + (length - 1) + " ");
|
2016-03-06 16:47:34 +00:00
|
|
|
|
|
|
|
final TextBlock label = getLabel(link);
|
2016-08-25 20:45:37 +00:00
|
|
|
if (TextBlockUtils.isEmpty(label, stringBounder) == false) {
|
2016-03-06 16:47:34 +00:00
|
|
|
final Dimension2D dimLabel = label.calculateDimension(stringBounder);
|
|
|
|
// System.err.println("dimLabel = " + dimLabel);
|
2016-04-04 19:05:10 +00:00
|
|
|
final CString hackDim = Macro.createHackInitDimensionFromLabel((int) dimLabel.getWidth(),
|
|
|
|
(int) dimLabel.getHeight());
|
|
|
|
agsafeset(e, new CString("label"), hackDim, new CString(""));
|
2016-04-22 20:36:08 +00:00
|
|
|
// System.err.print("label=" + hackDim.getContent());
|
2016-03-06 16:47:34 +00:00
|
|
|
}
|
2018-10-21 19:44:14 +00:00
|
|
|
final TextBlock q1 = getQualifier(link, 1);
|
|
|
|
if (q1 != null) {
|
|
|
|
final Dimension2D dimLabel = q1.calculateDimension(stringBounder);
|
|
|
|
// System.err.println("dimLabel = " + dimLabel);
|
|
|
|
final CString hackDim = Macro.createHackInitDimensionFromLabel((int) dimLabel.getWidth(),
|
|
|
|
(int) dimLabel.getHeight());
|
|
|
|
agsafeset(e, new CString("taillabel"), hackDim, new CString(""));
|
|
|
|
}
|
|
|
|
final TextBlock q2 = getQualifier(link, 2);
|
|
|
|
if (q2 != null) {
|
|
|
|
final Dimension2D dimLabel = q2.calculateDimension(stringBounder);
|
|
|
|
// System.err.println("dimLabel = " + dimLabel);
|
|
|
|
final CString hackDim = Macro.createHackInitDimensionFromLabel((int) dimLabel.getWidth(),
|
|
|
|
(int) dimLabel.getHeight());
|
|
|
|
agsafeset(e, new CString("headlabel"), hackDim, new CString(""));
|
|
|
|
}
|
2016-04-22 20:36:08 +00:00
|
|
|
// System.err.println();
|
2016-03-06 16:47:34 +00:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
static private List<String> getFailureText3(Throwable exception) {
|
2016-04-04 19:05:10 +00:00
|
|
|
exception.printStackTrace();
|
2016-03-06 16:47:34 +00:00
|
|
|
final List<String> strings = new ArrayList<String>();
|
|
|
|
strings.add("An error has occured : " + exception);
|
2016-12-21 22:10:29 +00:00
|
|
|
final String quote = StringUtils.rot(QuoteUtils.getSomeQuote());
|
2016-03-06 16:47:34 +00:00
|
|
|
strings.add("<i>" + quote);
|
|
|
|
strings.add(" ");
|
|
|
|
GraphvizCrash.addProperties(strings);
|
|
|
|
strings.add(" ");
|
|
|
|
strings.add("Sorry, the subproject Smetana is not finished yet...");
|
|
|
|
strings.add(" ");
|
2016-04-22 20:36:08 +00:00
|
|
|
strings.add("You should send this diagram and this image to <b>plantuml@gmail.com</b> or");
|
|
|
|
strings.add("post to <b>http://plantuml.com/qa</b> to solve this issue.");
|
2016-03-06 16:47:34 +00:00
|
|
|
strings.add(" ");
|
|
|
|
return strings;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void printEntityNew(ILeaf ent) {
|
|
|
|
if (ent.isRemoved()) {
|
2020-11-21 17:33:24 +00:00
|
|
|
System.err.println("Jdot STRANGE: entity is removed");
|
|
|
|
return;
|
2016-03-06 16:47:34 +00:00
|
|
|
}
|
|
|
|
final IEntityImage image = printEntityInternal(ent);
|
2020-02-18 21:24:31 +00:00
|
|
|
final Node shape = getBibliotekon().createNode(ent, image, dotStringFactory.getColorSequence(), stringBounder);
|
2016-03-06 16:47:34 +00:00
|
|
|
// dotStringFactory.addShape(shape);
|
|
|
|
}
|
|
|
|
|
|
|
|
private Bibliotekon getBibliotekon() {
|
2016-04-04 19:05:10 +00:00
|
|
|
return dotStringFactory.getBibliotekon();
|
2016-03-06 16:47:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private IEntityImage printEntityInternal(ILeaf ent) {
|
|
|
|
if (ent.isRemoved()) {
|
|
|
|
throw new IllegalStateException();
|
|
|
|
}
|
|
|
|
if (ent.getSvekImage() == null) {
|
|
|
|
ISkinParam skinParam = diagram.getSkinParam();
|
|
|
|
if (skinParam.sameClassWidth()) {
|
2020-11-21 17:33:24 +00:00
|
|
|
System.err.println("NOT YET IMPLEMENED");
|
|
|
|
// throw new UnsupportedOperationException();
|
2016-03-06 16:47:34 +00:00
|
|
|
// final double width = getMaxWidth();
|
|
|
|
// skinParam = new SkinParamSameClassWidth(dotData.getSkinParam(), width);
|
|
|
|
}
|
|
|
|
|
2018-08-26 12:09:50 +00:00
|
|
|
return GeneralImageBuilder.createEntityImageBlock(ent, skinParam, diagram.isHideEmptyDescriptionForState(),
|
2017-05-10 19:51:15 +00:00
|
|
|
diagram, getBibliotekon(), null, diagram.getUmlDiagramType(), diagram.getLinks());
|
2016-03-06 16:47:34 +00:00
|
|
|
}
|
|
|
|
return ent.getSvekImage();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|