Move over to the union tag class. Yeah, this is crazy to be doing close

to a release, but you know, momentum.


git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/kdesupport/taglib@768978 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
This commit is contained in:
Scott Wheeler 2008-01-31 03:41:31 +00:00
parent f38f6588a2
commit be5c25bbfa
3 changed files with 128 additions and 210 deletions

View File

@ -23,6 +23,7 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tagunion.h>
#include <id3v2tag.h>
#include <id3v2header.h>
#include <id3v1tag.h>
@ -37,118 +38,29 @@
using namespace TagLib;
#define combine(method) \
if(file->ID3v2Tag() && !file->ID3v2Tag()->method().isEmpty()) \
return file->ID3v2Tag()->method(); \
\
if(file->APETag() && !file->APETag()->method().isEmpty()) \
return file->APETag()->method(); \
\
if(file->ID3v1Tag()) \
return file->ID3v1Tag()->method(); \
\
return String::null \
namespace
{
enum { ID3v2Index = 0, APEIndex = 1, ID3v1Index = 2 };
#define combineNumber(method) \
if(file->ID3v2Tag() && file->ID3v2Tag()->method() > 0) \
return file->ID3v2Tag()->method(); \
\
if(file->APETag() && file->APETag()->method() > 0) \
return file->APETag()->method(); \
if(file->ID3v1Tag()) \
return file->ID3v1Tag()->method(); \
\
return 0
#define setCombined(method, value) \
file->ID3v2Tag(true)->set##method(value); \
file->ID3v1Tag(true)->set##method(value); \
if(file->APETag(false)) \
file->APETag(false)->set##method(value)
namespace TagLib {
/*!
* A union of the ID3v2 and ID3v1 tags.
*/
class MPEGTag : public TagLib::Tag
class MPEGTag : public TagUnion
{
public:
MPEGTag(MPEG::File *f) : TagLib::Tag(), file(f) {}
virtual String title() const
virtual Tag *tag(int index, AccessType type) const
{
combine(title);
}
if(type == Write)
{
if(index == ID3v2Index && !(*this)[ID3v2Index])
{
const_cast<MPEGTag *>(this)->setTag(ID3v2Index, new ID3v2::Tag);
}
else if(index == ID3v1Index && !(*this)[ID3v1Index])
{
const_cast<MPEGTag *>(this)->setTag(ID3v1Index, new ID3v1::Tag);
}
}
virtual String artist() const
{
combine(artist);
return TagUnion::tag(index);
}
virtual String album() const
{
combine(album);
}
virtual String comment() const
{
combine(comment);
}
virtual String genre() const
{
combine(genre);
}
virtual uint year() const
{
combineNumber(year);
}
virtual uint track() const
{
combineNumber(track);
}
virtual void setTitle(const String &s)
{
setCombined(Title, s);
}
virtual void setArtist(const String &s)
{
setCombined(Artist, s);
}
virtual void setAlbum(const String &s)
{
setCombined(Album, s);
}
virtual void setComment(const String &s)
{
setCombined(Comment, s);
}
virtual void setGenre(const String &s)
{
setCombined(Genre, s);
}
virtual void setYear(uint i)
{
setCombined(Year, i);
}
virtual void setTrack(uint i)
{
setCombined(Track, i);
}
private:
MPEG::File *file;
};
}
@ -157,41 +69,35 @@ class MPEG::File::FilePrivate
public:
FilePrivate(ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
ID3v2FrameFactory(frameFactory),
ID3v2Tag(0),
ID3v2Location(-1),
ID3v2OriginalSize(0),
APETag(0),
APELocation(-1),
APEOriginalSize(0),
ID3v1Tag(0),
ID3v1Location(-1),
tag(0),
hasID3v2(false),
hasID3v1(false),
hasAPE(false),
properties(0) {}
properties(0)
{
~FilePrivate() {
delete ID3v2Tag;
delete ID3v1Tag;
delete APETag;
delete tag;
}
~FilePrivate()
{
delete properties;
}
const ID3v2::FrameFactory *ID3v2FrameFactory;
ID3v2::Tag *ID3v2Tag;
long ID3v2Location;
uint ID3v2OriginalSize;
APE::Tag *APETag;
long APELocation;
uint APEOriginalSize;
ID3v1::Tag *ID3v1Tag;
long ID3v1Location;
MPEGTag *tag;
MPEGTag tag;
// These indicate whether the file *on disk* has these tags, not if
// this data structure does. This is used in computing offsets.
@ -211,10 +117,9 @@ MPEG::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(file)
{
d = new FilePrivate;
if(isOpen()) {
d->tag = new MPEGTag(this);
if(isOpen())
read(readProperties, propertiesStyle);
}
}
MPEG::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
@ -222,10 +127,9 @@ MPEG::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
TagLib::File(file)
{
d = new FilePrivate(frameFactory);
if(isOpen()) {
d->tag = new MPEGTag(this);
if(isOpen())
read(readProperties, propertiesStyle);
}
}
MPEG::File::~File()
@ -235,7 +139,7 @@ MPEG::File::~File()
TagLib::Tag *MPEG::File::tag() const
{
return d->tag;
return &d->tag;
}
MPEG::Properties *MPEG::File::audioProperties() const
@ -258,7 +162,7 @@ bool MPEG::File::save(int tags, bool stripOthers)
if(tags == NoTags && stripOthers)
return strip(AllTags);
if(!d->ID3v2Tag && !d->ID3v1Tag && !d->APETag) {
if(!ID3v2Tag() && !ID3v1Tag() && !APETag()) {
if((d->hasID3v1 || d->hasID3v2 || d->hasAPE) && stripOthers)
return strip(AllTags);
@ -274,33 +178,33 @@ bool MPEG::File::save(int tags, bool stripOthers)
// Create the tags if we've been asked to. Copy the values from the tag that
// does exist into the new tag.
if((tags & ID3v2) && d->ID3v1Tag)
Tag::duplicate(d->ID3v1Tag, ID3v2Tag(true), false);
if((tags & ID3v2) && ID3v1Tag())
Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false);
if((tags & ID3v1) && d->ID3v2Tag)
Tag::duplicate(d->ID3v2Tag, ID3v1Tag(true), false);
if((tags & ID3v1) && d->tag[ID3v2Index])
Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false);
bool success = true;
if(ID3v2 & tags) {
if(d->ID3v2Tag && !d->ID3v2Tag->isEmpty()) {
if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) {
if(!d->hasID3v2)
d->ID3v2Location = 0;
insert(d->ID3v2Tag->render(), d->ID3v2Location, d->ID3v2OriginalSize);
insert(ID3v2Tag()->render(), d->ID3v2Location, d->ID3v2OriginalSize);
d->hasID3v2 = true;
// v1 tag location has changed, update if it exists
if(d->ID3v1Tag)
if(ID3v1Tag())
d->ID3v1Location = findID3v1();
// APE tag location has changed, update if it exists
if(d->APETag)
if(APETag())
d->APELocation = findAPE();
}
else if(stripOthers)
@ -310,10 +214,10 @@ bool MPEG::File::save(int tags, bool stripOthers)
success = strip(ID3v2) && success;
if(ID3v1 & tags) {
if(d->ID3v1Tag && !d->ID3v1Tag->isEmpty()) {
if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
int offset = d->hasID3v1 ? -128 : 0;
seek(offset, End);
writeBlock(d->ID3v1Tag->render());
writeBlock(ID3v1Tag()->render());
d->hasID3v1 = true;
d->ID3v1Location = findID3v1();
}
@ -325,13 +229,13 @@ bool MPEG::File::save(int tags, bool stripOthers)
// Dont save an APE-tag unless one has been created
if((APE & tags) && d->APETag) {
if((APE & tags) && APETag()) {
if(d->hasAPE)
insert(d->APETag->render(), d->APELocation, d->APEOriginalSize);
insert(APETag()->render(), d->APELocation, d->APEOriginalSize);
else {
if(d->hasID3v1) {
insert(d->APETag->render(), d->ID3v1Location, 0);
d->APEOriginalSize = d->APETag->footer()->completeTagSize();
insert(APETag()->render(), d->ID3v1Location, 0);
d->APEOriginalSize = APETag()->footer()->completeTagSize();
d->hasAPE = true;
d->APELocation = d->ID3v1Location;
d->ID3v1Location += d->APEOriginalSize;
@ -339,8 +243,8 @@ bool MPEG::File::save(int tags, bool stripOthers)
else {
seek(0, End);
d->APELocation = tell();
writeBlock(d->APETag->render());
d->APEOriginalSize = d->APETag->footer()->completeTagSize();
writeBlock(APETag()->render());
d->APEOriginalSize = APETag()->footer()->completeTagSize();
d->hasAPE = true;
}
}
@ -353,35 +257,25 @@ bool MPEG::File::save(int tags, bool stripOthers)
ID3v2::Tag *MPEG::File::ID3v2Tag(bool create)
{
if(!create || d->ID3v2Tag)
return d->ID3v2Tag;
// no ID3v2 tag exists and we've been asked to create one
d->ID3v2Tag = new ID3v2::Tag;
return d->ID3v2Tag;
return static_cast<ID3v2::Tag *>(
d->tag.tag(ID3v2Index, create ? MPEGTag::Write : MPEGTag::Read));
}
ID3v1::Tag *MPEG::File::ID3v1Tag(bool create)
{
if(!create || d->ID3v1Tag)
return d->ID3v1Tag;
// no ID3v1 tag exists and we've been asked to create one
d->ID3v1Tag = new ID3v1::Tag;
return d->ID3v1Tag;
return static_cast<ID3v1::Tag *>(
d->tag.tag(ID3v1Index, create ? MPEGTag::Write : MPEGTag::Read));
}
APE::Tag *MPEG::File::APETag(bool create)
{
if(!create || d->APETag)
return d->APETag;
if(!create || d->tag[APEIndex])
return static_cast<APE::Tag *>(d->tag[APEIndex]);
// no APE tag exists and we've been asked to create one
debug("Creating APE Tag.");
d->APETag = new APE::Tag;
return d->APETag;
d->tag.setTag(APEIndex, new APE::Tag);
return static_cast<APE::Tag *>(d->tag[APEIndex]);
}
bool MPEG::File::strip(int tags)
@ -401,19 +295,18 @@ bool MPEG::File::strip(int tags, bool freeMemory)
d->ID3v2Location = -1;
d->ID3v2OriginalSize = 0;
d->hasID3v2 = false;
if(freeMemory) {
delete d->ID3v2Tag;
d->ID3v2Tag = 0;
}
if(freeMemory)
d->tag.setTag(ID3v2Index, 0);
// v1 tag location has changed, update if it exists
if(d->ID3v1Tag)
if(ID3v1Tag())
d->ID3v1Location = findID3v1();
// APE tag location has changed, update if it exists
if(d->APETag)
if(APETag())
d->APELocation = findAPE();
}
@ -421,10 +314,9 @@ bool MPEG::File::strip(int tags, bool freeMemory)
truncate(d->ID3v1Location);
d->ID3v1Location = -1;
d->hasID3v1 = false;
if(freeMemory) {
delete d->ID3v1Tag;
d->ID3v1Tag = 0;
}
if(freeMemory)
d->tag.setTag(ID3v1Index, 0);
}
if((tags & APE) && d->hasAPE) {
@ -435,10 +327,9 @@ bool MPEG::File::strip(int tags, bool freeMemory)
if (d->ID3v1Location > d->APELocation)
d->ID3v1Location -= d->APEOriginalSize;
}
if(freeMemory) {
delete d->APETag;
d->APETag = 0;
}
if(freeMemory)
d->tag.setTag(APEIndex, 0);
}
return true;
@ -497,15 +388,15 @@ long MPEG::File::firstFrameOffset()
{
long position = 0;
if(d->ID3v2Tag)
position = d->ID3v2Location + d->ID3v2Tag->header()->completeTagSize();
if(ID3v2Tag())
position = d->ID3v2Location + ID3v2Tag()->header()->completeTagSize();
return nextFrameOffset(position);
}
long MPEG::File::lastFrameOffset()
{
return previousFrameOffset(d->ID3v1Tag ? d->ID3v1Location - 1 : length());
return previousFrameOffset(ID3v1Tag() ? d->ID3v1Location - 1 : length());
}
////////////////////////////////////////////////////////////////////////////////
@ -520,14 +411,12 @@ void MPEG::File::read(bool readProperties, Properties::ReadStyle propertiesStyle
if(d->ID3v2Location >= 0) {
d->ID3v2Tag = new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory);
d->tag.setTag(ID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
d->ID3v2OriginalSize = d->ID3v2Tag->header()->completeTagSize();
d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
if(d->ID3v2Tag->header()->tagSize() <= 0) {
delete d->ID3v2Tag;
d->ID3v2Tag = 0;
}
if(ID3v2Tag()->header()->tagSize() <= 0)
d->tag.setTag(ID3v2Index, 0);
else
d->hasID3v2 = true;
}
@ -537,7 +426,7 @@ void MPEG::File::read(bool readProperties, Properties::ReadStyle propertiesStyle
d->ID3v1Location = findID3v1();
if(d->ID3v1Location >= 0) {
d->ID3v1Tag = new ID3v1::Tag(this, d->ID3v1Location);
d->tag.setTag(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->hasID3v1 = true;
}
@ -547,11 +436,13 @@ void MPEG::File::read(bool readProperties, Properties::ReadStyle propertiesStyle
if(d->APELocation >= 0) {
d->APETag = new APE::Tag(this, d->APELocation);
debug("Found APE");
d->APEOriginalSize = d->APETag->footer()->completeTagSize();
d->tag.setTag(APEIndex, new APE::Tag(this, d->APELocation));
d->APELocation = d->APELocation + d->APETag->footer()->size() - d->APEOriginalSize;
d->APEOriginalSize = APETag()->footer()->completeTagSize();
d->APELocation = d->APELocation + APETag()->footer()->size() - d->APEOriginalSize;
d->hasAPE = true;
}

View File

@ -28,30 +28,30 @@
using namespace TagLib;
#define stringUnion(method) \
if(d->tags[0] && !d->tags[0]->method().isEmpty()) \
return d->tags[0]->method(); \
if(d->tags[1] && !d->tags[1]->method().isEmpty()) \
return d->tags[0]->method(); \
if(d->tags[2] && !d->tags[2]->method().isEmpty()) \
return d->tags[0]->method(); \
if(tag(0, Read) && !tag(0, Read)->method().isEmpty()) \
return tag(0, Read)->method(); \
if(tag(1, Read) && !tag(1, Read)->method().isEmpty()) \
return tag(1, Read)->method(); \
if(tag(2, Read) && !tag(2, Read)->method().isEmpty()) \
return tag(2, Read)->method(); \
return String::null \
#define numberUnion(method) \
if(d->tags[0] && d->tags[0]->method() > 0) \
return d->tags[0]->method(); \
if(d->tags[0] && d->tags[0]->method() > 0) \
return d->tags[0]->method(); \
if(d->tags[0] && d->tags[0]->method() > 0) \
return d->tags[0]->method(); \
if(tag(0, Read) && tag(0, Read)->method() > 0) \
return tag(0, Read)->method(); \
if(tag(1, Read) && tag(1, Read)->method() > 0) \
return tag(1, Read)->method(); \
if(tag(2, Read) && tag(2, Read)->method() > 0) \
return tag(2, Read)->method(); \
return 0
#define setUnion(method, value) \
if(d->tags[0]) \
d->tags[0]->set##method(value); \
if(d->tags[1]) \
d->tags[1]->set##method(value); \
if(d->tags[2]) \
d->tags[2]->set##method(value); \
if(tag(0, Write)) \
tag(0, Write)->set##method(value); \
if(tag(1, Write)) \
tag(1, Write)->set##method(value); \
if(tag(2, Write)) \
tag(2, Write)->set##method(value); \
class TagUnion::TagUnionPrivate
{
@ -85,11 +85,16 @@ TagUnion::~TagUnion()
delete d;
}
Tag *TagUnion::tag(int index) const
Tag *TagUnion::operator[](int index) const
{
return d->tags[index];
}
Tag *TagUnion::tag(int index, AccessType) const
{
return (*this)[index];
}
void TagUnion::setTag(int index, Tag *tag)
{
delete d->tags[index];

View File

@ -38,10 +38,32 @@ namespace TagLib {
{
public:
enum AccessType { Read, Write };
/*!
* Creates a TagLib::Tag that is the union of \a first, \a second, and
* \a third. The TagUnion takes ownership of these tags and will handle
* their deletion.
*/
TagUnion(Tag *first = 0, Tag *second = 0, Tag *third = 0);
virtual ~TagUnion();
Tag *tag(int index) const;
/*!
* Simply returns the value for the the tag at \a index.
*
* \note This does not call tag()
*
* \see tag()
*/
Tag *operator[](int index) const;
/*!
* By default just a call to operator[], but may be overridden if, for
* instance, it is desirable to create frames on write.
*/
virtual Tag *tag(int index, AccessType type = Read) const;
void setTag(int index, Tag *tag);
virtual String title() const;