1
0
mirror of https://github.com/octoleo/plantuml.git synced 2024-11-25 14:27:33 +00:00

Merge pull request #1718 from Benjamin-Davies/cheneer

Implement Chen Notation for ER-diagrams
This commit is contained in:
PlantUML 2024-03-21 22:30:28 +01:00 committed by GitHub
commit 4afe3ef7fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 5466 additions and 6 deletions

View File

@ -48,6 +48,7 @@ import net.sourceforge.plantuml.activitydiagram3.ActivityDiagramFactory3;
import net.sourceforge.plantuml.api.PSystemFactory; import net.sourceforge.plantuml.api.PSystemFactory;
import net.sourceforge.plantuml.board.BoardDiagramFactory; import net.sourceforge.plantuml.board.BoardDiagramFactory;
import net.sourceforge.plantuml.bpm.BpmDiagramFactory; import net.sourceforge.plantuml.bpm.BpmDiagramFactory;
import net.sourceforge.plantuml.cheneer.ChenEerDiagramFactory;
import net.sourceforge.plantuml.chronology.ChronologyDiagramFactory; import net.sourceforge.plantuml.chronology.ChronologyDiagramFactory;
import net.sourceforge.plantuml.classdiagram.ClassDiagramFactory; import net.sourceforge.plantuml.classdiagram.ClassDiagramFactory;
import net.sourceforge.plantuml.core.Diagram; import net.sourceforge.plantuml.core.Diagram;
@ -277,6 +278,8 @@ public class PSystemBuilder {
factories.add(new HclDiagramFactory()); factories.add(new HclDiagramFactory());
factories.add(new PSystemEbnfFactory()); factories.add(new PSystemEbnfFactory());
factories.add(new PSystemRegexFactory()); factories.add(new PSystemRegexFactory());
factories.add(new ChenEerDiagramFactory());
} }
private boolean isOk(Diagram ps) { private boolean isOk(Diagram ps) {

View File

@ -62,6 +62,8 @@ public enum LeafType {
PORTIN, PORTOUT, PORTIN, PORTOUT,
CHEN_ENTITY, CHEN_RELATIONSHIP, CHEN_ATTRIBUTE, CHEN_CIRCLE,
STILL_UNKNOWN; STILL_UNKNOWN;
public static LeafType getLeafType(String type) { public static LeafType getLeafType(String type) {

View File

@ -0,0 +1,95 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/paypal
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*
* Original Author: Arnaud Roques
*
*/
package net.sourceforge.plantuml.cheneer;
import java.util.Map;
import java.util.Stack;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.classdiagram.AbstractEntityDiagram;
import net.sourceforge.plantuml.core.UmlSource;
import net.sourceforge.plantuml.skin.UmlDiagramType;
public class ChenEerDiagram extends AbstractEntityDiagram {
public ChenEerDiagram(UmlSource source, Map<String, String> skinParam) {
super(source, UmlDiagramType.CHEN_EER, skinParam);
}
private final Stack<Entity> ownerStack = new Stack<Entity>();
/**
* Pushes the owner of the following attributes.
*
* @see #peekOwner()
* @param group the entity that owns the following attributes
*/
public void pushOwner(Entity group) {
ownerStack.push(group);
}
/**
* Pops an attribute owner from the stack. See also {@link #peekOwner()}.
*
* @see #peekOwner()
* @return true if an owner was popped, false if the stack was empty
*/
public boolean popOwner() {
if (ownerStack.isEmpty()) {
return false;
}
ownerStack.pop();
return true;
}
/**
* Returns the owner of the current attribute.
*
* <p>
* This is used to link attributes based on their lexical position (how they
* appear in sources) without nesting the entities (like how packages are
* done). It is for this reason that we can't use CucaDiagram.getCurrentGroup,
* as that method nests the entities.
*
* @return the owner of the current attribute, or null if there is no owner
*/
public Entity peekOwner() {
if (ownerStack.isEmpty()) {
return null;
}
return ownerStack.peek();
}
}

View File

@ -0,0 +1,78 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/paypal
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*
* Original Author: Arnaud Roques
*
*
*/
package net.sourceforge.plantuml.cheneer;
import java.util.List;
import java.util.Map;
import net.sourceforge.plantuml.AbstractPSystem;
import net.sourceforge.plantuml.cheneer.command.CommandAssociate;
import net.sourceforge.plantuml.cheneer.command.CommandCreateAttribute;
import net.sourceforge.plantuml.cheneer.command.CommandCreateEntity;
import net.sourceforge.plantuml.cheneer.command.CommandEndGroup;
import net.sourceforge.plantuml.cheneer.command.CommandMultiSubclass;
import net.sourceforge.plantuml.cheneer.command.CommandSimpleSubclass;
import net.sourceforge.plantuml.command.Command;
import net.sourceforge.plantuml.command.CommonCommands;
import net.sourceforge.plantuml.command.PSystemCommandFactory;
import net.sourceforge.plantuml.core.DiagramType;
import net.sourceforge.plantuml.core.UmlSource;
public class ChenEerDiagramFactory extends PSystemCommandFactory {
public ChenEerDiagramFactory() {
super(DiagramType.CHEN_EER);
}
@Override
protected void initCommandsList(List<Command> cmds) {
CommonCommands.addTitleCommands(cmds);
CommonCommands.addCommonCommands2(cmds);
cmds.add(new CommandCreateEntity());
cmds.add(new CommandCreateAttribute());
cmds.add(new CommandAssociate());
cmds.add(new CommandEndGroup());
cmds.add(new CommandSimpleSubclass());
cmds.add(new CommandMultiSubclass());
}
@Override
public AbstractPSystem createEmptyDiagram(UmlSource source, Map<String, String> skinParam) {
return new ChenEerDiagram(source, skinParam);
}
}

View File

@ -0,0 +1,108 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/paypal
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*
* Original Author: Arnaud Roques
*
*
*/
package net.sourceforge.plantuml.cheneer.command;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.abel.Link;
import net.sourceforge.plantuml.abel.LinkArg;
import net.sourceforge.plantuml.cheneer.ChenEerDiagram;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.command.SingleLineCommand2;
import net.sourceforge.plantuml.decoration.LinkDecor;
import net.sourceforge.plantuml.decoration.LinkType;
import net.sourceforge.plantuml.klimt.color.NoSuchColorException;
import net.sourceforge.plantuml.klimt.creole.Display;
import net.sourceforge.plantuml.plasma.Quark;
import net.sourceforge.plantuml.regex.IRegex;
import net.sourceforge.plantuml.regex.RegexConcat;
import net.sourceforge.plantuml.regex.RegexLeaf;
import net.sourceforge.plantuml.regex.RegexOptional;
import net.sourceforge.plantuml.regex.RegexResult;
import net.sourceforge.plantuml.utils.LineLocation;
public class CommandAssociate extends SingleLineCommand2<ChenEerDiagram> {
public CommandAssociate() {
super(getRegexConcat());
}
protected static IRegex getRegexConcat() {
return RegexConcat.build(CommandCreateEntity.class.getName(), RegexLeaf.start(), //
new RegexLeaf("NAME1", "([\\w-]+)"), //
RegexLeaf.spaceZeroOrMore(), //
new RegexLeaf("PARTICIPATION", "([-=])"), //
new RegexOptional( //
new RegexLeaf("CARDINALITY", "(\\w+|\\(\\w+,[%s]*\\w+\\))")), //
new RegexLeaf("PARTICIPATION2", "([-=])"), //
RegexLeaf.spaceZeroOrMore(), //
new RegexLeaf("NAME2", "([\\w-]+)"), //
RegexLeaf.end());
}
@Override
protected CommandExecutionResult executeArg(ChenEerDiagram diagram, LineLocation location, RegexResult arg)
throws NoSuchColorException {
final String name1 = diagram.cleanId(arg.get("NAME1", 0));
final String name2 = diagram.cleanId(arg.get("NAME2", 0));
final boolean isDouble = arg.get("PARTICIPATION", 0).equals("=");
final String cardinality = arg.get("CARDINALITY", 0);
final Quark<Entity> quark1 = diagram.quarkInContext(true, name1);
final Entity entity1 = quark1.getData();
if (entity1 == null) {
return CommandExecutionResult.error("No such entity: " + name1);
}
final Quark<Entity> quark2 = diagram.quarkInContext(true, name2);
final Entity entity2 = quark2.getData();
if (entity2 == null) {
return CommandExecutionResult.error("No such entity: " + name2);
}
LinkType linkType = new LinkType(LinkDecor.NONE, LinkDecor.NONE);
if (isDouble) {
linkType = linkType.goBold();
}
final Link link = new Link(diagram.getEntityFactory(), diagram.getCurrentStyleBuilder(), entity1, entity2,
linkType,
LinkArg.build(Display.getWithNewlines(cardinality), 3));
link.setPortMembers(diagram.getPortId(entity1.getName()), diagram.getPortId(entity2.getName()));
diagram.addLink(link);
return CommandExecutionResult.ok();
}
}

View File

@ -0,0 +1,126 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/paypal
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*
* Original Author: Arnaud Roques
*
*
*/
package net.sourceforge.plantuml.cheneer.command;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.abel.LeafType;
import net.sourceforge.plantuml.abel.Link;
import net.sourceforge.plantuml.abel.LinkArg;
import net.sourceforge.plantuml.cheneer.ChenEerDiagram;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.command.SingleLineCommand2;
import net.sourceforge.plantuml.decoration.LinkDecor;
import net.sourceforge.plantuml.decoration.LinkType;
import net.sourceforge.plantuml.klimt.color.NoSuchColorException;
import net.sourceforge.plantuml.klimt.creole.Display;
import net.sourceforge.plantuml.plasma.Quark;
import net.sourceforge.plantuml.regex.IRegex;
import net.sourceforge.plantuml.regex.RegexConcat;
import net.sourceforge.plantuml.regex.RegexLeaf;
import net.sourceforge.plantuml.regex.RegexOptional;
import net.sourceforge.plantuml.regex.RegexResult;
import net.sourceforge.plantuml.stereo.Stereotype;
import net.sourceforge.plantuml.utils.LineLocation;
public class CommandCreateAttribute extends SingleLineCommand2<ChenEerDiagram> {
public CommandCreateAttribute() {
super(getRegexConcat());
}
protected static IRegex getRegexConcat() {
return RegexConcat.build(CommandCreateEntity.class.getName(), RegexLeaf.start(), //
RegexLeaf.spaceZeroOrMore(),
new RegexOptional( // Copied from CommandCreatePackageBlock
new RegexConcat( //
new RegexLeaf("DISPLAY", "[%g]([^%g]+)[%g]"), //
RegexLeaf.spaceOneOrMore(), //
new RegexLeaf("as"), //
RegexLeaf.spaceOneOrMore() //
)), //
new RegexLeaf("CODE", "([%pLN%s_.:]+)"), //
RegexLeaf.spaceZeroOrMore(), //
new RegexLeaf("STEREO", "(<<.*>>)?"), //
RegexLeaf.spaceZeroOrMore(), //
new RegexLeaf("COMPOSITE", "(\\{)?"), //
RegexLeaf.end());
}
@Override
protected CommandExecutionResult executeArg(ChenEerDiagram diagram, LineLocation location, RegexResult arg)
throws NoSuchColorException {
final Entity owner = diagram.peekOwner();
if (owner == null) {
return CommandExecutionResult.error("Attribute must be inside an entity, relationship or another attribute");
}
final LeafType type = LeafType.CHEN_ATTRIBUTE;
final String idShort = diagram.cleanId(arg.get("CODE", 0).trim());
final String id = owner.getName() + "/" + idShort;
final Quark<Entity> quark = diagram.quarkInContext(true, id);
String displayText = arg.get("DISPLAY", 0);
if (displayText == null)
displayText = idShort;
final String stereo = arg.get("STEREO", 0);
final boolean composite = arg.get("COMPOSITE", 0) != null;
Entity entity = quark.getData();
if (entity == null) {
final Display display = Display.getWithNewlines(displayText);
entity = diagram.reallyCreateLeaf(quark, display, type, null);
} else {
return CommandExecutionResult.error("Attribute already exists");
}
if (stereo != null) {
entity.setStereotype(Stereotype.build(stereo));
entity.setStereostyle(stereo);
}
final LinkType linkType = new LinkType(LinkDecor.NONE, LinkDecor.NONE);
final Link link = new Link(diagram.getEntityFactory(), diagram.getCurrentStyleBuilder(), entity, owner, linkType,
LinkArg.build(Display.NULL, 2));
diagram.addLink(link);
if (composite) {
diagram.pushOwner(entity);
}
return CommandExecutionResult.ok();
}
}

View File

@ -0,0 +1,123 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/paypal
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*
* Original Author: Arnaud Roques
*
*
*/
package net.sourceforge.plantuml.cheneer.command;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.abel.LeafType;
import net.sourceforge.plantuml.cheneer.ChenEerDiagram;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.command.SingleLineCommand2;
import net.sourceforge.plantuml.klimt.color.NoSuchColorException;
import net.sourceforge.plantuml.klimt.creole.Display;
import net.sourceforge.plantuml.plasma.Quark;
import net.sourceforge.plantuml.regex.IRegex;
import net.sourceforge.plantuml.regex.RegexConcat;
import net.sourceforge.plantuml.regex.RegexLeaf;
import net.sourceforge.plantuml.regex.RegexOptional;
import net.sourceforge.plantuml.regex.RegexOr;
import net.sourceforge.plantuml.regex.RegexResult;
import net.sourceforge.plantuml.stereo.Stereotype;
import net.sourceforge.plantuml.utils.LineLocation;
public class CommandCreateEntity extends SingleLineCommand2<ChenEerDiagram> {
public CommandCreateEntity() {
super(getRegexConcat());
}
protected static IRegex getRegexConcat() {
return RegexConcat.build(CommandCreateEntity.class.getName(), RegexLeaf.start(), //
new RegexLeaf("TYPE", "(entity|relationship)"), //
RegexLeaf.spaceOneOrMore(), //
new RegexOptional( // Copied from CommandCreatePackageBlock
new RegexConcat( //
new RegexLeaf("DISPLAY", "[%g]([^%g]+)[%g]"), //
RegexLeaf.spaceOneOrMore(), //
new RegexLeaf("as"), //
RegexLeaf.spaceOneOrMore() //
)), //
new RegexLeaf("CODE", "([%pLN_.]+)"), //
RegexLeaf.spaceZeroOrMore(), //
new RegexLeaf("STEREO", "(<<.+>>)?"), //
RegexLeaf.spaceZeroOrMore(), //
new RegexLeaf("\\{"), //
RegexLeaf.end());
}
@Override
protected CommandExecutionResult executeArg(ChenEerDiagram diagram, LineLocation location, RegexResult arg)
throws NoSuchColorException {
LeafType type;
switch (arg.get("TYPE", 0)) {
case "entity":
type = LeafType.CHEN_ENTITY;
break;
case "relationship":
type = LeafType.CHEN_RELATIONSHIP;
break;
default:
throw new IllegalStateException();
}
final String idShort = arg.get("CODE", 0);
final Quark<Entity> quark = diagram.quarkInContext(true, diagram.cleanId(idShort));
String displayText = arg.get("DISPLAY", 0);
if (displayText == null)
displayText = quark.getName();
final String stereo = arg.get("STEREO", 0);
Entity entity = quark.getData();
if (entity == null) {
Display display = Display.getWithNewlines(displayText);
entity = diagram.reallyCreateLeaf(quark, display, type, null);
} else {
if (entity.muteToType(type, null) == false)
return CommandExecutionResult.error("Bad name");
}
if (stereo != null) {
entity.setStereotype(Stereotype.build(stereo));
entity.setStereostyle(stereo);
}
diagram.pushOwner(entity);
return CommandExecutionResult.ok();
}
}

View File

@ -0,0 +1,67 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/paypal
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*
* Original Author: Arnaud Roques
*
*
*/
package net.sourceforge.plantuml.cheneer.command;
import net.sourceforge.plantuml.cheneer.ChenEerDiagram;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.command.SingleLineCommand2;
import net.sourceforge.plantuml.regex.IRegex;
import net.sourceforge.plantuml.regex.RegexConcat;
import net.sourceforge.plantuml.regex.RegexLeaf;
import net.sourceforge.plantuml.regex.RegexResult;
import net.sourceforge.plantuml.utils.LineLocation;
public class CommandEndGroup extends SingleLineCommand2<ChenEerDiagram> {
public CommandEndGroup() {
super(getRegexConcat());
}
protected static IRegex getRegexConcat() {
return RegexConcat.build(CommandEndGroup.class.getName(), RegexLeaf.start(), //
new RegexLeaf("\\}"), //
RegexLeaf.end()); //
}
@Override
protected CommandExecutionResult executeArg(ChenEerDiagram diagram, LineLocation location, RegexResult arg) {
if (!diagram.popOwner()) {
return CommandExecutionResult.error("Unbalanced brackets");
}
return CommandExecutionResult.ok();
}
}

View File

@ -0,0 +1,138 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/paypal
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*
* Original Author: Arnaud Roques
*
*
*/
package net.sourceforge.plantuml.cheneer.command;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.abel.LeafType;
import net.sourceforge.plantuml.abel.Link;
import net.sourceforge.plantuml.abel.LinkArg;
import net.sourceforge.plantuml.cheneer.ChenEerDiagram;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.command.SingleLineCommand2;
import net.sourceforge.plantuml.decoration.LinkDecor;
import net.sourceforge.plantuml.decoration.LinkType;
import net.sourceforge.plantuml.klimt.color.NoSuchColorException;
import net.sourceforge.plantuml.klimt.creole.Display;
import net.sourceforge.plantuml.plasma.Quark;
import net.sourceforge.plantuml.regex.IRegex;
import net.sourceforge.plantuml.regex.RegexConcat;
import net.sourceforge.plantuml.regex.RegexLeaf;
import net.sourceforge.plantuml.regex.RegexResult;
import net.sourceforge.plantuml.utils.LineLocation;
public class CommandMultiSubclass extends SingleLineCommand2<ChenEerDiagram> {
public CommandMultiSubclass() {
super(getRegexConcat());
}
protected static IRegex getRegexConcat() {
return RegexConcat.build(CommandCreateEntity.class.getName(), RegexLeaf.start(), //
new RegexLeaf("SUPERCLASS", "([\\w-]+)"), //
RegexLeaf.spaceZeroOrMore(), //
new RegexLeaf("PARTICIPATION", "([-=])"), //
new RegexLeaf("(>)"), //
new RegexLeaf("PARTICIPATION2", "([-=])"), //
RegexLeaf.spaceZeroOrMore(), //
new RegexLeaf("SYMBOL", "([doU])"), //
RegexLeaf.spaceZeroOrMore(), //
new RegexLeaf("\\{"), //
new RegexLeaf("SUBCLASSES", "(.+)"), //
new RegexLeaf("\\}"), //
RegexLeaf.end());
}
@Override
protected CommandExecutionResult executeArg(ChenEerDiagram diagram, LineLocation location, RegexResult arg)
throws NoSuchColorException {
final String superclass = diagram.cleanId(arg.get("SUPERCLASS", 0));
final String subclasses = arg.get("SUBCLASSES", 0);
final List<String> subclassIds = Arrays.stream(subclasses.split(","))
.map(String::trim)
.map(diagram::cleanId)
.collect(Collectors.toList());
final boolean isDouble = arg.get("PARTICIPATION", 0).equals("=");
final String symbol = arg.get("SYMBOL", 0);
final Quark<Entity> centerQuark = diagram.quarkInContext(false, superclass + "/" + symbol + subclasses + "/center");
final Entity centerEntity = diagram.reallyCreateLeaf(centerQuark, Display.create(symbol), LeafType.CHEN_CIRCLE, null);
final Quark<Entity> superclassQuark = diagram.quarkInContext(true, superclass);
final Entity superclassEntity = superclassQuark.getData();
if (superclassEntity == null) {
return CommandExecutionResult.error("No such entity: " + superclass);
}
LinkType linkType = new LinkType(LinkDecor.NONE, LinkDecor.NONE);
if (isDouble) {
linkType = linkType.goBold();
}
if (symbol.equals("U")) {
linkType = linkType.withMiddleSuperset();
}
final Link link = new Link(diagram.getEntityFactory(), diagram.getCurrentStyleBuilder(), superclassEntity,
centerEntity,
linkType,
LinkArg.build(Display.NULL, 2));
link.setPortMembers(diagram.getPortId(superclassEntity.getName()), diagram.getPortId(centerEntity.getName()));
diagram.addLink(link);
for (String subclass : subclassIds) {
final Quark<Entity> subclassQuark = diagram.quarkInContext(true, subclass);
final Entity subclassEntity = subclassQuark.getData();
if (subclassEntity == null) {
return CommandExecutionResult.error("No such entity: " + subclass);
}
LinkType subclassLinkType = new LinkType(LinkDecor.NONE, LinkDecor.NONE);
if (!symbol.equals("U")) {
subclassLinkType = subclassLinkType.withMiddleSuperset();
}
final Link subclassLink = new Link(diagram.getEntityFactory(), diagram.getCurrentStyleBuilder(), centerEntity,
subclassEntity,
subclassLinkType,
LinkArg.build(Display.NULL, 3));
diagram.addLink(subclassLink);
}
return CommandExecutionResult.ok();
}
}

View File

@ -0,0 +1,111 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/paypal
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*
* Original Author: Arnaud Roques
*
*
*/
package net.sourceforge.plantuml.cheneer.command;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.abel.Link;
import net.sourceforge.plantuml.abel.LinkArg;
import net.sourceforge.plantuml.cheneer.ChenEerDiagram;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.command.SingleLineCommand2;
import net.sourceforge.plantuml.decoration.LinkDecor;
import net.sourceforge.plantuml.decoration.LinkType;
import net.sourceforge.plantuml.klimt.color.NoSuchColorException;
import net.sourceforge.plantuml.klimt.creole.Display;
import net.sourceforge.plantuml.plasma.Quark;
import net.sourceforge.plantuml.regex.IRegex;
import net.sourceforge.plantuml.regex.RegexConcat;
import net.sourceforge.plantuml.regex.RegexLeaf;
import net.sourceforge.plantuml.regex.RegexResult;
import net.sourceforge.plantuml.utils.LineLocation;
public class CommandSimpleSubclass extends SingleLineCommand2<ChenEerDiagram> {
public CommandSimpleSubclass() {
super(getRegexConcat());
}
protected static IRegex getRegexConcat() {
return RegexConcat.build(CommandCreateEntity.class.getName(), RegexLeaf.start(), //
new RegexLeaf("NAME1", "([\\w-]+)"), //
RegexLeaf.spaceZeroOrMore(), //
new RegexLeaf("PARTICIPATION", "([-=])"), //
new RegexLeaf("DIRECTION", "([<>])"), //
new RegexLeaf("PARTICIPATION2", "([-=])"), //
RegexLeaf.spaceZeroOrMore(), //
new RegexLeaf("NAME2", "([\\w-]+)"), //
RegexLeaf.end());
}
@Override
protected CommandExecutionResult executeArg(ChenEerDiagram diagram, LineLocation location, RegexResult arg)
throws NoSuchColorException {
String name1 = diagram.cleanId(arg.get("NAME1", 0));
String name2 = diagram.cleanId(arg.get("NAME2", 0));
final boolean isDouble = arg.get("PARTICIPATION", 0).equals("=");
final boolean isSuperset = arg.get("DIRECTION", 0).equals(">");
final Quark<Entity> quark1 = diagram.quarkInContext(true, name1);
final Entity entity1 = quark1.getData();
if (entity1 == null) {
return CommandExecutionResult.error("No such entity: " + name1);
}
final Quark<Entity> quark2 = diagram.quarkInContext(true, name2);
final Entity entity2 = quark2.getData();
if (entity2 == null) {
return CommandExecutionResult.error("No such entity: " + name2);
}
LinkType linkType = new LinkType(LinkDecor.NONE, LinkDecor.NONE);
if (isDouble) {
linkType = linkType.goBold();
}
if (isSuperset) {
linkType = linkType.withMiddleSuperset();
} else {
linkType = linkType.withMiddleSubset();
}
final Link link = new Link(diagram.getEntityFactory(), diagram.getCurrentStyleBuilder(), entity1, entity2,
linkType,
LinkArg.build(Display.NULL, 3));
link.setPortMembers(diagram.getPortId(entity1.getName()), diagram.getPortId(entity2.getName()));
diagram.addLink(link);
return CommandExecutionResult.ok();
}
}

View File

@ -40,7 +40,7 @@ import net.sourceforge.plantuml.utils.StartUtils;
public enum DiagramType { public enum DiagramType {
// ::remove folder when __HAXE__ // ::remove folder when __HAXE__
UML, BPM, DITAA, DOT, PROJECT, JCCKIT, SALT, FLOW, CREOLE, MATH, LATEX, DEFINITION, GANTT, CHRONOLOGY, NW, MINDMAP, UML, BPM, DITAA, DOT, PROJECT, JCCKIT, SALT, FLOW, CREOLE, MATH, LATEX, DEFINITION, GANTT, CHRONOLOGY, NW, MINDMAP,
WBS, WIRE, JSON, GIT, BOARD, YAML, HCL, EBNF, REGEX, FILES, UNKNOWN; WBS, WIRE, JSON, GIT, BOARD, YAML, HCL, EBNF, REGEX, FILES, CHEN_EER, UNKNOWN;
static public DiagramType getTypeFromArobaseStart(String s) { static public DiagramType getTypeFromArobaseStart(String s) {
s = s.toLowerCase(); s = s.toLowerCase();
@ -130,6 +130,9 @@ public enum DiagramType {
if (StartUtils.startsWithSymbolAnd("startchronology", s)) if (StartUtils.startsWithSymbolAnd("startchronology", s))
return CHRONOLOGY; return CHRONOLOGY;
if (StartUtils.startsWithSymbolAnd("startchen", s))
return CHEN_EER;
return UNKNOWN; return UNKNOWN;
} }
} }

View File

@ -40,10 +40,11 @@ import net.sourceforge.plantuml.svek.extremity.MiddleCircleCircledMode;
import net.sourceforge.plantuml.svek.extremity.MiddleFactory; import net.sourceforge.plantuml.svek.extremity.MiddleFactory;
import net.sourceforge.plantuml.svek.extremity.MiddleFactoryCircle; import net.sourceforge.plantuml.svek.extremity.MiddleFactoryCircle;
import net.sourceforge.plantuml.svek.extremity.MiddleFactoryCircleCircled; import net.sourceforge.plantuml.svek.extremity.MiddleFactoryCircleCircled;
import net.sourceforge.plantuml.svek.extremity.MiddleFactorySubset;
public enum LinkMiddleDecor { public enum LinkMiddleDecor {
NONE, CIRCLE, CIRCLE_CIRCLED, CIRCLE_CIRCLED1, CIRCLE_CIRCLED2; NONE, CIRCLE, CIRCLE_CIRCLED, CIRCLE_CIRCLED1, CIRCLE_CIRCLED2, SUBSET, SUPERSET;
public MiddleFactory getMiddleFactory(HColor backColor, HColor diagramBackColor) { public MiddleFactory getMiddleFactory(HColor backColor, HColor diagramBackColor) {
if (this == CIRCLE) if (this == CIRCLE)
@ -58,6 +59,12 @@ public enum LinkMiddleDecor {
if (this == CIRCLE_CIRCLED2) if (this == CIRCLE_CIRCLED2)
return new MiddleFactoryCircleCircled(MiddleCircleCircledMode.MODE2, backColor, diagramBackColor); return new MiddleFactoryCircleCircled(MiddleCircleCircledMode.MODE2, backColor, diagramBackColor);
if (this == SUBSET)
return new MiddleFactorySubset(false);
if (this == SUPERSET)
return new MiddleFactorySubset(true);
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -148,6 +148,14 @@ public class LinkType {
return new LinkType(decor1, decor2, LinkMiddleDecor.CIRCLE_CIRCLED2, linkStyle); return new LinkType(decor1, decor2, LinkMiddleDecor.CIRCLE_CIRCLED2, linkStyle);
} }
public LinkType withMiddleSubset() {
return new LinkType(decor1, decor2, LinkMiddleDecor.SUBSET, linkStyle);
}
public LinkType withMiddleSuperset() {
return new LinkType(decor1, decor2, LinkMiddleDecor.SUPERSET, linkStyle);
}
public LinkType getInvisible() { public LinkType getInvisible() {
return new LinkType(decor1, decor2, middleDecor, LinkStyle.INVISIBLE()); return new LinkType(decor1, decor2, middleDecor, LinkStyle.INVISIBLE());
} }

View File

@ -39,7 +39,7 @@ import net.sourceforge.plantuml.style.SName;
public enum UmlDiagramType { public enum UmlDiagramType {
SEQUENCE, STATE, CLASS, OBJECT, ACTIVITY, DESCRIPTION, COMPOSITE, FLOW, TIMING, BPM, NWDIAG, MINDMAP, WBS, WIRE, SEQUENCE, STATE, CLASS, OBJECT, ACTIVITY, DESCRIPTION, COMPOSITE, FLOW, TIMING, BPM, NWDIAG, MINDMAP, WBS, WIRE,
HELP, GANTT, SALT, JSON, GIT, BOARD, YAML, HCL, EBNF, REGEX, FILES, CHRONOLOGY; HELP, GANTT, SALT, JSON, GIT, BOARD, YAML, HCL, EBNF, REGEX, FILES, CHRONOLOGY, CHEN_EER;
public SName getStyleName() { public SName getStyleName() {
if (this == SEQUENCE) if (this == SEQUENCE)

View File

@ -64,6 +64,10 @@ public enum SName {
component, // component, //
composite, // composite, //
robust, // robust, //
chenAttribute, //
chenEerDiagram, //
chenEntity, //
chenRelationship, //
concise, // concise, //
clock, // clock, //
componentDiagram, // componentDiagram, //

View File

@ -99,6 +99,10 @@ import net.sourceforge.plantuml.svek.image.EntityImageArcCircle;
import net.sourceforge.plantuml.svek.image.EntityImageAssociation; import net.sourceforge.plantuml.svek.image.EntityImageAssociation;
import net.sourceforge.plantuml.svek.image.EntityImageAssociationPoint; import net.sourceforge.plantuml.svek.image.EntityImageAssociationPoint;
import net.sourceforge.plantuml.svek.image.EntityImageBranch; import net.sourceforge.plantuml.svek.image.EntityImageBranch;
import net.sourceforge.plantuml.svek.image.EntityImageChenAttribute;
import net.sourceforge.plantuml.svek.image.EntityImageChenCircle;
import net.sourceforge.plantuml.svek.image.EntityImageChenEntity;
import net.sourceforge.plantuml.svek.image.EntityImageChenRelationship;
import net.sourceforge.plantuml.svek.image.EntityImageCircleEnd; import net.sourceforge.plantuml.svek.image.EntityImageCircleEnd;
import net.sourceforge.plantuml.svek.image.EntityImageCircleStart; import net.sourceforge.plantuml.svek.image.EntityImageCircleStart;
import net.sourceforge.plantuml.svek.image.EntityImageClass; import net.sourceforge.plantuml.svek.image.EntityImageClass;
@ -254,6 +258,18 @@ public final class GeneralImageBuilder {
if (leaf.getLeafType() == LeafType.TIPS) if (leaf.getLeafType() == LeafType.TIPS)
return new EntityImageTips(leaf, skinParam, bibliotekon, umlDiagramType); return new EntityImageTips(leaf, skinParam, bibliotekon, umlDiagramType);
if (leaf.getLeafType() == LeafType.CHEN_ENTITY)
return new EntityImageChenEntity(leaf, skinParam);
if (leaf.getLeafType() == LeafType.CHEN_RELATIONSHIP)
return new EntityImageChenRelationship(leaf, skinParam);
if (leaf.getLeafType() == LeafType.CHEN_ATTRIBUTE)
return new EntityImageChenAttribute(leaf, skinParam);
if (leaf.getLeafType() == LeafType.CHEN_CIRCLE)
return new EntityImageChenCircle(leaf, skinParam);
// TODO Clean // TODO Clean
if (leaf.getLeafType() == LeafType.DOMAIN && leaf.getStereotype() != null if (leaf.getLeafType() == LeafType.DOMAIN && leaf.getStereotype() != null
&& leaf.getStereotype().isMachineOrSpecification()) && leaf.getStereotype().isMachineOrSpecification())

View File

@ -0,0 +1,52 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/paypal
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*
* Original Author: Arnaud Roques
*
*
*/
package net.sourceforge.plantuml.svek.extremity;
import net.sourceforge.plantuml.klimt.shape.UDrawable;
public class MiddleFactorySubset implements MiddleFactory {
private final boolean reverse;
public MiddleFactorySubset(boolean reverse) {
this.reverse = reverse;
}
public UDrawable createUDrawable(double angle) {
return new MiddleSubset(angle, reverse);
}
}

View File

@ -0,0 +1,82 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/paypal
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*
* Original Author: Arnaud Roques
*
*
*/
package net.sourceforge.plantuml.svek.extremity;
import net.sourceforge.plantuml.klimt.UStroke;
import net.sourceforge.plantuml.klimt.UTranslate;
import net.sourceforge.plantuml.klimt.drawing.UGraphic;
import net.sourceforge.plantuml.klimt.geom.XPoint2D;
import net.sourceforge.plantuml.klimt.shape.UEllipse;
import net.sourceforge.plantuml.klimt.shape.ULine;
class MiddleSubset extends Extremity {
private final double radius = 6;
private final double length = 10;
private final double angle;
private final boolean reverse;
public MiddleSubset(double angle, boolean reverse) {
this.angle = angle;
this.reverse = reverse;
}
@Override
public XPoint2D somePoint() {
return null;
}
public void drawU(UGraphic ug) {
ug = ug.apply(UStroke.withThickness(1.5));
final double rotate = reverse ? -45 : 135;
ug.apply(new UTranslate(-radius, -radius)).draw(new UEllipse(2 * radius, 2 * radius, angle + rotate, 180));
final double sin = Math.sin(Math.toRadians(angle + rotate));
final double cos = Math.cos(Math.toRadians(angle + rotate));
ug.apply(new UTranslate(radius * cos, radius * -sin)).draw(new ULine(length * sin, length * cos));
ug.apply(new UTranslate(radius * -cos, radius * sin)).draw(new ULine(length * sin, length * cos));
}
@Override
public double getDecorationLength() {
return radius;
}
}

View File

@ -0,0 +1,177 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/paypal
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*
* Original Author: Arnaud Roques
*
*
*/
package net.sourceforge.plantuml.svek.image;
import java.util.Collections;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.klimt.UGroupType;
import net.sourceforge.plantuml.klimt.UShape;
import net.sourceforge.plantuml.klimt.UStroke;
import net.sourceforge.plantuml.klimt.UTranslate;
import net.sourceforge.plantuml.klimt.color.ColorType;
import net.sourceforge.plantuml.klimt.color.Colors;
import net.sourceforge.plantuml.klimt.color.HColor;
import net.sourceforge.plantuml.klimt.creole.CreoleMode;
import net.sourceforge.plantuml.klimt.drawing.UGraphic;
import net.sourceforge.plantuml.klimt.font.FontConfiguration;
import net.sourceforge.plantuml.klimt.font.StringBounder;
import net.sourceforge.plantuml.klimt.geom.HorizontalAlignment;
import net.sourceforge.plantuml.klimt.geom.XDimension2D;
import net.sourceforge.plantuml.klimt.shape.TextBlock;
import net.sourceforge.plantuml.klimt.shape.TextBlockInEllipse;
import net.sourceforge.plantuml.klimt.shape.UEllipse;
import net.sourceforge.plantuml.style.ISkinParam;
import net.sourceforge.plantuml.style.PName;
import net.sourceforge.plantuml.style.SName;
import net.sourceforge.plantuml.style.Style;
import net.sourceforge.plantuml.style.StyleSignatureBasic;
import net.sourceforge.plantuml.svek.AbstractEntityImage;
import net.sourceforge.plantuml.svek.ShapeType;
import net.sourceforge.plantuml.url.Url;
public class EntityImageChenAttribute extends AbstractEntityImage {
final static private int MARGIN = 6;
final private boolean isKey;
final private boolean isMulti;
final private boolean isDerived;
final private TextBlock title;
final private Url url;
public EntityImageChenAttribute(Entity entity, ISkinParam skinParam) {
super(entity, skinParam);
isKey = hasStereotype("<<key>>");
isMulti = hasStereotype("<<multi>>");
isDerived = hasStereotype("<<derived>>");
FontConfiguration titleFontConfiguration = getStyleTitle(entity, skinParam)
.getFontConfiguration(getSkinParam().getIHtmlColorSet(), entity.getColors());
if (isKey) {
titleFontConfiguration = titleFontConfiguration.underline();
}
title = entity.getDisplay().create8(titleFontConfiguration, HorizontalAlignment.CENTER, skinParam, CreoleMode.FULL,
getStyle().wrapWidth());
url = entity.getUrl99();
}
private boolean hasStereotype(String stereotype) {
return getEntity().getStereotype() != null && getEntity().getStereotype().toString().contains(stereotype);
}
private Style getStyle() {
return getStyle(getEntity(), getSkinParam());
}
private static Style getStyle(Entity group, ISkinParam skinParam) {
return StyleSignatureBasic.of(SName.root, SName.element, SName.chenEerDiagram, SName.chenAttribute)
.withTOBECHANGED(group.getStereotype()).getMergedStyle(skinParam.getCurrentStyleBuilder());
}
private static Style getStyleTitle(Entity group, ISkinParam skinParam) {
return StyleSignatureBasic.of(SName.root, SName.element, SName.chenEerDiagram, SName.chenAttribute, SName.title)
.withTOBECHANGED(group.getStereotype()).getMergedStyle(skinParam.getCurrentStyleBuilder());
}
@Override
public ShapeType getShapeType() {
return ShapeType.OVAL;
}
@Override
public XDimension2D calculateDimension(StringBounder stringBounder) {
final TextBlockInEllipse ellipse = new TextBlockInEllipse(title, stringBounder);
return ellipse.calculateDimension(stringBounder);
}
@Override
public void drawU(UGraphic ug) {
ug.startGroup(Collections.singletonMap(UGroupType.ID, getEntity().getQuark().toStringPoint()));
if (url != null)
ug.startUrl(url);
final XDimension2D dimTotal = calculateDimension(ug.getStringBounder());
final XDimension2D dimTitle = title.calculateDimension(ug.getStringBounder());
UStroke stroke = getStyle().getStroke(getEntity().getColors());
if (isDerived) {
stroke = new UStroke(10, 10, stroke.getThickness());
}
ug = applyColor(ug);
ug = ug.apply(stroke);
ug.draw(getShape(dimTotal));
if (isMulti) {
ug.apply(new UTranslate(3, 3)).draw(getShape(dimTotal.delta(-6)));
}
final double xTitle = (dimTotal.getWidth() - dimTitle.getWidth()) / 2;
final double yTitle = MARGIN;
title.drawU(ug.apply(new UTranslate(xTitle, yTitle)));
if (url != null)
ug.closeUrl();
ug.closeGroup();
}
final protected UGraphic applyColor(UGraphic ug) {
Colors colors = getEntity().getColors();
HColor border = colors.getColor(ColorType.LINE);
if (border == null)
border = getStyle().value(PName.LineColor).asColor(getSkinParam().getIHtmlColorSet());
ug = ug.apply(border);
HColor backcolor = colors.getColor(ColorType.BACK);
if (backcolor == null)
backcolor = getStyle().value(PName.BackGroundColor).asColor(getSkinParam().getIHtmlColorSet());
ug = ug.apply(backcolor.bg());
return ug;
}
private UShape getShape(XDimension2D dimTotal) {
return UEllipse.build(dimTotal.getWidth(), dimTotal.getHeight());
}
}

View File

@ -0,0 +1,175 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/paypal
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*
* Original Author: Arnaud Roques
*
*
*/
package net.sourceforge.plantuml.svek.image;
import java.util.Collections;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.klimt.UGroupType;
import net.sourceforge.plantuml.klimt.UShape;
import net.sourceforge.plantuml.klimt.UStroke;
import net.sourceforge.plantuml.klimt.UTranslate;
import net.sourceforge.plantuml.klimt.color.ColorType;
import net.sourceforge.plantuml.klimt.color.Colors;
import net.sourceforge.plantuml.klimt.color.HColor;
import net.sourceforge.plantuml.klimt.creole.CreoleMode;
import net.sourceforge.plantuml.klimt.drawing.UGraphic;
import net.sourceforge.plantuml.klimt.font.FontConfiguration;
import net.sourceforge.plantuml.klimt.font.StringBounder;
import net.sourceforge.plantuml.klimt.geom.HorizontalAlignment;
import net.sourceforge.plantuml.klimt.geom.XDimension2D;
import net.sourceforge.plantuml.klimt.shape.TextBlock;
import net.sourceforge.plantuml.klimt.shape.TextBlockInEllipse;
import net.sourceforge.plantuml.klimt.shape.UEllipse;
import net.sourceforge.plantuml.style.ISkinParam;
import net.sourceforge.plantuml.style.PName;
import net.sourceforge.plantuml.style.SName;
import net.sourceforge.plantuml.style.Style;
import net.sourceforge.plantuml.style.StyleSignatureBasic;
import net.sourceforge.plantuml.svek.AbstractEntityImage;
import net.sourceforge.plantuml.svek.ShapeType;
import net.sourceforge.plantuml.url.Url;
public class EntityImageChenCircle extends AbstractEntityImage {
final static private int MARGIN = 6;
final private boolean isKey;
final private boolean isMulti;
final private boolean isDerived;
final private TextBlock title;
final private Url url;
public EntityImageChenCircle(Entity entity, ISkinParam skinParam) {
super(entity, skinParam);
isKey = hasStereotype("<<key>>");
isMulti = hasStereotype("<<multi>>");
isDerived = hasStereotype("<<derived>>");
FontConfiguration titleFontConfiguration = getStyleTitle(entity, skinParam)
.getFontConfiguration(getSkinParam().getIHtmlColorSet(), entity.getColors());
if (isKey) {
titleFontConfiguration = titleFontConfiguration.underline();
}
title = entity.getDisplay().create8(titleFontConfiguration, HorizontalAlignment.CENTER, skinParam, CreoleMode.FULL,
getStyle().wrapWidth());
url = entity.getUrl99();
}
private boolean hasStereotype(String stereotype) {
return getEntity().getStereotype() != null && getEntity().getStereotype().toString().contains(stereotype);
}
private Style getStyle() {
return getStyle(getEntity(), getSkinParam());
}
private static Style getStyle(Entity group, ISkinParam skinParam) {
return StyleSignatureBasic.of(SName.root, SName.element, SName.chenEerDiagram, SName.circle)
.withTOBECHANGED(group.getStereotype()).getMergedStyle(skinParam.getCurrentStyleBuilder());
}
private static Style getStyleTitle(Entity group, ISkinParam skinParam) {
return StyleSignatureBasic.of(SName.root, SName.element, SName.chenEerDiagram, SName.circle, SName.title)
.withTOBECHANGED(group.getStereotype()).getMergedStyle(skinParam.getCurrentStyleBuilder());
}
@Override
public ShapeType getShapeType() {
return ShapeType.OVAL;
}
@Override
public XDimension2D calculateDimension(StringBounder stringBounder) {
return new XDimension2D(25, 25);
}
@Override
public void drawU(UGraphic ug) {
ug.startGroup(Collections.singletonMap(UGroupType.ID, getEntity().getQuark().toStringPoint()));
if (url != null)
ug.startUrl(url);
final XDimension2D dimTotal = calculateDimension(ug.getStringBounder());
final XDimension2D dimTitle = title.calculateDimension(ug.getStringBounder());
UStroke stroke = getStyle().getStroke(getEntity().getColors());
if (isDerived) {
stroke = new UStroke(10, 10, stroke.getThickness());
}
ug = applyColor(ug);
ug = ug.apply(stroke);
ug.draw(getShape(dimTotal));
if (isMulti) {
ug.apply(new UTranslate(3, 3)).draw(getShape(dimTotal.delta(-6)));
}
final double xTitle = (dimTotal.getWidth() - dimTitle.getWidth()) / 2;
final double yTitle = (dimTotal.getHeight() - dimTitle.getHeight()) / 2;
title.drawU(ug.apply(new UTranslate(xTitle, yTitle)));
if (url != null)
ug.closeUrl();
ug.closeGroup();
}
final protected UGraphic applyColor(UGraphic ug) {
Colors colors = getEntity().getColors();
HColor border = colors.getColor(ColorType.LINE);
if (border == null)
border = getStyle().value(PName.LineColor).asColor(getSkinParam().getIHtmlColorSet());
ug = ug.apply(border);
HColor backcolor = colors.getColor(ColorType.BACK);
if (backcolor == null)
backcolor = getStyle().value(PName.BackGroundColor).asColor(getSkinParam().getIHtmlColorSet());
ug = ug.apply(backcolor.bg());
return ug;
}
private UShape getShape(XDimension2D dimTotal) {
return UEllipse.build(dimTotal.getWidth(), dimTotal.getHeight());
}
}

View File

@ -0,0 +1,162 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/paypal
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*
* Original Author: Arnaud Roques
*
*
*/
package net.sourceforge.plantuml.svek.image;
import java.util.Collections;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.klimt.UGroupType;
import net.sourceforge.plantuml.klimt.UStroke;
import net.sourceforge.plantuml.klimt.UTranslate;
import net.sourceforge.plantuml.klimt.color.ColorType;
import net.sourceforge.plantuml.klimt.color.Colors;
import net.sourceforge.plantuml.klimt.color.HColor;
import net.sourceforge.plantuml.klimt.creole.CreoleMode;
import net.sourceforge.plantuml.klimt.drawing.UGraphic;
import net.sourceforge.plantuml.klimt.font.FontConfiguration;
import net.sourceforge.plantuml.klimt.font.StringBounder;
import net.sourceforge.plantuml.klimt.geom.HorizontalAlignment;
import net.sourceforge.plantuml.klimt.geom.XDimension2D;
import net.sourceforge.plantuml.klimt.shape.TextBlock;
import net.sourceforge.plantuml.klimt.shape.URectangle;
import net.sourceforge.plantuml.style.ISkinParam;
import net.sourceforge.plantuml.style.PName;
import net.sourceforge.plantuml.style.SName;
import net.sourceforge.plantuml.style.Style;
import net.sourceforge.plantuml.style.StyleSignatureBasic;
import net.sourceforge.plantuml.svek.AbstractEntityImage;
import net.sourceforge.plantuml.svek.ShapeType;
import net.sourceforge.plantuml.url.Url;
public class EntityImageChenEntity extends AbstractEntityImage {
final private boolean isWeak;
final private TextBlock title;
final private Url url;
public EntityImageChenEntity(Entity entity, ISkinParam skinParam) {
super(entity, skinParam);
isWeak = hasStereotype("<<weak>>");
final FontConfiguration titleFontConfiguration = getStyleStateTitle(entity, skinParam)
.getFontConfiguration(getSkinParam().getIHtmlColorSet(), entity.getColors());
title = entity.getDisplay().create8(titleFontConfiguration, HorizontalAlignment.CENTER, skinParam, CreoleMode.FULL,
getStyleState().wrapWidth());
url = entity.getUrl99();
}
private boolean hasStereotype(String stereotype) {
return getEntity().getStereotype() != null && getEntity().getStereotype().toString().contains(stereotype);
}
private Style getStyleState() {
return getStyleState(getEntity(), getSkinParam());
}
private static Style getStyleState(Entity group, ISkinParam skinParam) {
return StyleSignatureBasic.of(SName.root, SName.element, SName.chenEerDiagram, SName.chenEntity)
.withTOBECHANGED(group.getStereotype()).getMergedStyle(skinParam.getCurrentStyleBuilder());
}
private static Style getStyleStateTitle(Entity group, ISkinParam skinParam) {
return StyleSignatureBasic.of(SName.root, SName.element, SName.chenEerDiagram, SName.chenEntity, SName.title)
.withTOBECHANGED(group.getStereotype()).getMergedStyle(skinParam.getCurrentStyleBuilder());
}
@Override
public ShapeType getShapeType() {
return ShapeType.RECTANGLE;
}
@Override
public XDimension2D calculateDimension(StringBounder stringBounder) {
final XDimension2D dim = title.calculateDimension(stringBounder);
return dim.delta(MARGIN * 2 + 2 * MARGIN_LINE);
}
@Override
public void drawU(UGraphic ug) {
ug.startGroup(Collections.singletonMap(UGroupType.ID, getEntity().getQuark().toStringPoint()));
if (url != null)
ug.startUrl(url);
final XDimension2D dimTotal = calculateDimension(ug.getStringBounder());
final XDimension2D dimTitle = title.calculateDimension(ug.getStringBounder());
final UStroke stroke = getStyleState().getStroke(getEntity().getColors());
ug = applyColor(ug);
ug = ug.apply(stroke);
ug.draw(getShape(dimTotal));
if (isWeak) {
ug.apply(new UTranslate(3, 3)).draw(getShape(dimTotal.delta(-6)));
}
final double xTitle = (dimTotal.getWidth() - dimTitle.getWidth()) / 2;
final double yTitle = MARGIN + MARGIN_LINE;
title.drawU(ug.apply(new UTranslate(xTitle, yTitle)));
if (url != null)
ug.closeUrl();
ug.closeGroup();
}
final protected UGraphic applyColor(UGraphic ug) {
Colors colors = getEntity().getColors();
HColor border = colors.getColor(ColorType.LINE);
if (border == null)
border = getStyleState().value(PName.LineColor).asColor(getSkinParam().getIHtmlColorSet());
ug = ug.apply(border);
HColor backcolor = colors.getColor(ColorType.BACK);
if (backcolor == null)
backcolor = getStyleState().value(PName.BackGroundColor).asColor(getSkinParam().getIHtmlColorSet());
ug = ug.apply(backcolor.bg());
return ug;
}
private URectangle getShape(XDimension2D dimTotal) {
return URectangle.build(dimTotal);
}
}

View File

@ -0,0 +1,178 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/paypal
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
*
* Original Author: Arnaud Roques
*
*
*/
package net.sourceforge.plantuml.svek.image;
import java.util.Arrays;
import java.util.Collections;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.klimt.UGroupType;
import net.sourceforge.plantuml.klimt.UShape;
import net.sourceforge.plantuml.klimt.UStroke;
import net.sourceforge.plantuml.klimt.UTranslate;
import net.sourceforge.plantuml.klimt.color.ColorType;
import net.sourceforge.plantuml.klimt.color.Colors;
import net.sourceforge.plantuml.klimt.color.HColor;
import net.sourceforge.plantuml.klimt.creole.CreoleMode;
import net.sourceforge.plantuml.klimt.drawing.UGraphic;
import net.sourceforge.plantuml.klimt.font.FontConfiguration;
import net.sourceforge.plantuml.klimt.font.StringBounder;
import net.sourceforge.plantuml.klimt.geom.HorizontalAlignment;
import net.sourceforge.plantuml.klimt.geom.XDimension2D;
import net.sourceforge.plantuml.klimt.geom.XPoint2D;
import net.sourceforge.plantuml.klimt.shape.TextBlock;
import net.sourceforge.plantuml.klimt.shape.UPolygon;
import net.sourceforge.plantuml.style.ISkinParam;
import net.sourceforge.plantuml.style.PName;
import net.sourceforge.plantuml.style.SName;
import net.sourceforge.plantuml.style.Style;
import net.sourceforge.plantuml.style.StyleSignatureBasic;
import net.sourceforge.plantuml.svek.AbstractEntityImage;
import net.sourceforge.plantuml.svek.ShapeType;
import net.sourceforge.plantuml.url.Url;
public class EntityImageChenRelationship extends AbstractEntityImage {
final private boolean isIdentifying;
final private TextBlock title;
final private Url url;
public EntityImageChenRelationship(Entity entity, ISkinParam skinParam) {
super(entity, skinParam);
isIdentifying = hasStereotype("<<identifying>>");
final FontConfiguration titleFontConfiguration = getStyleTitle(entity, skinParam)
.getFontConfiguration(getSkinParam().getIHtmlColorSet(), entity.getColors());
title = entity.getDisplay().create8(titleFontConfiguration, HorizontalAlignment.CENTER, skinParam, CreoleMode.FULL,
getStyle().wrapWidth());
url = entity.getUrl99();
}
private boolean hasStereotype(String stereotype) {
return getEntity().getStereotype() != null && getEntity().getStereotype().toString().contains(stereotype);
}
private Style getStyle() {
return getStyle(getEntity(), getSkinParam());
}
private static Style getStyle(Entity group, ISkinParam skinParam) {
return StyleSignatureBasic.of(SName.root, SName.element, SName.chenEerDiagram, SName.chenRelationship)
.withTOBECHANGED(group.getStereotype()).getMergedStyle(skinParam.getCurrentStyleBuilder());
}
private static Style getStyleTitle(Entity group, ISkinParam skinParam) {
return StyleSignatureBasic.of(SName.root, SName.element, SName.chenEerDiagram, SName.chenRelationship, SName.title)
.withTOBECHANGED(group.getStereotype()).getMergedStyle(skinParam.getCurrentStyleBuilder());
}
@Override
public ShapeType getShapeType() {
return ShapeType.DIAMOND;
}
@Override
public XDimension2D calculateDimension(StringBounder stringBounder) {
final XDimension2D dimTitle = title.calculateDimension(stringBounder);
// Fit a diamond with aspect ratio 2 around the text
// diagonal = dimTitle dot ([1, 2] / sqrt(5)) + 2 * MARGIN
// dimTotal = diagonal * [sqrt(5), sqrt(5) / 2]
final double diagonal = (dimTitle.getWidth() + 2 * dimTitle.getHeight()) / Math.sqrt(5) + 2 * MARGIN;
return new XDimension2D(diagonal * Math.sqrt(5), diagonal * Math.sqrt(5) / 2);
}
@Override
public void drawU(UGraphic ug) {
ug.startGroup(Collections.singletonMap(UGroupType.ID, getEntity().getQuark().toStringPoint()));
if (url != null)
ug.startUrl(url);
final XDimension2D dimTotal = calculateDimension(ug.getStringBounder());
final XDimension2D dimTitle = title.calculateDimension(ug.getStringBounder());
final UStroke stroke = getStyle().getStroke(getEntity().getColors());
ug = applyColor(ug);
ug = ug.apply(stroke);
ug.draw(getShape(dimTotal));
if (isIdentifying) {
ug.apply(new UTranslate(10, 5)).draw(getShape(dimTotal.delta(-20, -10)));
}
final double xTitle = (dimTotal.getWidth() - dimTitle.getWidth()) / 2;
final double yTitle = (dimTotal.getHeight() - dimTitle.getHeight()) / 2;
title.drawU(ug.apply(new UTranslate(xTitle, yTitle)));
if (url != null)
ug.closeUrl();
ug.closeGroup();
}
final protected UGraphic applyColor(UGraphic ug) {
Colors colors = getEntity().getColors();
HColor border = colors.getColor(ColorType.LINE);
if (border == null)
border = getStyle().value(PName.LineColor).asColor(getSkinParam().getIHtmlColorSet());
ug = ug.apply(border);
HColor backcolor = colors.getColor(ColorType.BACK);
if (backcolor == null)
backcolor = getStyle().value(PName.BackGroundColor).asColor(getSkinParam().getIHtmlColorSet());
ug = ug.apply(backcolor.bg());
return ug;
}
private UShape getShape(XDimension2D dimTotal) {
final double width = dimTotal.getWidth();
final double height = dimTotal.getHeight();
final XPoint2D p1 = new XPoint2D(0, height / 2);
final XPoint2D p2 = new XPoint2D(width / 2, 0);
final XPoint2D p3 = new XPoint2D(width, height / 2);
final XPoint2D p4 = new XPoint2D(width / 2, height);
return new UPolygon(Arrays.asList(p1, p2, p3, p4));
}
}

View File

@ -0,0 +1,126 @@
package net.sourceforge.plantuml.cheneer.command;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import org.junit.jupiter.api.Test;
import net.sourceforge.plantuml.abel.Link;
import net.sourceforge.plantuml.cheneer.ChenEerDiagram;
import net.sourceforge.plantuml.command.Command;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.core.UmlSource;
import net.sourceforge.plantuml.klimt.color.NoSuchColorException;
import net.sourceforge.plantuml.regex.IRegex;
import net.sourceforge.plantuml.regex.RegexResult;
import net.sourceforge.plantuml.utils.BlocLines;
public class CommandAssociateTest {
private final Command<ChenEerDiagram> command = new CommandAssociate();
private final ChenEerDiagram diagram = new ChenEerDiagram(UmlSource.create(new ArrayList<>(), false), null);
@Test
void test_parse() {
IRegex regex = CommandAssociate.getRegexConcat();
RegexResult matcher = regex.matcher("E1 -- E2");
assertThat(matcher).isNotNull();
assertThat(matcher.get("NAME1", 0)).isEqualTo("E1");
assertThat(matcher.get("NAME2", 0)).isEqualTo("E2");
assertThat(matcher.get("PARTICIPATION", 0)).isEqualTo("-");
assertThat(matcher.get("CARDINALITY", 0)).isNull();
}
@Test
void test_parseWithCompulsoryParticipation() {
IRegex regex = CommandAssociate.getRegexConcat();
RegexResult matcher = regex.matcher("E1 == E2");
assertThat(matcher).isNotNull();
assertThat(matcher.get("NAME1", 0)).isEqualTo("E1");
assertThat(matcher.get("NAME2", 0)).isEqualTo("E2");
assertThat(matcher.get("PARTICIPATION", 0)).isEqualTo("=");
assertThat(matcher.get("CARDINALITY", 0)).isNull();
}
@Test
void test_parseWithCardinality() {
IRegex regex = CommandAssociate.getRegexConcat();
RegexResult matcher = regex.matcher("E1 -N- E2");
assertThat(matcher).isNotNull();
assertThat(matcher.get("NAME1", 0)).isEqualTo("E1");
assertThat(matcher.get("NAME2", 0)).isEqualTo("E2");
assertThat(matcher.get("PARTICIPATION", 0)).isEqualTo("-");
assertThat(matcher.get("CARDINALITY", 0)).isEqualTo("N");
}
@Test
void test_parseCardinalityRange() {
IRegex regex = CommandAssociate.getRegexConcat();
RegexResult matcher = regex.matcher("E1 -(1, n)- E2");
assertThat(matcher).isNotNull();
assertThat(matcher.get("NAME1", 0)).isEqualTo("E1");
assertThat(matcher.get("NAME2", 0)).isEqualTo("E2");
assertThat(matcher.get("PARTICIPATION", 0)).isEqualTo("-");
assertThat(matcher.get("CARDINALITY", 0)).isEqualTo("(1, n)");
}
@Test
void test_execute() throws NoSuchColorException {
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E1 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E2 {"));
BlocLines lines = BlocLines.singleString("E1 -- E2");
CommandExecutionResult result = command.execute(diagram, lines);
assertThat(result).matches(CommandExecutionResult::isOk);
assertThat(diagram.getLinks().size()).isEqualTo(1);
Link link = diagram.getLinks().get(0);
assertThat(link.getEntity1().getName()).isEqualTo("E1");
assertThat(link.getEntity2().getName()).isEqualTo("E2");
assertThat(link.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link.getLabel().toString()).isEqualTo("NULL");
}
@Test
void test_executeWithCompulsoryParticipation() throws NoSuchColorException {
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E1 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E2 {"));
BlocLines lines = BlocLines.singleString("E1 == E2");
CommandExecutionResult result = command.execute(diagram, lines);
assertThat(result).matches(CommandExecutionResult::isOk);
assertThat(diagram.getLinks().size()).isEqualTo(1);
Link link = diagram.getLinks().get(0);
assertThat(link.getEntity1().getName()).isEqualTo("E1");
assertThat(link.getEntity2().getName()).isEqualTo("E2");
assertThat(link.getType().getStyle().getStroke3().getThickness()).isEqualTo(2);
assertThat(link.getLabel().toString()).isEqualTo("NULL");
}
@Test
void test_executeWithCardinality() throws NoSuchColorException {
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E1 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E2 {"));
BlocLines lines = BlocLines.singleString("E1 -N- E2");
CommandExecutionResult result = command.execute(diagram, lines);
assertThat(result).matches(CommandExecutionResult::isOk);
assertThat(diagram.getLinks().size()).isEqualTo(1);
Link link = diagram.getLinks().get(0);
assertThat(link.getEntity1().getName()).isEqualTo("E1");
assertThat(link.getEntity2().getName()).isEqualTo("E2");
assertThat(link.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link.getLabel().toString()).isEqualTo("[N]");
}
}

View File

@ -0,0 +1,173 @@
package net.sourceforge.plantuml.cheneer.command;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import org.junit.jupiter.api.Test;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.abel.Link;
import net.sourceforge.plantuml.cheneer.ChenEerDiagram;
import net.sourceforge.plantuml.command.Command;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.core.UmlSource;
import net.sourceforge.plantuml.klimt.color.NoSuchColorException;
import net.sourceforge.plantuml.regex.IRegex;
import net.sourceforge.plantuml.regex.RegexResult;
import net.sourceforge.plantuml.utils.BlocLines;
public class CommandCreateAttributeTest {
private final Command<ChenEerDiagram> command = new CommandCreateAttribute();
private final ChenEerDiagram diagram = new ChenEerDiagram(UmlSource.create(new ArrayList<>(), false), null);
@Test
void test_parse() {
IRegex regex = CommandCreateAttribute.getRegexConcat();
RegexResult matcher = regex.matcher("attr");
assertThat(matcher).isNotNull();
assertThat(matcher.get("DISPLAY", 0)).isNull();
assertThat(matcher.get("CODE", 0)).isEqualTo("attr");
assertThat(matcher.get("STEREO", 0)).isNull();
assertThat(matcher.get("COMPOSITE", 0)).isNull();
}
@Test
void test_parseWithType() {
IRegex regex = CommandCreateAttribute.getRegexConcat();
RegexResult matcher = regex.matcher("attr : String");
assertThat(matcher).isNotNull();
assertThat(matcher.get("DISPLAY", 0)).isNull();
assertThat(matcher.get("CODE", 0)).isEqualTo("attr : String");
assertThat(matcher.get("STEREO", 0)).isNull();
assertThat(matcher.get("COMPOSITE", 0)).isNull();
}
@Test
void test_parseComposite() {
IRegex regex = CommandCreateAttribute.getRegexConcat();
RegexResult matcher = regex.matcher("attr{");
assertThat(matcher).isNotNull();
assertThat(matcher.get("DISPLAY", 0)).isNull();
assertThat(matcher.get("CODE", 0)).isEqualTo("attr");
assertThat(matcher.get("STEREO", 0)).isNull();
assertThat(matcher.get("COMPOSITE", 0)).isNotNull();
}
@Test
void test_parseWithDisplay() {
IRegex regex = CommandCreateAttribute.getRegexConcat();
RegexResult matcher = regex.matcher("\"My Attribute\" as attr");
assertThat(matcher).isNotNull();
assertThat(matcher.get("DISPLAY", 0)).isEqualTo("My Attribute");
assertThat(matcher.get("CODE", 0)).isEqualTo("attr");
assertThat(matcher.get("STEREO", 0)).isNull();
assertThat(matcher.get("COMPOSITE", 0)).isNull();
}
@Test
void test_parseWithStereo() {
IRegex regex = CommandCreateAttribute.getRegexConcat();
RegexResult matcher = regex.matcher("attr<<red>>");
assertThat(matcher).isNotNull();
assertThat(matcher.get("DISPLAY", 0)).isNull();
assertThat(matcher.get("CODE", 0)).isEqualTo("attr");
assertThat(matcher.get("STEREO", 0)).isEqualTo("<<red>>");
assertThat(matcher.get("COMPOSITE", 0)).isNull();
}
@Test
void test_execute() throws NoSuchColorException {
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E {"));
BlocLines lines = BlocLines.singleString("\"Attribute\" as attr<<stereo>>");
CommandExecutionResult result = command.execute(diagram, lines);
assertThat(result).matches(CommandExecutionResult::isOk);
Entity entity = diagram.quarkInContext(true, "E").getData();
assertThat(entity).isNotNull();
Entity attribute = diagram.quarkInContext(true, "E/attr").getData();
assertThat(attribute).isNotNull();
assertThat(attribute.getDisplay().toString()).isEqualTo("[Attribute]");
assertThat(attribute.getStereotype().toString()).isEqualTo("<<stereo>>");
assertThat(diagram.getLinks().size()).isEqualTo(1);
Link link = diagram.getLinks().get(0);
assertThat(link).isNotNull();
assertThat(link.getEntity1()).isSameAs(attribute);
assertThat(link.getEntity2()).isSameAs(entity);
}
@Test
void test_executeNested() throws NoSuchColorException {
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E {"));
CommandExecutionResult result1 = command.execute(diagram, BlocLines.singleString("attr1{"));
CommandExecutionResult result2 = command.execute(diagram, BlocLines.singleString("attr2"));
assertThat(result1).matches(CommandExecutionResult::isOk);
assertThat(result2).matches(CommandExecutionResult::isOk);
Entity entity = diagram.quarkInContext(true, "E").getData();
assertThat(entity).isNotNull();
Entity attribute1 = diagram.quarkInContext(true, "E/attr1").getData();
assertThat(attribute1).isNotNull();
assertThat(attribute1.getDisplay().toString()).isEqualTo("[attr1]");
Entity attribute2 = diagram.quarkInContext(true, "E/attr1/attr2").getData();
assertThat(attribute2).isNotNull();
assertThat(attribute2.getDisplay().toString()).isEqualTo("[attr2]");
assertThat(diagram.getLinks().size()).isEqualTo(2);
Link link1 = diagram.getLinks().get(0);
assertThat(link1).isNotNull();
assertThat(link1.getEntity1()).isSameAs(attribute1);
assertThat(link1.getEntity2()).isSameAs(entity);
Link link2 = diagram.getLinks().get(1);
assertThat(link2).isNotNull();
assertThat(link2.getEntity1()).isSameAs(attribute2);
assertThat(link2.getEntity2()).isSameAs(attribute1);
}
@Test
void test_executeNonNested() throws NoSuchColorException {
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E {"));
CommandExecutionResult result1 = command.execute(diagram, BlocLines.singleString("attr1"));
CommandExecutionResult result2 = command.execute(diagram, BlocLines.singleString("attr2"));
assertThat(result1).matches(CommandExecutionResult::isOk);
assertThat(result2).matches(CommandExecutionResult::isOk);
Entity entity = diagram.quarkInContext(true, "E").getData();
assertThat(entity).isNotNull();
Entity attribute1 = diagram.quarkInContext(true, "E/attr1").getData();
assertThat(attribute1).isNotNull();
assertThat(attribute1.getDisplay().toString()).isEqualTo("[attr1]");
Entity attribute2 = diagram.quarkInContext(true, "E/attr2").getData();
assertThat(attribute2).isNotNull();
assertThat(attribute2.getDisplay().toString()).isEqualTo("[attr2]");
assertThat(diagram.getLinks().size()).isEqualTo(2);
Link link1 = diagram.getLinks().get(0);
assertThat(link1).isNotNull();
assertThat(link1.getEntity1()).isSameAs(attribute1);
assertThat(link1.getEntity2()).isSameAs(entity);
Link link2 = diagram.getLinks().get(1);
assertThat(link2).isNotNull();
assertThat(link2.getEntity1()).isSameAs(attribute2);
assertThat(link2.getEntity2()).isSameAs(entity);
}
}

View File

@ -0,0 +1,123 @@
package net.sourceforge.plantuml.cheneer.command;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import org.junit.jupiter.api.Test;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.abel.LeafType;
import net.sourceforge.plantuml.cheneer.ChenEerDiagram;
import net.sourceforge.plantuml.command.Command;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.core.UmlSource;
import net.sourceforge.plantuml.klimt.color.NoSuchColorException;
import net.sourceforge.plantuml.regex.IRegex;
import net.sourceforge.plantuml.regex.RegexResult;
import net.sourceforge.plantuml.utils.BlocLines;
public class CommandCreateEntityTest {
private final Command<ChenEerDiagram> command = new CommandCreateEntity();
private final ChenEerDiagram diagram = new ChenEerDiagram(UmlSource.create(new ArrayList<>(), false), null);
@Test
void test_parseEntity() {
IRegex regex = CommandCreateEntity.getRegexConcat();
RegexResult matcher = regex.matcher("entity Bus_123 {");
assertThat(matcher).isNotNull();
assertThat(matcher.get("TYPE", 0)).isEqualTo("entity");
assertThat(matcher.get("DISPLAY", 0)).isNull();
assertThat(matcher.get("CODE", 0)).isEqualTo("Bus_123");
assertThat(matcher.get("STEREO", 0)).isNull();
}
@Test
void test_parseRelationship() {
IRegex regex = CommandCreateEntity.getRegexConcat();
RegexResult matcher = regex.matcher("relationship Drives {");
assertThat(matcher).isNotNull();
assertThat(matcher.get("TYPE", 0)).isEqualTo("relationship");
assertThat(matcher.get("DISPLAY", 0)).isNull();
assertThat(matcher.get("CODE", 0)).isEqualTo("Drives");
assertThat(matcher.get("STEREO", 0)).isNull();
}
@Test
void test_parseInvalid() {
IRegex regex = CommandCreateEntity.getRegexConcat();
RegexResult matcher = regex.matcher("class MyClass {");
assertThat(matcher).isNull();
}
@Test
void test_parseEntityWithStereo() {
IRegex regex = CommandCreateEntity.getRegexConcat();
RegexResult matcher = regex.matcher("entity Bus_123 <<red>> {");
assertThat(matcher).isNotNull();
assertThat(matcher.get("TYPE", 0)).isEqualTo("entity");
assertThat(matcher.get("DISPLAY", 0)).isNull();
assertThat(matcher.get("CODE", 0)).isEqualTo("Bus_123");
assertThat(matcher.get("STEREO", 0)).isEqualTo("<<red>>");
}
@Test
void test_parseEntityWithDisplayName() {
IRegex regex = CommandCreateEntity.getRegexConcat();
RegexResult matcher = regex.matcher("entity \"Red Bus :)\" as Bus_123 <<red>> {");
assertThat(matcher).isNotNull();
assertThat(matcher.get("TYPE", 0)).isEqualTo("entity");
assertThat(matcher.get("DISPLAY", 0)).isEqualTo("Red Bus :)");
assertThat(matcher.get("CODE", 0)).isEqualTo("Bus_123");
assertThat(matcher.get("STEREO", 0)).isEqualTo("<<red>>");
}
@Test
void test_executeWithEntity() throws NoSuchColorException {
BlocLines lines = BlocLines.singleString("entity \"display\" as code <<stereo>> {");
CommandExecutionResult result = command.execute(diagram, lines);
assertThat(result).matches(CommandExecutionResult::isOk);
Entity entity = diagram.quarkInContext(true, "code").getData();
assertThat(entity).isNotNull();
assertThat(entity.getLeafType()).isEqualTo(LeafType.CHEN_ENTITY);
assertThat(entity.getDisplay().toString()).isEqualTo("[display]");
assertThat(entity.getStereotype().toString()).isEqualTo("<<stereo>>");
assertThat(diagram.peekOwner()).isSameAs(entity);
}
@Test
void test_executeWithRelationship() throws NoSuchColorException {
BlocLines lines = BlocLines.singleString("relationship \"display\" as code <<stereo>> {");
CommandExecutionResult result = command.execute(diagram, lines);
assertThat(result).matches(CommandExecutionResult::isOk);
Entity entity = diagram.quarkInContext(true, "code").getData();
assertThat(entity).isNotNull();
assertThat(entity.getLeafType()).isEqualTo(LeafType.CHEN_RELATIONSHIP);
assertThat(entity.getDisplay().toString()).isEqualTo("[display]");
assertThat(entity.getStereotype().toString()).isEqualTo("<<stereo>>");
assertThat(diagram.peekOwner()).isSameAs(entity);
}
@Test
void test_executeWithEntityWhenAlreadyExists() throws NoSuchColorException {
BlocLines lines = BlocLines.singleString("entity \"display\" as code <<stereo>> {");
command.execute(diagram, lines);
CommandExecutionResult result = command.execute(diagram, lines);
assertThat(result).matches(CommandExecutionResult::isOk);
}
}

View File

@ -0,0 +1,49 @@
package net.sourceforge.plantuml.cheneer.command;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import net.sourceforge.plantuml.cheneer.ChenEerDiagram;
import net.sourceforge.plantuml.command.Command;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.core.UmlSource;
import net.sourceforge.plantuml.klimt.color.NoSuchColorException;
import net.sourceforge.plantuml.regex.IRegex;
import net.sourceforge.plantuml.regex.RegexResult;
import net.sourceforge.plantuml.utils.BlocLines;
@ExtendWith(MockitoExtension.class)
public class CommandEndGroupTest {
private final Command<ChenEerDiagram> command = new CommandEndGroup();
@Spy
private final ChenEerDiagram diagram = new ChenEerDiagram(UmlSource.create(new ArrayList<>(), false), null);
@Test
void test_parse() {
IRegex regex = CommandEndGroup.getRegexConcat();
RegexResult matcher = regex.matcher("}");
assertThat(matcher).isNotNull();
}
@Test
void test_execute() throws NoSuchColorException {
diagram.pushOwner(null);
BlocLines lines = BlocLines.singleString("}");
CommandExecutionResult result = command.execute(diagram, lines);
assertThat(result).matches(CommandExecutionResult::isOk);
Mockito.verify(diagram).popOwner();
}
}

View File

@ -0,0 +1,213 @@
package net.sourceforge.plantuml.cheneer.command;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import org.junit.jupiter.api.Test;
import net.sourceforge.plantuml.abel.Link;
import net.sourceforge.plantuml.cheneer.ChenEerDiagram;
import net.sourceforge.plantuml.command.Command;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.core.UmlSource;
import net.sourceforge.plantuml.decoration.LinkMiddleDecor;
import net.sourceforge.plantuml.klimt.color.NoSuchColorException;
import net.sourceforge.plantuml.regex.IRegex;
import net.sourceforge.plantuml.regex.RegexResult;
import net.sourceforge.plantuml.utils.BlocLines;
public class CommandMultiSubclassTest {
private final Command<ChenEerDiagram> command = new CommandMultiSubclass();
private final ChenEerDiagram diagram = new ChenEerDiagram(UmlSource.create(new ArrayList<>(), false), null);
@Test
void test_parse() {
IRegex regex = CommandMultiSubclass.getRegexConcat();
RegexResult matcher = regex.matcher("E1 ->- d { E2, E3, E4 }");
assertThat(matcher).isNotNull();
assertThat(matcher.get("SUPERCLASS", 0)).isEqualTo("E1");
assertThat(matcher.get("SUBCLASSES", 0)).isEqualTo(" E2, E3, E4 ");
assertThat(matcher.get("PARTICIPATION", 0)).isEqualTo("-");
assertThat(matcher.get("SYMBOL", 0)).isEqualTo("d");
}
@Test
void test_parseWithCompulsoryParticipation() {
IRegex regex = CommandMultiSubclass.getRegexConcat();
RegexResult matcher = regex.matcher("E1 =>= d { E2, E3, E4 }");
assertThat(matcher).isNotNull();
assertThat(matcher.get("SUPERCLASS", 0)).isEqualTo("E1");
assertThat(matcher.get("SUBCLASSES", 0)).isEqualTo(" E2, E3, E4 ");
assertThat(matcher.get("PARTICIPATION", 0)).isEqualTo("=");
assertThat(matcher.get("SYMBOL", 0)).isEqualTo("d");
}
@Test
void test_parseOverlapping() {
IRegex regex = CommandMultiSubclass.getRegexConcat();
RegexResult matcher = regex.matcher("E1 ->- o { E2, E3, E4 }");
assertThat(matcher).isNotNull();
assertThat(matcher.get("SUPERCLASS", 0)).isEqualTo("E1");
assertThat(matcher.get("SUBCLASSES", 0)).isEqualTo(" E2, E3, E4 ");
assertThat(matcher.get("PARTICIPATION", 0)).isEqualTo("-");
assertThat(matcher.get("SYMBOL", 0)).isEqualTo("o");
}
@Test
void test_parseCategory() {
IRegex regex = CommandMultiSubclass.getRegexConcat();
RegexResult matcher = regex.matcher("E1 ->- U { E2, E3, E4 }");
assertThat(matcher).isNotNull();
assertThat(matcher.get("SUPERCLASS", 0)).isEqualTo("E1");
assertThat(matcher.get("SUBCLASSES", 0)).isEqualTo(" E2, E3, E4 ");
assertThat(matcher.get("PARTICIPATION", 0)).isEqualTo("-");
assertThat(matcher.get("SYMBOL", 0)).isEqualTo("U");
}
@Test
void test_executeOverlappingSubclasses() throws NoSuchColorException {
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E1 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E2 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E3 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E4 {"));
BlocLines lines = BlocLines.singleString("E1 ->- o { E2, E3, E4 }");
CommandExecutionResult result = command.execute(diagram, lines);
assertThat(result).matches(CommandExecutionResult::isOk);
assertThat(diagram.getLinks().size()).isEqualTo(4);
Link link1 = diagram.getLinks().get(0);
assertThat(link1.getEntity1().getName()).isEqualTo("E1");
assertThat(link1.getEntity2().getName()).isEqualTo("E1/o E2, E3, E4 /center");
assertThat(link1.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link1.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.NONE);
Link link2 = diagram.getLinks().get(1);
assertThat(link2.getEntity1().getName()).isEqualTo("E1/o E2, E3, E4 /center");
assertThat(link2.getEntity2().getName()).isEqualTo("E2");
assertThat(link2.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link2.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.SUPERSET);
Link link3 = diagram.getLinks().get(2);
assertThat(link3.getEntity1().getName()).isEqualTo("E1/o E2, E3, E4 /center");
assertThat(link3.getEntity2().getName()).isEqualTo("E3");
assertThat(link3.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link3.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.SUPERSET);
Link link4 = diagram.getLinks().get(3);
assertThat(link4.getEntity1().getName()).isEqualTo("E1/o E2, E3, E4 /center");
assertThat(link4.getEntity2().getName()).isEqualTo("E4");
assertThat(link4.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link4.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.SUPERSET);
}
@Test
void test_executeOverlappingSubclassesWithCompulsaryParticipation() throws NoSuchColorException {
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E1 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E2 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E3 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E4 {"));
BlocLines lines = BlocLines.singleString("E1 =>= o { E2, E3, E4 }");
CommandExecutionResult result = command.execute(diagram, lines);
assertThat(result).matches(CommandExecutionResult::isOk);
assertThat(diagram.getLinks().size()).isEqualTo(4);
Link link1 = diagram.getLinks().get(0);
assertThat(link1.getEntity1().getName()).isEqualTo("E1");
assertThat(link1.getEntity2().getName()).isEqualTo("E1/o E2, E3, E4 /center");
assertThat(link1.getType().getStyle().getStroke3().getThickness()).isEqualTo(2);
assertThat(link1.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.NONE);
Link link2 = diagram.getLinks().get(1);
assertThat(link2.getEntity1().getName()).isEqualTo("E1/o E2, E3, E4 /center");
assertThat(link2.getEntity2().getName()).isEqualTo("E2");
assertThat(link2.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link2.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.SUPERSET);
Link link3 = diagram.getLinks().get(2);
assertThat(link3.getEntity1().getName()).isEqualTo("E1/o E2, E3, E4 /center");
assertThat(link3.getEntity2().getName()).isEqualTo("E3");
assertThat(link3.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link3.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.SUPERSET);
Link link4 = diagram.getLinks().get(3);
assertThat(link4.getEntity1().getName()).isEqualTo("E1/o E2, E3, E4 /center");
assertThat(link4.getEntity2().getName()).isEqualTo("E4");
assertThat(link4.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link4.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.SUPERSET);
}
@Test
void test_executeCategories() throws NoSuchColorException {
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E1 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E2 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E3 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E4 {"));
BlocLines lines = BlocLines.singleString("E1 ->- U { E2, E3, E4 }");
CommandExecutionResult result = command.execute(diagram, lines);
assertThat(result).matches(CommandExecutionResult::isOk);
assertThat(diagram.getLinks().size()).isEqualTo(4);
Link link1 = diagram.getLinks().get(0);
assertThat(link1.getEntity1().getName()).isEqualTo("E1");
assertThat(link1.getEntity2().getName()).isEqualTo("E1/U E2, E3, E4 /center");
assertThat(link1.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link1.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.SUPERSET);
Link link2 = diagram.getLinks().get(1);
assertThat(link2.getEntity1().getName()).isEqualTo("E1/U E2, E3, E4 /center");
assertThat(link2.getEntity2().getName()).isEqualTo("E2");
assertThat(link2.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link2.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.NONE);
Link link3 = diagram.getLinks().get(2);
assertThat(link3.getEntity1().getName()).isEqualTo("E1/U E2, E3, E4 /center");
assertThat(link3.getEntity2().getName()).isEqualTo("E3");
assertThat(link3.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link3.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.NONE);
Link link4 = diagram.getLinks().get(3);
assertThat(link4.getEntity1().getName()).isEqualTo("E1/U E2, E3, E4 /center");
assertThat(link4.getEntity2().getName()).isEqualTo("E4");
assertThat(link4.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link4.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.NONE);
}
@Test
void test_executeCategoriesWithCompulsaryParticipation() throws NoSuchColorException {
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E1 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E2 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E3 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E4 {"));
BlocLines lines = BlocLines.singleString("E1 =>= U { E2, E3, E4 }");
CommandExecutionResult result = command.execute(diagram, lines);
assertThat(result).matches(CommandExecutionResult::isOk);
assertThat(diagram.getLinks().size()).isEqualTo(4);
Link link1 = diagram.getLinks().get(0);
assertThat(link1.getEntity1().getName()).isEqualTo("E1");
assertThat(link1.getEntity2().getName()).isEqualTo("E1/U E2, E3, E4 /center");
assertThat(link1.getType().getStyle().getStroke3().getThickness()).isEqualTo(2);
assertThat(link1.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.SUPERSET);
Link link2 = diagram.getLinks().get(1);
assertThat(link2.getEntity1().getName()).isEqualTo("E1/U E2, E3, E4 /center");
assertThat(link2.getEntity2().getName()).isEqualTo("E2");
assertThat(link2.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link2.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.NONE);
Link link3 = diagram.getLinks().get(2);
assertThat(link3.getEntity1().getName()).isEqualTo("E1/U E2, E3, E4 /center");
assertThat(link3.getEntity2().getName()).isEqualTo("E3");
assertThat(link3.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link3.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.NONE);
Link link4 = diagram.getLinks().get(3);
assertThat(link4.getEntity1().getName()).isEqualTo("E1/U E2, E3, E4 /center");
assertThat(link4.getEntity2().getName()).isEqualTo("E4");
assertThat(link4.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link4.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.NONE);
}
}

View File

@ -0,0 +1,112 @@
package net.sourceforge.plantuml.cheneer.command;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import org.junit.jupiter.api.Test;
import net.sourceforge.plantuml.abel.Link;
import net.sourceforge.plantuml.cheneer.ChenEerDiagram;
import net.sourceforge.plantuml.command.Command;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.core.UmlSource;
import net.sourceforge.plantuml.decoration.LinkMiddleDecor;
import net.sourceforge.plantuml.klimt.color.NoSuchColorException;
import net.sourceforge.plantuml.regex.IRegex;
import net.sourceforge.plantuml.regex.RegexResult;
import net.sourceforge.plantuml.utils.BlocLines;
public class CommandSimpleSubclassTest {
private final Command<ChenEerDiagram> command = new CommandSimpleSubclass();
private final ChenEerDiagram diagram = new ChenEerDiagram(UmlSource.create(new ArrayList<>(), false), null);
@Test
void test_parseSubset() {
IRegex regex = CommandSimpleSubclass.getRegexConcat();
RegexResult matcher = regex.matcher("E1 -<- E2");
assertThat(matcher).isNotNull();
assertThat(matcher.get("NAME1", 0)).isEqualTo("E1");
assertThat(matcher.get("NAME2", 0)).isEqualTo("E2");
assertThat(matcher.get("PARTICIPATION", 0)).isEqualTo("-");
assertThat(matcher.get("DIRECTION", 0)).isEqualTo("<");
}
@Test
void test_parseSubsetWithCompulsoryParticipation() {
IRegex regex = CommandSimpleSubclass.getRegexConcat();
RegexResult matcher = regex.matcher("E1 =<= E2");
assertThat(matcher).isNotNull();
assertThat(matcher.get("NAME1", 0)).isEqualTo("E1");
assertThat(matcher.get("NAME2", 0)).isEqualTo("E2");
assertThat(matcher.get("PARTICIPATION", 0)).isEqualTo("=");
assertThat(matcher.get("DIRECTION", 0)).isEqualTo("<");
}
@Test
void test_parseSuperset() {
IRegex regex = CommandSimpleSubclass.getRegexConcat();
RegexResult matcher = regex.matcher("E1 ->- E2");
assertThat(matcher).isNotNull();
assertThat(matcher.get("NAME1", 0)).isEqualTo("E1");
assertThat(matcher.get("NAME2", 0)).isEqualTo("E2");
assertThat(matcher.get("PARTICIPATION", 0)).isEqualTo("-");
assertThat(matcher.get("DIRECTION", 0)).isEqualTo(">");
}
@Test
void test_executeSubset() throws NoSuchColorException {
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E1 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E2 {"));
BlocLines lines = BlocLines.singleString("E1 -<- E2");
CommandExecutionResult result = command.execute(diagram, lines);
assertThat(result).matches(CommandExecutionResult::isOk);
Link link = diagram.getLinks().get(0);
assertThat(link.getEntity1().getName()).isEqualTo("E1");
assertThat(link.getEntity2().getName()).isEqualTo("E2");
assertThat(link.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.SUBSET);
}
@Test
void test_executeSubsetWithCompulsoryParticipation() throws NoSuchColorException {
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E1 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E2 {"));
BlocLines lines = BlocLines.singleString("E1 =<= E2");
CommandExecutionResult result = command.execute(diagram, lines);
assertThat(result).matches(CommandExecutionResult::isOk);
Link link = diagram.getLinks().get(0);
assertThat(link.getEntity1().getName()).isEqualTo("E1");
assertThat(link.getEntity2().getName()).isEqualTo("E2");
assertThat(link.getType().getStyle().getStroke3().getThickness()).isEqualTo(2);
assertThat(link.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.SUBSET);
}
@Test
void test_executeSuperset() throws NoSuchColorException {
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E1 {"));
new CommandCreateEntity().execute(diagram, BlocLines.singleString("entity E2 {"));
BlocLines lines = BlocLines.singleString("E1 ->- E2");
CommandExecutionResult result = command.execute(diagram, lines);
assertThat(result).matches(CommandExecutionResult::isOk);
Link link = diagram.getLinks().get(0);
assertThat(link.getEntity1().getName()).isEqualTo("E1");
assertThat(link.getEntity2().getName()).isEqualTo("E2");
assertThat(link.getType().getStyle().getStroke3().getThickness()).isEqualTo(1);
assertThat(link.getType().getMiddleDecor()).isEqualTo(LinkMiddleDecor.SUPERSET);
}
}

View File

@ -0,0 +1,94 @@
package nonreg.simple;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import nonreg.BasicTest;
/*
Test diagram MUST be put between triple quotes
"""
@startchen movies
entity "Director" as DIRECTOR {
"No." as Number <<key>>
Name {
Fname
Lname
}
Born : DATE
Died
Age
}
entity "Customer" as CUSTOMER {
Number <<key>>
Bonus <<derived>>
Name <<multi>>
}
entity "Movie" as MOVIE {
Code
}
relationship "was-rented-to" as RENTED_TO {
Date
}
RENTED_TO -1- CUSTOMER
RENTED_TO -N- MOVIE
RENTED_TO -(N,M)- DIRECTOR
entity "Parent" as PARENT {
}
entity "Member" as MEMBER {
}
CUSTOMER ->- PARENT
MEMBER -<- CUSTOMER
entity "Kid" as CHILD <<weak>> {
Name <<key>>
}
relationship "is-parent-of" as PARENT_OF <<identifying>> {
}
PARENT_OF -1- PARENT
PARENT_OF =N= CHILD
entity "Little Kid" as TODDLER {
FavoriteToy
}
entity "Primary-Aged Kid" as PRIMARY_AGE {
FavoriteColor
}
entity "Teenager" as TEEN {
Hobby
}
CHILD =>= d { TODDLER, PRIMARY_AGE, TEEN }
entity "Human" as PERSON {
}
PERSON ->- U { CUSTOMER, DIRECTOR }
@endchen
"""
*/
public class ChenMovieAlias_Test extends BasicTest {
@Test
void testSimple() throws IOException {
checkImage("(30 entities)");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,94 @@
package nonreg.simple;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import nonreg.BasicTest;
/*
Test diagram MUST be put between triple quotes
"""
@startchen movies
entity DIRECTOR {
Number <<key>>
Name {
Fname
Lname
}
Born : DATE
Died
Age
}
entity CUSTOMER {
Number <<key>>
Bonus <<derived>>
Name <<multi>>
}
entity MOVIE {
Code
}
relationship RENTED_TO {
Date
}
RENTED_TO -1- CUSTOMER
RENTED_TO -N- MOVIE
RENTED_TO -(N,M)- DIRECTOR
entity PARENT {
}
entity MEMBER {
}
CUSTOMER ->- PARENT
MEMBER -<- CUSTOMER
entity CHILD <<weak>> {
Name <<key>>
}
relationship PARENT_OF <<identifying>> {
}
PARENT_OF -1- PARENT
PARENT_OF =N= CHILD
entity TODDLER {
FavoriteToy
}
entity PRIMARY_AGE {
FavoriteColor
}
entity TEEN {
Hobby
}
CHILD =>= d { TODDLER, PRIMARY_AGE, TEEN }
entity PERSON {
}
PERSON ->- U { CUSTOMER, DIRECTOR }
@endchen
"""
*/
public class ChenMovieExtended_Test extends BasicTest {
@Test
void testSimple() throws IOException {
checkImage("(30 entities)");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,56 @@
package nonreg.simple;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import nonreg.BasicTest;
/*
Test diagram MUST be put between triple quotes
"""
@startchen movies
entity DIRECTOR {
Number <<key>>
Name {
Fname
Lname
}
Born : DATE
Died
Age
}
entity CUSTOMER {
Number <<key>>
Bonus <<derived>>
Name <<multi>>
}
entity MOVIE {
Code
}
relationship RENTED_TO {
Date
}
RENTED_TO -1- CUSTOMER
RENTED_TO -N- MOVIE
RENTED_TO -(N,M)- DIRECTOR
@endchen
"""
*/
public class ChenMovie_Test extends BasicTest {
@Test
void testSimple() throws IOException {
checkImage("(16 entities)");
}
}

View File

@ -0,0 +1,555 @@
package nonreg.simple;
public class ChenMovie_TestResult {
}
/*
"""
DPI: 96
dimension: [ 1357.2949 ; 320.0000 ]
scaleFactor: 1.0000
seed: 1893551533522493087
svgLinkTarget: _top
hoverPathColorRGB: null
preserveAspectRatio: none
RECTANGLE:
pt1: [ 411.5000 ; 281.0000 ]
pt2: [ 526.4536 ; 315.0000 ]
xCorner: 0
yCorner: 0
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: DIRECTOR
position: [ 421.5000 ; 301.8889 ]
orientation: 0
font: SansSerif.plain/14 []
color: ff000000
extendedColor: NULL_COLOR
ELLIPSE:
pt1: [ 654.5000 ; 209.0000 ]
pt2: [ 773.7119 ; 237.6424 ]
start: 0.0
extend: 0.0
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: Number
position: [ 669.6174 ; 225.8889 ]
orientation: 0
font: SansSerif.plain/14 [UNDERLINE]
color: ff000000
extendedColor: NULL_COLOR
ELLIPSE:
pt1: [ 74.0000 ; 210.5000 ]
pt2: [ 174.4750 ; 236.2990 ]
start: 0.0
extend: 0.0
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: Name
position: [ 90.8355 ; 227.3889 ]
orientation: 0
font: SansSerif.plain/14 []
color: ff000000
extendedColor: NULL_COLOR
ELLIPSE:
pt1: [ 138.5000 ; 107.5000 ]
pt2: [ 231.5753 ; 133.2990 ]
start: 0.0
extend: 0.0
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: Fname
position: [ 154.2519 ; 124.3889 ]
orientation: 0
font: SansSerif.plain/14 []
color: ff000000
extendedColor: NULL_COLOR
ELLIPSE:
pt1: [ 6.0000 ; 106.5000 ]
pt2: [ 120.0711 ; 134.1142 ]
start: 0.0
extend: 0.0
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: Lname
position: [ 21.8671 ; 123.3889 ]
orientation: 0
font: SansSerif.plain/14 []
color: ff000000
extendedColor: NULL_COLOR
ELLIPSE:
pt1: [ 192.0000 ; 203.0000 ]
pt2: [ 371.7951 ; 243.7590 ]
start: 0.0
extend: 0.0
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: Born : DATE
position: [ 202.3602 ; 219.8889 ]
orientation: 0
font: SansSerif.plain/14 []
color: ff000000
extendedColor: NULL_COLOR
ELLIPSE:
pt1: [ 390.0000 ; 210.5000 ]
pt2: [ 463.9416 ; 236.2990 ]
start: 0.0
extend: 0.0
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: Died
position: [ 402.9498 ; 227.3889 ]
orientation: 0
font: SansSerif.plain/14 []
color: ff000000
extendedColor: NULL_COLOR
ELLIPSE:
pt1: [ 482.0000 ; 210.5000 ]
pt2: [ 540.3515 ; 236.2990 ]
start: 0.0
extend: 0.0
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: Age
position: [ 492.6667 ; 227.3889 ]
orientation: 0
font: SansSerif.plain/14 []
color: ff000000
extendedColor: NULL_COLOR
RECTANGLE:
pt1: [ 917.0000 ; 281.0000 ]
pt2: [ 1049.4781 ; 315.0000 ]
xCorner: 0
yCorner: 0
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: CUSTOMER
position: [ 927.0000 ; 301.8889 ]
orientation: 0
font: SansSerif.plain/14 []
color: ff000000
extendedColor: NULL_COLOR
ELLIPSE:
pt1: [ 1091.5000 ; 209.0000 ]
pt2: [ 1210.7119 ; 237.6424 ]
start: 0.0
extend: 0.0
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: Number
position: [ 1106.6174 ; 225.8889 ]
orientation: 0
font: SansSerif.plain/14 [UNDERLINE]
color: ff000000
extendedColor: NULL_COLOR
ELLIPSE:
pt1: [ 791.5000 ; 209.5000 ]
pt2: [ 906.1275 ; 237.2255 ]
start: 0.0
extend: 0.0
stroke: 10.0-10.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: Bonus
position: [ 807.2808 ; 226.3889 ]
orientation: 0
font: SansSerif.plain/14 []
color: ff000000
extendedColor: NULL_COLOR
ELLIPSE:
pt1: [ 973.0000 ; 210.5000 ]
pt2: [ 1073.4750 ; 236.2990 ]
start: 0.0
extend: 0.0
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
ELLIPSE:
pt1: [ 976.0000 ; 213.5000 ]
pt2: [ 1070.4750 ; 233.2990 ]
start: 0.0
extend: 0.0
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: Name
position: [ 989.8355 ; 227.3889 ]
orientation: 0
font: SansSerif.plain/14 []
color: ff000000
extendedColor: NULL_COLOR
RECTANGLE:
pt1: [ 1274.0000 ; 281.0000 ]
pt2: [ 1352.2949 ; 315.0000 ]
xCorner: 0
yCorner: 0
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: MOVIE
position: [ 1284.0000 ; 301.8889 ]
orientation: 0
font: SansSerif.plain/14 []
color: ff000000
extendedColor: NULL_COLOR
ELLIPSE:
pt1: [ 1228.5000 ; 210.5000 ]
pt2: [ 1317.6043 ; 236.2990 ]
start: 0.0
extend: 0.0
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: Code
position: [ 1243.6703 ; 227.3889 ]
orientation: 0
font: SansSerif.plain/14 []
color: ff000000
extendedColor: NULL_COLOR
POLYGON:
points:
- [ 852.5000 ; 120.3553 ]
- [ 943.2105 ; 75.0000 ]
- [ 1033.9210 ; 120.3553 ]
- [ 943.2105 ; 165.7105 ]
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: RENTED_TO
position: [ 877.6803 ; 124.2441 ]
orientation: 0
font: SansSerif.plain/14 []
color: ff000000
extendedColor: NULL_COLOR
ELLIPSE:
pt1: [ 901.0000 ; 12.0000 ]
pt2: [ 984.5211 ; 37.7990 ]
start: 0.0
extend: 0.0
stroke: 0.0-0.0-0.5
shadow: 0
color: ff181818
backcolor: fff1f1f1
TEXT:
text: Date
position: [ 915.3527 ; 28.8889 ]
orientation: 0
font: SansSerif.plain/14 []
color: ff000000
extendedColor: NULL_COLOR
PATH:
- type: SEG_MOVETO
pt1: [ 665.3328 ; 232.0606 ]
- type: SEG_CUBICTO
pt1: [ 641.4594 ; 239.5693 ]
pt2: [ 611.1286 ; 248.9786 ]
pt3: [ 584.0000 ; 257.0000 ]
- type: SEG_CUBICTO
pt1: [ 563.3889 ; 263.0943 ]
pt2: [ 540.7253 ; 269.5323 ]
pt3: [ 520.6400 ; 275.1474 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ff181818
backcolor: NULL_COLOR
PATH:
- type: SEG_MOVETO
pt1: [ 151.7733 ; 230.5368 ]
- type: SEG_CUBICTO
pt1: [ 159.9663 ; 233.2134 ]
pt2: [ 168.7450 ; 235.8814 ]
pt3: [ 177.0000 ; 238.0000 ]
- type: SEG_CUBICTO
pt1: [ 255.1385 ; 258.0537 ]
pt2: [ 347.1264 ; 273.7179 ]
pt3: [ 405.2514 ; 282.6619 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ff181818
backcolor: NULL_COLOR
PATH:
- type: SEG_MOVETO
pt1: [ 171.5202 ; 127.8846 ]
- type: SEG_CUBICTO
pt1: [ 159.7490 ; 147.3746 ]
pt2: [ 137.0409 ; 184.9732 ]
pt3: [ 125.3504 ; 204.3296 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ff181818
backcolor: NULL_COLOR
PATH:
- type: SEG_MOVETO
pt1: [ 64.9747 ; 128.7040 ]
- type: SEG_CUBICTO
pt1: [ 76.8591 ; 148.3814 ]
pt2: [ 99.0526 ; 185.1281 ]
pt3: [ 110.5964 ; 204.2416 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ff181818
backcolor: NULL_COLOR
PATH:
- type: SEG_MOVETO
pt1: [ 326.6049 ; 238.1195 ]
- type: SEG_CUBICTO
pt1: [ 356.3505 ; 249.6519 ]
pt2: [ 393.4909 ; 264.0513 ]
pt3: [ 421.5014 ; 274.9110 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ff181818
backcolor: NULL_COLOR
PATH:
- type: SEG_MOVETO
pt1: [ 428.1247 ; 230.7986 ]
- type: SEG_CUBICTO
pt1: [ 435.1459 ; 242.9185 ]
pt2: [ 445.9126 ; 261.5039 ]
pt3: [ 453.6250 ; 274.8170 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ff181818
backcolor: NULL_COLOR
PATH:
- type: SEG_MOVETO
pt1: [ 497.8753 ; 230.7986 ]
- type: SEG_CUBICTO
pt1: [ 490.8541 ; 242.9185 ]
pt2: [ 480.0874 ; 261.5039 ]
pt3: [ 472.3750 ; 274.8170 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ff181818
backcolor: NULL_COLOR
PATH:
- type: SEG_MOVETO
pt1: [ 1113.4005 ; 232.1367 ]
- type: SEG_CUBICTO
pt1: [ 1085.2657 ; 244.2782 ]
pt2: [ 1044.0729 ; 262.0549 ]
pt3: [ 1014.3238 ; 274.8930 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ff181818
backcolor: NULL_COLOR
PATH:
- type: SEG_MOVETO
pt1: [ 867.2664 ; 231.6292 ]
- type: SEG_CUBICTO
pt1: [ 889.7416 ; 243.7893 ]
pt2: [ 923.2277 ; 261.9068 ]
pt3: [ 947.2935 ; 274.9275 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ff181818
backcolor: NULL_COLOR
PATH:
- type: SEG_MOVETO
pt1: [ 1010.2145 ; 230.7986 ]
- type: SEG_CUBICTO
pt1: [ 1003.5277 ; 242.9185 ]
pt2: [ 993.2737 ; 261.5039 ]
pt3: [ 985.9285 ; 274.8170 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ff181818
backcolor: NULL_COLOR
PATH:
- type: SEG_MOVETO
pt1: [ 1273.7855 ; 230.7986 ]
- type: SEG_CUBICTO
pt1: [ 1280.4723 ; 242.9185 ]
pt2: [ 1290.7263 ; 261.5039 ]
pt3: [ 1298.0715 ; 274.8170 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ff181818
backcolor: NULL_COLOR
PATH:
- type: SEG_MOVETO
pt1: [ 937.0000 ; 32.2235 ]
- type: SEG_CUBICTO
pt1: [ 937.0000 ; 41.6286 ]
pt2: [ 937.0000 ; 55.2460 ]
pt3: [ 937.0000 ; 68.6542 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ff181818
backcolor: NULL_COLOR
PATH:
- type: SEG_MOVETO
pt1: [ 935.4608 ; 160.0549 ]
- type: SEG_CUBICTO
pt1: [ 935.8346 ; 183.6946 ]
pt2: [ 938.2163 ; 212.9522 ]
pt3: [ 946.0000 ; 238.0000 ]
- type: SEG_CUBICTO
pt1: [ 950.1123 ; 251.2335 ]
pt2: [ 958.0422 ; 264.6940 ]
pt3: [ 964.8519 ; 274.7384 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ff181818
backcolor: NULL_COLOR
EMPTY:
pt1: [ 952.0000 ; 217.0000 ]
pt2: [ 964.3969 ; 230.0000 ]
TEXT:
text: 1
position: [ 953.0000 ; 226.5556 ]
orientation: 0
font: SansSerif.plain/11 []
color: ff000000
extendedColor: NULL_COLOR
PATH:
- type: SEG_MOVETO
pt1: [ 1027.7972 ; 121.8769 ]
- type: SEG_CUBICTO
pt1: [ 1129.4735 ; 131.1293 ]
pt2: [ 1284.8054 ; 152.3173 ]
pt3: [ 1321.0000 ; 197.0000 ]
- type: SEG_CUBICTO
pt1: [ 1338.9667 ; 219.1802 ]
pt2: [ 1326.2568 ; 254.3104 ]
pt3: [ 1316.1265 ; 274.7586 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ff181818
backcolor: NULL_COLOR
EMPTY:
pt1: [ 1336.0000 ; 217.0000 ]
pt2: [ 1348.5088 ; 230.0000 ]
TEXT:
text: N
position: [ 1337.0000 ; 226.5556 ]
orientation: 0
font: SansSerif.plain/11 []
color: ff000000
extendedColor: NULL_COLOR
PATH:
- type: SEG_MOVETO
pt1: [ 846.3294 ; 120.6490 ]
- type: SEG_CUBICTO
pt1: [ 770.3065 ; 128.4455 ]
pt2: [ 661.2914 ; 148.0025 ]
pt3: [ 579.0000 ; 197.0000 ]
- type: SEG_CUBICTO
pt1: [ 558.1640 ; 209.4060 ]
pt2: [ 561.2682 ; 222.0525 ]
pt3: [ 543.0000 ; 238.0000 ]
- type: SEG_CUBICTO
pt1: [ 527.0605 ; 251.9147 ]
pt2: [ 507.2472 ; 265.0672 ]
pt3: [ 491.3266 ; 274.7892 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ff181818
backcolor: NULL_COLOR
EMPTY:
pt1: [ 585.0000 ; 217.0000 ]
pt2: [ 645.5178 ; 230.0000 ]
TEXT:
text: (N,M)
position: [ 586.0000 ; 226.5556 ]
orientation: 0
font: SansSerif.plain/11 []
color: ff000000
extendedColor: NULL_COLOR
"""
*/