Improve FrameFactory subclassing

Change the ID3v2 frame factory so it is possible to subclass it for
implementing own frames.  Additionally it splits up the createFrame()
method to make its usage in subclasses a bit more easy.
This commit is contained in:
Max-F-Helm 2022-03-03 21:29:05 +01:00 committed by Urs Fleisch
parent c54c924333
commit 271bd05afa
2 changed files with 50 additions and 10 deletions

View File

@ -116,9 +116,9 @@ FrameFactory *FrameFactory::instance()
return &factory;
}
Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHeader) const
std::pair<Frame::Header *, bool> FrameFactory::prepareFrameHeader(
ByteVector &data, const Header *tagHeader) const
{
ByteVector data = origData;
unsigned int version = tagHeader->majorVersion();
auto header = new Frame::Header(data, version);
ByteVector frameID = header->frameID();
@ -131,7 +131,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHe
header->frameSize() > data.size())
{
delete header;
return nullptr;
return {nullptr, false};
}
#ifndef NO_ITUNES_HACKS
@ -148,7 +148,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHe
for(auto it = frameID.cbegin(); it != frameID.cend(); it++) {
if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) {
delete header;
return nullptr;
return {nullptr, false};
}
}
@ -165,24 +165,39 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHe
if(!zlib::isAvailable() && header->compression()) {
debug("Compressed frames are currently not supported.");
return new UnknownFrame(data, header);
return {header, false};
}
if(header->encryption()) {
debug("Encrypted frames are currently not supported.");
return new UnknownFrame(data, header);
return {header, false};
}
if(!updateFrame(header)) {
header->setTagAlterPreservation(true);
return new UnknownFrame(data, header);
return {header, false};
}
// updateFrame() might have updated the frame ID.
return {header, true};
}
frameID = header->frameID();
Frame *FrameFactory::createFrame(const ByteVector &origData,
const Header *tagHeader) const
{
ByteVector data = origData;
auto [header, ok] = prepareFrameHeader(data, tagHeader);
if(!ok) {
// check if frame is valid and return as UnknownFrame
return header ? new UnknownFrame(data, header) : nullptr;
}
return createFrame(data, header, tagHeader);
}
// This is where things get necissarily nasty. Here we determine which
Frame *FrameFactory::createFrame(const ByteVector &data, Frame::Header *header,
const Header *tagHeader) const {
ByteVector frameID = header->frameID();
// This is where things get necessarily nasty. Here we determine which
// Frame subclass (or if none is found simply an Frame) based
// on the frame ID. Since there are a lot of possibilities, that means
// a lot of if blocks.

View File

@ -127,6 +127,31 @@ namespace TagLib {
*/
virtual bool updateFrame(Frame::Header *header) const;
/*!
* Creates and prepares the frame header for createFrame().
*
* \param data data of the frame (might be modified)
* \param tagHeader the tag header
* \param header reference to frame header pointer, will be set to
* nullptr if the frame is invalid
* \return {header, ok}: header is a created frame header or nullptr
* if the frame is invalid; ok is true if the frame is supported.
*/
std::pair<Frame::Header *, bool> prepareFrameHeader(
ByteVector &data, const Header *tagHeader) const;
/*!
* Create a frame based on \a data. \a header should be a valid frame
* header and \a tagHeader a valid ID3v2::Header instance.
*
* This method is called by the public overloaded method
* createFrame(const ByteVector &, const Header *) after creating
* \a header from verified \a data using prepareFrameHeader(), so
* this method is provided to be reimplemented in derived classes.
*/
virtual Frame *createFrame(const ByteVector &data, Frame::Header *header,
const Header *tagHeader) const;
private:
FrameFactory(const FrameFactory &) = delete;
FrameFactory &operator=(const FrameFactory &) = delete;