一步一步将PlantUML类图导出为自定义格式的XMI文件

一步一步将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.XmiClassDiagramAbstractXmiClassDiagramStandard的定义:

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的迭代器
  • nameisAbstractisInterfaceisStatic 这些属性是如何获取的

还有 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_STANDARDXMI_STARXMI_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.CucaDiagramXmiMakercreateFiles方法,其定义如下:

    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 XmiClassDiagramStarprivate 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 XmiClassDiagramStarprivate 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();// 导出XMIXmlDiagramTransformer 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/53916.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW编程语言出于什么原因开发的?

LabVIEW最初由美国国家仪器公司&#xff08;NI&#xff09;于1986年开发&#xff0c;目的是为工程师和科学家提供一种图形化编程环境&#xff0c;简化数据采集、仪器控制、自动化测试和测量系统开发等工作。开发LabVIEW的主要原因包括以下几点&#xff1a; 简化复杂系统开发&am…

Redis面对数据量庞大处理方法

当Redis面对数据量庞大时&#xff0c;其应对策略需要从多个维度出发&#xff0c;包括数据分片、内存优化、持久化策略、使用集群、硬件升级、数据淘汰策略、合理设计数据结构以及监控系统性能等。以下是对这些策略的详细阐述&#xff0c;以期提供不少于2000字的深入解答。 一、…

别用 npm config set registry 设置淘宝镜像了!!!

常规写法 npm config set registry https://registry.npmmirror.com我相信大部分人都会用这个命令来切换淘宝镜像。我之前也是&#xff0c;我有一个问题那就是我每当想切换镜像的时候都会搜一下淘宝npm镜像。因为我大部分时候都会忘记这个命令是什么样子的。 大宝贝 nrm 自动…

哈工大“计算机设计与实践”(cpu)处理器实验设计报告

哈工大“计算机设计与实践”&#xff08;cpu&#xff09;处理器实验设计报告 【哈工大“计算机设计与实践”&#xff08;cpu&#xff09;处理器实验设计报告】 在计算机科学领域&#xff0c;CPU&#xff08;中央处理器&#xff09;是计算机系统的核心部件&#xff0c;负责执行指…

91、K8s之ingress上集

一、Ingress service模式&#xff1a; loadbalance NodePort&#xff1a;每个节点都会有一个指定的端口 30000-32767 内网 clusterip&#xff1a;默认模式&#xff0c;只能pod内部访问 externalName&#xff1a;需要dns提供域名 1.1、对外提供服务的ingress service&…

SQL Server小技巧之遍历日期

使用背景 一般项目中会遇到&#xff0c;求每日的日报这种&#xff0c;以及计算2个日期内的工作日&#xff0c;或者休息日可能会用到&#xff0c;计算休息日可以用额外的一个字段用来标记当前日期是否是休息日 遍历方式一 DECLARE StartDate DATE 2023-01-01, EndDate DATE …

jmeter之TPS计算公式

需求&#xff1a; 如何确定环境当中的TPS指标 PV:&#xff08;Page View&#xff09;即页面访问量&#xff0c;每打开一次页面PV计数1&#xff0c;刷新页面也是。PV只统计页面访问次 数。 UV(Unique Visitor),唯一访问用户数&#xff0c;用来衡量真实访问网站的用户数量。 一般…

中国电子学会202406青少年软件编程(Python)等级考试试卷(三级)真题与解析

