]> granicus.if.org Git - taglib/commitdiff
Started to work on ID3v2.
authorMichael Helmling <helmling@mathematik.uni-kl.de>
Sat, 21 Jan 2012 22:05:59 +0000 (23:05 +0100)
committerMichael Helmling <helmling@mathematik.uni-kl.de>
Sat, 21 Jan 2012 22:05:59 +0000 (23:05 +0100)
12 files changed:
taglib/mpc/mpcfile.cpp
taglib/mpc/mpcfile.h
taglib/mpeg/id3v2/frames/textidentificationframe.cpp
taglib/mpeg/id3v2/frames/textidentificationframe.h
taglib/mpeg/id3v2/id3v2frame.cpp
taglib/mpeg/id3v2/id3v2frame.h
taglib/mpeg/id3v2/id3v2tag.cpp
taglib/mpeg/id3v2/id3v2tag.h
taglib/mpeg/mpegfile.cpp
taglib/mpeg/mpegfile.h
taglib/toolkit/tpropertymap.cpp
taglib/toolkit/tpropertymap.h

index 2482a90c7ad45bdfff4b39ece845063c4388c59b..6b372a6057323d132b5e97da74916d2aa1d9b9e8 100644 (file)
@@ -113,27 +113,34 @@ TagLib::Tag *MPC::File::tag() const
   return &d->tag;
 }
 
-TagLib::TagDict MPC::File::toDict(void) const
+PropertyMap MPC::File::properties() const
 {
-  // once Tag::toDict() is virtual, this case distinction could actually be done
-  // within TagUnion.
-  if (d->hasAPE)
-    return d->tag.access<APE::Tag>(APEIndex, false)->toDict();
-  if (d->hasID3v1)
-    return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->toDict();
-  return TagLib::TagDict();
+  if(d->hasAPE)
+    return d->tag.access<APE::Tag>(APEIndex, false)->properties();
+  if(d->hasID3v1)
+    return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->properties();
+  return PropertyMap();
+}
+
+void MPC::File::removeUnsupportedProperties(const StringList &properties)
+{
+  if(d->hasAPE)
+    d->tag.access<APE::Tag>(APEIndex, false)->removeUnsupportedProperties(properties);
+  if(d->hasID3v1)
+    d->tag.access<ID3v1::Tag>(ID3v1Index, false)->removeUnsupportedProperties(properties);
 }
 
-void MPC::File::fromDict(const TagDict &dict)
+PropertyMap MPC::File::setProperties(const PropertyMap &properties)
 {
-  if (d->hasAPE)
-    d->tag.access<APE::Tag>(APEIndex, false)->fromDict(dict);
-  else if (d->hasID3v1)
-    d->tag.access<ID3v1::Tag>(ID3v1Index, false)->fromDict(dict);
+  if(d->hasAPE)
+    return d->tag.access<APE::Tag>(APEIndex, false)->setProperties(properties);
+  else if(d->hasID3v1)
+    return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->setProperties(properties);
   else
-    d->tag.access<APE::Tag>(APEIndex, true)->fromDict(dict);
+    return d->tag.access<APE::Tag>(APE, true)->setProperties(properties);
 }
 
