/* * 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 SpiffPropsWriter.cpp * Implementation of SpiffPropsWriter. */ #include #include #include #include #include #include using namespace std; using namespace Spiff::Toolbox; namespace Spiff { /// @cond DOXYGEN_NON_API /** * D object for SpiffPropsWriter. */ class SpiffPropsWriterPrivate { friend class SpiffPropsWriter; SpiffProps * props; ///< Playlist properties to write int version; ///< XSPF version bool trackListEmpty; ///< Tracklist empty flag, no initialization needed std::list > initNamespaces; ///< Namespaces to register when writing /** * Creates a new D object. */ SpiffPropsWriterPrivate(SpiffProps * props) : props(props), version(-1) { } /** * Copy constructor. * * @param source Source to copy from */ SpiffPropsWriterPrivate(const SpiffPropsWriterPrivate & source) : props(source.props), version(source.version), trackListEmpty(source.trackListEmpty) { copyNamespaceInits(this->initNamespaces, source.initNamespaces); } /** * Assignment operator. * * @param source Source to copy from */ SpiffPropsWriterPrivate & operator=(const SpiffPropsWriterPrivate & source) { if (this != &source) { this->props = source.props; this->version = source.version; this->trackListEmpty = source.trackListEmpty; freeNamespaceInits(this->initNamespaces); copyNamespaceInits(this->initNamespaces, source.initNamespaces); } return *this; } static void freeNamespaceInits(std::list > & container) { list >::iterator iter = container.begin(); while (iter != container.end()) { pair & entry = *iter; delete [] entry.second; iter++; } container.clear(); } static void copyNamespaceInits( std::list > & dest, const std::list > & source) { list >::const_iterator iter = source.begin(); while (iter != source.end()) { const pair & entry = *iter; const XML_Char * const uri = entry.first; XML_Char * const prefixSuggestion = newAndCopy(entry.second); dest.push_back( pair( uri, prefixSuggestion)); iter++; } } /** * Destroys this D object. */ ~SpiffPropsWriterPrivate() { freeNamespaceInits(this->initNamespaces); } }; /// @endcond SpiffPropsWriter::SpiffPropsWriter(SpiffProps * props) : SpiffDataWriter(props), d(new SpiffPropsWriterPrivate(props)) { } SpiffPropsWriter::SpiffPropsWriter(const SpiffPropsWriter & source) : SpiffDataWriter(source), d(new SpiffPropsWriterPrivate(*(source.d))) { } SpiffPropsWriter & SpiffPropsWriter::operator=(const SpiffPropsWriter & source) { if (this != &source) { SpiffDataWriter::operator=(source); *(this->d) = *(source.d); } return *this; } SpiffPropsWriter::~SpiffPropsWriter() { delete this->d; } void SpiffPropsWriter::writeAttribution() { int index = 0; pair * entry = this->d->props->getAttribution(index++); if (entry == NULL) { return; } const XML_Char * atts[1] = {NULL}; this->output->writeHomeStart(_PT("attribution"), atts); do { writePrimitive(entry->first ? _PT("location") : _PT("identifier"), entry->second); delete entry; // since the pair was created for us entry = this->d->props->getAttribution(index++); } while (entry != NULL); this->output->writeHomeEnd(_PT("attribution")); } void SpiffPropsWriter::writeDate() { const SpiffDateTime * const dateTime = this->d->props->getDate(); if (dateTime != NULL) { const int charCount = 10 + 1 + 8 + 6 + 1; XML_Char buffer[charCount]; ::PORT_SNPRINTF(buffer, charCount, _PT("%04i-%02i-%02iT%02i:%02i:%02i%s%02i:%02i"), dateTime->getYear(), dateTime->getMonth(), dateTime->getDay(), dateTime->getHour(), dateTime->getMinutes(), dateTime->getSeconds(), (dateTime->getDistHours() < 0) ? _PT("-") : _PT("+"), std::abs(dateTime->getDistHours()), std::abs(dateTime->getDistMinutes())); writePrimitive(_PT("date"), buffer); } } void SpiffPropsWriter::writeIdentifier() { const XML_Char * const identifier = this->d->props->getIdentifier(); if (identifier != NULL) { writePrimitive(_PT("identifier"), identifier); } } void SpiffPropsWriter::writeLicense() { const XML_Char * const license = this->d->props->getLicense(); if (license != NULL) { writePrimitive(_PT("license"), license); } } void SpiffPropsWriter::writeLocation() { const XML_Char * const location = this->d->props->getLocation(); if (location != NULL) { writePrimitive(_PT("location"), location); } } void SpiffPropsWriter::writePlaylistClose() { this->output->writeHomeEnd(_PT("playlist")); } void SpiffPropsWriter::writePlaylistOpen() { // Make namespace registration list const int count = static_cast(this->d->initNamespaces.size()); list >::iterator iter = this->d->initNamespaces.begin(); const XML_Char ** nsRegs = new const XML_Char *[2 * (1 + count) + 1]; nsRegs[0] = SpiffXmlFormatter::namespaceKey; nsRegs[1] = _PT(""); int walk = 2; while (iter != this->d->initNamespaces.end()) { pair & entry = *iter; nsRegs[walk] = entry.first; nsRegs[walk + 1] = entry.second; walk += 2; iter++; } nsRegs[walk] = NULL; // Make version const int charCount = 16; XML_Char versionText[charCount]; ::PORT_SNPRINTF(versionText, charCount, _PT("%i"), this->d->version); const XML_Char * atts[3] = {_PT("version"), versionText, NULL}; // Write tag this->output->writeStart(SpiffXmlFormatter::namespaceKey, _PT("playlist"), atts, nsRegs); // Delete reg list SpiffPropsWriterPrivate::freeNamespaceInits(this->d->initNamespaces); delete [] nsRegs; } void SpiffPropsWriter::writeTrackListClose() { if (this->d->trackListEmpty) { // Whole tag was written already return; } this->output->writeHomeEnd(_PT("trackList")); } void SpiffPropsWriter::writeTrackListOpen() { const XML_Char * atts[1] = {NULL}; if (this->d->trackListEmpty) { if (this->d->version > 0) { // Empty trackList allowed this->output->writeHomeStart(_PT("trackList"), atts); this->output->writeHomeEnd(_PT("trackList")); } else { // Empty trackList forbidden this->output->writeHomeStart(_PT("trackList"), atts); this->output->writeHomeStart(_PT("track"), atts); this->output->writeHomeEnd(_PT("track")); this->output->writeHomeEnd(_PT("trackList")); } } else { this->output->writeHomeStart(_PT("trackList"), atts); } } /** * Initializes the writer. * * @param output Output formatter * @param version XSPF version */ void SpiffPropsWriter::init(SpiffXmlFormatter & output, int version) { this->output = &output; if ((version < 0) || (version > 1)) { this->d->version = 1; } else { this->d->version = version; } } /** * Opens the playlist tag and writes all playlist * properties not handled by writeStartTracklist(). */ void SpiffPropsWriter::writeStartPlaylist() { writePlaylistOpen(); writeTitle(); writeCreator(); writeAnnotation(); writeInfo(); writeLocation(); writeIdentifier(); writeImage(); writeDate(); writeLicense(); writeAttribution(); writeLinks(); writeMetas(); if (this->d->version > 0) { writeExtensions(); } } /** * Writes all playlist properties not handled by * writeStartPlaylist() and opens the trackList tag. * * @param trackListEmpty Tracklist empty flag */ void SpiffPropsWriter::writeStartTracklist(bool trackListEmpty) { this->d->trackListEmpty = trackListEmpty; writeTrackListOpen(); } /** * Closes the trackList tag. */ void SpiffPropsWriter::writeEndTracklist() { writeTrackListClose(); } /** * Closes the playlist tag. */ void SpiffPropsWriter::writeEndPlaylist() { writePlaylistClose(); } void SpiffPropsWriter::registerNamespace(const XML_Char * uri, const XML_Char * prefixSuggestion) { // TODO too late? this->d->initNamespaces.push_back(pair(uri, newAndCopy(prefixSuggestion))); } }