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

cdc.gv.GvWriter Maven / Gradle / Ivy

There is a newer version: 0.100.2
Show newest version
package cdc.gv;

import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.Flushable;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;

import cdc.gv.GvWriterContext.Status;
import cdc.gv.atts.GvAttributeName;
import cdc.gv.atts.GvAttributes;
import cdc.gv.atts.GvClusterAttributes;
import cdc.gv.atts.GvEdgeAttributes;
import cdc.gv.atts.GvGraphAttributes;
import cdc.gv.atts.GvNodeAttributes;
import cdc.gv.atts.GvSubgraphAttributes;
import cdc.gv.support.GvSupport;
import cdc.io.utils.NonCloseableOutputStream;
import cdc.util.lang.Checks;
import cdc.util.lang.InvalidStateException;

/**
 * Class facilitating creation of GraphViz files.
 *
 * @author Damien Carbonne
 *
 */
public class GvWriter implements Flushable, Closeable {
    private GvWriterContext context = new GvWriterContext();
    private String indentString = "  ";
    private final Writer writer;
    private static final String EOL = System.lineSeparator();

    /**
     * String used to represent an edge. This may be "--" for undirected graphs
     * or {@literal "->"} for directed graphs.
     */
    private String edge = null;

    /**
     * Creates a GvWriter that will print to passed writer.
     *
     * @param writer The writer.
     */
    public GvWriter(Writer writer) {
        Checks.isNotNull(writer, "writer");
        this.writer = writer;
    }

    /**
     * Creates a GvWriter that will print to System.out.
     */
    public GvWriter() {
        this(new BufferedWriter(new OutputStreamWriter(new NonCloseableOutputStream(System.out))));
    }

    /**
     * Creates a GvWriter that will print to a file using default encoding.
     *
     * @param filename Name of the file to create.
     * @throws IOException If the named file exists but is a directory rather than
     *             a regular file, does not exist but cannot be created, or cannot
     *             be opened for any other reason
     */
    public GvWriter(String filename) throws IOException {
        this(new BufferedWriter(new FileWriter(filename)));
    }

    /**
     * Creates a GvWriter that will print to a file using default encoding.
     *
     * @param file File to create.
     * @throws IOException If the file exists but is a directory rather than
     *             a regular file, does not exist but cannot be created, or
     *             cannot be opened for any other reason
     */
    public GvWriter(File file) throws IOException {
        this(new BufferedWriter(new FileWriter(file)));
    }