+
 MPC::Properties *MPC::File::audioProperties() const
 {
   return d->properties;
index 6ff91e719ba3adb4ac3dd10b169a65f2a666b073..c906ae6709de18a00855cbc761657112c84e65b4 100644 (file)
@@ -109,18 +109,20 @@ namespace TagLib {
       virtual TagLib::Tag *tag() const;
 
       /*!
-       * Implements the unified tag dictionary interface -- export function.
+       * Implements the unified property interface -- export function.
        * If the file contains both an APE and an ID3v1 tag, only the APE
-       * tag  will be converted to the TagDict.
+       * tag  will be converted to the PropertyMap.
        */
-      TagDict toDict() const;
+      PropertyMap properties() const;
+
+      void removeUnsupportedProperties(const StringList &properties);
 
       /*!
-       * Implements the unified tag dictionary interface -- import function.
+       * Implements the unified property interface -- import function.
        * As with the export, only one tag is taken into account. If the file
-       * has no tag at all, APE will be created.
+       * has no tag at all, an APE tag will be created.
        */
-      void fromDict(const TagDict &);
+      PropertyMap setProperties(const PropertyMap &);
 
       /*!
        * Returns the MPC::Properties for this file.  If no audio properties
index cf72492211bbe003b34c4b1bea2ae1dd1736731b..06489ea342972f2e2e8ca4389b53ca3249c396bf 100644 (file)
@@ -238,6 +238,28 @@ void UserTextIdentificationFrame::setDescription(const String &s)
   TextIdentificationFrame::setText(l);
 }
 
+PropertyMap UserTextIdentificationFrame::asProperties() const
+{
+  String tagName = description();
+  StringList l(fieldList());
+  // this is done because taglib stores the description also as first entry
+  // in the field list. (why?)
+  StringList::Iterator tagIt = l.find(tagName);
+  if(tagIt != l.end())
+     l.erase(tagIt);
+  // Quodlibet/Exfalso use QuodLibet::<tagname> if you set an arbitrary ID3
+  // tag.
+  int pos = tagName.find("::");
+  tagName = (pos != -1) ? tagName.substr(pos+2).upper() : tagName.upper();
+  PropertyMap map;
+  String key = map.prepareKey(tagName);
+  if(key.isNull()) // this frame's description is not a valid PropertyMap key -> add to unsupported list
+    map.unsupportedData().append("TXXX/" + description());
+  else
+    map.insert(key, l);
+  return map;
+}
+
 UserTextIdentificationFrame *UserTextIdentificationFrame::find(
   ID3v2::Tag *tag, const String &description) // static
 {
index 418ef9704b82fecb2f9d965dbbbab310927071a7..d4049d716a9b8aa0078f6b3dd163819bda5f371c 100644 (file)
@@ -236,6 +236,11 @@ namespace TagLib {
       void setText(const String &text);
       void setText(const StringList &fields);
 
+      /*!
+       * Reimplement function.
+       */
+      PropertyMap asProperties() const;
+
       /*!
        * Searches for the user defined text frame with the description \a description
        * in \a tag.  This returns null if no matching frames were found.
index 7979cf504032194e9394b83a3e4e08aa67b3d2e4..3fcc8c8090241a421fa38588db8ebfcde5b4bf12 100644 (file)
@@ -262,6 +262,142 @@ String::Type Frame::checkTextEncoding(const StringList &fields, String::Type enc
   return checkEncoding(fields, encoding, header()->version());
 }
 
+static const uint frameTranslationSize = 55;
+static const char *frameTranslation[][2] = {
+  // Text information frames
+  { "TALB", "ALBUM"},
+  { "TBPM", "BPM" },
+  { "TCOM", "COMPOSER" },
+  { "TCON", "GENRE" },
+  { "TCOP", "COPYRIGHT" },
+  { "TDEN", "ENCODINGTIME" },
+  { "TDLY", "PLAYLISTDELAY" },
+  { "TDOR", "ORIGINALDATE" },
+  { "TDRC", "DATE" },
+  // { "TRDA", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
+  // { "TDAT", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
+  // { "TYER", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
+  // { "TIME", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
+  { "TDRL", "RELEASEDATE" },
+  { "TDTG", "TAGGINGDATE" },
+  { "TENC", "ENCODEDBY" },
+  { "TEXT", "LYRICIST" },
+  { "TFLT", "FILETYPE" },
+  { "TIPL", "INVOLVEDPEOPLE" },
+  { "TIT1", "CONTENTGROUP" },
+  { "TIT2", "TITLE"},
+  { "TIT3", "SUBTITLE" },
+  { "TKEY", "INITIALKEY" },
+  { "TLAN", "LANGUAGE" },
+  { "TLEN", "LENGTH" },
+  { "TMCL", "MUSICIANCREDITS" },
+  { "TMED", "MEDIATYPE" },
+  { "TMOO", "MOOD" },
+  { "TOAL", "ORIGINALALBUM" },
+  { "TOFN", "ORIGINALFILENAME" },
+  { "TOLY", "ORIGINALLYRICIST" },
+  { "TOPE", "ORIGINALARTIST" },
+  { "TOWN", "OWNER" },
+  { "TPE1", "ARTIST"},
+  { "TPE2", "ALBUMARTIST" }, // id3's spec says 'PERFORMER', but most programs use 'ALBUMARTIST'
+  { "TPE3", "CONDUCTOR" },
+  { "TPE4", "REMIXER" }, // could also be ARRANGER
+  { "TPOS", "DISCNUMBER" },
+  { "TPRO", "PRODUCEDNOTICE" },
+  { "TPUB", "PUBLISHER" },
+  { "TRCK", "TRACKNUMBER" },
+  { "TRSN", "RADIOSTATION" },
+  { "TRSO", "RADIOSTATIONOWNER" },
+  { "TSOA", "ALBUMSORT" },
+  { "TSOP", "ARTISTSORT" },
+  { "TSOT", "TITLESORT" },
+  { "TSO2", "ALBUMARTISTSORT" }, // non-standard, used by iTunes
+  { "TSRC", "ISRC" },
+  { "TSSE", "ENCODING" },
+  // URL frames
+  { "WCOP", "COPYRIGHTURL" },
+  { "WOAF", "FILEWEBPAGE" },
+  { "WOAR", "ARTISTWEBPAGE" },
+  { "WOAS", "AUDIOSOURCEWEBPAGE" },
+  { "WORS", "RADIOSTATIONWEBPAGE" },
+  { "WPAY", "PAYMENTWEBPAGE" },
+  { "WPUB", "PUBLISHERWEBPAGE" },
+  { "WXXX", "URL"},
+  // Other frames
+  { "COMM", "COMMENT" },
+  { "USLT", "LYRICS" },
+};
+
+Map<ByteVector, String> &idMap()
+{
+  static Map<ByteVector, String> m;
+    if(m.isEmpty())
+      for(size_t i = 0; i < frameTranslationSize; ++i)
+        m[frameTranslation[i][0]] = frameTranslation[i][1];
+  return m;
+}
+
+// list of deprecated frames and their successors
+static const uint deprecatedFramesSize = 4;
+static const char *deprecatedFrames[][2] = {
+  {"TRDA", "TDRC"}, // 2.3 -> 2.4 (http://en.wikipedia.org/wiki/ID3)
+  {"TDAT", "TDRC"}, // 2.3 -> 2.4
+  {"TYER", "TDRC"}, // 2.3 -> 2.4
+  {"TIME", "TDRC"}, // 2.3 -> 2.4
+};
+
+FrameIDMap &deprecationMap()
+{
+  static FrameIDMap depMap;
+  if(depMap.isEmpty())
+    for(uint i = 0; i < deprecatedFramesSize; ++i)
+      depMap[deprecatedFrames[i][0]] = deprecatedFrames[i][1];
+  return depMap;
+}
+
+String Frame::frameIDToKey(const ByteVector &id)
+{
+  Map<ByteVector, String> &m = idMap();
+  if(m.contains(id))
+    return m[id];
+  if(deprecationMap().contains(id))
+    return m[deprecationMap()[id]];
+  return String::null;
+}
+
+ByteVector Frame::keyToFrameID(const String &s)
+{
+  static Map<String, ByteVector> m;
+  if(m.isEmpty())
+    for(size_t i = 0; i < frameTranslationSize; ++i)
+      m[frameTranslation[i][1]] = frameTranslation[i][0];
+  if(m.contains(s.upper()))
+    return m[s];
+  return ByteVector::null;
+}
+
+PropertyMap Frame::asProperties() const
+{
+  const ByteVector &id = frameID();
+  // workaround until this function is virtual
+  if(id == "TXXX")
+    return dynamic_cast< const UserTextIdentificationFrame* >(this)->asProperties();
+  else if(id[0] == 'T')
+    return dynamic_cast< const TextIdentificationFrame* >(this)->asProperties();
+  else if(id == "WXXX")
+    return dynamic_cast< const UserUrlLinkFrame* >(this)->asProperties();
+  else if(id[0] == 'W')
+    return dynamic_cast< const UrlLinkFrame* >(this)->asProperties();
+  else if(id == "COMM")
+    return dynamic_cast< const CommentsFrame* >(this)->asProperties();
+  else if(id == "USLT")
+    return dynamic_cast< const UnsynchronizedLyricsFrame* >(this)->asProperties();
+  else {
+    PropertyMap m;
+    m.unsupportedData().append(id);
+    return m;
+  }
+}
 ////////////////////////////////////////////////////////////////////////////////
 // Frame::Header class
 ////////////////////////////////////////////////////////////////////////////////
index 2b6bcd88869e74c6c370ac992ec34f31b8fe3b5d..8901295a01095a3b7734114067925634331cb109 100644 (file)
@@ -222,6 +222,27 @@ namespace TagLib {
       String::Type checkTextEncoding(const StringList &fields,
                                      String::Type encoding) const;
 
+
+      /*!
+       * Parses the contents of this frame as PropertyMap. If that fails, the returend
+       * PropertyMap will be empty, and its unsupportedData() will contain this frame's
+       * ID.
+       * BIC: Will be a virtual function in future releases.
+       */
+      PropertyMap asProperties() const;
+
+      /*!
+       * Returns an appropriate ID3 frame ID for the given free-form tag key. This method
+       * will return ByteVector::null if no specialized translation is found.
+       */
+      static ByteVector keyToFrameID(const String &);
+
+      /*!
+       * Returns a free-form tag name for the given ID3 frame ID. Note that this does not work
+       * for general frame IDs such as TXXX or WXXX; in such a case String::null is returned.
+       */
+      static String frameIDToKey(const ByteVector &);
+
     private:
       Frame(const Frame &);
       Frame &operator=(const Frame &);
index dd3e97a7737a9236367732386d63d0b6b646d356..397fe07f13c631ac58c38b2f19a31d7b1a2242b5 100644 (file)
 #include "id3v2extendedheader.h"
 #include "id3v2footer.h"
 #include "id3v2synchdata.h"
-#include "id3v2dicttools.h"
 #include "tbytevector.h"
 #include "id3v1genres.h"
 
-#include "frames/textidentificationframe.h"
-#include "frames/commentsframe.h"
-#include "frames/urllinkframe.h"
-#include "frames/uniquefileidentifierframe.h"
-#include "frames/unsynchronizedlyricsframe.h"
-#include "frames/unknownframe.h"
-
 using namespace TagLib;
 using namespace ID3v2;
 
@@ -334,25 +326,19 @@ void ID3v2::Tag::removeFrames(const ByteVector &id)
     removeFrame(*it, true);
 }
 
-TagDict ID3v2::Tag::toDict(StringList *ignoreInfo) const
+PropertyMap ID3v2::Tag::properties() const
 {
-  TagDict dict;
-  FrameList::ConstIterator frameIt = frameList().begin();
-  for (; frameIt != frameList().end(); ++frameIt) {
-    ByteVector id = (*frameIt)->frameID();
-
+  PropertyMap properties;
+  for(FrameList::ConstIterator it = frameList().begin(); it != frameList().end(); ++it) {
+    ByteVector id = (*it)->frameID();
     if (ignored(id)) {
       debug("toDict() found ignored id3 frame: " + id);
-      if (ignoreInfo)
-       ignoreInfo->append(String(id) + " frame is not supported");
     } else if (deprecated(id)) {
       debug("toDict() found deprecated id3 frame: " + id);
-      if (ignoreInfo)
-        ignoreInfo->append(String(id) + " frame is deprecated");
     } else {
         // in the future, something like dict[frame->tagName()].append(frame->values())
         // might replace the following lines.
-        KeyValuePair kvp = parseFrame(*frameIt);
+        KeyValuePair kvp = parseFrame(*it);
         dict[kvp.first].append(kvp.second);
     }
   }
index 33d9a33cc1780880d952c28e21dc0724682a8b63..56c055a3b2c777e412344614662e3c3bac1beba0 100644 (file)
@@ -261,22 +261,26 @@ namespace TagLib {
       void removeFrames(const ByteVector &id);
 
       /*!
-       * Implements the unified tag dictionary interface -- export function.
+       * Implements the unified property interface -- export function.
        * This function does some work to translate the hard-specified ID3v2
-       * frame types into a free-form string-to-stringlist dictionary.
+       * frame types into a free-form string-to-stringlist PropertyMap.
        *
-       * If the optional pointer to a StringList is given, that list will
-       * be filled with a descriptive text for each ID3v2 frame that could
-       * not be incorporated into the dict interface (binary data, unsupported
-       * frames, ...).
        */
-      TagDict toDict(StringList *ignoredInfo = 0) const;
+      PropertyMap properties() const;
 
       /*!
-       * Implements the unified tag dictionary interface -- import function.
-       * See the comments in toDict().
+       * Removes unsupported frames given by \a properties. The elements of
+       * \a properties must be taken from properties().unsupportedData() and
+       * are the four-byte frame IDs of ID3 frames which are not compatible
+       * with the PropertyMap schema.
        */
-      void fromDict(const TagDict &);
+      void removeUnsupportedProperties(const StringList &properties);
+
+      /*!
+       * Implements the unified property interface -- import function.
+       * See the comments in properties().
+       */
+      PropertyMap setProperties(const PropertyMap &);
 
       /*!
        * Render the tag back to binary data, suitable to be written to disk.
index 645409fa9b0019777d8f1dc6e4c050ea1898e1a6..3a7be29e0c7d0505abc9d857b413cc2b11f5a405 100644 (file)
@@ -133,29 +133,38 @@ TagLib::Tag *MPEG::File::tag() const
   return &d->tag;
 }
 
-TagLib::TagDict MPEG::File::toDict(void) const
+PropertyMap MPEG::File::properties() const
 {
-  // once Tag::toDict() is virtual, this case distinction could actually be done
+  // once Tag::properties() is virtual, this case distinction could actually be done
   // within TagUnion.
-  if (d->hasID3v2)
-    return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->toDict();
-  if (d->hasAPE)
-    return d->tag.access<APE::Tag>(APEIndex, false)->toDict();
-  if (d->hasID3v1)
-    return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->toDict();
-  return TagLib::TagDict();
+  if(d->hasID3v2)
+    return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->properties();
+  if(d->hasAPE)
+    return d->tag.access<APE::Tag>(APEIndex, false)->properties();
+  if(d->hasID3v1)
+    return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->properties();
+  return PropertyMap();
 }
 
-void MPEG::File::fromDict(const TagDict &dict)
+void MPEG::File::removeUnsupportedProperties(const StringList &properties)
 {
-  if (d->hasID3v2)
-    d->tag.access<ID3v2::Tag>(ID3v2Index, false)->fromDict(dict);
-  else if (d->hasAPE)
-    d->tag.access<APE::Tag>(APEIndex, false)->fromDict(dict);
-  else if (d->hasID3v1)
-    d->tag.access<ID3v1::Tag>(ID3v1Index, false)->fromDict(dict);
+  if(d->hasID3v2)
+    d->tag.access<ID3v2::Tag>(ID3v2Index, false)->removeUnsupportedProperties(properties);
+  else if(d->hasAPE)
+    d->tag.access<APE::Tag>(APEIndex, false)->removeUnsupportedProperties(properties);
+  else if(d->hasID3v1)
+    d->tag.access<ID3v1::Tag>(ID3v1Index, false)->removeUnsupportedProperties(properties);
+}
+PropertyMap MPEG::File::setProperties(const PropertyMap &properties)
+{
+  if(d->hasID3v2)
+    return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->setProperties(properties);
+  else if(d->hasAPE)
+    return d->tag.access<APE::Tag>(APEIndex, false)->setProperties(properties);
+  else if(d->hasID3v1)
+    return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->setProperties(properties);
   else
-    d->tag.access<ID3v2::Tag>(ID3v2Index, true)->fromDict(dict);
+    return d->tag.access<ID3v2::Tag>(ID3v2Index, true)->setProperties(properties);
 }
 
 MPEG::Properties *MPEG::File::audioProperties() const
index 75da7b0a6a545548388ec41abcf26a2e5296506b..dbd0e017a089eb885f47f892147723feff9839c0 100644 (file)
@@ -130,19 +130,21 @@ namespace TagLib {
       virtual Tag *tag() const;
 
       /*!
-       * Implements the unified tag dictionary interface -- export function.
-       * If the file contains more than one tag (e.g. ID3v2 and v1), only the
+       * Implements the unified property interface -- export function.
+       * If the file contains more than one tag, only the
        * first one (in the order ID3v2, APE, ID3v1) will be converted to the
-       * TagDict.
+       * PropertyMap.
        */
-      TagDict toDict() const;
+      PropertyMap properties() const;
+
+      void removeUnsupportedProperties(const StringList &properties);
 
       /*!
        * Implements the unified tag dictionary interface -- import function.
        * As with the export, only one tag is taken into account. If the file
        * has no tag at all, ID3v2 will be created.
        */
-      void fromDict(const TagDict &);
+      PropertyMap setProperties(const PropertyMap &);
 
       /*!
        * Returns the MPEG::Properties for this file.  If no audio properties
index 2e043a3393f5b408d0ca7694aa5ad6a50f8a44f8..66fb38cb3b575d77374fbc20cb7d1cdbe56b6b36 100644 (file)
 
 using namespace TagLib;
 
-typedef Map<String,StringList> supertype;
 
-PropertyMap::PropertyMap() : Map<String,StringList>()
+PropertyMap::PropertyMap() : SimplePropertyMap()
 {
 }
 
-PropertyMap::PropertyMap(const PropertyMap &m) : Map<String,StringList>(m)
+PropertyMap::PropertyMap(const PropertyMap &m) : SimplePropertyMap(m)
 {
 }
 
@@ -43,11 +42,11 @@ bool PropertyMap::insert(const String &key, const StringList &values)
   if(realKey.isNull())
     return false;
 
-  Iterator result = supertype::find(realKey);
+  Iterator result = SimplePropertyMap::find(realKey);
   if(result == end())
-    supertype::insert(realKey, values);
+    SimplePropertyMap::insert(realKey, values);
   else
-    supertype::operator[](realKey).append(values);
+    SimplePropertyMap::operator[](realKey).append(values);
   return true;
 }
 
@@ -56,8 +55,8 @@ 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);
+  SimplePropertyMap::erase(realKey);
+  SimplePropertyMap::insert(realKey, values);
   return true;
 }
 
@@ -66,7 +65,7 @@ PropertyMap::Iterator PropertyMap::find(const String &key)
   String realKey = prepareKey(key);
   if(realKey.isNull())
     return end();
-  return supertype::find(realKey);
+  return SimplePropertyMap::find(realKey);
 }
 
 PropertyMap::ConstIterator PropertyMap::find(const String &key) const
@@ -74,40 +73,46 @@ PropertyMap::ConstIterator PropertyMap::find(const String &key) const
   String realKey = prepareKey(key);
   if(realKey.isNull())
     return end();
-  return supertype::find(realKey);
+  return SimplePropertyMap::find(realKey);
 }
 
 bool PropertyMap::contains(const String &key) const
 {
   String realKey = prepareKey(key);
   // we consider keys with empty value list as not present
-  if(realKey.isNull() || supertype::operator[](realKey).isEmpty())
+  if(realKey.isNull() || SimplePropertyMap::operator[](realKey).isEmpty())
     return false;
-  return supertype::contains(realKey);
+  return SimplePropertyMap::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())
+  if(realKey.isNull())
     return *this;
-  supertype::erase(realKey);
+  SimplePropertyMap::erase(realKey);
+  return *this;
+}
+
+PropertyMap &PropertyMap::merge(const PropertyMap &other)
+{
+  for(PropertyMap::ConstIterator it = other.begin(); it != other.end(); ++it) {
+    insert(it->first, it->second);
+  }
+  unsupported.append(other.unsupported);
   return *this;
 }
 
 const StringList &PropertyMap::operator[](const String &key) const
 {
   String realKey = prepareKey(key);
-  return supertype::operator[](realKey);
+  return SimplePropertyMap::operator[](realKey);
 }
 
 StringList &PropertyMap::operator[](const String &key)
 {
   String realKey = prepareKey(key);
-  return supertype::operator[](realKey);
+  return SimplePropertyMap::operator[](realKey);
 }
 
 void PropertyMap::removeEmpty()
@@ -125,6 +130,11 @@ StringList &PropertyMap::unsupportedData()
   return unsupported;
 }
 
+const StringList &PropertyMap::unsupportedData() const
+{
+  return unsupported;
+}
+
 static String PropertyMap::prepareKey(const String &proposed) {
   if(proposed.isEmpty())
     return String::null;
index 4f7bf555117b2e0c717f42e97329def6e123c0ee..e41c190bb90124dab87026a72e8e63267746b200 100644 (file)
@@ -27,6 +27,7 @@
 
 namespace TagLib {
 
+  typedef Map<String,StringList> SimplePropertyMap;
 
   //! A map for format-independent <key,valuelist> tag representations.
 
@@ -46,12 +47,12 @@ namespace TagLib {
    *
    */
 
-  class TAGLIB_EXPORT PropertyMap: public Map<String,StringList>
+  class TAGLIB_EXPORT PropertyMap: public SimplePropertyMap
   {
   public:
 
-    typedef Map<String,StringList>::Iterator Iterator;
-    typedef Map<String,StringList>::ConstIterator ConstIterator;
+    typedef SimplePropertyMap::Iterator Iterator;
+    typedef SimplePropertyMap::ConstIterator ConstIterator;
 
     PropertyMap();
 
@@ -95,6 +96,14 @@ namespace TagLib {
      */
     PropertyMap &erase(const String &key);
 
+    /*!
+     * Merge the contents of \a other into this PropertyMap.
+     * If a key is contained in both maps, the values of the second
+     * are appended to that of the first.
+     * The unsupportedData() lists are concatenated as well.
+     */
+    PropertyMap &merge(const PropertyMap &other);
+
     /*!
      * Returns a reference to the value associated with \a key.
      *
@@ -121,6 +130,7 @@ namespace TagLib {
      * same PropertyMap as argument.
      */
     StringList &unsupportedData();
+    const StringList &unsupportedData() const;
 
     /*!
      * Removes all entries which have an empty value list.