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.OutputStream;
036import java.io.PrintWriter;
037import java.io.Writer;
038
039import org.graphstream.graph.Edge;
040import org.graphstream.graph.Graph;
041import org.graphstream.graph.Node;
042
043/**
044 * Base implementation for graph output to files.
045 * 
046 * <p>
047 * This class provides base services to write graphs into files using a specific
048 * file format. It allows to create an output stream. By default a print stream
049 * for easy text output, but binary files are possible.
050 * </p>
051 * 
052 * <p>
053 * It handles completely the {@link #writeAll(Graph, OutputStream)},
054 * {@link #writeAll(Graph, String)}, {@link #begin(OutputStream)},
055 * {@link #begin(String)}, {@link #flush()} and {@link #end()} methods. You
056 * should not have to modify or override these.
057 * </p>
058 * 
059 * <p>
060 * In order to implement an output you have to:
061 * <ul>
062 * <li>Eventually override {@link #createWriter(OutputStream)} or
063 * {@link #createWriter(String)} to replace the default instance of PrintStream
064 * created for you.</li>
065 * <li>Implement the {@link #outputHeader()} method. This method is called at
066 * start, before any graph event is sent to output. Use it to output the header
067 * of your file.</li>
068 * <li>Implement the {@link #outputEndOfFile()} method. This method is called at
069 * the end of the output, just before closing the output stream. Use it to
070 * output any terminating syntax for the file format you implement.</li>
071 * <li>Implement all the methods of {@link org.graphstream.stream.Sink}. All
072 * these methods will be called for each graph event and must export these
073 * events to the file you are writing. You should use the {@link #output} field
074 * to write to the file. This field has type {@link java.io.OutputStream} but by
075 * default is of type {@link java.io.PrintStream}, as most of the file format
076 * will be textual.</li>
077 * </ul>
078 * </p>
079 */
080public abstract class FileSinkBase implements FileSink {
081        // Attribute
082
083        /**
084         * The output.
085         */
086        protected Writer output;
087
088        // Command
089
090        public void writeAll(Graph graph, String fileName) throws IOException {
091                begin(fileName);
092                exportGraph(graph);
093                end();
094        }
095
096        public void writeAll(Graph graph, OutputStream stream) throws IOException {
097                begin(stream);
098                exportGraph(graph);
099                end();
100        }
101
102        public void writeAll(Graph graph, Writer writer) throws IOException {
103                begin(writer);
104                exportGraph(graph);
105                end();
106        }
107
108        /**
109         * Echo each element and attribute of the graph to the actual output.
110         * 
111         * The elements are echoed as add events (add node, add edge, add
112         * attribute). This method guarantees there are no change or delete events.
113         * 
114         * @param graph
115         *            The graph to export.
116         */
117        protected void exportGraph(Graph graph) {
118                String graphId = graph.getId();
119                long timeId = 0;
120
121                for (String key : graph.getAttributeKeySet())
122                        graphAttributeAdded(graphId, timeId++, key, graph.getAttribute(key));
123
124                for (Node node : graph) {
125                        String nodeId = node.getId();
126                        nodeAdded(graphId, timeId++, nodeId);
127
128                        if (node.getAttributeCount() > 0)
129                                for (String key : node.getAttributeKeySet())
130                                        nodeAttributeAdded(graphId, timeId++, nodeId, key,
131                                                        node.getAttribute(key));
132                }
133
134                for (Edge edge : graph.getEachEdge()) {
135                        String edgeId = edge.getId();
136                        edgeAdded(graphId, timeId++, edgeId, edge.getNode0().getId(), edge
137                                        .getNode1().getId(), edge.isDirected());
138
139                        if (edge.getAttributeCount() > 0)
140                                for (String key : edge.getAttributeKeySet())
141                                        edgeAttributeAdded(graphId, timeId++, edgeId, key,
142                                                        edge.getAttribute(key));
143                }
144        }
145
146        /*
147         * (non-Javadoc)
148         * @see org.graphstream.stream.file.FileSink#begin(java.lang.String)
149         */
150        public void begin(String fileName) throws IOException {
151                if (output != null)
152                        throw new IOException(
153                                        "cannot call begin() twice without calling end() before.");
154
155                output = createWriter(fileName);
156
157                outputHeader();
158        }
159
160        /*
161         * (non-Javadoc)
162         * @see org.graphstream.stream.file.FileSink#begin(java.io.OutputStream)
163         */
164        public void begin(OutputStream stream) throws IOException {
165                if (output != null)
166                        throw new IOException(
167                                        "cannot call begin() twice without calling end() before.");
168
169                output = createWriter(stream);
170
171                outputHeader();
172        }
173
174        /*
175         * (non-Javadoc)
176         * @see org.graphstream.stream.file.FileSink#begin(java.io.Writer)
177         */
178        public void begin(Writer writer) throws IOException {
179                if (output != null)
180                        throw new IOException(
181                                        "cannot call begin() twice without calling end() before.");
182
183                output = createWriter(writer);
184
185                outputHeader();
186        }
187
188        /*
189         * (non-Javadoc)
190         * @see org.graphstream.stream.file.FileSink#flush()
191         */
192        public void flush() throws IOException {
193                if (output != null)
194                        output.flush();
195        }
196
197        /*
198         * (non-Javadoc)
199         * @see org.graphstream.stream.file.FileSink#end()
200         */
201        public void end() throws IOException {
202                outputEndOfFile();
203                output.flush();
204                output.close();
205                output = null;
206        }
207
208        /**
209         * Method called at start just after the {@link #output} field is created.
210         * Use it to output the header of the file.
211         * 
212         * @throws IOException
213         *             If any I/O error occurs.
214         */
215        protected abstract void outputHeader() throws IOException;
216
217        /**
218         * Method called at the end just before the {@link #output} field is flushed
219         * and closed. Use it to output any information that closes the file.
220         * 
221         * @throws IOException
222         *             If any I/O error occurs.
223         */
224        protected abstract void outputEndOfFile() throws IOException;
225
226        /**
227         * Create a a writer from a file name. Override this method if the default
228         * PrintWriter does not suits your needs. This method is called by
229         * {@link #begin(String)} and {@link #writeAll(Graph, String)}.
230         * 
231         * @param fileName
232         *            Name of the file to output to.
233         * @return A new writer.
234         * @throws IOException
235         *             If any I/O error occurs.
236         */
237        protected Writer createWriter(String fileName) throws IOException {
238                return new PrintWriter(fileName);
239        }
240
241        /**
242         * Create a writer from an existing output stream. Override this method if
243         * the default PrintWriter does not suits your needs. This method is called
244         * by {@link #begin(OutputStream)} and
245         * {@link #writeAll(Graph, OutputStream)}. This method does not create an
246         * output stream if the given stream is already instance of PrintStream.
247         * 
248         * @param stream
249         *            An already existing output stream.
250         * @return A new writer.
251         * @throws IOException
252         *             If any I/O error occurs.
253         */
254        protected Writer createWriter(OutputStream stream) throws IOException {
255                return new PrintWriter(stream);
256        }
257
258        /**
259         * Create a writer from an existing writer. Override this method if the
260         * default PrintWriter does not suits your needs. This method is called by
261         * {@link #begin(Writer)} and {@link #writeAll(Graph, Writer)}. This method
262         * does not create a new writer if the given writer is already instance of
263         * PrintWriter.
264         * 
265         * @param writer
266         *            An already existing writer.
267         * @return A new writer.
268         * @throws IOException
269         *             If any I/O error occurs.
270         */
271        protected Writer createWriter(Writer writer) throws IOException {
272                if (writer instanceof PrintWriter)
273                        return writer;
274
275                return new PrintWriter(writer);
276        }
277}