一步一步將PlantUML類圖匯出為自定義格式的XMI檔案
說明:
- 首次發表日期:2024-09-08
- PlantUML官網: https://plantuml.com/zh/
- PlantUML命令列文件: https://plantuml.com/zh/command-line#6a26f548831e6a8c
- PlantUML XMI文件: https://plantuml.com/zh/xmi
- 進一步用處:根據匯出的XMI檔案生成程式碼
PlantUML(Java)中獲取類圖以及plantuml xmi程式碼是如何遍歷實體並獲取屬性的
以下是來自 https://stackoverflow.com/questions/76599075/parse-uml-class-diagram-with-plantuml-java-api 的程式碼:
String source = "@startuml\n" +
PUT YOUR CLASS DIAGRAM HERE
"@enduml";
SourceStringReader r = new SourceStringReader(source);
FileOutputStream file = new FileOutputStream("testGroovy2.png");
ClassDiagram cd = (ClassDiagram) r.getBlocks().get(0).getDiagram();
Collection<Quark<Entity>> classes =
cd.getEntityFactory().root().getChildren();
classes.stream().forEach(c-> System.out.println(c));
從中知道如何獲取ClassDiagram
檢視 net.sourceforge.plantuml.xmi.XmiClassDiagramAbstract
中XmiClassDiagramStandard
的定義:
public class XmiClassDiagramStandard extends XmiClassDiagramAbstract implements XmlDiagramTransformer {
public XmiClassDiagramStandard(ClassDiagram classDiagram) throws ParserConfigurationException {
super(classDiagram);
Iterator var2 = classDiagram.getEntityFactory().leafs().iterator();
while(var2.hasNext()) {
Entity ent = (Entity)var2.next();
Element cla = this.createEntityNode(ent);
if (cla != null) {
this.ownedElementRoot.appendChild(cla);
this.done.add(ent);
}
}
}
}
從中知道如何遍歷entity
檢視 net.sourceforge.plantuml.xmi.XmiClassDiagramStandard
中的createEntityNode
方法:
protected final Element createEntityNode(Entity entity) {
Element cla = this.document.createElement("UML:Class");
if (entity.getLeafType() == LeafType.NOTE) {
return null;
} else {
cla.setAttribute("xmi.id", entity.getUid());
cla.setAttribute("name", entity.getDisplay().get(0).toString());
Stereotype stereotype = entity.getStereotype();
if (stereotype != null) {
Element stereo = this.document.createElement("UML:ModelElement.stereotype");
Iterator var5 = stereotype.getMultipleLabels().iterator();
while(var5.hasNext()) {
String s = (String)var5.next();
Element name = this.document.createElement("UML:Stereotype");
name.setAttribute("name", s);
stereo.appendChild(name);
}
cla.appendChild(stereo);
}
LeafType type = entity.getLeafType();
if (type == LeafType.ABSTRACT_CLASS) {
cla.setAttribute("isAbstract", "true");
} else if (type == LeafType.INTERFACE) {
cla.setAttribute("isInterface", "true");
}
if (entity.isStatic()) {
cla.setAttribute("isStatic", "true");
}
if (entity.getVisibilityModifier() == VisibilityModifier.PRIVATE_FIELD || entity.getVisibilityModifier() == VisibilityModifier.PRIVATE_METHOD) {
cla.setAttribute("visibility", entity.getVisibilityModifier().getXmiVisibility());
}
Element feature = this.document.createElement("UML:Classifier.feature");
cla.appendChild(feature);
Member m;
Element operation;
VisibilityModifier visibility;
ListIterator var13;
CharSequence cs;
for(var13 = entity.getBodier().getFieldsToDisplay().iterator(); var13.hasNext(); feature.appendChild(operation)) {
cs = (CharSequence)var13.next();
m = (Member)cs;
operation = this.document.createElement("UML:Attribute");
operation.setAttribute("xmi.id", "att" + this.classDiagram.getUniqueSequence());
operation.setAttribute("name", m.getDisplay(false));
visibility = m.getVisibilityModifier();
if (visibility != null) {
operation.setAttribute("visibility", visibility.getXmiVisibility());
}
if (m.isStatic()) {
operation.setAttribute("isStatic", "true");
}
}
for(var13 = entity.getBodier().getMethodsToDisplay().iterator(); var13.hasNext(); feature.appendChild(operation)) {
cs = (CharSequence)var13.next();
m = (Member)cs;
operation = this.document.createElement("UML:Operation");
operation.setAttribute("xmi.id", "att" + this.classDiagram.getUniqueSequence());
operation.setAttribute("name", m.getDisplay(false));
visibility = m.getVisibilityModifier();
if (visibility != null) {
operation.setAttribute("visibility", visibility.getXmiVisibility());
}
if (m.isStatic()) {
operation.setAttribute("isStatic", "true");
}
}
return cla;
}
}
從中可以看出:
entity.getBodier().getFieldsToDisplay().iterator()
是獲取entity的attribute的迭代器entity.getBodier().getMethodsToDisplay().iterator()
是獲取entity的operation的迭代器name
,isAbstract
,isInterface
和isStatic
這些屬性是如何獲取的
還有 https://github.com/plantuml/plantuml/pull/1298/files 是2023年2月的一個PR(寫本文時尚未Merge),可以作為參考(有待閱讀)。
使用JAVA程式碼將PlantUML類圖匯出為XMI
透過呼叫exportDiagram方法
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.SourceStringReader;
import java.io.FileOutputStream;
public class Main {
public static void main(String[] args) throws IOException {
OutputStream output = new FileOutputStream("test.xmi");
FileFormatOption fileFormatOption = new FileFormatOption(FileFormat.XMI_ARGO);
String content = new String(Files.readAllBytes(Paths.get("a.puml")));
SourceStringReader reader = new SourceStringReader(content);
reader.getBlocks().get(0).getDiagram().exportDiagram(output, 0, fileFormatOption);
}
}
其中FileFormat
是一個列舉,定義了XMI_STANDARD
,XMI_STAR
和XMI_ARGO
等格式
分析getDiagram的呼叫鏈
getDiagram
方法來自net.sourceforge.plantuml.core.Diagram
提供的exportDiagram
介面方法,其實現:
public final ImageData exportDiagram(OutputStream os, int index, FileFormatOption fileFormatOption) throws IOException {
long now = System.currentTimeMillis();
ImageData var6;
try {
var6 = this.exportDiagramNow(os, index, fileFormatOption);
} finally {
if (OptionFlags.getInstance().isEnableStats()) {
StatsUtilsIncrement.onceMoreGenerate(System.currentTimeMillis() - now, this.getClass(), fileFormatOption.getFileFormat());
}
}
return var6;
}
其中主要是呼叫了net.sourceforge.plantuml.AbstractPSystem
中的exportDiagramNow
介面方法。而其在net.sourceforge.plantuml.UmlDiagram
中的實現如下:
protected final ImageData exportDiagramNow(OutputStream os, int index, FileFormatOption fileFormatOption) throws IOException {
fileFormatOption = fileFormatOption.withTikzFontDistortion(this.getSkinParam().getTikzFontDistortion());
if (fileFormatOption.getFileFormat() == FileFormat.PDF) {
return this.exportDiagramInternalPdf(os, index);
} else {
try {
ImageData imageData = this.exportDiagramInternal(os, index, fileFormatOption);
this.lastInfo = new XDimension2D((double)imageData.getWidth(), (double)imageData.getHeight());
return imageData;
} catch (NoStyleAvailableException var5) {
this.exportDiagramError(os, var5, fileFormatOption, (String)null);
return ImageDataSimple.error(var5);
} catch (UnparsableGraphvizException var6) {
Logme.error(var6);
this.exportDiagramError(os, var6.getCause(), fileFormatOption, var6.getGraphvizVersion());
return ImageDataSimple.error(var6);
} catch (Throwable var7) {
this.exportDiagramError(os, var7, fileFormatOption, (String)null);
return ImageDataSimple.error(var7);
}
}
}
可以看出其主要是呼叫了net.sourceforge.plantuml.UmlDiagram.exportDiagramInternal
介面方法,由於當前的類是ClassDiagram
,其將呼叫net.sourceforge.plantuml.classdiagram.ClassDiagram
實現的exportDiagramInternal
方法:
protected final ImageData exportDiagramInternal(OutputStream os, int index, FileFormatOption fileFormatOption) throws IOException {
return this.useLayoutExplicit != 0 ? this.exportLayoutExplicit(os, index, fileFormatOption) : super.exportDiagramInternal(os, index, fileFormatOption);
}
其中net.sourceforge.plantuml.classdiagram.ClassDiagram
繼承net.sourceforge.plantuml.objectdiagram.AbstractClassOrObjectDiagram
繼承net.sourceforge.plantuml.classdiagram.AbstractEntityDiagram
繼承net.atmp.CucaDiagram
而net.atmp.CucaDiagram.exportDiagramInternal
方法中涉及XMI的部分如下:
this.createFilesXmi(os, fileFormat);
return ImageDataSimple.ok();
其呼叫了net.atmp.CucaDiagram.createFilesXmi
方法,其定義如下:
private void createFilesXmi(OutputStream suggestedFile, FileFormat fileFormat) throws IOException {
CucaDiagramXmiMaker maker = new CucaDiagramXmiMaker(this, fileFormat);
maker.createFiles(suggestedFile);
}
其中呼叫了net.sourceforge.plantuml.xmi.CucaDiagramXmiMaker
的createFiles
方法,其定義如下:
public void createFiles(OutputStream fos) throws IOException {
try {
Object xmi;
if (this.diagram instanceof StateDiagram) {
xmi = new XmiStateDiagram((StateDiagram)this.diagram);
} else if (this.diagram instanceof DescriptionDiagram) {
xmi = this.createDescriptionDiagram();
} else {
if (!(this.diagram instanceof ClassDiagram)) {
throw new UnsupportedOperationException("Diagram type " + this.diagram.getUmlDiagramType() + " is not supported in XMI");
}
xmi = this.createClassDiagram();
}
((XmlDiagramTransformer)xmi).transformerXml(fos);
} catch (ParserConfigurationException var3) {
Log.error(var3.toString());
Logme.error(var3);
throw new IOException(var3.toString());
} catch (TransformerException var4) {
Log.error(var4.toString());
Logme.error(var4);
throw new IOException(var4.toString());
}
}
可以看出涉及建立xmi檔案的關鍵2行為:
xmi = this.createClassDiagram();
((XmlDiagramTransformer)xmi).transformerXml(fos);
而createClassDiagram
的定義如下:
private XmlDiagramTransformer createClassDiagram() throws ParserConfigurationException {
if (this.fileFormat == FileFormat.XMI_STANDARD) {
return new XmiClassDiagramStandard((ClassDiagram)this.diagram);
} else if (this.fileFormat == FileFormat.XMI_ARGO) {
return new XmiClassDiagramArgo((ClassDiagram)this.diagram);
} else if (this.fileFormat == FileFormat.XMI_SCRIPT) {
return new XmiClassDiagramScript((ClassDiagram)this.diagram);
} else if (this.fileFormat == FileFormat.XMI_STAR) {
return new XmiClassDiagramStar((ClassDiagram)this.diagram);
} else {
throw new UnsupportedOperationException();
}
}
分析getDiagram方法後,呼叫內部API匯出XMI
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.SourceStringReader;
import net.sourceforge.plantuml.xmi.XmiClassDiagramArgo;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.io.FileOutputStream;
public class Main {
public static void main(String[] args) throws IOException, ParserConfigurationException, TransformerException {
OutputStream fos = new FileOutputStream("test2.xmi");
FileFormatOption fileFormatOption = new FileFormatOption(FileFormat.XMI_ARGO);
String content = new String(Files.readAllBytes(Paths.get("a.puml")));
SourceStringReader reader = new SourceStringReader(content);
ClassDiagram classDiagram = (ClassDiagram) reader.getBlocks().get(0).getDiagram();
Object xmi = new XmiClassDiagramArgo(classDiagram);
((XmlDiagramTransformer)xmi).transformerXml(fos);
}
}
讀取一個uml檔案並列印其中的屬性資訊
現在瞭解了一些後,使用以下Java程式碼來檢視UML檔案的資訊
package org.example;
import net.sourceforge.plantuml.SourceStringReader;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.abel.LeafType;
import net.sourceforge.plantuml.abel.Link;
import net.sourceforge.plantuml.classdiagram.ClassDiagram;
import net.sourceforge.plantuml.cucadiagram.Member;
import net.sourceforge.plantuml.skin.VisibilityModifier;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.ListIterator;
public class Main {
public static void main(String[] args) throws IOException, ParserConfigurationException, TransformerException {
OutputStream output = new FileOutputStream("test.xmi");
// String source = "@startuml\n";
// source += "Bob -> Alice : hello\n";
// source += "@enduml\n";
String content = new String(Files.readAllBytes(Paths.get("a.puml")));
SourceStringReader reader = new SourceStringReader(content);
ClassDiagram classDiagram = (ClassDiagram) reader.getBlocks().get(0).getDiagram();
String titleName = classDiagram.getTitleDisplay().get(0).toString();
System.out.println("title: " + titleName);
Iterator var2 = classDiagram.getEntityFactory().leafs().iterator();
while(var2.hasNext()) {
Entity entity = (Entity)var2.next();
System.out.println("----------------------------------");
System.out.println("uid: " + entity.getUid());
if (entity.getLeafType() == LeafType.NOTE) {
// entity.isAloneAndUnlinked();
// System.out.println("Entity type: " + entity.getLeafType());
} else {
Iterator var1 = classDiagram.getLinks().iterator();
while(var1.hasNext()) {
Link link = (Link)var1.next();
if (link.contains(entity)) {
Entity other = link.getOther(entity);
if (other.getLeafType() == LeafType.NOTE) {
System.out.println("Note: " + other.getDisplay().get(0).toString());
}
}
}
System.out.println("Entity type: " + entity.getLeafType());
String entityName = entity.getDisplay().get(0).toString();
System.out.println("Entity name: " + entityName);
System.out.println("Package: " + entity.getQuark().getParent().toString());
LeafType type = entity.getLeafType();
if (type == LeafType.ABSTRACT_CLASS) {
System.out.println("isAbstract");
} else if (type == LeafType.INTERFACE) {
System.out.println("isInterface");
}
if (entity.isStatic()) {
System.out.println(("isStatic"));
}
if (entity.getVisibilityModifier() == VisibilityModifier.PRIVATE_FIELD || entity.getVisibilityModifier() == VisibilityModifier.PRIVATE_METHOD) {
System.out.println("visibility: " + entity.getVisibilityModifier().getXmiVisibility());
}
Member m;
VisibilityModifier visibility;
ListIterator var13;
CharSequence cs;
for (var13 = entity.getBodier().getFieldsToDisplay().iterator(); var13.hasNext();) {
cs = (CharSequence)var13.next();
m = (Member) cs;
System.out.println("field name: " + m.getDisplay(false));
visibility = m.getVisibilityModifier();
if (visibility != null) {
System.out.println("visibility: " + visibility.getXmiVisibility());
}
if (m.isStatic()) {
System.out.println("isStatic");
}
}
for (var13 = entity.getBodier().getMethodsToDisplay().iterator(); var13.hasNext();) {
cs = (CharSequence)var13.next();
m = (Member) cs;
System.out.println("operation name: " + m.getDisplay(false));
visibility = m.getVisibilityModifier();
if (visibility != null) {
System.out.println("visibility: " + visibility.getXmiVisibility());
}
if (m.isStatic()) {
System.out.println("isStatic");
}
}
}
}
// Write the first image to "png"
// String desc = reader.outputImage(output).getDescription();
// Return a null string if no generation
}
}
建立JAVA專案並打包為JAR包:將PlantUML匯出為自定義格式的XMI檔案
建立專案
使用IDEA建立一個maven專案,然後開啟plantuml的maven倉庫頁面: https://mvnrepository.com/artifact/net.sourceforge.plantuml/plantuml/1.2024.6 將dependency標籤複製到pom.xml中
新增用於自定義XMI格式的類
由於不能引用XmiClassDiagramAbstract
(提示 is not public in 'net.sourceforge.plantuml.xmi'. Cannot be accessed from outside package),所以在src/main/java
下新建package net.sourceforge.plantuml.xmi
然後新增類XmiClassDiagramCustom
(extends XmiClassDiagramAbstract):
package net.sourceforge.plantuml.xmi;
import java.util.Iterator;
import java.util.ListIterator;
import javax.xml.parsers.ParserConfigurationException;
import net.sourceforge.plantuml.abel.Entity;
import net.sourceforge.plantuml.abel.LeafType;
import net.sourceforge.plantuml.abel.Link;
import net.sourceforge.plantuml.classdiagram.ClassDiagram;
import net.sourceforge.plantuml.cucadiagram.Member;
import net.sourceforge.plantuml.decoration.LinkDecor;
import net.sourceforge.plantuml.klimt.creole.Display;
import net.sourceforge.plantuml.skin.VisibilityModifier;
import net.sourceforge.plantuml.stereo.Stereotype;
import org.w3c.dom.Element;
public class XmiClassDiagramCustom extends XmiClassDiagramAbstract implements XmlDiagramTransformer {
public XmiClassDiagramCustom(ClassDiagram classDiagram) throws ParserConfigurationException {
super(classDiagram);
Iterator var2 = classDiagram.getEntityFactory().leafs().iterator();
Element titleElement = this.document.createElement("UML:Title");
titleElement.setAttribute("name", classDiagram.getTitleDisplay().get(0).toString());
this.ownedElementRoot.appendChild(titleElement);
while(var2.hasNext()) {
Entity ent = (Entity)var2.next();
Element cla = this.createEntityNodeCustom(ent, classDiagram);
if (cla != null) {
this.ownedElementRoot.appendChild(cla);
this.done.add(ent);
}
}
var2 = classDiagram.getLinks().iterator();
while(var2.hasNext()) {
Link link = (Link)var2.next();
this.addLink(link);
}
}
protected final Element createEntityNodeCustom(Entity entity, ClassDiagram classDiagram) {
Element cla = this.document.createElement("UML:Class");
if (entity.getLeafType() == LeafType.NOTE) {
return null;
} else {
cla.setAttribute("xmi.id", entity.getUid());
cla.setAttribute("name", entity.getDisplay().get(0).toString());
// Get Note:
Iterator var1 = classDiagram.getLinks().iterator();
while(var1.hasNext()) {
Link link = (Link)var1.next();
if (link.contains(entity)) {
Entity other = link.getOther(entity);
if (other.getLeafType() == LeafType.NOTE) {
cla.setAttribute("note", other.getDisplay().get(0).toString());
// System.out.println("Note: " + other.getDisplay().get(0).toString());
}
}
}
cla.setAttribute("package", entity.getQuark().getParent().toString());
Stereotype stereotype = entity.getStereotype();
if (stereotype != null) {
Element stereo = this.document.createElement("UML:ModelElement.stereotype");
Iterator var5 = stereotype.getMultipleLabels().iterator();
while(var5.hasNext()) {
String s = (String)var5.next();
Element name = this.document.createElement("UML:Stereotype");
name.setAttribute("name", s);
stereo.appendChild(name);
}
cla.appendChild(stereo);
}
LeafType type = entity.getLeafType();
if (type == LeafType.ABSTRACT_CLASS) {
cla.setAttribute("isAbstract", "true");
} else if (type == LeafType.INTERFACE) {
cla.setAttribute("isInterface", "true");
}
if (entity.isStatic()) {
cla.setAttribute("isStatic", "true");
}
if (entity.getVisibilityModifier() == VisibilityModifier.PRIVATE_FIELD || entity.getVisibilityModifier() == VisibilityModifier.PRIVATE_METHOD) {
cla.setAttribute("visibility", entity.getVisibilityModifier().getXmiVisibility());
}
Element feature = this.document.createElement("UML:Classifier.feature");
cla.appendChild(feature);
Member m;
Element operation;
VisibilityModifier visibility;
ListIterator var13;
CharSequence cs;
for(var13 = entity.getBodier().getFieldsToDisplay().iterator(); var13.hasNext(); feature.appendChild(operation)) {
cs = (CharSequence)var13.next();
m = (Member)cs;
operation = this.document.createElement("UML:Attribute");
operation.setAttribute("xmi.id", "att" + this.classDiagram.getUniqueSequence());
operation.setAttribute("name", m.getDisplay(false));
visibility = m.getVisibilityModifier();
if (visibility != null) {
operation.setAttribute("visibility", visibility.getXmiVisibility());
}
if (m.isStatic()) {
operation.setAttribute("isStatic", "true");
}
}
for(var13 = entity.getBodier().getMethodsToDisplay().iterator(); var13.hasNext(); feature.appendChild(operation)) {
cs = (CharSequence)var13.next();
m = (Member)cs;
operation = this.document.createElement("UML:Operation");
operation.setAttribute("xmi.id", "att" + this.classDiagram.getUniqueSequence());
operation.setAttribute("name", m.getDisplay(false));
visibility = m.getVisibilityModifier();
if (visibility != null) {
operation.setAttribute("visibility", visibility.getXmiVisibility());
}
if (m.isStatic()) {
operation.setAttribute("isStatic", "true");
}
}
return cla;
}
}
// copy from XmiClassDiagramStar
private void addLink(Link link) {
if (!link.isHidden() && !link.isInvis()) {
String assId = "ass" + this.classDiagram.getUniqueSequence();
if (link.getType().getDecor1() != LinkDecor.EXTENDS && link.getType().getDecor2() != LinkDecor.EXTENDS) {
Element association = this.document.createElement("UML:Association");
association.setAttribute("xmi.id", assId);
association.setAttribute("namespace", CucaDiagramXmiMaker.getModel(this.classDiagram));
if (!Display.isNull(link.getLabel())) {
association.setAttribute("name", this.forXMI(link.getLabel()));
}
Element connection = this.document.createElement("UML:Association.connection");
Element end1 = this.document.createElement("UML:AssociationEnd");
end1.setAttribute("xmi.id", "end" + this.classDiagram.getUniqueSequence());
end1.setAttribute("association", assId);
end1.setAttribute("type", link.getEntity1().getUid());
if (link.getQuantifier1() != null) {
end1.setAttribute("name", this.forXMI(link.getQuantifier1()));
}
Element endparticipant1 = this.document.createElement("UML:AssociationEnd.participant");
if (link.getType().getDecor2() == LinkDecor.COMPOSITION) {
end1.setAttribute("aggregation", "composite");
}
if (link.getType().getDecor2() == LinkDecor.AGREGATION) {
end1.setAttribute("aggregation", "aggregate");
}
end1.appendChild(endparticipant1);
connection.appendChild(end1);
Element end2 = this.document.createElement("UML:AssociationEnd");
end2.setAttribute("xmi.id", "end" + this.classDiagram.getUniqueSequence());
end2.setAttribute("association", assId);
end2.setAttribute("type", link.getEntity2().getUid());
if (link.getQuantifier2() != null) {
end2.setAttribute("name", this.forXMI(link.getQuantifier2()));
}
Element endparticipant2 = this.document.createElement("UML:AssociationEnd.participant");
if (link.getType().getDecor1() == LinkDecor.COMPOSITION) {
end2.setAttribute("aggregation", "composite");
}
if (link.getType().getDecor1() == LinkDecor.AGREGATION) {
end2.setAttribute("aggregation", "aggregate");
}
end2.appendChild(endparticipant2);
connection.appendChild(end2);
association.appendChild(connection);
this.ownedElementRoot.appendChild(association);
} else {
this.addExtension(link, assId);
}
}
}
// copy from XmiClassDiagramStar
private void addExtension(Link link, String assId) {
Element association = this.document.createElement("UML:Generalization");
association.setAttribute("xmi.id", assId);
association.setAttribute("namespace", CucaDiagramXmiMaker.getModel(this.classDiagram));
if (link.getLabel() != null) {
association.setAttribute("name", this.forXMI(link.getLabel()));
}
if (link.getType().getDecor1() == LinkDecor.EXTENDS) {
association.setAttribute("child", link.getEntity1().getUid());
association.setAttribute("parent", link.getEntity2().getUid());
} else {
if (link.getType().getDecor2() != LinkDecor.EXTENDS) {
throw new IllegalStateException();
}
association.setAttribute("child", link.getEntity2().getUid());
association.setAttribute("parent", link.getEntity1().getUid());
}
this.ownedElementRoot.appendChild(association);
}
}
新增匯出類(入口)
新增package org.export
,並新增類UML2XMIExporter
:
package org.export;
import net.sourceforge.plantuml.SourceStringReader;
import net.sourceforge.plantuml.classdiagram.ClassDiagram;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import net.sourceforge.plantuml.xmi.XmiClassDiagramCustom;
import net.sourceforge.plantuml.xmi.XmlDiagramTransformer;
public class UML2XMIExporter {
public static void main(String[] args) throws IOException, ParserConfigurationException, TransformerException {
String content = new String(Files.readAllBytes(Paths.get(args[0])));
OutputStream outputCustom = Files.newOutputStream(Paths.get(args[1]));
SourceStringReader reader = new SourceStringReader(content);
ClassDiagram classDiagram = (ClassDiagram) reader.getBlocks().get(0).getDiagram();
// 匯出XMI
XmlDiagramTransformer xmi = new XmiClassDiagramCustom(classDiagram);
xmi.transformerXml(outputCustom);
}
}
打包
接下來,參考 https://blog.csdn.net/weixin_41229430/article/details/138963215 打包為jar包
建立檔案: src/main/assembly/assembly.xml, 內容如下:
<assembly>
<id>assembly</id>
<formats>
<format>zip</format>
<format>jar</format>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<!-- -->
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>conf</outputDirectory>
<includes>
<include>*.xml</include>
<include>*.properties</include>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
<fileMode>0644</fileMode>
</fileSet>
<fileSet>
<directory>assembly/bin</directory>
<outputDirectory>bin</outputDirectory>
<fileMode>0755</fileMode>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<outputDirectory>lib</outputDirectory>
</dependencySet>
</dependencySets>
</assembly>
然後在pom.xml中新增
<build>
<plugins>
<!--主要用於將 Maven 專案打包成可執行的程式或分發包 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>make-assembly2</id><!--名字任意 -->
<phase>package</phase><!-- 繫結到package生命週期階段上 -->
<goals>
<goal>single</goal><!-- 只執行一次 -->
</goals>
<configuration>
<descriptors>
<descriptor>${basedir}/src/main/assembly/assembly.xml</descriptor>
</descriptors>
<archive>
<manifest>
<mainClass>org.export.UML2XMIExporter</mainClass>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<useUniqueVersions>false</useUniqueVersions>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
點選IDEA右側的m圖示(maven),雙擊Lifecycle下的package。然後在target目錄下可以看到已經生成了jar包。
開啟command prompt命令列(不要使用powershell)執行:
java -Dfile.encoding=UTF-8 -jar custom-plantuml-xmi-export-1.0-SNAPSHOT-jar-with-dependencies.jar input.puml output.xmi