一步一步将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.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包。
打开comman prompt命令行(不要使用powershell)运行:
java -Dfile.encoding=UTF-8 -jar custom-plantuml-xmi-export-1.0-SNAPSHOT-jar-with-dependencies.jar input.puml output.xmi
												
											一步一步将PlantUML类图导出为自定义格式的XMI文件的更多相关文章
- [emacs] Drawing uml under emacs org-mode using plantUML - 类图
		
[emacs] Drawing uml under emacs org-mode using plantUML - 类图 // */ // ]]> [emacs] Drawing uml u ...
 - PlantUML类图
		
PlantUML类图 雨客 2016-04-08 11:38:03 浏览796 评论0 摘要: 类之间的关系 PlantUML用下面的符号来表示类之间的关系: 泛化,Generalization: ...
 - CFileDialog类与16进制格式的dat文件
		
CFileDialog类与16进制格式的dat文件 要将数据保存为.dat文件,而且是16进制的数字,怎么保存? 要读取.dat文件,文件是16进制的,怎么读取? 用CFileDialog类可以得到[ ...
 - 从 Java 代码逆向工程生成 UML 类图和序列图
		
from:http://blog.itpub.net/14780914/viewspace-588975/ 本文面向于那些软件架构师,设计师和开发人员,他们想使用 IBM® Rational® Sof ...
 - 用MyEclipse将java文件转换成UML类图
		
用MyEclipse将java文件转换成UML类图 参考: 用MyEclipse将java文件转换成UML类图 - 君临天下的博客 - CSDN博客 http://blog.csdn.net/dan ...
 - UML作业第三次:分析《书店图书销售管理系统,绘制类图
		
plantuml类图绘制方法的学习: 1.关于类图的学习: 类图显示了系统的静态结构. 类:类图中的主要元素,用矩形表示.矩形的上层表示类名.中层表示属性.下层表示方法. 类之间的关系:关联.依赖.聚 ...
 - Rational Rose正逆向工程(类图转Java代码,Java代码转类图)
		
 一,正向工程 1.设置默认语言为Java,Tools->Options->Notation->default:选择Java. 2.设置环境变量Class ...
 - .NET手撸绘制TypeScript类图——上篇
		
.NET手撸绘制TypeScript类图--上篇 近年来随着交互界面的精细化,TypeScript越来越流行,前端的设计也越来复杂,而类图正是用简单的箭头和方块,反映对象与对象之间关系/依赖的好方式. ...
 - 四步轻松实现用Visio画UML类图
		
本节和大家一起学习一下用Visio画UML类图的方法,主要有四个步骤,这里和大家分享一下,相信通过本节的学习,你对Visio画UML类图的步骤一定会有所了解. 用Visio画UML类图 对于画类图的工 ...
 - 积跬步,聚小流------关于UML类图
		
UML的存在 类图是使用频率比較高的UML图,它用于描写叙述系统中所含的类以及它们之间的相互关系,帮助人们简化对系统的理解,也是系统分析和设计阶段的重要产物,也是系统编码和測试的重要类型根据. UML ...
 
随机推荐
- 六.黑马程序员-eclipse的使用和快捷键
			
1.Eclipse的概述 A: 是一个集成开发工具,专门针对java的 B: Eclipse 免费的 开源 C: MyEclipse 收费的 具体良好的插件扩展功能,针对插件收费2.Eclipse的使 ...
 - Java-MVC开发模式
			
MVC开发模式 1. jsp演变历史 1. 早期只有Servlet,只能使用response输出标签数据,非常麻烦 2. 后来又jsp,简化了Servlet的开发,如果过度使用jsp,在jsp中即写大 ...
 - 安装和引入方式在Element UI (Vue 2)和Element Plus (Vue 3)中的不同
			
安装和引入方式 Element UI (Vue 2): // main.js import Vue from 'vue'; import ElementUI from 'element-ui'; im ...
 - oeasy教您玩转linux 010216 随机诗词 fortunezh
			
我们来回顾一下 上一部分我们都讲了什么? 下载fortune 输出重定向到cowsay 多重输出重定向 fortune的细节 有没有中️文的fortune呢 # 搜索一下fortune apt sea ...
 - SQL Server调用OLE对象
			
T-SQL 中是可以调用 OLE 的,将这一功能应用到触发器.存储过程等对象中,SQL Server 运用变得更贴近我们的功能,更加满足我们的需要. T-SQL 中有七个存储过程是围绕本节内容进行的, ...
 - 小技巧:初始化后查看容器内某一bean的信息
			
1.debug 2. 3.与容器名对应,可以看到容器的对应信息 4.输入表达式可以直接获取对应结果信息,这里查看的是默认SpringSecurity过滤链的bean
 - scratch源码下载 | 飞天厨师
			
程序说明: <飞天厨师>是一款使用Scratch平台制作的游戏程序.在这个游戏中,玩家将控制一名厨师角色,他在天空中不断掉落.玩家需要利用方向键左右移动厨师,以便他能够准确地踩在空中的食物 ...
 - 【SVN】提交失败报错
			
SVN提交失败: 最后信息是提示 请输入日志消息,至少需要20个字符,提交终止 问题原因是: 提交的时候不要把提交信息换行来写,SVN只会读取第一行内容 如果消息没有问题还提交失败,可能是文件因为提交 ...
 - 【Tomcat】IDEA工程没有EE规范的jar包?
			
发现了一个问题,我安装了2种版本的Tomcat 一个是8版本,另一个是10版本 我在已经使用8版本的工程中,更换成使用10版本,当然一开始部署运行正常 但是关闭了工程之后,再次打开就发现,这些EE规范 ...
 - 【Git】03 撤销 & 版本回退
			
回退分为三种情况,每种情况对应了我们文件的存储区域 工作区 | 暂存区 | 版本区(当前分支) 1.文件可能存放在工作区,没有被Git追踪[红色标记状态] 2.文件可能已经添加到暂存区,没有被Git提 ...