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::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 APE::File::fromDict(const TagDict &dict)
+PropertyMap APE::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>(APE, true)->fromDict(dict);
+ return d->tag.access<APE::Tag>(APE, true)->setProperties(properties);
}
APE::Properties *APE::File::audioProperties() const
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.
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();
}
}
}
+ return PropertyMap;
}
APE::Footer *APE::Tag::footer() const
* 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.
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::removeUnsupportedProperties(const StringList &unsupported)
+{
+ 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);
}
-void FLAC::File::fromDict(const TagDict &dict)
+PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
{
- 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)
+ 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
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
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
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
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()
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.
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;
}
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 &);
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) {
#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
/*!
* in TagLib::AudioProperties, TagLib::File and TagLib::FileRef.
*/
+ class PropertyMap;
+
class TAGLIB_EXPORT Tag
{
public:
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.
+ */
+ PropertyMap properties() const;
+
+ /*!
+ * 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.
*/
- TagDict toDict() const;
+ void removeUnsupportedProperties(const StringList& properties);
/*!
- * 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.
+ * 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.
*/
- void fromDict(const TagDict &);
+ PropertyMap setProperties(const PropertyMap &properties);
/*!
* Returns the track name; if no track name is present in the tag
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)->fromDict(dict);
+ dynamic_cast<APE::File* >(this)->removeUnsupportedProperties(properties);
else if (dynamic_cast<FLAC::File* >(this))
- dynamic_cast<FLAC::File* >(this)->fromDict(dict);
+ 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))
+ return dynamic_cast<APE::File* >(this)->setProperties(properties);
+ else if (dynamic_cast<FLAC::File* >(this))
+ 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)
class String;
class Tag;
class AudioProperties;
+ class PropertyMap;
//! A file class with some useful methods for tag manipulation
* 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
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);
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);
PropertyMap::Iterator PropertyMap::find(const String &key)
{
String realKey = prepareKey(key);
- if (realKey.isNull())
+ if(realKey.isNull())
return end();
return supertype::find(realKey);
}
PropertyMap::ConstIterator PropertyMap::find(const String &key) const
{
String realKey = prepareKey(key);
- if (realKey.isNull())
+ if(realKey.isNull())
return end();
return supertype::find(realKey);
}
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);
}
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)
* 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>
*/
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;
};