/* * libSpiff - XSPF playlist handling library * * Copyright (C) 2007, Sebastian Pipping / Xiph.Org Foundation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the Xiph.Org Foundation nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Sebastian Pipping, sping@xiph.org */ /** * @file SpiffWriter.cpp * Implementation of SpiffWriter. */ #include #include #include #include using namespace std; namespace Spiff { /// @cond DOXYGEN_NON_API /** * D object for SpiffWriter. */ class SpiffWriterPrivate { friend class SpiffWriter; SpiffXmlFormatter * formatter; ///< Output formatter in use SpiffPropsWriter * propsWriter; ///< Related playlist properties std::basic_ostringstream * accum; ///< Output text accumulator bool trackListEmpty; ///< Tracklist empty flag bool headerWritten; ///< playlist and trackList tag opened flag bool footerWritten; ///< trackList and trackList tag closed flag int version; ///< XSPF version to use /** * Creates a new D object. */ SpiffWriterPrivate(int version, SpiffXmlFormatter & formatter, SpiffPropsWriter & propsWriter) : formatter(&formatter), propsWriter(&propsWriter), accum(new basic_ostringstream()), trackListEmpty(true), headerWritten(false), footerWritten(false), version(version) { } /** * Copy constructor. * * @param source Source to copy from */ SpiffWriterPrivate(const SpiffWriterPrivate & source) : formatter(source.formatter), propsWriter(source.propsWriter), accum(new basic_ostringstream()), trackListEmpty(source.trackListEmpty), headerWritten(source.headerWritten), footerWritten(source.footerWritten), version(source.version) { this->accum->str(source.accum->str()); } /** * Assignment operator. * * @param source Source to copy from */ SpiffWriterPrivate & operator=(const SpiffWriterPrivate & source) { if (this != &source) { this->formatter = source.formatter; this->propsWriter = source.propsWriter; this->accum->str(source.accum->str()); this->trackListEmpty = source.trackListEmpty; this->headerWritten = source.headerWritten; this->footerWritten = source.footerWritten; this->version = source.version; } return *this; } /** * Destroys this D object. */ ~SpiffWriterPrivate() { if (this->accum != NULL) { delete this->accum; } } }; /// @endcond SpiffWriter::SpiffWriter(int version, SpiffXmlFormatter & formatter, SpiffPropsWriter & propsWriter) : d(new SpiffWriterPrivate(version, formatter, propsWriter)) { // Fix invalid version if ((this->d->version < 0) || (this->d->version > 1)) { this->d->version = 1; } // Init formatter formatter.setOutput(*(this->d->accum)); propsWriter.init(*(this->d->formatter), this->d->version); } SpiffWriter::SpiffWriter(const SpiffWriter & source) : d(new SpiffWriterPrivate(*(source.d))) { } SpiffWriter & SpiffWriter::operator=(const SpiffWriter & source) { if (this != &source) { *(this->d) = *(source.d); } return *this; } SpiffWriter::~SpiffWriter() { delete this->d; } void SpiffWriter::registerNamespace(const XML_Char * uri, const XML_Char * prefixSuggestion) { // Too late? if (this->d->headerWritten) { return; } this->d->propsWriter->registerNamespace(uri, prefixSuggestion); } void SpiffWriter::addTrack(SpiffTrackWriter & trackWriter) { // Playlist already finalized? if (this->d->footerWritten) { return; } // First track ever? if (!this->d->headerWritten) { this->d->propsWriter->writeStartPlaylist(); this->d->propsWriter->writeStartTracklist(false); this->d->headerWritten = true; } trackWriter.init(*(this->d->formatter), this->d->version); trackWriter.write(); this->d->trackListEmpty = false; } void SpiffWriter::onBeforeWrite() { // Header if (!this->d->headerWritten) { this->d->propsWriter->writeStartPlaylist(); this->d->propsWriter->writeStartTracklist(true); this->d->headerWritten = true; } // Footer if (!this->d->footerWritten) { this->d->propsWriter->writeEndTracklist(); this->d->propsWriter->writeEndPlaylist(); this->d->footerWritten = true; } } int SpiffWriter::write(const XML_Char * filename) { // Backward compatibility return writeFile(filename); } int SpiffWriter::writeFile(const XML_Char * filename) { // Open file FILE * const file = ::PORT_FOPEN(filename, _PT("wb")); if (file == NULL) { // TODO return SPIFF_WRITER_ERROR_OPENING; } onBeforeWrite(); // Get final input basic_string final = this->d->accum->str(); const XML_Char * const rawFinal = final.c_str(); const int rawFinalLen = static_cast(::PORT_STRLEN(rawFinal)); // UTF-8 conversion on Unicode Windows #if (defined(UNICODE) && (defined(__WIN32__) || defined(WIN32))) char * rawFinalUtf8 = new char[rawFinalLen * 4]; const int rawFinalUtf8Len = ::WideCharToMultiByte(CP_UTF8, 0, rawFinal, rawFinalLen, rawFinalUtf8, rawFinalLen * 4, NULL, NULL); ::fwrite(rawFinalUtf8, 1, rawFinalUtf8Len, file); delete [] rawFinalUtf8; #else ::fwrite(rawFinal, sizeof(XML_Char), rawFinalLen, file); #endif ::fclose(file); return SPIFF_WRITER_SUCCESS; } int SpiffWriter::writeMemory(char * & memory, int & numBytes) { onBeforeWrite(); // Get final input basic_string final = this->d->accum->str(); const XML_Char * const rawFinal = final.c_str(); const int rawFinalLen = static_cast(::PORT_STRLEN(rawFinal)); // UTF-8 conversion on Unicode Windows #if (defined(UNICODE) && (defined(__WIN32__) || defined(WIN32))) memory = new char[rawFinalLen * 4]; numBytes = ::WideCharToMultiByte(CP_UTF8, 0, rawFinal, rawFinalLen, memory, rawFinalLen * 4, NULL, NULL); #else memory = new char[rawFinalLen + 1]; memcpy(memory, rawFinal, rawFinalLen); memory[rawFinalLen] = '\0'; numBytes = rawFinalLen; #endif return SPIFF_WRITER_SUCCESS; } void SpiffWriter::reset(int version, SpiffXmlFormatter & formatter, SpiffPropsWriter & propsWriter) { // Fix invalid version if ((version < 0) || (version > 1)) { this->d->version = 1; } else { this->d->version = version; } // Init formatter and content writer this->d->formatter = &formatter; this->d->propsWriter = &propsWriter; formatter.setOutput(*this->d->accum); propsWriter.init(*this->d->formatter, this->d->version); this->d->trackListEmpty = true; this->d->headerWritten = false; this->d->footerWritten = false; // Clear buffer delete this->d->accum; this->d->accum = new basic_ostringstream; } }