202406Python 三级真题 一、选择题 第 1 题 现有一组初始记录无序的数据“5,8,6,3,9,2”,使用冒泡排序算法,按从小到大的顺序排列,第一轮排序的结果为? A:5,6,3,8,9,2 B:5,6,3,8,2,9 C:5,6,8,3,2,9 D:5,8,3,6,9,2 第 2 题 列表l=[9,…

携手鲲鹏,长亮科技加速银行核心系统升级

新经济周期下&#xff0c;银行净息差持续收窄、盈利压力加大、市场竞争日趋加剧。同时&#xff0c;国家相关政策不断出台&#xff0c;对金融科技的自主创新与安全可控提出了更高要求。 在这样的大背景下&#xff0c;银行业的数字化转型已经步入深水区。其中&#xff0c;核心系统…

ubuntu 执行定时任务crontab -e 无法输入的问题

界面显示 GNU nano 4.8 /tmp/crontab.l0A1HJ/crontab # Edit this file to introduce tasks to be run by cron. # # Each task to run has to be defined t…

appium server gui详细按照步骤

1.安装appium server desktop Appium安装提供两种方式:桌面版和命令行版。其中桌面版又分为 Appium GuI 和 Appium Desktop 。作为初学者&#xff0c;用桌面版&#xff0c;对初学者比较友好。 官网下载地址&#xff1a;Releases appium/appium-desktop GitHubTags appium/…

OpenCV class2-C#+winfrom显示控件使用窗口大小并内存管理

一.控件效果说明 二.代码声明&#xff08;已经循环读取10000次&#xff09; 全局 OpenCvSharp.Point point new OpenCvSharp.Point(0, 0); OpenCvSharp.Size size2; Mat src new Mat(); 初始化 size2 new OpenCvSharp.Size(pictureBox1.Size.Width, pictureBox1.Size.Hei…

MySQL迁移达梦报错,DMException: 第1 行附近出现错误: 无效的表或视图名[ACT_GE_PROPERTY]

达梦数据库选好模式和登录用户&#xff0c;迁移时的目标模式名要和达梦的当前登录的用户名相同&#xff0c;否则查询的时候需要“form 模式名.表名”&#xff0c;只from表名就会报表不存在的错误。

【YashanDB知识库】DBeaver无法访问数据库

本文转自YashanDB官网&#xff0c;具体内容请见DBeaver无法访问数据库 【标题】DBeaver无法访问数据库 【问题分类】安装部署 【关键字】DBeaver无法访问数据库 【问题描述】数据库部署完成后&#xff0c;无法通过DBeaver进行访问 【问题原因分析】首先通过ping和telnet检…

汽车无钥匙启动功能工作原理

移‌动管家无钥匙启动‌是一种科技化的汽车启动方式&#xff0c;它允许车主在不使用传统钥匙的情况下启动车辆。这种技术通过智能感应系统实现&#xff0c;车主只需携带智能钥匙&#xff0c;当靠近车辆时&#xff0c;车辆能够自动解锁并准备启动。启动车辆时&#xff0c;车主无…

水库大坝安全监测方案,双重守护,安全无忧

水库作为重要的水利设施&#xff0c;在防洪、灌溉及供水等方面发挥着重要作用。然而随着时间的推移&#xff0c;大坝面临着自然老化、设计标准不足及极端天气等多重挑战&#xff0c;其安全性与稳定性日益受到关注。水库堤坝险情导致的洪涝灾害给人民生命财产和经济社会发展带来…

Android 车联网——汽车系统介绍(附2)

汽车系统指的是由多个模块或组件组成的系统,如发动机系统、制动系统、空调系统等,这些系统通常由多个 ECU 协同工作来完成特定的任务。 一、汽车系统 1、防抱死制动系统 ABS(Anti-lock Braking System,防抱死制动系统)是一项重要的汽车安全技术,其主要功能是在车辆紧急…

TDengine 与 SCADA 强强联合:提升工业数据管理的效率与精准

随着时序数据库&#xff08;Time Series Database&#xff09;的日益普及&#xff0c;越来越多的工业自动化控制&#xff08;工控&#xff09;人员开始认识到其强大能力。然而&#xff0c;时序数据库在传统实时数据库应用领域&#xff0c;特别是在过程监控层的推广仍面临挑战&a…

凸优化学习(2)——梯度类方法求解(gradient descent)

&#x1f345; 写在前面 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;这里是hyk写算法了吗&#xff0c;一枚致力于学习算法和人工智能领域的小菜鸟。 &#x1f50e;个人主页&#xff1a;主页链接&#xff08;欢迎各位大佬光临指导&#xff09; ⭐️近…

python加载chgcar, aeccar压缩数据

数据来源&#xff1a;materials project网站下载的.json.gz压缩包 使用gzip包可以免解压直接读取 解压出来json文件的数据结构大概如代码所示 使用pymatgen.io.vasp的Chgcar对象&#xff0c;可以装载从json文件读出的结构数据和电荷密度数据 with gzip.open(os.path.join(self.…