/* * 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 SpiffReader.h * Interface of SpiffReader. */ #ifndef SPIFF_READER_H #define SPIFF_READER_H #include "SpiffDefines.h" #include namespace Spiff { /// @cond DOXYGEN_IGNORE // Error messages with ONE "%s" in it #define SPIFF_READER_TEXT_ONE_ATTRIBUTE_FORBIDDEN _PT("Attribute '%s' not allowed.") #define SPIFF_READER_TEXT_ONE_EXPAT_ERROR _PT("Expat error '%s'") #define SPIFF_READER_TEXT_ONE_ELEMENT_FORBIDDEN _PT("Element '%s' not allowed.") #define SPIFF_READER_TEXT_ONE_ELEMENT_FORBIDDEN_VERSION_ZERO _PT("Element '%s' not allowed in XSPF-0.") #define SPIFF_READER_TEXT_ONE_FILE_READING_ERROR _PT("File '%s' could not be read.") #define SPIFF_READER_TEXT_ONE_WRONG_ROOT_NAME _PT("Root element must be '") SPIFF_NS_HOME SPIFF_NS_SEP_STRING _PT("playlist', not '%s'.") #define SPIFF_READER_TEXT_ONE_WRONG_VERSION _PT("Version must be '0' or '1', not '%s'.") // Error messages with ZERO "%s" in it #define SPIFF_READER_TEXT_ZERO_ATTRIBUTE_MISSING(name) _PT("Attribute '") name _PT("' missing.") #define SPIFF_READER_TEXT_ZERO_ELEMENT_MISSING(ns, name) _PT("Element '") ns SPIFF_NS_SEP_STRING name _PT("' missing.") #define SPIFF_READER_TEXT_ZERO_ELEMENT_MISSING_VERSION_ZERO(ns, name) _PT("Element '") ns SPIFF_NS_SEP_STRING name _PT("' missing. This is not allowed in XSPF-0.") #define SPIFF_READER_TEXT_ZERO_FILENAME_NULL _PT("Filename must not be NULL.") #define SPIFF_READER_TEXT_ZERO_TOO_MANY_ELEMENTS(ns, name) _PT("Only one '") ns SPIFF_NS_SEP_STRING name _PT("' allowed.") #define SPIFF_READER_TEXT_ZERO_WRONG_ATTRIBUTE_TYPE(attr, type) _PT("Attribute '") attr _PT("' is not a valid ") type _PT(".") #define SPIFF_READER_TEXT_ZERO_WRONG_CONTENT_TYPE(ns, elem, type) _PT("Content of '") ns SPIFF_NS_SEP_STRING elem _PT("' is not a valid ") type _PT(".") #define SPIFF_READER_TEXT_ZERO_TEXT_FORBIDDEN(ns, elem) _PT("Content of '") ns SPIFF_NS_SEP_STRING elem _PT("' must be whitespace or child elements, not text.") /// @endcond /** * Specifies the result of a parse operation. */ enum SpiffReaderReturnCode { SPIFF_READER_SUCCESS, ///< Everything fine SPIFF_READER_ERROR_NO_INPUT, ///< No input given SPIFF_READER_ERROR_ELEMENT_TOOMANY, ///< Element occurs more often than allowed SPIFF_READER_ERROR_ELEMENT_FORBIDDEN, ///< Element is not allowed at that place SPIFF_READER_ERROR_ELEMENT_MISSING, ///< Required element missing SPIFF_READER_ERROR_ATTRIBUTE_INVALID, ///< Attribute with invalid value SPIFF_READER_ERROR_ATTRIBUTE_MISSING, ///< Required attribute missing SPIFF_READER_ERROR_ATTRIBUTE_FORBIDDEN, ///< Attribute not allowed at that place SPIFF_READER_ERROR_CONTENT_INVALID, ///< Element body has invalid format // This one must come last! SPIFF_READER_ERROR_EXPAT /// First Expat error code }; /* playlist 1 title ? creator ? annotation ? info ? location ? identifier ? image ? date ? license ? attribution ? location * identifier * link * meta * extension * ... * trackList 1 track +|* location * identifier * title ? creator ? annotation ? info ? image ? album ? trackNum (uint > 0) ? duration (uint) ? link * meta * extension * */ /** * Specifies the type of tag element. */ enum SpiffTag { // Stack returns 0 if empty TAG_UNKNOWN, ///< Unknown type TAG_PLAYLIST, ///< playlist tag TAG_PLAYLIST_TITLE, ///< playlist.title tag TAG_PLAYLIST_CREATOR, ///< playlist.creator tag TAG_PLAYLIST_ANNOTATION, ///< playlist.annotation tag TAG_PLAYLIST_INFO, ///< playlist.info tag TAG_PLAYLIST_LOCATION, ///< playlist.location tag TAG_PLAYLIST_IDENTIFIER, ///< playlist.identifier tag TAG_PLAYLIST_IMAGE, ///< playlist.image tag TAG_PLAYLIST_DATE, ///< playlist.date tag TAG_PLAYLIST_LICENSE, ///< playlist.license tag TAG_PLAYLIST_ATTRIBUTION, ///< playlist.attribution tag TAG_PLAYLIST_ATTRIBUTION_LOCATION, ///< playlist.attribution.location tag TAG_PLAYLIST_ATTRIBUTION_IDENTIFIER, ///< playlist.attribution.identifier tag TAG_PLAYLIST_LINK, ///< playlist.link tag TAG_PLAYLIST_META, ///< playlist.meta tag TAG_PLAYLIST_EXTENSION, ///< playlist.extension tag TAG_PLAYLIST_TRACKLIST, ///< playlist.tracklist tag TAG_PLAYLIST_TRACKLIST_TRACK, ///< playlist.tracklist.track tag TAG_PLAYLIST_TRACKLIST_TRACK_LOCATION, ///< playlist.tracklist.track.location tag TAG_PLAYLIST_TRACKLIST_TRACK_IDENTIFIER, ///< playlist.tracklist.track.identifier tag TAG_PLAYLIST_TRACKLIST_TRACK_TITLE, ///< playlist.tracklist.track.title tag TAG_PLAYLIST_TRACKLIST_TRACK_CREATOR, ///< playlist.tracklist.track.creator tag TAG_PLAYLIST_TRACKLIST_TRACK_ANNOTATION, ///< playlist.tracklist.track.annotation tag TAG_PLAYLIST_TRACKLIST_TRACK_INFO, ///< playlist.tracklist.track.info tag TAG_PLAYLIST_TRACKLIST_TRACK_IMAGE, ///< playlist.tracklist.track.image tag TAG_PLAYLIST_TRACKLIST_TRACK_ALBUM, ///< playlist.tracklist.track.album tag TAG_PLAYLIST_TRACKLIST_TRACK_TRACKNUM, ///< playlist.tracklist.track.tracknum tag TAG_PLAYLIST_TRACKLIST_TRACK_DURATION, ///< playlist.tracklist.track.duration tag TAG_PLAYLIST_TRACKLIST_TRACK_LINK, ///< playlist.tracklist.track.link tag TAG_PLAYLIST_TRACKLIST_TRACK_META, ///< playlist.tracklist.track.meta tag TAG_PLAYLIST_TRACKLIST_TRACK_EXTENSION, ///< playlist.tracklist.track.extension tag // New tags can start from this value TAG_USER ///< First user/extension tag }; class SpiffProps; class SpiffDateTime; class SpiffTrack; class SpiffReaderCallback; class SpiffStack; class SpiffChunkCallback; class SpiffExtensionReaderFactory; class SpiffExtensionReader; class SpiffReaderPrivate; /** * Reads a XSPF playlist from a file. */ class SpiffReader { private: /// @cond DOXYGEN_NON_API SpiffReaderPrivate * const d; ///< D pointer /// @endcond public: /** * Creates a new reader. * * @param handlerFactory Factory used to create handlers */ SpiffReader(SpiffExtensionReaderFactory * handlerFactory = NULL); /** * Copy constructor. * * @param source Source to copy from */ SpiffReader(const SpiffReader & source); /** * Assignment operator. * * @param source Source to copy from */ SpiffReader & operator=(const SpiffReader & source); /** * Frees all own memory. */ ~SpiffReader(); /** * Reads an XSPF playlist from a file. * * @deprecated Will be removed with 0.8.x, use parseFile instead * @param filename Filename of the file to read * @param callback Reader callback that will receive the playlist's information */ int parse(const XML_Char * filename, SpiffReaderCallback * callback); /** * Reads an XSPF playlist from a file. * * @param filename Filename of the file to read * @param callback Reader callback that will receive the playlist's information */ int parseFile(const XML_Char * filename, SpiffReaderCallback * callback); /** * Reads an XSPF playlist from a block of memory. * * @param memory Memory block to parse * @param numBytes Size of memory in bytes * @param callback Reader callback that will receive the playlist's information */ int parseMemory(const char * memory, int numBytes, SpiffReaderCallback * callback); /** * Reads an XSPF playlist from a chunk callback. * * @param inputCallback Chunk callback,must not be NULL * @param dataCallback Reader callback that will receive the playlist's information */ int parseChunks(SpiffChunkCallback * inputCallback, SpiffReaderCallback * dataCallback); /** * Returns the error text. This pointer is only valid * for the lifetime of this reader. * * @return Detailed error description */ const XML_Char * getErrorText(); /** * Returns the line which caused the parse error. * * @return Error line */ int getErrorLine(); private: /** * Make the parser reusable so it can parse another file. */ void makeReusable(); /** * Sets the error code and text. * * @param code Error code * @param text Error description */ void setError(int code, const XML_Char * text); /** * Sets the error code and text. * * @param code Error code * @param format Error description format string containg %s * @param param Text parameter to insert for %s */ void setError(int code, const XML_Char * format, const XML_Char * param); /** * Sets the error code and text based on information from Expat. * This is needed for all non-XSPF errors, e.g. XML errors. */ void setExpatError(); /** * Halts the parser. */ void stop(); /** * Forwards tag opening handling to the correct handler. * * @param fullName Full tag name (" ") * @param atts Alternating list of attribute keys and values */ void handleStart(const XML_Char * fullName, const XML_Char ** atts); /** * Handles tag opening on level one (e.g. playlist). * * @param fullName Full tag name (" ") * @param atts Alternating list of attribute keys and values * @return Continue parsing flag */ bool handleStartOne(const XML_Char * fullName, const XML_Char ** atts); /** * Handles tag opening on level two (e.g. playlist.title). * * @param fullName Full tag name (" ") * @param atts Alternating list of attribute keys and values * @return Continue parsing flag */ bool handleStartTwo(const XML_Char * fullName, const XML_Char ** atts); /** * Handles tag opening on level three (e.g. playlist.trackList.track). * * @param fullName Full tag name (" ") * @param atts Alternating list of attribute keys and values * @return Continue parsing flag */ bool handleStartThree(const XML_Char * fullName, const XML_Char ** atts); /** * Handles tag opening on level four (e.g. playlist.trackList.track.title). * * @param fullName Full tag name (" ") * @param atts Alternating list of attribute keys and values * @return Continue parsing flag */ bool handleStartFour(const XML_Char * fullName, const XML_Char ** atts); /** * Handles element content. * * @param s Text content * @param len Characters allowed to read */ void handleCharacters(const XML_Char * s, int len); /** * Forwards tag closing handling to the correct handler. * * @param fullName Full tag name (" ") */ void handleEnd(const XML_Char * fullName); /** * Handles tag closing on level one (e.g. playlist). * * @param fullName Full tag name (" ") * @return Continue parsing flag */ bool handleEndOne(const XML_Char * fullName); /** * Handles tag closing on level two (e.g. playlist.title). * * @param fullName Full tag name (" ") * @return Continue parsing flag */ bool handleEndTwo(const XML_Char * fullName); /** * Handles tag closing on level three (e.g. playlist.trackList.track). * * @param fullName Full tag name (" ") * @return Continue parsing flag */ bool handleEndThree(const XML_Char * fullName); /** * Handles tag closing on level four (e.g. playlist.trackList.track.title). * * @param fullName Full tag name (" ") * @return Continue parsing flag */ bool handleEndFour(const XML_Char * fullName); /** * Checks the attributes of the playlist tag * and extracts the XSPF version from it. * * @param atts Alternating list of attribute keys and values * @return Attributes okay flag */ bool handlePlaylistAttribs(const XML_Char ** atts); /** * Checks the attributes of the meta tag for * both playlist.meta and playlist.trackList.track.meta. * * @param atts Alternating list of attribute keys and values * @return Attributes okay flag */ bool handleMetaLinkAttribs(const XML_Char ** atts); /** * Checks the attributes of the extension tag for * both playlist.extension and playlist.trackList.track.extension. * * @param atts Alternating list of attribute keys and values * @param application Points to the application URI in case of success * @return Attributes okay flag */ bool handleExtensionAttribs(const XML_Char ** atts, const XML_Char ** application); /** * Checks the attributes of a tag that must not have any attributes. * * @param atts Alternating list of attribute keys and values * @return No attributes flag */ bool handleNoAttribs(const XML_Char ** atts); /** * Resets the error status to success. */ void clearError(); public: /** * Checks wether text is a valid URI. * * @param text Text * @return Valid URI flag */ static bool isURI(const XML_Char * text); /** * Extracts an integer from text. * * @param text Text * @param inclusiveMinimum Inclusive minimum * @param output Integer storage destination * @return Valid integer less or equal inclusiveMinimum flag */ static bool extractInteger(const XML_Char * text, int inclusiveMinimum, int * output); /** * Extracts a dateTime from text. * * @param text Text * @param output dateTime storage destination * @return Valid dateTime flag */ static bool extractDateTime(const XML_Char * text, SpiffDateTime * output); /** * Checks a string for being all whitespace. * Whitespace is: ' ', '\\r', '\\n', '\\t' as defined here: * http://www.w3.org/TR/xmlschema-2/#rf-whiteSpace * * @param text Text to check * @param numChars Length of text in characters * @return All-whitespace flag */ static bool isWhiteSpace(const XML_Char * text, int numChars); /** * Cuts off whitespace at head and tail. The source string * is not modified so this more is a detection function. * * @param input Source text * @param inputNumChars Length of source in characters * @param blackSpaceStart Pointer to first non-white character * @param blackSpaceNumChars Length of characters until whitespace tail */ static void cutOffWhiteSpace(const XML_Char * input, int inputNumChars, const XML_Char * & blackSpaceStart, int & blackSpaceNumChars); private: /** * Forwards tag opening handling to the correct class instance. * * @param userData Instance pointer * @param fullName Full tag name (" ") * @param atts Alternating list of attribute keys and values */ static void masterStart(void * userData, const XML_Char * fullName, const XML_Char ** atts); /** * Forwards tag closing handling to the correct class instance. * * @param userData Instance pointer * @param fullName Full tag name (" ") */ static void masterEnd(void * userData, const XML_Char * fullName); /** * Forwards element content handling to the correct class instance. * * @param userData Instance pointer * @param s Text content * @param len Characters allowed to read */ static void masterCharacters(void * userData, const XML_Char * s, int len); /** * Initializes parsing. * * @param callback Reader callback that will receive the playlist's information */ void onBeforeParse(SpiffReaderCallback * callback); /** * Cleans up behind parsing. */ void onAfterParse(); /** * Returns the internal tag stack. * * @return Tag stack */ SpiffStack * getStack(); friend class SpiffExtensionReader; }; } #endif // SPIFF_READER_H