taglib/ogg/xiphcomment.cpp
Scott Wheeler 071c691a9a Make removeField() actually remove the map content, not just
empty the list.  Also added an erase method to Map that works on keys
rather than itterators.


git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/kdesupport/taglib@572681 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
2006-08-13 15:25:48 +00:00

308 lines
8.0 KiB
C++

/***************************************************************************
copyright : (C) 2003 by Scott Wheeler
email : wheeler@kde.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#include <tbytevector.h>
#include <tdebug.h>
#include <xiphcomment.h>
using namespace TagLib;
class Ogg::XiphComment::XiphCommentPrivate
{
public:
FieldListMap fieldListMap;
String vendorID;
String commentField;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Ogg::XiphComment::XiphComment() : TagLib::Tag()
{
d = new XiphCommentPrivate;
}
Ogg::XiphComment::XiphComment(const ByteVector &data) : TagLib::Tag()
{
d = new XiphCommentPrivate;
parse(data);
}
Ogg::XiphComment::~XiphComment()
{
delete d;
}
String Ogg::XiphComment::title() const
{
if(d->fieldListMap["TITLE"].isEmpty())
return String::null;
return d->fieldListMap["TITLE"].front();
}
String Ogg::XiphComment::artist() const
{
if(d->fieldListMap["ARTIST"].isEmpty())
return String::null;
return d->fieldListMap["ARTIST"].front();
}
String Ogg::XiphComment::album() const
{
if(d->fieldListMap["ALBUM"].isEmpty())
return String::null;
return d->fieldListMap["ALBUM"].front();
}
String Ogg::XiphComment::comment() const
{
if(!d->fieldListMap["DESCRIPTION"].isEmpty()) {
d->commentField = "DESCRIPTION";
return d->fieldListMap["DESCRIPTION"].front();
}
if(!d->fieldListMap["COMMENT"].isEmpty()) {
d->commentField = "COMMENT";
return d->fieldListMap["COMMENT"].front();
}
return String::null;
}
String Ogg::XiphComment::genre() const
{
if(d->fieldListMap["GENRE"].isEmpty())
return String::null;
return d->fieldListMap["GENRE"].front();
}
TagLib::uint Ogg::XiphComment::year() const
{
if(d->fieldListMap["DATE"].isEmpty())
return 0;
return d->fieldListMap["DATE"].front().toInt();
}
TagLib::uint Ogg::XiphComment::track() const
{
if(d->fieldListMap["TRACKNUMBER"].isEmpty())
return 0;
return d->fieldListMap["TRACKNUMBER"].front().toInt();
}
void Ogg::XiphComment::setTitle(const String &s)
{
addField("TITLE", s);
}
void Ogg::XiphComment::setArtist(const String &s)
{
addField("ARTIST", s);
}
void Ogg::XiphComment::setAlbum(const String &s)
{
addField("ALBUM", s);
}
void Ogg::XiphComment::setComment(const String &s)
{
addField(d->commentField.isEmpty() ? "DESCRIPTION" : d->commentField, s);
}
void Ogg::XiphComment::setGenre(const String &s)
{
addField("GENRE", s);
}
void Ogg::XiphComment::setYear(uint i)
{
if(i == 0)
removeField("DATE");
else
addField("DATE", String::number(i));
}
void Ogg::XiphComment::setTrack(uint i)
{
if(i == 0)
removeField("TRACKNUMBER");
else
addField("TRACKNUMBER", String::number(i));
}
bool Ogg::XiphComment::isEmpty() const
{
FieldListMap::ConstIterator it = d->fieldListMap.begin();
for(; it != d->fieldListMap.end(); ++it)
if(!(*it).second.isEmpty())
return false;
return true;
}
TagLib::uint Ogg::XiphComment::fieldCount() const
{
uint count = 0;
FieldListMap::ConstIterator it = d->fieldListMap.begin();
for(; it != d->fieldListMap.end(); ++it)
count += (*it).second.size();
return count;
}
const Ogg::FieldListMap &Ogg::XiphComment::fieldListMap() const
{
return d->fieldListMap;
}
String Ogg::XiphComment::vendorID() const
{
return d->vendorID;
}
void Ogg::XiphComment::addField(const String &key, const String &value, bool replace)
{
if(replace)
removeField(key.upper());
if(!key.isEmpty())
d->fieldListMap[key.upper()].append(value);
}
void Ogg::XiphComment::removeField(const String &key, const String &value)
{
if(!value.isNull()) {
StringList::Iterator it = d->fieldListMap[key].begin();
for(; it != d->fieldListMap[key].end(); ++it) {
if(value == *it)
d->fieldListMap[key].erase(it);
}
}
else
d->fieldListMap.erase(key);
}
bool Ogg::XiphComment::contains(const String &key) const
{
return d->fieldListMap.contains(key) && !d->fieldListMap[key].isEmpty();
}
ByteVector Ogg::XiphComment::render() const
{
return render(true);
}
ByteVector Ogg::XiphComment::render(bool addFramingBit) const
{
ByteVector data;
// Add the vendor ID length and the vendor ID. It's important to use the
// lenght of the data(String::UTF8) rather than the lenght of the the string
// since this is UTF8 text and there may be more characters in the data than
// in the UTF16 string.
ByteVector vendorData = d->vendorID.data(String::UTF8);
data.append(ByteVector::fromUInt(vendorData.size(), false));
data.append(vendorData);
// Add the number of fields.
data.append(ByteVector::fromUInt(fieldCount(), false));
// Iterate over the the field lists. Our iterator returns a
// std::pair<String, StringList> where the first String is the field name and
// the StringList is the values associated with that field.
FieldListMap::ConstIterator it = d->fieldListMap.begin();
for(; it != d->fieldListMap.end(); ++it) {
// And now iterate over the values of the current list.
String fieldName = (*it).first;
StringList values = (*it).second;
StringList::ConstIterator valuesIt = values.begin();
for(; valuesIt != values.end(); ++valuesIt) {
ByteVector fieldData = fieldName.data(String::UTF8);
fieldData.append('=');
fieldData.append((*valuesIt).data(String::UTF8));
data.append(ByteVector::fromUInt(fieldData.size(), false));
data.append(fieldData);
}
}
// Append the "framing bit".
if(addFramingBit)
data.append(char(1));
return data;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void Ogg::XiphComment::parse(const ByteVector &data)
{
// The first thing in the comment data is the vendor ID length, followed by a
// UTF8 string with the vendor ID.
int pos = 0;
int vendorLength = data.mid(0, 4).toUInt(false);
pos += 4;
d->vendorID = String(data.mid(pos, vendorLength), String::UTF8);
pos += vendorLength;
// Next the number of fields in the comment vector.
int commentFields = data.mid(pos, 4).toUInt(false);
pos += 4;
for(int i = 0; i < commentFields; i++) {
// Each comment field is in the format "KEY=value" in a UTF8 string and has
// 4 bytes before the text starts that gives the length.
int commentLength = data.mid(pos, 4).toUInt(false);
pos += 4;
String comment = String(data.mid(pos, commentLength), String::UTF8);
pos += commentLength;
int commentSeparatorPosition = comment.find("=");
String key = comment.substr(0, commentSeparatorPosition);
String value = comment.substr(commentSeparatorPosition + 1);
addField(key, value, false);
}
}