From d11189b975d4062898881d3e4f163bacfb9ae5dc Mon Sep 17 00:00:00 2001 From: Michael Helmling Date: Mon, 16 Jan 2012 22:37:30 +0100 Subject: [PATCH] Basic implementation of a PropertyMap. Implemented key/valuelist property map with case-insensitive ASCII keys and StringList values. Todo: - subclass StringList to add flags indicating whether a value could be written to the specific file format - add member attribute indicating list of frames that could not be parsed into the PropertyMap representation. --- taglib/CMakeLists.txt | 2 + taglib/mpeg/id3v2/id3v2dicttools.cpp | 27 ++++++ taglib/toolkit/tpropertymap.cpp | 120 +++++++++++++++++++++++++++ taglib/toolkit/tpropertymap.h | 118 ++++++++++++++++++++++++++ 4 files changed, 267 insertions(+) create mode 100644 taglib/toolkit/tpropertymap.cpp create mode 100644 taglib/toolkit/tpropertymap.h diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt index ec8a5d81..a7777a5b 100644 --- a/taglib/CMakeLists.txt +++ b/taglib/CMakeLists.txt @@ -48,6 +48,7 @@ set(tag_HDRS toolkit/tfilestream.h toolkit/tmap.h toolkit/tmap.tcc + toolkit/tpropertymap.h mpeg/mpegfile.h mpeg/mpegproperties.h mpeg/mpegheader.h @@ -277,6 +278,7 @@ set(toolkit_SRCS toolkit/tfile.cpp toolkit/tfilestream.cpp toolkit/tdebug.cpp + toolkit/tpropertymap.cpp toolkit/unicode.cpp ) diff --git a/taglib/mpeg/id3v2/id3v2dicttools.cpp b/taglib/mpeg/id3v2/id3v2dicttools.cpp index 7ee6c83d..63a0398b 100644 --- a/taglib/mpeg/id3v2/id3v2dicttools.cpp +++ b/taglib/mpeg/id3v2/id3v2dicttools.cpp @@ -134,6 +134,33 @@ namespace TagLib { return m; } + // list of TXXX frame description conversions + static const uint txxxConversionSize = 4; + static const char *txxxConversionFrames[][2] = { + {"MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID"}, + {"MusicBrainz Disc Id", "MUSICBRAINZ_DISCID"}, + {"MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID"}, + {"MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID"}, + {"MusicMagic Fingerprint", "MUSICIP_FINGERPRINT"}, + {"MusicIP PUID", "MUSICIP_PUID"}, + {"MusicBrainz Album Release Country", "RELEASECOUNTRY"}, + {"MusicBrainz Album Status", "MUSICBRAINZ_ALBUMSTATUS"}, + {"MusicBrainz Album Type", "MUSICBRAINZ_ALBUMTYPE"}, + {"MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASE_GROUPID"}, + {"MusicBrainz Work Id", "MUSICBRAINZ_WORKID"}, + {"MusicBrainz Original Album Id", "MUSICBRAINZ_ORIGINALALBUMID"}, + {"Acoustid Fingerprint", "ACOUSTID_FINGERPRINT"}, + {"Acoustid Id", "ACOUSTID_ID"} + }; + + FrameIDMap &txxxConversionMap() + { + static FrameIDMap txxxMap; + if (txxxMap.isEmpty()) + for(uint i = 0; i < txxxConversionSize; ++i) + txxxMap[txxxConversionFrames[i][0]] = txxxConversionFrames[i][1]; + return txxxMap; + } String frameIDToTagName(const ByteVector &id) { Map &m = idMap(); diff --git a/taglib/toolkit/tpropertymap.cpp b/taglib/toolkit/tpropertymap.cpp new file mode 100644 index 00000000..08b94ba7 --- /dev/null +++ b/taglib/toolkit/tpropertymap.cpp @@ -0,0 +1,120 @@ +/*************************************************************************** + copyright : (C) 2012 by Michael Helmling + email : helmling@mathematik.uni-kl.de + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301 USA * + ***************************************************************************/ + +#include "tpropertymap.h" + +using namespace TagLib; + +typedef Map supertype; + +PropertyMap::PropertyMap() : Map() +{ +} + +PropertyMap::PropertyMap(const PropertyMap &m) : Map(m) +{ +} + +PropertyMap::~PropertyMap() +{ +} + +bool PropertyMap::insert(const String &key, const StringList &values) +{ + String realKey = prepareKey(key); + if (realKey.isNull()) + return false; + + supertype::operator[](realKey).append(values); + return true; +} + +bool PropertyMap::replace(const String &key, const StringList &values) +{ + String realKey = prepareKey(key); + if (realKey.isNull()) + return false; + supertype::erase(realKey); + supertype::insert(realKey, values); + return true; +} + +PropertyMap::Iterator PropertyMap::find(const String &key) +{ + String realKey = prepareKey(key); + if (realKey.isNull()) + return end(); + return supertype::find(realKey); +} + +PropertyMap::ConstIterator PropertyMap::find(const String &key) const +{ + String realKey = prepareKey(key); + if (realKey.isNull()) + return end(); + return supertype::find(realKey); +} + +bool PropertyMap::contains(const String &key) const +{ + String realKey = prepareKey(key); + if (realKey.isNull()) + return false; + return supertype::contains(realKey); +} + +/*! + * Erase the \a key and its values from the map. + */ +PropertyMap &PropertyMap::erase(const String &key) +{ + String realKey = prepareKey(key); + if (realKey.isNull()) + return *this; + supertype::erase(realKey); + return *this; +} + +const StringList &PropertyMap::operator[](const String &key) const +{ + String realKey = prepareKey(key); + return supertype::operator[](realKey); +} + +StringList &PropertyMap::operator[](const String &key) +{ + String realKey = prepareKey(key); + if (realKey.isNull()) + return supertype::operator[](realKey); // invalid case + if (!supertype::contains(realKey)) + supertype::insert(realKey, StringList()); + return supertype::operator[](realKey); +} + +String PropertyMap::prepareKey(const String &proposed) const { + if (proposed.isEmpty()) + return String::null; + for (String::ConstIterator it = proposed.begin(); it != proposed.end(); it++) + // forbid non-printable, non-ascii, '=' (#61) and '~' (#126) + if (*it < 32 || *it >= 128 || *it == 61 || *it == 126) + return String::null; + return proposed.upper(); +} diff --git a/taglib/toolkit/tpropertymap.h b/taglib/toolkit/tpropertymap.h new file mode 100644 index 00000000..f82a7dfb --- /dev/null +++ b/taglib/toolkit/tpropertymap.h @@ -0,0 +1,118 @@ +/*************************************************************************** + copyright : (C) 2012 by Michael Helmling + email : helmling@mathematik.uni-kl.de + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef PROPERTYMAP_H_ +#define PROPERTYMAP_H_ + +#include "tmap.h" +#include "tstringlist.h" + +namespace TagLib { + + + //! A map for format-independent tag representations. + + /*! + * This map implements a generic representation of textual audio metadata + * ("tags") realized as pairs of a case-insensitive key + * and a nonempty list of corresponding values, each value being an an arbitrary + * Unicode String. + * The key has the same restrictions as in the vorbis comment specification, + * i.e. it must contain at least one character; all printable ASCII characters + * except '=' and '~' are allowed. + * + */ + + class PropertyMap: public Map + { + public: + + typedef Map::Iterator Iterator; + typedef Map::ConstIterator ConstIterator; + + PropertyMap(); + + PropertyMap(const PropertyMap &m); + + virtual ~PropertyMap(); + + /*! + * Inserts \a values under \a key in the map. If \a key already exists, + * then \values will be appended to the existing StringList. + * The returned value indicates success, i.e. whether \a key is a + * valid key. + */ + bool insert(const String &key, const StringList &values); + + /*! + * Replaces any existing values for \a key with the given \a values, + * and simply insert them if \a key did not exist before. + * The returned value indicates success, i.e. whether \a key is a + * valid key. + */ + bool replace(const String &key, const StringList &values); + + /*! + * Find the first occurrence of \a key. + */ + Iterator find(const String &key); + + /*! + * Find the first occurrence of \a key. + */ + ConstIterator find(const String &key) const; + + /*! + * Returns true if the map contains values for \a key. + */ + bool contains(const String &key) const; + + /*! + * Erase the \a key and its values from the map. + */ + PropertyMap &erase(const String &key); + + /*! + * Returns a reference to the value associated with \a key. + * + * \note: This has undefined behavior if the key is not valid. + */ + const StringList &operator[](const String &key) const; + + /*! + * Returns a reference to the value associated with \a key. + * + * If \a key is not present in the map, an empty list is inserted and + * returned. + * + * \note: This has undefined behavior if the key is not valid. + */ + StringList &operator[](const String &key); + + /*! + * Converts \a proposed into another String suitable to be used as + * a key, or returns String::null if this is not possible. + */ + String prepareKey(const String &proposed) const; + }; + +} +#endif /* PROPERTYMAP_H_ */