All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.structurizr.export.plantuml.PatchedC4PlantUMLExporter Maven / Gradle / Ivy

The newest version!
package com.structurizr.export.plantuml;

import com.structurizr.export.Diagram;
import com.structurizr.export.IndentingWriter;
import com.structurizr.model.*;
import com.structurizr.util.StringUtils;
import com.structurizr.view.*;

import java.util.Map;

import static java.lang.String.format;

/**
 * 

This is a patched version of the {@link C4PlantUMLExporter} which is required to fix code which is private * or package-private. As soon as corresponding pull requests in the original repository are merged and released, this * class can be removed again.

* *

Do not extend from this class directly!

*/ public class PatchedC4PlantUMLExporter extends AbstractPlantUMLExporter { /** *

Set this property to true by calling {@link Configuration#addProperty(String, String)} in your * {@link ViewSet} in order to have all {@link ModelItem#getProperties()} (for {@link Component}s and * {@link Relationship}s) being printed in the PlantUML diagrams.

* *

The default value is false.

* *

TODO remove as soon as https://github.com/structurizr/export/pull/12 is merged and released

* * @see ViewSet#getConfiguration() * @see Configuration#getProperties() */ public static final String PLANTUML_ADD_PROPERTIES_PROPERTY = "plantuml.addProperties"; private int groupId = 0; public PatchedC4PlantUMLExporter() { } @Override protected boolean isAnimationSupported(View view) { return !(view instanceof DynamicView); } @Override protected void writeHeader(View view, IndentingWriter writer) { super.writeHeader(view, writer); writer.writeLine("!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4.puml"); writer.writeLine("!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml"); if (view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof Container || e instanceof ContainerInstance)) { writer.writeLine("!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml"); } if (view.getElements().stream().map(ElementView::getElement).anyMatch(e -> e instanceof Component)) { writer.writeLine("!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml"); } if (view instanceof DeploymentView) { writer.writeLine("!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Deployment.puml"); } writeIncludes(view, writer); writer.writeLine(); } @Override protected void writeFooter(View view, IndentingWriter writer) { if ("true".equalsIgnoreCase(view.getViewSet().getConfiguration().getProperties().getOrDefault(PLANTUML_LEGEND_PROPERTY, "true"))) { writer.writeLine(); writer.writeLine("SHOW_LEGEND()"); } super.writeFooter(view, writer); } @Override protected void startEnterpriseBoundary(View view, String enterpriseName, IndentingWriter writer) { writer.writeLine(String.format("Enterprise_Boundary(enterprise, \"%s\") {", enterpriseName)); writer.indent(); } @Override protected void endEnterpriseBoundary(View view, IndentingWriter writer) { writer.outdent(); writer.writeLine("}"); writer.writeLine(); } @Override protected void startGroupBoundary(View view, String group, IndentingWriter writer) { writer.writeLine(String.format("Boundary(group_%s, \"%s\") {", groupId++, group)); writer.indent(); } @Override protected void endGroupBoundary(View view, IndentingWriter writer) { writer.outdent(); writer.writeLine("}"); writer.writeLine(); } @Override protected void startSoftwareSystemBoundary(View view, SoftwareSystem softwareSystem, IndentingWriter writer) { writer.writeLine(String.format("System_Boundary(\"%s_boundary\", \"%s\") {", idOf(softwareSystem), softwareSystem.getName())); writer.indent(); } @Override protected void endSoftwareSystemBoundary(View view, IndentingWriter writer) { writer.outdent(); writer.writeLine("}"); writer.writeLine(); } @Override protected void startContainerBoundary(View view, Container container, IndentingWriter writer) { writer.writeLine(String.format("Container_Boundary(\"%s_boundary\", \"%s\") {", idOf(container), container.getName())); writer.indent(); } @Override protected void endContainerBoundary(View view, IndentingWriter writer) { writer.outdent(); writer.writeLine("}"); writer.writeLine(); } @Override protected void startDeploymentNodeBoundary(DeploymentView view, DeploymentNode deploymentNode, IndentingWriter writer) { String url = deploymentNode.getUrl(); if (!StringUtils.isNullOrEmpty(url)) { url = "[[" + url + "]]"; } else { url = ""; } if (StringUtils.isNullOrEmpty(deploymentNode.getTechnology())) { writer.writeLine( format("Deployment_Node(%s, \"%s\", $tags=\"%s\")%s {", idOf(deploymentNode), deploymentNode.getName() + (deploymentNode.getInstances() > 1 ? " (x" + deploymentNode.getInstances() + ")" : ""), tagsOf(deploymentNode), url ) ); } else { writer.writeLine( format("Deployment_Node(%s, \"%s\", \"%s\", $tags=\"%s\")%s {", idOf(deploymentNode), deploymentNode.getName() + (deploymentNode.getInstances() > 1 ? " (x" + deploymentNode.getInstances() + ")" : ""), deploymentNode.getTechnology(), tagsOf(deploymentNode), url ) ); } writer.indent(); if (!isVisible(view, deploymentNode)) { writer.writeLine("hide " + idOf(deploymentNode)); } } @Override protected void endDeploymentNodeBoundary(View view, IndentingWriter writer) { writer.outdent(); writer.writeLine("}"); writer.writeLine(); } @Override public Diagram export(CustomView view) { return null; } @Override public Diagram export(DynamicView view) { if (useSequenceDiagrams(view)) { throw new UnsupportedOperationException("Sequence diagrams are not supported by C4-PlantUML"); } else { return super.export(view); } } @Override protected void writeElement(View view, Element element, IndentingWriter writer) { if (element instanceof CustomElement) { return; } Element elementToWrite = element; String id = idOf(element); String url = element.getUrl(); if (!StringUtils.isNullOrEmpty(url)) { url = "[[" + url + "]]"; } else { url = ""; } addProperties(view, writer, element); if (element instanceof StaticStructureElementInstance) { StaticStructureElementInstance elementInstance = (StaticStructureElementInstance) element; element = elementInstance.getElement(); if (StringUtils.isNullOrEmpty(url)) { url = element.getUrl(); if (!StringUtils.isNullOrEmpty(url)) { url = "[[" + url + "]]"; } else { url = ""; } } } String name = element.getName(); String description = element.getDescription(); if (StringUtils.isNullOrEmpty(description)) { description = ""; } if (element instanceof Person) { Person person = (Person) element; if (person.getLocation() == Location.External) { writer.writeLine(String.format("Person_Ext(%s, \"%s\", \"%s\", $tags=\"%s\")%s", id, name, description, tagsOf(elementToWrite), url)); } else { writer.writeLine(String.format("Person(%s, \"%s\", \"%s\", $tags=\"%s\")%s", id, name, description, tagsOf(elementToWrite), url)); } } else if (element instanceof SoftwareSystem) { SoftwareSystem softwareSystem = (SoftwareSystem) element; if (softwareSystem.getLocation() == Location.External) { writer.writeLine(String.format("System_Ext(%s, \"%s\", \"%s\", $tags=\"%s\")%s", id, name, description, tagsOf(elementToWrite), url)); } else { writer.writeLine(String.format("System(%s, \"%s\", \"%s\", $tags=\"%s\")%s", id, name, description, tagsOf(elementToWrite), url)); } } else if (element instanceof Container) { Container container = (Container) element; ElementStyle elementStyle = view.getViewSet().getConfiguration().getStyles().findElementStyle(element); String shape = ""; if (elementStyle.getShape() == Shape.Cylinder) { shape = "Db"; } else if (elementStyle.getShape() == Shape.Pipe) { shape = "Queue"; } if (StringUtils.isNullOrEmpty(container.getTechnology())) { writer.writeLine(String.format("Container%s(%s, \"%s\", \"%s\", $tags=\"%s\")%s", shape, id, name, description, tagsOf(elementToWrite), url)); } else { writer.writeLine(String.format("Container%s(%s, \"%s\", \"%s\", \"%s\", $tags=\"%s\")%s", shape, id, name, container.getTechnology(), description, tagsOf(elementToWrite), url)); } } else if (element instanceof Component) { Component component = (Component) element; if (StringUtils.isNullOrEmpty(component.getTechnology())) { writer.writeLine(String.format("Component(%s, \"%s\", \"%s\", $tags=\"%s\")%s", id, name, description, tagsOf(elementToWrite), url)); } else { writer.writeLine(String.format("Component(%s, \"%s\", \"%s\", \"%s\", $tags=\"%s\")%s", id, name, component.getTechnology(), description, tagsOf(elementToWrite), url)); } } else if (element instanceof InfrastructureNode) { InfrastructureNode infrastructureNode = (InfrastructureNode) element; if (StringUtils.isNullOrEmpty(infrastructureNode.getTechnology())) { writer.writeLine(format("Deployment_Node(%s, \"%s\", $tags=\"%s\")%s", idOf(infrastructureNode), name, tagsOf(elementToWrite), url)); } else { if (StringUtils.isNullOrEmpty(infrastructureNode.getTechnology())) { writer.writeLine(format("Deployment_Node(%s, \"%s\", \"%s\", $tags=\"%s\")%s", idOf(infrastructureNode), name, infrastructureNode.getTechnology(), tagsOf(elementToWrite), url)); } } } if (!isVisible(view, elementToWrite)) { writer.writeLine("hide " + id); } } private String tagsOf(Element element) { String tags; if (element instanceof StaticStructureElementInstance) { tags = ((StaticStructureElementInstance) element).getElement().getTags() + "," + element.getTags(); } else { tags = element.getTags(); } return prepareTagsForC4PlantUml(tags); } private String tagsOf(Relationship relationship) { String tags; if (!StringUtils.isNullOrEmpty(relationship.getLinkedRelationshipId())) { tags = relationship.getModel().getRelationship(relationship.getLinkedRelationshipId()).getTags(); if (!StringUtils.isNullOrEmpty(relationship.getTags())) { tags = tags + "," + relationship.getTags(); } } else { tags = relationship.getTags(); } return prepareTagsForC4PlantUml(tags); } /** * C4PlantUML docu says: "If 2 tags define the same skinparam, the first definition is used.". As {@link Element#getTags()} * puts the default tags ("Container", "RelationShip") to the first position, this would mean you can never override any tags. * We therefore reverse the list of tags here and separate them with `+` instead of `,` characters. * * @param tags the original tags list from Structurizr * @return the tag string is as required by C4PlantUML */ private String prepareTagsForC4PlantUml(String tags) { final StringBuilder result = new StringBuilder(); final String[] split = tags.split(","); for (int i = split.length - 1; i >= 0; i--) { result.append(split[i]); if (i != 0) { result.append("+"); } } return result.toString(); } @Override protected void writeRelationship(View view, RelationshipView relationshipView, IndentingWriter writer) { Relationship relationship = relationshipView.getRelationship(); Element source = relationship.getSource(); Element destination = relationship.getDestination(); if (source instanceof CustomElement || destination instanceof CustomElement) { return; } addProperties(view, writer, relationship); if (relationshipView.isResponse() != null && relationshipView.isResponse()) { source = relationship.getDestination(); destination = relationship.getSource(); } String description = ""; if (!StringUtils.isNullOrEmpty(relationshipView.getOrder())) { description = relationshipView.getOrder() + ". "; } description += (hasValue(relationshipView.getDescription()) ? relationshipView.getDescription() : hasValue(relationshipView.getRelationship().getDescription()) ? relationshipView.getRelationship().getDescription() : ""); if (StringUtils.isNullOrEmpty(relationship.getTechnology())) { writer.writeLine(format("Rel_D(%s, %s, \"%s\", $tags=\"%s\")", idOf(source), idOf(destination), description, tagsOf(relationship))); } else { writer.writeLine(format("Rel_D(%s, %s, \"%s\", \"%s\", $tags=\"%s\")", idOf(source), idOf(destination), description, relationship.getTechnology(), tagsOf(relationship))); } } /** * TODO remove as soon as https://github.com/structurizr/export/pull/12 is merged and released */ private void addProperties(View view, IndentingWriter writer, ModelItem element) { if ("true".equalsIgnoreCase(view.getViewSet().getConfiguration().getProperties().getOrDefault(PLANTUML_ADD_PROPERTIES_PROPERTY, "false"))) { Map properties = element.getProperties(); if (!properties.isEmpty()) { writer.writeLine("WithoutPropertyHeader()"); properties.keySet().stream().sorted().forEach(key -> writer.writeLine(String.format("AddProperty(\"%s\",\"%s\")", key, properties.get(key))) ); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy