001/*
002 * Copyright 2006 - 2013
003 *     Stefan Balev     <stefan.balev@graphstream-project.org>
004 *     Julien Baudry    <julien.baudry@graphstream-project.org>
005 *     Antoine Dutot    <antoine.dutot@graphstream-project.org>
006 *     Yoann Pigné      <yoann.pigne@graphstream-project.org>
007 *     Guilhelm Savin   <guilhelm.savin@graphstream-project.org>
008 * 
009 * This file is part of GraphStream <http://graphstream-project.org>.
010 * 
011 * GraphStream is a library whose purpose is to handle static or dynamic
012 * graph, create them from scratch, file or any source and display them.
013 * 
014 * This program is free software distributed under the terms of two licenses, the
015 * CeCILL-C license that fits European law, and the GNU Lesser General Public
016 * License. You can  use, modify and/ or redistribute the software under the terms
017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by
019 * the Free Software Foundation, either version 3 of the License, or (at your
020 * option) any later version.
021 * 
022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
024 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
025 * 
026 * You should have received a copy of the GNU Lesser General Public License
027 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
028 * 
029 * The fact that you are presently reading this means that you have had
030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
031 */
032package org.graphstream.stream.file;
033
034import java.io.IOException;
035import java.io.PrintWriter;
036
037/**
038 * Graph writer for the GraphViz DOT format.
039 */
040public class FileSinkDOT extends FileSinkBase {
041        // Attribute
042
043        /**
044         * The output.
045         */
046        protected PrintWriter out;
047
048        /**
049         * The graph name (set as soon as known).
050         */
051        protected String graphName = "";
052
053        /**
054         * Is the graph directed ?
055         */
056        protected boolean digraph;
057
058        /**
059         * What element ?.
060         */
061        protected enum What {
062                NODE, EDGE, OTHER
063        };
064
065        /**
066         * Build a new DOT sink to export undirected graph.
067         */
068        public FileSinkDOT() {
069                this(false);
070        }
071
072        /**
073         * Build a new DOT sink specifying if the graph is directed or not.
074         * 
075         * @param digraph
076         *            true if the graph is directed
077         */
078        public FileSinkDOT(boolean digraph) {
079                this.digraph = digraph;
080        }
081
082        // Command
083
084        /**
085         * Set flag indicating if exported graph is directed or not.
086         * 
087         * @param digraph
088         *            true is exported graph is directed
089         */
090        public void setDirected(boolean digraph) {
091                this.digraph = digraph;
092        }
093
094        /**
095         * Get the flag indicating if exported graph is directed or not.
096         * 
097         * @return true if exported graph is directed
098         */
099        public boolean isDirected() {
100                return digraph;
101        }
102
103        @Override
104        protected void outputHeader() throws IOException {
105                out = (PrintWriter) output;
106                out.printf("%s {%n", digraph ? "digraph" : "graph");
107
108                if (graphName.length() > 0)
109                        out.printf("\tgraph [label=%s];%n", graphName);
110        }
111
112        @Override
113        protected void outputEndOfFile() throws IOException {
114                out.printf("}%n");
115        }
116
117        public void edgeAttributeAdded(String graphId, long timeId, String edgeId,
118                        String attribute, Object value) {
119                // NOP
120        }
121
122        public void edgeAttributeChanged(String graphId, long timeId,
123                        String edgeId, String attribute, Object oldValue, Object newValue) {
124                // NOP
125        }
126
127        public void edgeAttributeRemoved(String graphId, long timeId,
128                        String edgeId, String attribute) {
129                // NOP
130        }
131
132        public void graphAttributeAdded(String graphId, long timeId,
133                        String attribute, Object value) {
134                out
135                                .printf("\tgraph [ %s ];%n", outputAttribute(attribute, value,
136                                                true));
137        }
138
139        public void graphAttributeChanged(String graphId, long timeId,
140                        String attribute, Object oldValue, Object newValue) {
141                out.printf("\tgraph [ %s ];%n", outputAttribute(attribute, newValue,
142                                true));
143        }
144
145        public void graphAttributeRemoved(String graphId, long timeId,
146                        String attribute) {
147                // NOP
148        }
149
150        public void nodeAttributeAdded(String graphId, long timeId, String nodeId,
151                        String attribute, Object value) {
152                out.printf("\t\"%s\" [ %s ];%n", nodeId, outputAttribute(attribute,
153                                value, true));
154        }
155
156        public void nodeAttributeChanged(String graphId, long timeId,
157                        String nodeId, String attribute, Object oldValue, Object newValue) {
158                out.printf("\t\"%s\" [ %s ];%n", nodeId, outputAttribute(attribute,
159                                newValue, true));
160        }
161
162        public void nodeAttributeRemoved(String graphId, long timeId,
163                        String nodeId, String attribute) {
164                // NOP
165        }
166
167        public void edgeAdded(String graphId, long timeId, String edgeId,
168                        String fromNodeId, String toNodeId, boolean directed) {
169                if (digraph) {
170                        out.printf("\t\"%s\" -> \"%s\"", fromNodeId, toNodeId);
171
172                        if (!directed)
173                                out.printf(" -> \"%s\"", fromNodeId);
174
175                        out.printf("\n;");
176                } else
177                        out.printf("\t\"%s\" -- \"%s\";%n", fromNodeId, toNodeId);
178        }
179
180        public void edgeRemoved(String graphId, long timeId, String edgeId) {
181                // NOP
182        }
183
184        public void graphCleared(String graphId, long timeId) {
185                // NOP
186        }
187
188        public void nodeAdded(String graphId, long timeId, String nodeId) {
189                out.printf("\t\"%s\";%n", nodeId);
190        }
191
192        public void nodeRemoved(String graphId, long timeId, String nodeId) {
193                // NOP
194        }
195
196        public void stepBegins(String graphId, long timeId, double step) {
197                // NOP
198        }
199
200        // Utility
201        /*
202         * protected void outputAttributes(Map<String, Object> attributes, What
203         * what) throws IOException { out.printf(" [");
204         * 
205         * boolean first = true;
206         * 
207         * for (String key : attributes.keySet()) { Object value =
208         * attributes.get(key);
209         * 
210         * if (what == What.NODE) { // if( ! nodeForbiddenAttrs.contains( key ) ) {
211         * first = outputAttribute(key, value, first); } } else if (what ==
212         * What.EDGE) { // if( ! edgeForbiddenAttrs.contains( key ) ) { first =
213         * outputAttribute(key, value, first); } } else { first =
214         * outputAttribute(key, value, first); }
215         * 
216         * }
217         * 
218         * out.printf("]"); }
219         */
220        protected String outputAttribute(String key, Object value, boolean first) {
221                boolean quote = true;
222
223                if (value instanceof Number)
224                        quote = false;
225
226                return String.format("%s\"%s\"=%s%s%s", first ? "" : ",", key,
227                                quote ? "\"" : "", value, quote ? "\"" : "");
228        }
229}