1 /**
2  * @file SedWriter.cpp
3  * @brief Implementation of the SedWriter class.
4  * @author DEVISER
5  *
6  * <!--------------------------------------------------------------------------
7  * This file is part of libSEDML. Please visit http://sed-ml.org for more
8  * information about SED-ML. The latest version of libSEDML can be found on
9  * github: https://github.com/fbergmann/libSEDML/
10  *
11 
12  * Copyright (c) 2013-2019, Frank T. Bergmann
13  * All rights reserved.
14  *
15 
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions are met:
18  *
19 
20  * 1. Redistributions of source code must retain the above copyright notice,
21  * this
22  * list of conditions and the following disclaimer.
23  * 2. Redistributions in binary form must reproduce the above copyright notice,
24  * this list of conditions and the following disclaimer in the documentation
25  * and/or other materials provided with the distribution.
26  *
27  * This library is free software; you can redistribute it and/or modify it
28  * under the terms of the GNU Lesser General Public License as published by the
29  * Free Software Foundation. A copy of the license agreement is provided in the
30  * file named "LICENSE.txt" included with this software distribution and also
31  * available online as http://sbml.org/software/libsbml/license.html
32  * ------------------------------------------------------------------------ -->
33  */
34 
35 
36 #include <ios>
37 #include <iostream>
38 #include <fstream>
39 #include <sstream>
40 
41 #include <sedml/common/common.h>
42 #include <sbml/xml/XMLOutputStream.h>
43 
44 #include <sedml/SedError.h>
45 #include <sedml/SedErrorLog.h>
46 #include <sedml/SedDocument.h>
47 #include <sedml/SedWriter.h>
48 
49 #include <sbml/compress/CompressCommon.h>
50 #include <sbml/compress/OutputCompressor.h>
51 
52 /** @cond doxygenIgnored */
53 
54 using namespace std;
55 
56 /** @endcond */
57 
58 LIBSEDML_CPP_NAMESPACE_BEGIN
59 
60 #ifdef __cplusplus
61 
62 /*
63  * Creates a new SedWriter.
64  */
SedWriter()65 SedWriter::SedWriter ()
66 {
67 }
68 
69 
70 /*
71  * Destroys this SedWriter.
72  */
~SedWriter()73 SedWriter::~SedWriter ()
74 {
75 }
76 
77 
78 /*
79  * Sets the name of this program. i.\ e.\ the one about to write out the
80  * SedDocument.  If the program name and version are set
81  * (setProgramVersion()), the following XML comment, intended for human
82  * consumption, will be written at the beginning of the document:
83  *
84  *   <!-- Created by <program name> version <program version>
85  *   on yyyy-MM-dd HH:mm with libsedml version <libsedml version>. -->
86  */
87 int
setProgramName(const std::string & name)88 SedWriter::setProgramName (const std::string& name)
89 {
90   mProgramName = name;
91   return LIBSEDML_OPERATION_SUCCESS;
92 }
93 
94 
95 /*
96  * Sets the version of this program. i.\ e.\ the one about to write out the
97  * SedDocument.  If the program version and name are set
98  * (setProgramName()), the following XML comment, intended for human
99  * consumption, will be written at the beginning of the document:
100  *
101  *   <!-- Created by <program name> version <program version>
102  *   on yyyy-MM-dd HH:mm with libsedml version <libsedml version>. -->
103  */
104 int
setProgramVersion(const std::string & version)105 SedWriter::setProgramVersion (const std::string& version)
106 {
107   mProgramVersion = version;
108   return LIBSEDML_OPERATION_SUCCESS;
109 }
110 
111 
112 /*
113  * Writes the given SedDocument to filename.
114  *
115  * If the filename ends with @em .gz, the file will be compressed by @em gzip.
116  * Similary, if the filename ends with @em .zip or @em .bz2, the file will be
117  * compressed by @em zip or @em bzip2, respectively. Otherwise, the fill will be
118  * uncompressed.
119  *
120  * @note To create a gzip/zip file, underlying libSEDML needs to be linked with zlib at
121  * compile time. Also, underlying libSEDML needs to be linked with bzip2 to create a
122  * bzip2 file.
123  * File unwritable error will be logged and @c false will be returned if a compressed
124  * file name is given and underlying libSEDML is not linked with the corresponding
125  * required library.
126  * SedWriter::hasZlib() and SedWriter::hasBzip2() can be used to check whether
127  * underlying libSEDML is linked with the library.
128  *
129  * @return true on success and false if the filename could not be opened
130  * for writing.
131  */
132 bool
writeSedML(const SedDocument * d,const std::string & filename)133 SedWriter::writeSedML (const SedDocument* d, const std::string& filename)
134 {
135   std::ostream* stream = NULL;
136 
137   try
138   {
139     // open an uncompressed XML file.
140     if ( string::npos != filename.find(".xml", filename.length() - 4) )
141     {
142       stream = new(std::nothrow) std::ofstream(filename.c_str());
143     }
144     // open a gzip file
145     else if ( string::npos != filename.find(".gz", filename.length() - 3) )
146     {
147      stream = OutputCompressor::openGzipOStream(filename);
148     }
149     // open a bz2 file
150     else if ( string::npos != filename.find(".bz2", filename.length() - 4) )
151     {
152       stream = OutputCompressor::openBzip2OStream(filename);
153     }
154     // open a zip file
155     else if ( string::npos != filename.find(".zip", filename.length() - 4) )
156     {
157       std::string filenameinzip = filename.substr(0, filename.length() - 4);
158 
159       if ( ( string::npos == filenameinzip.find(".xml",  filenameinzip.length() - 4) ) &&
160            ( string::npos == filenameinzip.find(".sedml", filenameinzip.length() - 5) )
161          )
162       {
163         filenameinzip += ".xml";
164       }
165 
166 
167 #if defined(WIN32) && !defined(CYGWIN)
168       char sepr = '\\';
169 #else
170       char sepr = '/';
171 #endif
172       size_t spos = filenameinzip.rfind(sepr, filenameinzip.length() - 1);
173       if( spos != string::npos )
174       {
175         filenameinzip = filenameinzip.substr(spos + 1, filenameinzip.length() - 1);
176       }
177 
178 
179       stream = OutputCompressor::openZipOStream(filename, filenameinzip);
180     }
181     else
182     {
183       stream = new(std::nothrow) std::ofstream(filename.c_str());
184     }
185   }
186   catch ( ZlibNotLinked& )
187   {
188     // libSEDML is not linked with zlib.
189     XMLErrorLog *log = (const_cast<SedDocument *>(d))->getErrorLog();
190     std::ostringstream oss;
191     oss << "Tried to write " << filename << ". Writing a gzip/zip file is not enabled because "
192         << "underlying libSEDML is not linked with zlib.";
193     log->add(XMLError( XMLFileUnwritable, oss.str(), 0, 0) );
194     return false;
195   }
196   catch ( Bzip2NotLinked& )
197   {
198     // libSEDML is not linked with bzip2.
199     XMLErrorLog *log = (const_cast<SedDocument *>(d))->getErrorLog();
200     std::ostringstream oss;
201     oss << "Tried to write " << filename << ". Writing a bzip2 file is not enabled because "
202         << "underlying libSEDML is not linked with bzip2.";
203     log->add(XMLError( XMLFileUnwritable, oss.str(), 0, 0) );
204     return false;
205   }
206 
207 
208   if ( stream == NULL || stream->fail() || stream->bad())
209   {
210     SedErrorLog *log = (const_cast<SedDocument *>(d))->getErrorLog();
211     log->logError(XMLFileUnwritable);
212     delete stream;
213     return false;
214   }
215 
216    bool result = writeSedML(d, *stream);
217    delete stream;
218 
219    return result;
220 
221 }
222 
223 
224 /*
225  * Writes the given SedDocument to the output stream.
226  *
227  * @return true on success and false if one of the underlying parser
228  * components fail (rare).
229  */
230 bool
writeSedML(const SedDocument * d,std::ostream & stream)231 SedWriter::writeSedML (const SedDocument* d, std::ostream& stream)
232 {
233   bool result = false;
234 
235   try
236   {
237     stream.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit);
238     XMLOutputStream xos(stream, "UTF-8", true, mProgramName,
239                                                mProgramVersion);
240     d->write(xos);
241     stream << endl;
242 
243     result = true;
244   }
245   catch (ios_base::failure&)
246   {
247     SedErrorLog *log = (const_cast<SedDocument *>(d))->getErrorLog();
248     log->logError(XMLFileOperationError);
249   }
250 
251   return result;
252 }
253 
254 
255 /** @cond doxygenLibsedmlInternal */
256 /*
257  * Writes the given SedDocument to an in-memory string and returns a
258  * pointer to it.  The string is owned by the caller and should be freed
259  * (with free()) when no longer needed.
260  *
261  * @return the string on success and 0 if one of the underlying parser
262  * components fail (rare).
263  */
264 LIBSEDML_EXTERN
265 char*
writeToString(const SedDocument * d)266 SedWriter::writeToString (const SedDocument* d)
267 {
268   ostringstream stream;
269   writeSedML(d, stream);
270 
271   return safe_strdup( stream.str().c_str() );
272 }
273 
274 std::string
writeSedMLToStdString(const SedDocument * d)275 SedWriter::writeSedMLToStdString(const SedDocument* d)
276 {
277   if (d == NULL) return "";
278 
279   ostringstream stream;
280   writeSedML(d, stream);
281   return stream.str();
282 }
283 
284 LIBSEDML_EXTERN
285 char*
writeSedMLToString(const SedDocument * d)286 SedWriter::writeSedMLToString (const SedDocument* d)
287 {
288   return writeToString(d);
289 }
290 /** @endcond */
291 
292 
293 LIBSEDML_EXTERN
294 bool
writeSedMLToFile(const SedDocument * d,const std::string & filename)295 SedWriter::writeSedMLToFile (const SedDocument* d, const std::string& filename)
296 {
297   return writeSedML(d, filename);
298 }
299 
300 
301 /*
302  * Predicate returning @c true if
303  * underlying libSEDML is linked with zlib.
304  *
305  * @return @c true if libSEDML is linked with zlib, @c false otherwise.
306  */
307 bool
hasZlib()308 SedWriter::hasZlib()
309 {
310   return LIBSBML_CPP_NAMESPACE ::hasZlib();
311 }
312 
313 
314 /*
315  * Predicate returning @c true if
316  * underlying libSEDML is linked with bzip2.
317  *
318  * @return @c true if libSEDML is linked with bzip2, @c false otherwise.
319  */
320 bool
hasBzip2()321 SedWriter::hasBzip2()
322 {
323   return LIBSBML_CPP_NAMESPACE ::hasBzip2();
324 }
325 
326 
327 #endif /* __cplusplus */
328 /** @cond doxygenIgnored */
329 LIBSEDML_EXTERN
330 SedWriter_t *
SedWriter_create()331 SedWriter_create ()
332 {
333   return new(nothrow) SedWriter;
334 }
335 
336 
337 LIBSEDML_EXTERN
338 void
SedWriter_free(SedWriter_t * sw)339 SedWriter_free (SedWriter_t *sw)
340 {
341   delete sw;
342 }
343 
344 
345 LIBSEDML_EXTERN
346 int
SedWriter_setProgramName(SedWriter_t * sw,const char * name)347 SedWriter_setProgramName (SedWriter_t *sw, const char *name)
348 {
349   if (sw != NULL)
350     return (name == NULL) ? sw->setProgramName("") : sw->setProgramName(name);
351   else
352     return LIBSEDML_INVALID_OBJECT;
353 }
354 
355 
356 LIBSEDML_EXTERN
357 int
SedWriter_setProgramVersion(SedWriter_t * sw,const char * version)358 SedWriter_setProgramVersion (SedWriter_t *sw, const char *version)
359 {
360   if (sw != NULL)
361     return (version == NULL) ? sw->setProgramVersion("") :
362                              sw->setProgramVersion(version);
363   else
364     return LIBSEDML_INVALID_OBJECT;
365 }
366 
367 
368 LIBSEDML_EXTERN
369 int
SedWriter_writeSedML(SedWriter_t * sw,const SedDocument_t * d,const char * filename)370 SedWriter_writeSedML ( SedWriter_t         *sw,
371                        const SedDocument_t *d,
372                        const char           *filename )
373 {
374   if (sw == NULL || d == NULL)
375     return 0;
376   else
377     return (filename != NULL) ?
378       static_cast<int>( sw->writeSedML(d, filename) ) : 0;
379 }
380 
381 
382 LIBSEDML_EXTERN
383 int
SedWriter_writeSedMLToFile(SedWriter_t * sw,const SedDocument_t * d,const char * filename)384 SedWriter_writeSedMLToFile ( SedWriter_t         *sw,
385                        const SedDocument_t *d,
386                        const char           *filename )
387 {
388   if (sw == NULL || d == NULL)
389     return 0;
390   else
391     return (filename != NULL) ?
392       static_cast<int>( sw->writeSedML(d, filename) ) : 0;
393 }
394 
395 
396 LIBSEDML_EXTERN
397 char *
SedWriter_writeSedMLToString(SedWriter_t * sw,const SedDocument_t * d)398 SedWriter_writeSedMLToString (SedWriter_t *sw, const SedDocument_t *d)
399 {
400   if (sw == NULL || d == NULL)
401     return 0;
402   else
403     return sw->writeToString(d);
404 }
405 
406 
407 LIBSEDML_EXTERN
408 int
SedWriter_hasZlib()409 SedWriter_hasZlib ()
410 {
411   return static_cast<int>( SedWriter::hasZlib() );
412 }
413 
414 
415 LIBSEDML_EXTERN
416 int
SedWriter_hasBzip2()417 SedWriter_hasBzip2 ()
418 {
419    return static_cast<int>( SedWriter::hasBzip2() );
420 }
421 
422 
423 LIBSEDML_EXTERN
424 int
writeSedML(const SedDocument_t * d,const char * filename)425 writeSedML (const SedDocument_t *d, const char *filename)
426 {
427   SedWriter sw;
428   if (d == NULL || filename == NULL)
429     return 0;
430   else
431     return static_cast<int>( sw.writeSedML(d, filename) );
432 }
433 
434 
435 LIBSEDML_EXTERN
436 int
writeSedMLToFile(const SedDocument_t * d,const char * filename)437 writeSedMLToFile (const SedDocument_t *d, const char *filename)
438 {
439   SedWriter sw;
440   if (d == NULL || filename == NULL)
441     return 0;
442   else
443     return static_cast<int>( sw.writeSedML(d, filename) );
444 }
445 
446 
447 LIBSEDML_EXTERN
448 char *
writeSedMLToString(const SedDocument_t * d)449 writeSedMLToString (const SedDocument_t *d)
450 {
451   SedWriter sw;
452   if (d == NULL)
453     return NULL;
454   else
455     return sw.writeToString(d);
456 }
457 
458 LIBSEDML_EXTERN
writeSedMLToStdString(const SedDocument * d)459 std::string writeSedMLToStdString(const SedDocument* d)
460 {
461   SedWriter sw;
462   if (d == NULL)
463     return "";
464   else
465     return sw.writeSedMLToStdString(d);
466 }
467 /** @endcond */
468 
469 LIBSEDML_CPP_NAMESPACE_END
470 
471