Migration to new PropertyMap ... done ape to mod.

This commit is contained in:
Michael Helmling 2012-01-21 14:52:24 +01:00
parent 18ae797df4
commit e4d955d6ef
18 changed files with 398 additions and 234 deletions

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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.

View File

@ -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;
}

View File

@ -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 &);

View File

@ -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) {

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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;
};