mirror of
https://github.com/taglib/taglib.git
synced 2025-05-27 21:20:26 -04:00
Migration to new PropertyMap ... done ape to mod.
This commit is contained in:
parent
18ae797df4
commit
e4d955d6ef
@ -109,23 +109,31 @@ TagLib::Tag *APE::File::tag() const
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
TagLib::TagDict APE::File::toDict(void) const
|
||||
PropertyMap APE::File::properties() const
|
||||
{
|
||||
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 APE::File::fromDict(const TagDict &dict)
|
||||
void APE::File::removeUnsupportedProperties(const StringList &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)
|
||||
d->tag.access<APE::Tag>(APEIndex, false)->removeUnsupportedProperties(properties);
|
||||
if(d->hasID3v1)
|
||||
d->tag.access<ID3v1::Tag>(ID3v1Index, false)->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap APE::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
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>(APE, true)->fromDict(dict);
|
||||
return d->tag.access<APE::Tag>(APE, true)->setProperties(properties);
|
||||
}
|
||||
|
||||
APE::Properties *APE::File::audioProperties() const
|
||||
|
@ -111,18 +111,24 @@ 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 APE
|
||||
* will be converted to the TagDict.
|
||||
* will be converted to the PropertyMap.
|
||||
*/
|
||||
TagDict toDict() const;
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- import function.
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* As for the export, only one tag is taken into account. If the file
|
||||
* has no tag at all, APE will be created.
|
||||
*/
|
||||
void fromDict(const TagDict &);
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
/*!
|
||||
* Returns the APE::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
|
@ -174,61 +174,73 @@ void APE::Tag::setTrack(uint i)
|
||||
addValue("TRACK", String::number(i), true);
|
||||
}
|
||||
|
||||
TagDict APE::Tag::toDict() const
|
||||
// conversions of tag keys between what we use in PropertyMap and what's usual
|
||||
// for APE tags
|
||||
static const uint keyConversionsSize = 5; //usual, APE
|
||||
static const char *keyConversions[][2] = {{"TRACKNUMBER", "TRACK" },
|
||||
{"DATE", "YEAR" },
|
||||
{"ALBUMARTIST", "ALBUM ARTIST"},
|
||||
{"DISCNUMBER", "DISC" },
|
||||
{"REMIXER", "MIXARTIST" }};
|
||||
|
||||
PropertyMap APE::Tag::properties() const
|
||||
{
|
||||
TagDict dict;
|
||||
PropertyMap properties;
|
||||
ItemListMap::ConstIterator it = itemListMap().begin();
|
||||
for (; it != itemListMap().end(); ++it) {
|
||||
String tagName = it->first.upper();
|
||||
// These two tags need to be handled specially; in APE tags the track number is usually
|
||||
// named TRACK instead of TRACKNUMBER, the date tag is YEAR instead of DATE
|
||||
//
|
||||
if (tagName == "TRACK")
|
||||
tagName = "TRACKNUMBER";
|
||||
else if (tagName == "YEAR")
|
||||
tagName = "DATE";
|
||||
else if (tagName == "ALBUM ARTIST")
|
||||
tagName = "ALBUMARTIST";
|
||||
if (it->second.type() == Item::Text)
|
||||
dict[tagName].append(it->second.toStringList());
|
||||
for(; it != itemListMap().end(); ++it) {
|
||||
String tagName = PropertyMap::prepareKey(it->first);
|
||||
// if the item is Binary or Locator, or if the key is an invalid string,
|
||||
// add to unsupportedData
|
||||
if(it->second.type() != Item::Text || tagName.isNull())
|
||||
properties.unsupportedData().append(it->first);
|
||||
else {
|
||||
// Some tags need to be handled specially
|
||||
for(uint i = 0; i < keyConversionsSize; ++i)
|
||||
if(tagName == keyConversions[i][1])
|
||||
tagName = keyConversions[i][0];
|
||||
properties[tagName].append(it->second.toStringList());
|
||||
}
|
||||
}
|
||||
return dict;
|
||||
return properties;
|
||||
}
|
||||
|
||||
void APE::Tag::fromDict(const TagDict &origDict)
|
||||
void APE::Tag::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
TagDict dict(origDict); // make a local copy that can be modified
|
||||
StringList::ConstIterator it = properties.begin();
|
||||
for(; it != properties.end(); ++it)
|
||||
removeItem(*it);
|
||||
}
|
||||
|
||||
// see comment in toDict() about TRACKNUMBER and YEAR
|
||||
if (dict.contains("TRACKNUMBER")) {
|
||||
dict.insert("TRACK", dict["TRACKNUMBER"]);
|
||||
dict.erase("TRACKNUMBER");
|
||||
}
|
||||
if (dict.contains("DATE")) {
|
||||
dict.insert("YEAR", dict["DATE"]);
|
||||
dict.erase("DATE");
|
||||
}
|
||||
PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
|
||||
{
|
||||
PropertyMap properties(origProps); // make a local copy that can be modified
|
||||
|
||||
// see comment in properties()
|
||||
for(uint i = 0; i < keyConversionsSize; ++i)
|
||||
if(properties.contains(keyConversions[i][0])) {
|
||||
properties.insert(keyConversions[i][1], properties[keyConversions[i][0]]);
|
||||
properties.erase(keyConversions[i][0]);
|
||||
}
|
||||
|
||||
// first check if tags need to be removed completely
|
||||
StringList toRemove;
|
||||
ItemListMap::ConstIterator remIt = itemListMap().begin();
|
||||
for (; remIt != itemListMap().end(); ++remIt) {
|
||||
if (remIt->second.type() != APE::Item::Text)
|
||||
// ignore binary and locator APE items
|
||||
continue;
|
||||
if (!dict.contains(remIt->first.upper()))
|
||||
for(; remIt != itemListMap().end(); ++remIt) {
|
||||
String key = PropertyMap::prepareKey(remIt->first);
|
||||
// only remove if a) key is valid, b) type is text, c) key not contained in new properties
|
||||
if(!key.isNull() && remIt->second.type() == APE::Item::Text && !properties.contains(key))
|
||||
toRemove.append(remIt->first);
|
||||
}
|
||||
|
||||
for (StringList::Iterator removeIt = toRemove.begin(); removeIt != toRemove.end(); removeIt++)
|
||||
removeItem(*removeIt);
|
||||
|
||||
// now sync in the "forward direction
|
||||
TagDict::ConstIterator it = dict.begin();
|
||||
for (; it != dict.end(); ++it) {
|
||||
// now sync in the "forward direction"
|
||||
PropertyMap::ConstIterator it = properties.begin();
|
||||
for(; it != properties.end(); ++it) {
|
||||
const String &tagName = it->first;
|
||||
if (!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) {
|
||||
if (it->second.size() == 0)
|
||||
if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) {
|
||||
if(it->second.size() == 0)
|
||||
removeItem(tagName);
|
||||
else {
|
||||
StringList::ConstIterator valueIt = it->second.begin();
|
||||
@ -239,6 +251,7 @@ void APE::Tag::fromDict(const TagDict &origDict)
|
||||
}
|
||||
}
|
||||
}
|
||||
return PropertyMap;
|
||||
}
|
||||
|
||||
APE::Footer *APE::Tag::footer() const
|
||||
|
@ -107,20 +107,25 @@ namespace TagLib {
|
||||
* Implements the unified tag dictionary interface -- export function.
|
||||
* APE tags are perfectly compatible with the dictionary interface because they
|
||||
* support both arbitrary tag names and multiple values. Currently only
|
||||
* APE items of type *Text* are handled by the dictionary interface, while
|
||||
* *Binary* and *Locator* items are simply ignored.
|
||||
* APE items of type *Text* are handled by the dictionary interface; all *Binary*
|
||||
* and *Locator* items will be put into the unsupportedData list and can be
|
||||
* deleted on request using removeUnsupportedProperties(). The same happens
|
||||
* to Text items if their key is invalid for PropertyMap (which should actually
|
||||
* never happen).
|
||||
*
|
||||
* The only conversion done by this export function is to rename the APE tags
|
||||
* TRACK to TRACKNUMBER and YEAR to DATE, respectively, in order to be compliant
|
||||
* with the names used in other formats.
|
||||
* TRACK to TRACKNUMBER, YEAR to DATE, and ALBUM ARTIST to ALBUMARTIST, respectively,
|
||||
* in order to be compliant with the names used in other formats.
|
||||
*/
|
||||
TagDict toDict() const;
|
||||
PropertyMap properties() const;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- import function. The same
|
||||
* comments as for the export function apply.
|
||||
*/
|
||||
void fromDict(const TagDict &);
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the tag's footer.
|
||||
|
@ -138,29 +138,39 @@ TagLib::Tag *FLAC::File::tag() const
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
TagLib::TagDict FLAC::File::toDict(void) const
|
||||
PropertyMap FLAC::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->hasXiphComment)
|
||||
return d->tag.access<Ogg::XiphComment>(XiphIndex, false)->toDict();
|
||||
if (d->hasID3v2)
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->toDict();
|
||||
if (d->hasID3v1)
|
||||
return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->toDict();
|
||||
return TagLib::TagDict();
|
||||
if(d->hasXiphComment)
|
||||
return d->tag.access<Ogg::XiphComment>(XiphIndex, false)->properties();
|
||||
if(d->hasID3v2)
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->properties();
|
||||
if(d->hasID3v1)
|
||||
return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->properties();
|
||||
return PropertyMap();
|
||||
}
|
||||
|
||||
void FLAC::File::fromDict(const TagDict &dict)
|
||||
void FLAC::File::removeUnsupportedProperties(const StringList &unsupported)
|
||||
{
|
||||
if (d->hasXiphComment)
|
||||
d->tag.access<Ogg::XiphComment>(XiphIndex, false)->fromDict(dict);
|
||||
else if (d->hasID3v2)
|
||||
d->tag.access<ID3v2::Tag>(ID3v2Index, false)->fromDict(dict);
|
||||
else if (d->hasID3v1)
|
||||
d->tag.access<ID3v1::Tag>(ID3v1Index, false)->fromDict(dict);
|
||||
if(d->hasXiphComment)
|
||||
d->tag.access<Ogg::XiphComment>(XiphIndex, false)->removeUnsupportedProperties(unsupported);
|
||||
if(d->hasID3v2)
|
||||
d->tag.access<ID3v2::Tag>(ID3v2Index, false)->removeUnsupportedProperties(unsupported);
|
||||
if(d->hasID3v1)
|
||||
d->tag.access<ID3v1::Tag>(ID3v1Index, false)->removeUnsupportedProperties(unsupported);
|
||||
}
|
||||
|
||||
PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(d->hasXiphComment)
|
||||
return d->tag.access<Ogg::XiphComment>(XiphIndex, false)->setProperties(properties);
|
||||
else if(d->hasID3v2)
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->setProperties(properties);
|
||||
else if(d->hasID3v1)
|
||||
return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->setProperties(properties);
|
||||
else
|
||||
d->tag.access<Ogg::XiphComment>(XiphIndex, true)->fromDict(dict);
|
||||
return d->tag.access<Ogg::XiphComment>(XiphIndex, true)->setProperties(properties);
|
||||
}
|
||||
|
||||
FLAC::Properties *FLAC::File::audioProperties() const
|
||||
|
@ -119,19 +119,21 @@ 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 more than one tag (e.g. XiphComment and ID3v1),
|
||||
* only the first one (in the order XiphComment, ID3v2, ID3v1) will be
|
||||
* converted to the TagDict.
|
||||
* converted to the PropertyMap.
|
||||
*/
|
||||
TagDict toDict() const;
|
||||
PropertyMap properties() const;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &);
|
||||
|
||||
/*!
|
||||
* 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, a XiphComment will be created.
|
||||
*/
|
||||
void fromDict(const TagDict &);
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the FLAC::Properties for this file. If no audio properties
|
||||
|
@ -65,14 +65,14 @@ Mod::Tag *IT::File::tag() const
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
TagDict IT::File::toDict() const
|
||||
PropertyMap IT::File::properties() const
|
||||
{
|
||||
return d->tag.toDict();
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
void IT::File::fromDict(const TagDict &tagDict)
|
||||
PropertyMap IT::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
d->tag.fromDict(tagDict);
|
||||
return d->tag.setProperties(properties);
|
||||
}
|
||||
|
||||
IT::Properties *IT::File::audioProperties() const
|
||||
|
@ -61,16 +61,16 @@ namespace TagLib {
|
||||
Mod::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Forwards to Mod::Tag::toDict().
|
||||
* Forwards to Mod::Tag::properties().
|
||||
* BIC: will be removed once File::toDict() is made virtual
|
||||
*/
|
||||
TagDict toDict() const;
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Forwards to Mod::Tag::fromDict().
|
||||
* BIC: will be removed once File::fromDict() is made virtual
|
||||
* Forwards to Mod::Tag::setProperties().
|
||||
* BIC: will be removed once File::setProperties() is made virtual
|
||||
*/
|
||||
void fromDict(const TagDict &);
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the IT::Properties for this file. If no audio properties
|
||||
|
@ -70,14 +70,14 @@ Mod::Properties *Mod::File::audioProperties() const
|
||||
return &d->properties;
|
||||
}
|
||||
|
||||
TagDict Mod::File::toDict() const
|
||||
PropertyMap Mod::File::properties() const
|
||||
{
|
||||
return d->tag.toDict();
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
void Mod::File::fromDict(const TagDict &tagDict)
|
||||
PropertyMap Mod::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
d->tag.fromDict(tagDict);
|
||||
return d->tag.setProperties(properties);
|
||||
}
|
||||
|
||||
bool Mod::File::save()
|
||||
|
@ -62,16 +62,16 @@ namespace TagLib {
|
||||
Mod::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- export function.
|
||||
* Forwards to Mod::Tag::toDict().
|
||||
* Implements the unified property interface -- export function.
|
||||
* Forwards to Mod::Tag::properties().
|
||||
*/
|
||||
TagDict toDict() const;
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- import function.
|
||||
* Forwards to Mod::Tag::fromDict().
|
||||
* Implements the unified property interface -- import function.
|
||||
* Forwards to Mod::Tag::setProperties().
|
||||
*/
|
||||
void fromDict(const TagDict &);
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
/*!
|
||||
* Returns the Mod::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
|
@ -121,30 +121,46 @@ void Mod::Tag::setTrackerName(const String &trackerName)
|
||||
d->trackerName = trackerName;
|
||||
}
|
||||
|
||||
TagDict Mod::Tag::toDict() const
|
||||
PropertyMap Mod::Tag::properties() const
|
||||
{
|
||||
TagDict dict;
|
||||
dict["TITLE"] = d->title;
|
||||
dict["COMMENT"] = d->comment;
|
||||
if (!(d->trackerName == String::null))
|
||||
dict["TRACKERNAME"] = d->trackerName;
|
||||
return dict;
|
||||
PropertyMap properties;
|
||||
properties["TITLE"] = d->title;
|
||||
properties["COMMENT"] = d->comment;
|
||||
if(!(d->trackerName.isNull()))
|
||||
properties["TRACKERNAME"] = d->trackerName;
|
||||
return properties;
|
||||
}
|
||||
|
||||
void Mod::Tag::fromDict(const TagDict &tagDict)
|
||||
PropertyMap Mod::Tag::setProperties(const PropertyMap &origProps)
|
||||
{
|
||||
if (tagDict.contains("TITLE") && !tagDict["TITILE"].isEmpty())
|
||||
d->title = tagDict["TITLE"][0];
|
||||
else
|
||||
PropertyMap properties(origProps);
|
||||
properties.removeEmpty();
|
||||
StringList oneValueSet;
|
||||
if(properties.contains("TITLE")) {
|
||||
d->title = properties["TITLE"].front();
|
||||
oneValueSet.append("TITLE");
|
||||
} else
|
||||
d->title = String::null;
|
||||
|
||||
if (tagDict.contains("COMMENT") && !tagDict["COMMENT"].isEmpty())
|
||||
d->comment = tagDict["COMMENT"][0];
|
||||
else
|
||||
if(properties.contains("COMMENT")) {
|
||||
d->comment = properties["COMMENT"].front();
|
||||
oneValueSet.append("COMMENT");
|
||||
} else
|
||||
d->comment = String::null;
|
||||
|
||||
if (tagDict.contains("TRACKERNAME") && !tagDict["TRACKERNAME"].isEmpty())
|
||||
d->trackerName = tagDict["TRACKERNAME"][0];
|
||||
else
|
||||
if(properties.contains("TRACKERNAME")) {
|
||||
d->trackerName = properties["TRACKERNAME"].front();
|
||||
oneValueSet.append("TRACKERNAME");
|
||||
} else
|
||||
d->trackerName = String::null;
|
||||
|
||||
// for each tag that has been set above, remove the first entry in the corresponding
|
||||
// value list. The others will be returned as unsupported by this format.
|
||||
for(StringList::Iterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) {
|
||||
if(properties[*it].size() == 1)
|
||||
properties.erase(*it);
|
||||
else
|
||||
properties[*it].erase( properties[*it].begin() );
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
@ -160,19 +160,20 @@ namespace TagLib {
|
||||
void setTrackerName(const String &trackerName);
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- export function.
|
||||
* Since the module tag is very limited, the exported dict is as well.
|
||||
* Implements the unified property interface -- export function.
|
||||
* Since the module tag is very limited, the exported map is as well.
|
||||
*/
|
||||
TagDict toDict() const;
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- import function.
|
||||
* Implements the unified property interface -- import function.
|
||||
* Because of the limitations of the module file tag, any tags besides
|
||||
* COMMENT, TITLE and, if it is an XM file, TRACKERNAME, will be
|
||||
* ignored. Additionally, if the dict contains tags with multiple values,
|
||||
* all but the first will be ignored.
|
||||
* returened. Additionally, if the map contains tags with multiple values,
|
||||
* all but the first will be contained in the returned map of unsupported
|
||||
* properties.
|
||||
*/
|
||||
void fromDict(const TagDict &);
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
private:
|
||||
Tag(const Tag &);
|
||||
|
110
taglib/tag.cpp
110
taglib/tag.cpp
@ -53,75 +53,101 @@ bool Tag::isEmpty() const
|
||||
track() == 0);
|
||||
}
|
||||
|
||||
TagDict Tag::toDict() const
|
||||
PropertyMap Tag::properties() const
|
||||
{
|
||||
TagDict dict;
|
||||
if (!(title() == String::null))
|
||||
dict["TITLE"].append(title());
|
||||
if (!(artist() == String::null))
|
||||
dict["ARTIST"].append(artist());
|
||||
if (!(album() == String::null))
|
||||
dict["ALBUM"].append(album());
|
||||
if (!(comment() == String::null))
|
||||
dict["COMMENT"].append(comment());
|
||||
if (!(genre() == String::null))
|
||||
dict["GENRE"].append(genre());
|
||||
if (!(year() == 0))
|
||||
dict["DATE"].append(String::number(year()));
|
||||
if (!(track() == 0))
|
||||
dict["TRACKNUMBER"].append(String::number(track()));
|
||||
return dict;
|
||||
PropertyMap map;
|
||||
if(!(title().isNull()))
|
||||
map["TITLE"].append(title());
|
||||
if(!(artist().isNull()))
|
||||
map["ARTIST"].append(artist());
|
||||
if(!(album().isNull()))
|
||||
map["ALBUM"].append(album());
|
||||
if(!(comment().isNull()))
|
||||
map["COMMENT"].append(comment());
|
||||
if(!(genre().isNull()))
|
||||
map["GENRE"].append(genre());
|
||||
if(!(year() == 0))
|
||||
map["DATE"].append(String::number(year()));
|
||||
if(!(track() == 0))
|
||||
map["TRACKNUMBER"].append(String::number(track()));
|
||||
return map;
|
||||
}
|
||||
|
||||
void Tag::fromDict(const TagDict &dict)
|
||||
void Tag::removeUnsupportedProperties(const StringList&)
|
||||
{
|
||||
if (dict.contains("TITLE") && dict["TITLE"].size() >= 1)
|
||||
setTitle(dict["TITLE"].front());
|
||||
else
|
||||
}
|
||||
|
||||
PropertyMap Tag::setProperties(const PropertyMap &origProps)
|
||||
{
|
||||
PropertyMap properties(origProps);
|
||||
properties.removeEmpty();
|
||||
StringList oneValueSet;
|
||||
// can this be simplified by using some preprocessor defines / function pointers?
|
||||
if(properties.contains("TITLE")) {
|
||||
setTitle(properties["TITLE"].front());
|
||||
oneValueSet.append("TITLE");
|
||||
} else
|
||||
setTitle(String::null);
|
||||
|
||||
if (dict.contains("ARTIST") && !dict["ARTIST"].isEmpty())
|
||||
setArtist(dict["ARTIST"].front());
|
||||
else
|
||||
if(properties.contains("ARTIST")) {
|
||||
setArtist(properties["ARTIST"].front());
|
||||
oneValueSet.append("ARTIST");
|
||||
} else
|
||||
setArtist(String::null);
|
||||
|
||||
if (dict.contains("ALBUM") && !dict["ALBUM"].isEmpty())
|
||||
setAlbum(dict["ALBUM"].front());
|
||||
else
|
||||
setAlbum(String::null);
|
||||
if(properties.contains("ALBUM")) {
|
||||
setAlbum(properties["ALBUM"].front());
|
||||
oneValueSet.append("ALBUM");
|
||||
} else
|
||||
setAlbum(String::null);
|
||||
|
||||
if (dict.contains("COMMENT") && !dict["COMMENT"].isEmpty())
|
||||
setComment(dict["COMMENT"].front());
|
||||
else
|
||||
if(properties.contains("COMMENT")) {
|
||||
setComment(properties["COMMENT"].front());
|
||||
oneValueSet.append("COMMENT");
|
||||
} else
|
||||
setComment(String::null);
|
||||
|
||||
if (dict.contains("GENRE") && !dict["GENRE"].isEmpty())
|
||||
setGenre(dict["GENRE"].front());
|
||||
else
|
||||
if(properties.contains("GENRE")) {
|
||||
setGenre(properties["GENRE"].front());
|
||||
oneValueSet.append("GENRE");
|
||||
} else
|
||||
setGenre(String::null);
|
||||
|
||||
if (dict.contains("DATE") && !dict["DATE"].isEmpty()) {
|
||||
if(properties.contains("DATE")) {
|
||||
bool ok;
|
||||
int date = dict["DATE"].front().toInt(&ok);
|
||||
if (ok)
|
||||
int date = properties["DATE"].front().toInt(&ok);
|
||||
if(ok) {
|
||||
setYear(date);
|
||||
else
|
||||
oneValueSet.append("DATE");
|
||||
} else
|
||||
setYear(0);
|
||||
}
|
||||
else
|
||||
setYear(0);
|
||||
|
||||
if (dict.contains("TRACKNUMBER") && !dict["TRACKNUMBER"].isEmpty()) {
|
||||
if(properties.contains("TRACKNUMBER")) {
|
||||
bool ok;
|
||||
int track = dict["TRACKNUMBER"].front().toInt(&ok);
|
||||
if (ok)
|
||||
int track = properties["TRACKNUMBER"].front().toInt(&ok);
|
||||
if(ok) {
|
||||
setTrack(track);
|
||||
else
|
||||
oneValueSet.append("TRACKNUMBER");
|
||||
} else
|
||||
setTrack(0);
|
||||
}
|
||||
else
|
||||
setYear(0);
|
||||
|
||||
// for each tag that has been set above, remove the first entry in the corresponding
|
||||
// value list. The others will be returned as unsupported by this format.
|
||||
for(StringList::Iterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) {
|
||||
if(properties[*it].size() == 1)
|
||||
properties.erase(*it);
|
||||
else
|
||||
properties[*it].erase( properties[*it].begin() );
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
void Tag::duplicate(const Tag *source, Tag *target, bool overwrite) // static
|
||||
{
|
||||
if(overwrite) {
|
||||
|
40
taglib/tag.h
40
taglib/tag.h
@ -28,17 +28,9 @@
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tstring.h"
|
||||
#include "tmap.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
/*!
|
||||
* This is used for the unified dictionary interface: the tags of a file are
|
||||
* represented as a dictionary mapping a string (the tag name) to a list of
|
||||
* strings (the values).
|
||||
*/
|
||||
typedef Map<String, StringList> TagDict;
|
||||
|
||||
//! A simple, generic interface to common audio meta data fields
|
||||
|
||||
/*!
|
||||
@ -49,6 +41,8 @@ namespace TagLib {
|
||||
* in TagLib::AudioProperties, TagLib::File and TagLib::FileRef.
|
||||
*/
|
||||
|
||||
class PropertyMap;
|
||||
|
||||
class TAGLIB_EXPORT Tag
|
||||
{
|
||||
public:
|
||||
@ -59,20 +53,30 @@ namespace TagLib {
|
||||
virtual ~Tag();
|
||||
|
||||
/*!
|
||||
* Unified tag dictionary interface -- export function. Converts the tags
|
||||
* of the specific metadata format into a "human-readable" map of strings
|
||||
* to lists of strings, being as precise as possible.
|
||||
* Exports the tags of the file as dictionary mapping (human readable) tag
|
||||
* names (Strings) to StringLists of tag values.
|
||||
* The default implementation in this class considers only the usual built-in
|
||||
* tags (artist, album, ...) and only one value per key.
|
||||
*/
|
||||
TagDict toDict() const;
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Unified tag dictionary interface -- import function. Converts a map
|
||||
* of strings to stringslists into the specific metadata format. Note that
|
||||
* not all formats can store arbitrary tags and values, so data might
|
||||
* be lost by this operation. Especially the default implementation handles
|
||||
* only single values of the default tags specified in this class.
|
||||
* Removes unsupported properties, or a subset of them, from the tag.
|
||||
* The parameter \a properties must contain only entries from
|
||||
* properties().unsupportedData().
|
||||
* BIC: Will become virtual in future releases. Currently the non-virtual
|
||||
* standard implementation of TagLib::Tag does nothing, since there are
|
||||
* no unsupported elements.
|
||||
*/
|
||||
void fromDict(const TagDict &);
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
|
||||
/*!
|
||||
* Sets the tags of this File to those specified in \a properties. This default
|
||||
* implementation sets only the tags for which setter methods exist in this class
|
||||
* (artist, album, ...), and only one value per key; the rest will be contained
|
||||
* in the returned PropertyMap.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
/*!
|
||||
* Returns the track name; if no track name is present in the tag
|
||||
|
@ -113,83 +113,116 @@ FileName File::name() const
|
||||
return d->stream->name();
|
||||
}
|
||||
|
||||
TagDict File::toDict() const
|
||||
PropertyMap File::properties() const
|
||||
{
|
||||
// ugly workaround until this method is virtual
|
||||
if (dynamic_cast<const APE::File* >(this))
|
||||
return dynamic_cast<const APE::File* >(this)->toDict();
|
||||
return dynamic_cast<const APE::File* >(this)->properties();
|
||||
if (dynamic_cast<const FLAC::File* >(this))
|
||||
return dynamic_cast<const FLAC::File* >(this)->toDict();
|
||||
return dynamic_cast<const FLAC::File* >(this)->properties();
|
||||
if (dynamic_cast<const IT::File* >(this))
|
||||
return dynamic_cast<const IT::File* >(this)->toDict();
|
||||
return dynamic_cast<const IT::File* >(this)->properties();
|
||||
if (dynamic_cast<const Mod::File* >(this))
|
||||
return dynamic_cast<const Mod::File* >(this)->toDict();
|
||||
return dynamic_cast<const Mod::File* >(this)->properties();
|
||||
if (dynamic_cast<const MPC::File* >(this))
|
||||
return dynamic_cast<const MPC::File* >(this)->toDict();
|
||||
return dynamic_cast<const MPC::File* >(this)->properties();
|
||||
if (dynamic_cast<const MPEG::File* >(this))
|
||||
return dynamic_cast<const MPEG::File* >(this)->toDict();
|
||||
return dynamic_cast<const MPEG::File* >(this)->properties();
|
||||
if (dynamic_cast<const Ogg::FLAC::File* >(this))
|
||||
return dynamic_cast<const Ogg::FLAC::File* >(this)->toDict();
|
||||
return dynamic_cast<const Ogg::FLAC::File* >(this)->properties();
|
||||
if (dynamic_cast<const Ogg::Speex::File* >(this))
|
||||
return dynamic_cast<const Ogg::Speex::File* >(this)->toDict();
|
||||
return dynamic_cast<const Ogg::Speex::File* >(this)->properties();
|
||||
if (dynamic_cast<const Ogg::Vorbis::File* >(this))
|
||||
return dynamic_cast<const Ogg::Vorbis::File* >(this)->toDict();
|
||||
return dynamic_cast<const Ogg::Vorbis::File* >(this)->properties();
|
||||
if (dynamic_cast<const RIFF::AIFF::File* >(this))
|
||||
return dynamic_cast<const RIFF::AIFF::File* >(this)->toDict();
|
||||
return dynamic_cast<const RIFF::AIFF::File* >(this)->properties();
|
||||
if (dynamic_cast<const RIFF::WAV::File* >(this))
|
||||
return dynamic_cast<const RIFF::WAV::File* >(this)->toDict();
|
||||
return dynamic_cast<const RIFF::WAV::File* >(this)->properties();
|
||||
if (dynamic_cast<const S3M::File* >(this))
|
||||
return dynamic_cast<const S3M::File* >(this)->toDict();
|
||||
return dynamic_cast<const S3M::File* >(this)->properties();
|
||||
if (dynamic_cast<const TrueAudio::File* >(this))
|
||||
return dynamic_cast<const TrueAudio::File* >(this)->toDict();
|
||||
return dynamic_cast<const TrueAudio::File* >(this)->properties();
|
||||
if (dynamic_cast<const WavPack::File* >(this))
|
||||
return dynamic_cast<const WavPack::File* >(this)->toDict();
|
||||
return dynamic_cast<const WavPack::File* >(this)->properties();
|
||||
if (dynamic_cast<const XM::File* >(this))
|
||||
return dynamic_cast<const XM::File* >(this)->toDict();
|
||||
return dynamic_cast<const XM::File* >(this)->properties();
|
||||
// no specialized implementation available -> use generic one
|
||||
// - ASF: ugly format, largely undocumented, not worth implementing
|
||||
// dict interface ...
|
||||
// - MP4: taglib's MP4::Tag does not really support anything beyond
|
||||
// the basic implementation, therefor we use just the default Tag
|
||||
// interface
|
||||
return tag()->toDict();
|
||||
return tag()->properties();
|
||||
}
|
||||
|
||||
void File::fromDict(const TagDict &dict)
|
||||
void File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
// here we only consider those formats that could possibly contain
|
||||
// unsupported properties
|
||||
if (dynamic_cast<APE::File* >(this))
|
||||
dynamic_cast<APE::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else if (dynamic_cast<FLAC::File* >(this))
|
||||
dynamic_cast<FLAC::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else if (dynamic_cast<MPC::File* >(this))
|
||||
dynamic_cast<MPC::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else if (dynamic_cast<MPEG::File* >(this))
|
||||
dynamic_cast<MPEG::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else if (dynamic_cast<Ogg::FLAC::File* >(this))
|
||||
dynamic_cast<Ogg::FLAC::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else if (dynamic_cast<Ogg::Speex::File* >(this))
|
||||
dynamic_cast<Ogg::Speex::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else if (dynamic_cast<Ogg::Vorbis::File* >(this))
|
||||
dynamic_cast<Ogg::Vorbis::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else if (dynamic_cast<RIFF::AIFF::File* >(this))
|
||||
dynamic_cast<RIFF::AIFF::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else if (dynamic_cast<RIFF::WAV::File* >(this))
|
||||
dynamic_cast<RIFF::WAV::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else if (dynamic_cast<S3M::File* >(this))
|
||||
dynamic_cast<S3M::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else if (dynamic_cast<TrueAudio::File* >(this))
|
||||
dynamic_cast<TrueAudio::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else if (dynamic_cast<WavPack::File* >(this))
|
||||
dynamic_cast<WavPack::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else if (dynamic_cast<XM::File* >(this))
|
||||
dynamic_cast<XM::File* >(this)->removeUnsupportedProperties(properties);
|
||||
else
|
||||
tag()->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if (dynamic_cast<APE::File* >(this))
|
||||
dynamic_cast<APE::File* >(this)->fromDict(dict);
|
||||
return dynamic_cast<APE::File* >(this)->setProperties(properties);
|
||||
else if (dynamic_cast<FLAC::File* >(this))
|
||||
dynamic_cast<FLAC::File* >(this)->fromDict(dict);
|
||||
return dynamic_cast<FLAC::File* >(this)->setProperties(properties);
|
||||
else if (dynamic_cast<IT::File* >(this))
|
||||
dynamic_cast<IT::File* >(this)->fromDict(dict);
|
||||
return dynamic_cast<IT::File* >(this)->setProperties(properties);
|
||||
else if (dynamic_cast<Mod::File* >(this))
|
||||
dynamic_cast<Mod::File* >(this)->fromDict(dict);
|
||||
return dynamic_cast<Mod::File* >(this)->setProperties(properties);
|
||||
else if (dynamic_cast<MPC::File* >(this))
|
||||
dynamic_cast<MPC::File* >(this)->fromDict(dict);
|
||||
return dynamic_cast<MPC::File* >(this)->setProperties(properties);
|
||||
else if (dynamic_cast<MPEG::File* >(this))
|
||||
dynamic_cast<MPEG::File* >(this)->fromDict(dict);
|
||||
return dynamic_cast<MPEG::File* >(this)->setProperties(properties);
|
||||
else if (dynamic_cast<Ogg::FLAC::File* >(this))
|
||||
dynamic_cast<Ogg::FLAC::File* >(this)->fromDict(dict);
|
||||
return dynamic_cast<Ogg::FLAC::File* >(this)->setProperties(properties);
|
||||
else if (dynamic_cast<Ogg::Speex::File* >(this))
|
||||
dynamic_cast<Ogg::Speex::File* >(this)->fromDict(dict);
|
||||
return dynamic_cast<Ogg::Speex::File* >(this)->setProperties(properties);
|
||||
else if (dynamic_cast<Ogg::Vorbis::File* >(this))
|
||||
dynamic_cast<Ogg::Vorbis::File* >(this)->fromDict(dict);
|
||||
return dynamic_cast<Ogg::Vorbis::File* >(this)->setProperties(properties);
|
||||
else if (dynamic_cast<RIFF::AIFF::File* >(this))
|
||||
dynamic_cast<RIFF::AIFF::File* >(this)->fromDict(dict);
|
||||
return dynamic_cast<RIFF::AIFF::File* >(this)->setProperties(properties);
|
||||
else if (dynamic_cast<RIFF::WAV::File* >(this))
|
||||
dynamic_cast<RIFF::WAV::File* >(this)->fromDict(dict);
|
||||
return dynamic_cast<RIFF::WAV::File* >(this)->setProperties(properties);
|
||||
else if (dynamic_cast<S3M::File* >(this))
|
||||
dynamic_cast<S3M::File* >(this)->fromDict(dict);
|
||||
return dynamic_cast<S3M::File* >(this)->setProperties(properties);
|
||||
else if (dynamic_cast<TrueAudio::File* >(this))
|
||||
dynamic_cast<TrueAudio::File* >(this)->fromDict(dict);
|
||||
return dynamic_cast<TrueAudio::File* >(this)->setProperties(properties);
|
||||
else if (dynamic_cast<WavPack::File* >(this))
|
||||
dynamic_cast<WavPack::File* >(this)->fromDict(dict);
|
||||
return dynamic_cast<WavPack::File* >(this)->setProperties(properties);
|
||||
else if (dynamic_cast<XM::File* >(this))
|
||||
dynamic_cast<XM::File* >(this)->fromDict(dict);
|
||||
return dynamic_cast<XM::File* >(this)->setProperties(properties);
|
||||
else
|
||||
tag()->fromDict(dict);
|
||||
|
||||
return tag()->setProperties(properties);
|
||||
}
|
||||
|
||||
ByteVector File::readBlock(ulong length)
|
||||
|
@ -37,6 +37,7 @@ namespace TagLib {
|
||||
class String;
|
||||
class Tag;
|
||||
class AudioProperties;
|
||||
class PropertyMap;
|
||||
|
||||
//! A file class with some useful methods for tag manipulation
|
||||
|
||||
@ -81,16 +82,32 @@ namespace TagLib {
|
||||
* Exports the tags of the file as dictionary mapping (human readable) tag
|
||||
* names (Strings) to StringLists of tag values. Calls the according specialization
|
||||
* in the File subclasses.
|
||||
* Will be made virtual in future releases.
|
||||
* For each metadata object of the file that could not be parsed into the PropertyMap
|
||||
* format, the returend map's unsupportedData() list will contain one entry identifying
|
||||
* that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties()
|
||||
* to remove (a subset of) them.
|
||||
* BIC: Will be made virtual in future releases.
|
||||
*/
|
||||
TagDict toDict() const;
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Sets the tags of this File to those specified by the given TagDict. Calls the
|
||||
* Removes unsupported properties, or a subset of them, from the file's metadata.
|
||||
* The parameter \a properties must contain only entries from
|
||||
* properties().unsupportedData().
|
||||
* BIC: Will be mad virtual in future releases.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
|
||||
/*!
|
||||
* Sets the tags of this File to those specified in \a properties. Calls the
|
||||
* according specialization method in the subclasses of File to do the translation
|
||||
* into the format-specific details.
|
||||
* If some value(s) could not be written imported to the specific metadata format,
|
||||
* the returned PropertyMap will contain those value(s). Otherwise it will be empty,
|
||||
* indicating that no problems occured.
|
||||
* BIC: will become pure virtual in the future
|
||||
*/
|
||||
void fromDict(const TagDict &);
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
/*!
|
||||
* Returns a pointer to this file's audio properties. This should be
|
||||
* reimplemented in the concrete subclasses. If no audio properties were
|
||||
|
@ -40,11 +40,11 @@ PropertyMap::~PropertyMap()
|
||||
bool PropertyMap::insert(const String &key, const StringList &values)
|
||||
{
|
||||
String realKey = prepareKey(key);
|
||||
if (realKey.isNull())
|
||||
if(realKey.isNull())
|
||||
return false;
|
||||
|
||||
Iterator result = supertype::find(realKey);
|
||||
if (result == end())
|
||||
if(result == end())
|
||||
supertype::insert(realKey, values);
|
||||
else
|
||||
supertype::operator[](realKey).append(values);
|
||||
@ -54,7 +54,7 @@ bool PropertyMap::insert(const String &key, const StringList &values)
|
||||
bool PropertyMap::replace(const String &key, const StringList &values)
|
||||
{
|
||||
String realKey = prepareKey(key);
|
||||
if (realKey.isNull())
|
||||
if(realKey.isNull())
|
||||
return false;
|
||||
supertype::erase(realKey);
|
||||
supertype::insert(realKey, values);
|
||||
@ -64,7 +64,7 @@ bool PropertyMap::replace(const String &key, const StringList &values)
|
||||
PropertyMap::Iterator PropertyMap::find(const String &key)
|
||||
{
|
||||
String realKey = prepareKey(key);
|
||||
if (realKey.isNull())
|
||||
if(realKey.isNull())
|
||||
return end();
|
||||
return supertype::find(realKey);
|
||||
}
|
||||
@ -72,7 +72,7 @@ PropertyMap::Iterator PropertyMap::find(const String &key)
|
||||
PropertyMap::ConstIterator PropertyMap::find(const String &key) const
|
||||
{
|
||||
String realKey = prepareKey(key);
|
||||
if (realKey.isNull())
|
||||
if(realKey.isNull())
|
||||
return end();
|
||||
return supertype::find(realKey);
|
||||
}
|
||||
@ -80,7 +80,8 @@ PropertyMap::ConstIterator PropertyMap::find(const String &key) const
|
||||
bool PropertyMap::contains(const String &key) const
|
||||
{
|
||||
String realKey = prepareKey(key);
|
||||
if (realKey.isNull())
|
||||
// we consider keys with empty value list as not present
|
||||
if(realKey.isNull() || supertype::operator[](realKey).isEmpty())
|
||||
return false;
|
||||
return supertype::contains(realKey);
|
||||
}
|
||||
@ -109,13 +110,23 @@ StringList &PropertyMap::operator[](const String &key)
|
||||
return supertype::operator[](realKey);
|
||||
}
|
||||
|
||||
void PropertyMap::removeEmpty()
|
||||
{
|
||||
StringList emptyKeys;
|
||||
for(Iterator it = begin(); it != end(); ++it)
|
||||
if(it->second.isEmpty())
|
||||
emptyKeys.append(it->first);
|
||||
for(StringList::Iterator emptyIt = emptyKeys.begin(); emptyIt != emptyKeys.end(); emptyIt++ )
|
||||
erase(*emptyIt);
|
||||
}
|
||||
|
||||
StringList &PropertyMap::unsupportedData()
|
||||
{
|
||||
return unsupported;
|
||||
}
|
||||
|
||||
String PropertyMap::prepareKey(const String &proposed) const {
|
||||
if (proposed.isEmpty())
|
||||
static String PropertyMap::prepareKey(const String &proposed) {
|
||||
if(proposed.isEmpty())
|
||||
return String::null;
|
||||
for (String::ConstIterator it = proposed.begin(); it != proposed.end(); it++)
|
||||
// forbid non-printable, non-ascii, '=' (#61) and '~' (#126)
|
||||
|
@ -39,6 +39,11 @@ namespace TagLib {
|
||||
* i.e. it must contain at least one character; all printable ASCII characters
|
||||
* except '=' and '~' are allowed.
|
||||
*
|
||||
* In order to be safe with other formats, keep these additional restrictions in mind:
|
||||
*
|
||||
* - APE only allows keys from 2 to 16 printable ASCII characters (including space),
|
||||
* with the exception of these strings: ID3, TAG, OggS, MP+
|
||||
*
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT PropertyMap: public Map<String,StringList>
|
||||
@ -117,13 +122,20 @@ namespace TagLib {
|
||||
*/
|
||||
StringList &unsupportedData();
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Removes all entries which have an empty value list.
|
||||
*/
|
||||
void removeEmpty();
|
||||
|
||||
/*!
|
||||
* 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;
|
||||
static String prepareKey(const String &proposed);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
StringList unsupported;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user