mirror of
https://github.com/octoleo/plantuml.git
synced 2024-06-14 14:12:27 +00:00
436 lines
16 KiB
Java
436 lines
16 KiB
Java
/* ========================================================================
|
|
* PlantUML : a free UML diagram generator
|
|
* ========================================================================
|
|
*
|
|
* (C) Copyright 2009, Arnaud Roques
|
|
*
|
|
* Project Info: http://plantuml.sourceforge.net
|
|
*
|
|
* 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 Lesser 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.
|
|
*
|
|
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
|
|
* in the United States and other countries.]
|
|
*
|
|
* Original Author: Arnaud Roques
|
|
*
|
|
* Revision $Revision: 5436 $
|
|
*
|
|
*/
|
|
package net.sourceforge.plantuml.classdiagram.command;
|
|
|
|
import java.util.Map;
|
|
|
|
import net.sourceforge.plantuml.Direction;
|
|
import net.sourceforge.plantuml.FontParam;
|
|
import net.sourceforge.plantuml.StringUtils;
|
|
import net.sourceforge.plantuml.UmlDiagramType;
|
|
import net.sourceforge.plantuml.command.CommandExecutionResult;
|
|
import net.sourceforge.plantuml.command.SingleLineCommand2;
|
|
import net.sourceforge.plantuml.command.regex.RegexConcat;
|
|
import net.sourceforge.plantuml.command.regex.RegexLeaf;
|
|
import net.sourceforge.plantuml.command.regex.RegexOr;
|
|
import net.sourceforge.plantuml.command.regex.RegexPartialMatch;
|
|
import net.sourceforge.plantuml.cucadiagram.Entity;
|
|
import net.sourceforge.plantuml.cucadiagram.EntityType;
|
|
import net.sourceforge.plantuml.cucadiagram.Group;
|
|
import net.sourceforge.plantuml.cucadiagram.IEntity;
|
|
import net.sourceforge.plantuml.cucadiagram.Link;
|
|
import net.sourceforge.plantuml.cucadiagram.LinkDecor;
|
|
import net.sourceforge.plantuml.cucadiagram.LinkType;
|
|
import net.sourceforge.plantuml.cucadiagram.Stereotype;
|
|
import net.sourceforge.plantuml.objectdiagram.AbstractClassOrObjectDiagram;
|
|
|
|
final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrObjectDiagram> {
|
|
|
|
public CommandLinkClass2(AbstractClassOrObjectDiagram diagram) {
|
|
super(diagram, getRegexConcat(diagram.getUmlDiagramType()));
|
|
}
|
|
|
|
static RegexConcat getRegexConcat(UmlDiagramType umlDiagramType) {
|
|
return new RegexConcat(
|
|
new RegexLeaf("HEADER", "^(?:@([\\d.]+)\\s+)?"),
|
|
new RegexOr(
|
|
new RegexLeaf("ENT1", "(?:" + optionalKeywords(umlDiagramType) + "\\s+)?"
|
|
+ "(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*|\"[^\"]+\")\\s*(\\<\\<.*\\>\\>)?"),
|
|
new RegexLeaf("COUPLE1",
|
|
"\\(\\s*(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s*,\\s*(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s*\\)")),
|
|
new RegexLeaf("\\s*"),
|
|
new RegexLeaf("FIRST_LABEL", "(?:\"([^\"]+)\")?"),
|
|
new RegexLeaf("\\s*"),
|
|
new RegexOr(
|
|
new RegexLeaf("LEFT_TO_RIGHT",
|
|
"(([-=.]+)(?:(left|right|up|down|le?|ri?|up?|do?)(?=[-=.]))?([-=.]*)(o +|[\\]>*+]|\\|[>\\]])?)"),
|
|
new RegexLeaf("RIGHT_TO_LEFT",
|
|
"(( +o|[\\[<*+]|[<\\[]\\|)?([-=.]*)(left|right|up|down|le?|ri?|up?|do?)?([-=.]+))"),
|
|
new RegexLeaf("NAV_AGREG_OR_COMPO_INV",
|
|
"(\\<([-=.]*)(left|right|up|down|le?|ri?|up?|do?[-=.]+)?([-=.]+)(o +|\\*))"),
|
|
new RegexLeaf("NAV_AGREG_OR_COMPO",
|
|
"(( +o|\\*)([-=.]+)(?:(left|right|up|down|le?|ri?|up?|do?)(?=[-=.]))?([-=.]*)\\>)")),
|
|
new RegexLeaf("\\s*"),
|
|
new RegexLeaf("SECOND_LABEL", "(?:\"([^\"]+)\")?"),
|
|
new RegexLeaf("\\s*"),
|
|
new RegexOr(
|
|
new RegexLeaf("ENT2", "(?:" + optionalKeywords(umlDiagramType) + "\\s+)?"
|
|
+ "(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*|\"[^\"]+\")\\s*(\\<\\<.*\\>\\>)?"),
|
|
new RegexLeaf("COUPLE2",
|
|
"\\(\\s*(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s*,\\s*(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s*\\)")),
|
|
// new RegexLeaf("\\s*"), new RegexLeaf("LABEL_LINK",
|
|
// "(?::\\s*([^\"]+))?$"));
|
|
new RegexLeaf("\\s*"), new RegexOr(null, true, new RegexLeaf("LABEL_LINK", ":\\s*([^\"]+)"),
|
|
new RegexLeaf("LABEL_LINK_XT", ":\\s*(\"[^\"]*\")?\\s*([^\"]*)\\s*(\"[^\"]*\")?")),
|
|
new RegexLeaf("$"));
|
|
}
|
|
|
|
private static String optionalKeywords(UmlDiagramType type) {
|
|
if (type == UmlDiagramType.CLASS) {
|
|
return "(interface|enum|abstract\\s+class|abstract|class)";
|
|
}
|
|
if (type == UmlDiagramType.OBJECT) {
|
|
return "(object)";
|
|
}
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
@Override
|
|
protected CommandExecutionResult executeArg(Map<String, RegexPartialMatch> arg) {
|
|
String ent1 = arg.get("ENT1").get(1);
|
|
String ent2 = arg.get("ENT2").get(1);
|
|
if (ent1 == null) {
|
|
return executeArgSpecial1(arg);
|
|
}
|
|
if (ent2 == null) {
|
|
return executeArgSpecial2(arg);
|
|
}
|
|
ent1 = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(ent1);
|
|
ent2 = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(ent2);
|
|
if (getSystem().isGroup(ent1) && getSystem().isGroup(ent2)) {
|
|
return executePackageLink(arg);
|
|
}
|
|
if (getSystem().isGroup(ent1) || getSystem().isGroup(ent2)) {
|
|
return CommandExecutionResult.error("Package can be only linked to other package");
|
|
}
|
|
|
|
final Entity cl1 = (Entity) getSystem().getOrCreateClass(ent1);
|
|
final Entity cl2 = (Entity) getSystem().getOrCreateClass(ent2);
|
|
|
|
if (arg.get("ENT1").get(0) != null) {
|
|
final EntityType type = EntityType.getEntityType(arg.get("ENT1").get(0));
|
|
if (type != EntityType.OBJECT) {
|
|
cl1.muteToType(type);
|
|
}
|
|
}
|
|
if (arg.get("ENT2").get(0) != null) {
|
|
final EntityType type = EntityType.getEntityType(arg.get("ENT2").get(0));
|
|
if (type != EntityType.OBJECT) {
|
|
cl2.muteToType(type);
|
|
}
|
|
}
|
|
if (arg.get("ENT1").get(2) != null) {
|
|
cl1
|
|
.setStereotype(new Stereotype(arg.get("ENT1").get(2), getSystem().getSkinParam()
|
|
.getCircledCharacterRadius(), getSystem().getSkinParam().getFont(
|
|
FontParam.CIRCLED_CHARACTER, null)));
|
|
}
|
|
if (arg.get("ENT2").get(2) != null) {
|
|
cl2
|
|
.setStereotype(new Stereotype(arg.get("ENT2").get(2), getSystem().getSkinParam()
|
|
.getCircledCharacterRadius(), getSystem().getSkinParam().getFont(
|
|
FontParam.CIRCLED_CHARACTER, null)));
|
|
}
|
|
|
|
final LinkType linkType = getLinkType(arg);
|
|
Direction dir = getDirection(arg);
|
|
final String queue;
|
|
if (dir == Direction.LEFT || dir == Direction.RIGHT) {
|
|
queue = "-";
|
|
} else {
|
|
queue = getQueue(arg);
|
|
}
|
|
if (dir != null && linkType.isExtendsOrAgregationOrCompositionOrPlus()) {
|
|
dir = dir.getInv();
|
|
}
|
|
|
|
String firstLabel = arg.get("FIRST_LABEL").get(0);
|
|
String secondLabel = arg.get("SECOND_LABEL").get(0);
|
|
|
|
String labelLink = null;
|
|
|
|
if (arg.get("LABEL_LINK").get(0) != null) {
|
|
labelLink = arg.get("LABEL_LINK").get(0);
|
|
} else if (arg.get("LABEL_LINK_XT").get(0) != null || arg.get("LABEL_LINK_XT").get(1) != null
|
|
|| arg.get("LABEL_LINK_XT").get(2) != null) {
|
|
labelLink = arg.get("LABEL_LINK_XT").get(1);
|
|
firstLabel = merge(firstLabel, arg.get("LABEL_LINK_XT").get(0));
|
|
secondLabel = merge(arg.get("LABEL_LINK_XT").get(2), secondLabel);
|
|
}
|
|
|
|
Link link = new Link(cl1, cl2, linkType, labelLink, queue.length(), firstLabel, secondLabel, getSystem()
|
|
.getLabeldistance(), getSystem().getLabelangle());
|
|
|
|
if (dir == Direction.LEFT || dir == Direction.UP) {
|
|
link = link.getInv();
|
|
}
|
|
|
|
// getSystem().resetPragmaLabel();
|
|
addLink(link, arg.get("HEADER").get(0));
|
|
|
|
return CommandExecutionResult.ok();
|
|
}
|
|
|
|
private String merge(String a, String b) {
|
|
if (a == null && b == null) {
|
|
return null;
|
|
}
|
|
if (a == null && b != null) {
|
|
return StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(b);
|
|
}
|
|
if (b == null && a != null) {
|
|
return StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(a);
|
|
}
|
|
return StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(a) + "\\n"
|
|
+ StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(b);
|
|
}
|
|
|
|
private void addLink(Link link, String weight) {
|
|
getSystem().addLink(link);
|
|
if (weight == null) {
|
|
final LinkType type = link.getType();
|
|
// --|> highest
|
|
// --*, -->, --o normal
|
|
// ..*, ..>, ..o lowest
|
|
// if (type.isDashed() == false) {
|
|
// if (type.contains(LinkDecor.EXTENDS)) {
|
|
// link.setWeight(3);
|
|
// }
|
|
// if (type.contains(LinkDecor.ARROW) ||
|
|
// type.contains(LinkDecor.COMPOSITION)
|
|
// || type.contains(LinkDecor.AGREGATION)) {
|
|
// link.setWeight(2);
|
|
// }
|
|
// }
|
|
} else {
|
|
link.setWeight(Double.parseDouble(weight));
|
|
}
|
|
}
|
|
|
|
private CommandExecutionResult executePackageLink(Map<String, RegexPartialMatch> arg) {
|
|
final String ent1 = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(arg.get("ENT1").get(1));
|
|
final String ent2 = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(arg.get("ENT2").get(1));
|
|
final Group cl1 = getSystem().getGroup(ent1);
|
|
final Group cl2 = getSystem().getGroup(ent2);
|
|
|
|
final LinkType linkType = getLinkType(arg);
|
|
final Direction dir = getDirection(arg);
|
|
final String queue;
|
|
if (dir == Direction.LEFT || dir == Direction.RIGHT) {
|
|
queue = "-";
|
|
} else {
|
|
queue = getQueue(arg);
|
|
}
|
|
|
|
final String labelLink = arg.get("LABEL_LINK").get(0);
|
|
final String firstLabel = arg.get("FIRST_LABEL").get(0);
|
|
final String secondLabel = arg.get("SECOND_LABEL").get(0);
|
|
Link link = new Link(cl1.getEntityCluster(), cl2.getEntityCluster(), linkType, labelLink, queue.length(),
|
|
firstLabel, secondLabel, getSystem().getLabeldistance(), getSystem().getLabelangle());
|
|
if (dir == Direction.LEFT || dir == Direction.UP) {
|
|
link = link.getInv();
|
|
}
|
|
|
|
getSystem().resetPragmaLabel();
|
|
addLink(link, arg.get("HEADER").get(0));
|
|
return CommandExecutionResult.ok();
|
|
}
|
|
|
|
private CommandExecutionResult executeArgSpecial1(Map<String, RegexPartialMatch> arg) {
|
|
final String clName1 = arg.get("COUPLE1").get(0);
|
|
final String clName2 = arg.get("COUPLE1").get(1);
|
|
if (getSystem().entityExist(clName1) == false) {
|
|
return CommandExecutionResult.error("No class " + clName1);
|
|
}
|
|
if (getSystem().entityExist(clName2) == false) {
|
|
return CommandExecutionResult.error("No class " + clName2);
|
|
}
|
|
|
|
final String ent2 = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(arg.get("ENT2").get(1));
|
|
final IEntity cl2 = getSystem().getOrCreateClass(ent2);
|
|
|
|
final LinkType linkType = getLinkType(arg);
|
|
final String label = arg.get("LABEL_LINK").get(0);
|
|
final int length = getQueue(arg).length();
|
|
final String weight = arg.get("HEADER").get(0);
|
|
|
|
final boolean result = getSystem().associationClass(1, clName1, clName2, cl2, linkType, label);
|
|
if (result == false) {
|
|
return CommandExecutionResult.error("Cannot have more than 2 assocications");
|
|
}
|
|
|
|
return CommandExecutionResult.ok();
|
|
}
|
|
|
|
private CommandExecutionResult executeArgSpecial2(Map<String, RegexPartialMatch> arg) {
|
|
final String clName1 = arg.get("COUPLE2").get(0);
|
|
final String clName2 = arg.get("COUPLE2").get(1);
|
|
if (getSystem().entityExist(clName1) == false) {
|
|
return CommandExecutionResult.error("No class " + clName1);
|
|
}
|
|
if (getSystem().entityExist(clName2) == false) {
|
|
return CommandExecutionResult.error("No class " + clName2);
|
|
}
|
|
|
|
final String ent1 = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(arg.get("ENT1").get(1));
|
|
final IEntity cl1 = getSystem().getOrCreateClass(ent1);
|
|
|
|
final LinkType linkType = getLinkType(arg);
|
|
final String label = arg.get("LABEL_LINK").get(0);
|
|
final int length = getQueue(arg).length();
|
|
final String weight = arg.get("HEADER").get(0);
|
|
|
|
final boolean result = getSystem().associationClass(2, clName1, clName2, cl1, linkType, label);
|
|
if (result == false) {
|
|
return CommandExecutionResult.error("Cannot have more than 2 assocications");
|
|
}
|
|
|
|
return CommandExecutionResult.ok();
|
|
}
|
|
|
|
private LinkType getLinkTypeNormal(RegexPartialMatch regexPartialMatch) {
|
|
final String queue = regexPartialMatch.get(1).trim() + regexPartialMatch.get(3).trim();
|
|
final String key = regexPartialMatch.get(4);
|
|
return getLinkType(queue, key);
|
|
}
|
|
|
|
private LinkType getLinkTypeInv(RegexPartialMatch regexPartialMatch) {
|
|
final String queue = regexPartialMatch.get(2).trim() + regexPartialMatch.get(4).trim();
|
|
final String key = regexPartialMatch.get(1);
|
|
return getLinkType(queue, key).getInv();
|
|
}
|
|
|
|
private LinkType getLinkType(String queue, String key) {
|
|
if (key != null) {
|
|
key = key.trim();
|
|
}
|
|
LinkType linkType = getLinkTypeFromKey(key);
|
|
|
|
if (queue.startsWith(".")) {
|
|
linkType = linkType.getDashed();
|
|
}
|
|
return linkType;
|
|
}
|
|
|
|
private LinkType getLinkType(Map<String, RegexPartialMatch> arg) {
|
|
if (arg.get("LEFT_TO_RIGHT").get(0) != null) {
|
|
return getLinkTypeNormal(arg.get("LEFT_TO_RIGHT"));
|
|
}
|
|
if (arg.get("RIGHT_TO_LEFT").get(0) != null) {
|
|
return getLinkTypeInv(arg.get("RIGHT_TO_LEFT"));
|
|
}
|
|
if (arg.get("NAV_AGREG_OR_COMPO_INV").get(0) != null) {
|
|
final String type = arg.get("NAV_AGREG_OR_COMPO_INV").get(4).trim();
|
|
final String queue = arg.get("NAV_AGREG_OR_COMPO_INV").get(1).trim()
|
|
+ arg.get("NAV_AGREG_OR_COMPO_INV").get(3).trim();
|
|
LinkType result;
|
|
if (type.equals("*")) {
|
|
result = new LinkType(LinkDecor.COMPOSITION, LinkDecor.ARROW);
|
|
} else if (type.equals("o")) {
|
|
result = new LinkType(LinkDecor.AGREGATION, LinkDecor.ARROW);
|
|
} else {
|
|
throw new IllegalArgumentException();
|
|
}
|
|
if (queue.startsWith(".")) {
|
|
result = result.getDashed();
|
|
}
|
|
return result;
|
|
}
|
|
if (arg.get("NAV_AGREG_OR_COMPO").get(0) != null) {
|
|
final String type = arg.get("NAV_AGREG_OR_COMPO").get(1).trim();
|
|
final String queue = arg.get("NAV_AGREG_OR_COMPO").get(2).trim()
|
|
+ arg.get("NAV_AGREG_OR_COMPO").get(4).trim();
|
|
LinkType result;
|
|
if (type.equals("*")) {
|
|
result = new LinkType(LinkDecor.ARROW, LinkDecor.COMPOSITION);
|
|
} else if (type.equals("o")) {
|
|
result = new LinkType(LinkDecor.ARROW, LinkDecor.AGREGATION);
|
|
} else {
|
|
throw new IllegalArgumentException();
|
|
}
|
|
if (queue.startsWith(".")) {
|
|
result = result.getDashed();
|
|
}
|
|
return result;
|
|
}
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
private String getQueue(Map<String, RegexPartialMatch> arg) {
|
|
if (arg.get("LEFT_TO_RIGHT").get(1) != null) {
|
|
return arg.get("LEFT_TO_RIGHT").get(1).trim() + arg.get("LEFT_TO_RIGHT").get(3).trim();
|
|
}
|
|
if (arg.get("RIGHT_TO_LEFT").get(2) != null) {
|
|
return arg.get("RIGHT_TO_LEFT").get(2).trim() + arg.get("RIGHT_TO_LEFT").get(4).trim();
|
|
}
|
|
if (arg.get("NAV_AGREG_OR_COMPO_INV").get(1) != null) {
|
|
return arg.get("NAV_AGREG_OR_COMPO_INV").get(1).trim() + arg.get("NAV_AGREG_OR_COMPO_INV").get(3).trim();
|
|
}
|
|
if (arg.get("NAV_AGREG_OR_COMPO").get(2) != null) {
|
|
return arg.get("NAV_AGREG_OR_COMPO").get(2).trim() + arg.get("NAV_AGREG_OR_COMPO").get(4).trim();
|
|
}
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
private Direction getDirection(Map<String, RegexPartialMatch> arg) {
|
|
if (arg.get("LEFT_TO_RIGHT").get(2) != null) {
|
|
return StringUtils.getQueueDirection(arg.get("LEFT_TO_RIGHT").get(2));
|
|
}
|
|
if (arg.get("RIGHT_TO_LEFT").get(3) != null) {
|
|
return StringUtils.getQueueDirection(arg.get("RIGHT_TO_LEFT").get(3)).getInv();
|
|
}
|
|
if (arg.get("NAV_AGREG_OR_COMPO").get(3) != null) {
|
|
return StringUtils.getQueueDirection(arg.get("NAV_AGREG_OR_COMPO").get(3)).getInv();
|
|
}
|
|
if (arg.get("NAV_AGREG_OR_COMPO_INV").get(2) != null) {
|
|
return StringUtils.getQueueDirection(arg.get("NAV_AGREG_OR_COMPO_INV").get(2));
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private LinkType getLinkTypeFromKey(String k) {
|
|
if (k == null) {
|
|
return new LinkType(LinkDecor.NONE, LinkDecor.NONE);
|
|
}
|
|
if (k.equals("+")) {
|
|
return new LinkType(LinkDecor.PLUS, LinkDecor.NONE);
|
|
}
|
|
if (k.equals("*")) {
|
|
return new LinkType(LinkDecor.COMPOSITION, LinkDecor.NONE);
|
|
}
|
|
if (k.equalsIgnoreCase("o")) {
|
|
return new LinkType(LinkDecor.AGREGATION, LinkDecor.NONE);
|
|
}
|
|
if (k.equals("<") || k.equals(">")) {
|
|
return new LinkType(LinkDecor.ARROW, LinkDecor.NONE);
|
|
}
|
|
if (k.equals("<|") || k.equals("|>")) {
|
|
return new LinkType(LinkDecor.EXTENDS, LinkDecor.NONE);
|
|
}
|
|
// return null;
|
|
throw new IllegalArgumentException(k);
|
|
}
|
|
|
|
}
|