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}