    public GvWriter(String filename,
                    String encoding)
            throws IOException {
        this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename), encoding)));
    }

    public GvWriter(File file,
                    String encoding)
            throws IOException {
        this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), encoding)));
    }

    public void indent(int delta) throws IOException {
        for (int i = context.getLevel() + delta; i > 0; i--) {
            writer.append(indentString);
        }
    }

    public void indent() throws IOException {
        indent(0);
    }

    /**
     * Prints a set of attributes, one per line.
     * 

* Only non null values are printed.
* *

     *    att1=?value1?;
     *    att2=?value2?;
     *    ...
* where ? is an appropriate delimiter. * * @param atts The attributes. * @throws IOException If an I/O error occurs */ private void printSplit(GvAttributes atts) throws IOException { if (atts != null) { for (final GvAttributeName name : GvAttributeName.values()) { final String value = atts.getValue(name); if (value != null) { indent(1); writer.append(name.encode()); writer.append("="); writer.append(name.getDelimiter(value)); writer.append(value); writer.append(name.getDelimiter(value)); writer.append(";"); writer.append(EOL); } } for (final String name : atts.getExtensionNames()) { final String value = atts.getExtensionValue(name); if (value != null) { indent(1); writer.append(name); writer.append("=\""); writer.append(value); writer.append("\";"); writer.append(EOL); } } } } /** * Prints a set of attributes on one line. *

* Only non null values are printed.
*

   '[' att1=?value1? att2=?value2? ...']'
* where ? is an appropriate delimiter. * * @param atts The attributes. * @throws IOException If an I/O error occurs. */ private void printSep(GvAttributes atts) throws IOException { if (atts != null) { boolean first = true; for (final GvAttributeName name : GvAttributeName.values()) { final String value = atts.getValue(name); if (value != null) { if (first) { writer.append(" ["); first = false; } else { writer.append(" "); } writer.append(name.encode()); writer.append("="); writer.append(name.getDelimiter(value)); writer.append(value); writer.append(name.getDelimiter(value)); } } for (final String name : atts.getExtensionNames()) { final String value = atts.getExtensionValue(name); if (value != null) { if (first) { writer.append(" ["); first = false; } else { writer.append(" "); } writer.append(name); writer.append("=\""); writer.append(value); writer.append("\""); } } if (!first) { writer.append("]"); } } } private void stateException(String message) { throw new InvalidStateException("Invalid state: " + context.getStatus() + ", when calling: " + message); } /** * @return The current tab size used for indentation. */ public final String getIndentString() { return indentString; } public final GvWriter setIndentString(String s) { this.indentString = s; return this; } /** * Writes a comment. *

* This can be done at any place when the writer is open. * * @param comment The comment to write * @return This GvWriter. * @throws IOException If an I/O error occurs */ public final GvWriter addComment(String comment) throws IOException { indent(1); writer.append("// "); writer.append(comment); writer.append(EOL); return this; } public final GvWriter print(String s) throws IOException { writer.append(s); return this; } public final GvWriter println(String s) throws IOException { print(s); return println(); } /** * Terminates the current line by writing a line separator string. * * @return This GvWriter. * @throws IOException If an I/O error occurs */ public final GvWriter println() throws IOException { writer.append(EOL); return this; } /** * Begins a directed or undirected graph. A call to endGraph should match * this call. * * @param name Name of the graph. * @param directed If true, the graph is directed. Otherwise it is * undirected. * @param atts Graph attributes. * @return This GvWriter. * @throws IOException If an I/O error occurs */ public final GvWriter beginGraph(String name, boolean directed, GvGraphAttributes atts) throws IOException { if (context.getStatus() == Status.IN_OPEN_STREAM) { context = context.pushContext(Status.IN_GRAPH); context.setName(name); } else { stateException("beginGraph()"); } if (directed) { writer.append("digraph "); this.edge = " -> "; } else { writer.append("graph "); this.edge = " -- "; } writer.append(GvSupport.toValidId(name)); writer.append(" {"); writer.append(EOL); printSplit(atts); return this; } /** * Defines default node attributes for the current graph or subgraph. * * @param atts Default node attributes. * @return This GvWriter. * @throws IOException If an I/O error occurs */ public final GvWriter setDefaultNodeAttributes(GvNodeAttributes atts) throws IOException { switch (context.getStatus()) { case IN_GRAPH: case IN_SUBGRAPH: break; default: stateException("setDefaultNodeAttributes()"); break; } indent(1); writer.append("node"); printSep(atts); writer.append(EOL); return this; } /** * Defines default edge attributes for the current graph or subgraph. * * @param atts Default edge attributes. * @return This GvWriter. * @throws IOException If an I/O error occurs */ public final GvWriter setDefaultEdgeAttributes(GvEdgeAttributes atts) throws IOException { switch (context.getStatus()) { case IN_GRAPH: case IN_SUBGRAPH: break; default: stateException("setDefaultEdgeAttributes()"); break; } indent(1); writer.append("edge"); printSep(atts); writer.append(EOL); return this; } /** * End the definition of the current graph. After that, comments and new * lines can be added, or the writer can be closed. * * @return This GvWriter. * @throws IOException If an I/O error occurs */ public final GvWriter endGraph() throws IOException { if (context.getStatus() == Status.IN_GRAPH) { context = context.popContext(); context.setStatus(Status.IN_CLOSE_STREAM); } else { stateException("endGraph()"); } writer.append("}"); writer.append(EOL); writer.flush(); return this; } /** * Begins the creation of a new subgraph, in the current graph, subgraph or * cluster. * * @param name Name of the subgraph. * @param atts Attributes of the subgraph. * @return This GvWriter. * @throws IOException If an I/O error occurs */ public final GvWriter beginSubgraph(String name, GvSubgraphAttributes atts) throws IOException { switch (context.getStatus()) { case IN_GRAPH: case IN_SUBGRAPH: case IN_CLUSTER: context = context.pushContext(Status.IN_SUBGRAPH); context.setName(name); break; default: stateException("beginSubgraph()"); break; } indent(0); if (name != null) { writer.append("subgraph "); writer.append(GvSupport.toValidId(name)); writer.append(" {"); } else { writer.append('{'); } writer.append(EOL); printSplit(atts); return this; } /** * Ends the definition of the current subgraph. * * @return This GvWriter. * @throws IOException If an I/O error occurs */ public final GvWriter endSubgraph() throws IOException { if (context.getStatus() == Status.IN_SUBGRAPH) { context = context.popContext(); } else { stateException("endSubgraph()"); } indent(1); writer.append("}"); writer.append(EOL); return this; } /** * Begins the creation of a cluster, in the current graph, subgraph or * cluster. * * @param name Name of the cluster. * @param atts Attributes of the cluster. * @return This GvWriter. * @throws IOException If an I/O error occurs */ public final GvWriter beginCluster(String name, GvClusterAttributes atts) throws IOException { switch (context.getStatus()) { case IN_GRAPH: case IN_SUBGRAPH: case IN_CLUSTER: context = context.pushContext(Status.IN_CLUSTER); context.setName(name); break; default: stateException("beginCluster()"); break; } indent(0); writer.append("subgraph "); writer.append(GvSupport.toClusterId(name)); writer.append(" {"); writer.append(EOL); printSplit(atts); return this; } public final GvWriter endCluster() throws IOException { if (context.getStatus() == Status.IN_CLUSTER) { context = context.popContext(); } else { stateException("endCluster()"); } indent(1); writer.append("}"); writer.append(EOL); return this; } /** * Adds a node to the current graph, subgraph or cluster. * * @param id Node identifier. * @param atts Node attributes. * @return This GvWriter. * @throws IOException If an I/O error occurs */ public final GvWriter addNode(String id, GvNodeAttributes atts) throws IOException { switch (context.getStatus()) { case IN_GRAPH: case IN_SUBGRAPH: case IN_CLUSTER: break; default: stateException("addNode()"); break; } indent(1); writer.append(GvSupport.toValidId(id)); printSep(atts); writer.append(EOL); return this; } public final GvWriter addNode(String id) throws IOException { return addNode(id, null); } /** * Adds an edge to the current graph, subgraph or cluster. * * @param sourceId Source node identifier. * @param targetId Target node identifier. * @param atts Edge attributes. * @return This GvWriter. * @throws IOException If an I/O error occurs */ public final GvWriter addEdge(String sourceId, String targetId, GvEdgeAttributes atts) throws IOException { switch (context.getStatus()) { case IN_GRAPH: case IN_SUBGRAPH: case IN_CLUSTER: break; default: stateException("addEdge()"); break; } indent(1); writer.append(GvSupport.toValidId(sourceId)); writer.append(edge); writer.append(GvSupport.toValidId(targetId)); printSep(atts); writer.append(EOL); return this; } public final GvWriter addEdge(String sourceId, String targetId) throws IOException { return addEdge(sourceId, targetId, null); } public final GvWriter addEdge(String sourceId, String sourceClusterId, String targetId, String targetClusterId, GvEdgeAttributes atts) throws IOException { final String pltail = atts.getValue(GvAttributeName.LTAIL); final String plhead = atts.getValue(GvAttributeName.LHEAD); if (sourceClusterId != null) { atts.setLogicalTail(sourceClusterId); } if (targetClusterId != null) { atts.setLogicalHead(targetClusterId); } addEdge(sourceId, targetId, atts); // Reset atts to previous values. atts.setLogicalTail(pltail); atts.setLogicalHead(plhead); return this; } public final GvWriter addEdge(String sourceId, String sourceClusterId, String targetId, String targetClusterId) throws IOException { return addEdge(sourceId, sourceClusterId, targetId, targetClusterId, new GvEdgeAttributes()); } @Override public void flush() throws IOException { writer.flush(); } @Override public void close() throws IOException { writer.close(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy