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

com.structurizr.autolayout.graphviz.GraphvizAutomaticLayout Maven / Gradle / Ivy

The newest version!
package com.structurizr.autolayout.graphviz;

import com.structurizr.Workspace;
import com.structurizr.export.Diagram;
import com.structurizr.view.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.BufferedWriter;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
import java.util.Locale;

/**
 * Applies the graphviz automatic layout to views in a Structurizr workspace.
 *
 * Note: this class assumes that the "dot" command is available.
 */
public class GraphvizAutomaticLayout {

    private static final Log log = LogFactory.getLog(GraphvizAutomaticLayout.class);

    private static final String DOT_EXECUTABLE = "dot";
    private static final String USE_SVG_OUTPUT_FORMAT_OPTION = "-Tsvg";
    private static final String AUTOMATICALLY_GENERATE_OUTPUT_FILE_OPTION = "-O";
    private static final String DOT_FILE_EXTENSION = ".dot";

    private final File path;

    private RankDirection rankDirection = RankDirection.TopBottom;
    private double rankSeparation = 1.0;
    private double nodeSeparation = 1.0;

    private int margin = 400;
    private boolean changePaperSize = true;

    private Locale locale = Locale.US;

    public GraphvizAutomaticLayout() {
        this(new File("."));
    }

    public GraphvizAutomaticLayout(File path) {
        this.path = path;
    }

    public void setRankDirection(RankDirection rankDirection) {
        this.rankDirection = rankDirection;
    }

    public void setRankSeparation(double rankSeparation) {
        this.rankSeparation = rankSeparation;
    }

    public void setNodeSeparation(double nodeSeparation) {
        this.nodeSeparation = nodeSeparation;
    }

    public void setMargin(int margin) {
        this.margin = margin;
    }

    public void setChangePaperSize(boolean changePaperSize) {
        this.changePaperSize = changePaperSize;
    }

    /**
     * Sets the locale used when writing DOT files.
     *
     * @param locale        a Locale instance
     */
    public void setLocale(Locale locale) {
        this.locale = locale;
    }

    private DOTExporter createDOTExporter() {
        DOTExporter exporter = new DOTExporter(rankDirection, rankSeparation, nodeSeparation);
        exporter.setLocale(locale);

        return exporter;
    }

    private void writeFile(Diagram diagram) throws Exception {
        File file = new File(path, diagram.getKey() + DOT_FILE_EXTENSION);
        log.debug("Writing " + file.getAbsolutePath());
        BufferedWriter writer = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8);
        writer.write(diagram.getDefinition());
        writer.flush();
        writer.close();

        if (!file.exists()) {
            log.error(file.getAbsolutePath() + " does not exist");
        }
    }

    private SVGReader createSVGReader() {
        return new SVGReader(path, margin, changePaperSize);
    }

    private void runGraphviz(View view) throws Exception {
        ProcessBuilder processBuilder = new ProcessBuilder().inheritIO();
        List command = List.of(
                DOT_EXECUTABLE,
                new File(path, view.getKey() + DOT_FILE_EXTENSION).getAbsolutePath(),
                USE_SVG_OUTPUT_FORMAT_OPTION,
                AUTOMATICALLY_GENERATE_OUTPUT_FILE_OPTION
        );

        processBuilder.command(command);

        StringBuilder buf = new StringBuilder();
        for (String s : command) {
            buf.append(s);
            buf.append(" ");
        }
        log.debug(buf);

        Process process = processBuilder.start();
        int exitCode = process.waitFor();
        assert exitCode == 0;

        String input = new String(process.getInputStream().readAllBytes());
        String error = new String(process.getErrorStream().readAllBytes());

        log.debug("stdout: " + input);
        log.debug("stderr: " + error);
    }

    public void apply(CustomView view) throws Exception {
        log.debug("Running Graphviz for view with key " + view.getKey());
        Diagram diagram = createDOTExporter().export(view);
        writeFile(diagram);
        runGraphviz(view);
        createSVGReader().parseAndApplyLayout(view);
    }

    public void apply(SystemLandscapeView view) throws Exception {
        log.debug("Running Graphviz for view with key " + view.getKey());
        Diagram diagram = createDOTExporter().export(view);
        writeFile(diagram);
        runGraphviz(view);
        createSVGReader().parseAndApplyLayout(view);
    }

    public void apply(SystemContextView view) throws Exception {
        log.debug("Running Graphviz for view with key " + view.getKey());
        Diagram diagram = createDOTExporter().export(view);
        writeFile(diagram);
        runGraphviz(view);
        createSVGReader().parseAndApplyLayout(view);
    }

    public void apply(ContainerView view) throws Exception {
        log.debug("Running Graphviz for view with key " + view.getKey());
        Diagram diagram = createDOTExporter().export(view);
        writeFile(diagram);
        runGraphviz(view);
        createSVGReader().parseAndApplyLayout(view);
    }

    public void apply(ComponentView view) throws Exception {
        log.debug("Running Graphviz for view with key " + view.getKey());
        Diagram diagram = createDOTExporter().export(view);
        writeFile(diagram);
        runGraphviz(view);
        createSVGReader().parseAndApplyLayout(view);
    }

    public void apply(DynamicView view) throws Exception {
        log.debug("Running Graphviz for view with key " + view.getKey());
        Diagram diagram = createDOTExporter().export(view);
        writeFile(diagram);
        runGraphviz(view);
        createSVGReader().parseAndApplyLayout(view);
    }

    public void apply(DeploymentView view) throws Exception {
        log.debug("Running Graphviz for view with key " + view.getKey());
        Diagram diagram = createDOTExporter().export(view);
        writeFile(diagram);
        runGraphviz(view);
        createSVGReader().parseAndApplyLayout(view);
    }

    public void apply(Workspace workspace) throws Exception {
        for (CustomView view : workspace.getViews().getCustomViews()) {
            apply(view);
        }

        for (SystemLandscapeView view : workspace.getViews().getSystemLandscapeViews()) {
            apply(view);
        }

        for (SystemContextView view : workspace.getViews().getSystemContextViews()) {
            apply(view);
        }

        for (ContainerView view : workspace.getViews().getContainerViews()) {
            apply(view);
        }

        for (ComponentView view : workspace.getViews().getComponentViews()) {
            apply(view);
        }

        for (DynamicView view : workspace.getViews().getDynamicViews()) {
            apply(view);
        }

        for (DeploymentView view : workspace.getViews().getDeploymentViews()) {
            apply(view);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy