Use static polymorphism in FieldMapBasedTag

This commit is contained in:
Martchus 2017-03-07 01:52:26 +01:00
parent 138fa32f29
commit 0daabba17a
11 changed files with 130 additions and 45 deletions

View File

@ -24,18 +24,17 @@ class FieldMapBasedTagTraits
* the tag fields using std::multimap. * the tag fields using std::multimap.
* *
* The FieldMapBasedTag class only provides the interface and common functionality. * The FieldMapBasedTag class only provides the interface and common functionality.
* It is meant to be subclassed. * It is meant to be subclassed using CRTP pattern.
* *
* \tparam FieldType Specifies the class used to store the fields. Should be a subclass * \remarks This template class is intended to be subclassed using
* of TagField. * with the "Curiously recurring template pattern".
*
* \tparam Compare Specifies the key comparsion function. Default is std::less.
*/ */
template <class ImplementationType> template <class ImplementationType>
class FieldMapBasedTag : public Tag class FieldMapBasedTag : public Tag
{ {
public:
friend class FieldMapBasedTagTraits<ImplementationType>; friend class FieldMapBasedTagTraits<ImplementationType>;
public:
typedef typename FieldMapBasedTagTraits<ImplementationType>::implementationType implementationType; typedef typename FieldMapBasedTagTraits<ImplementationType>::implementationType implementationType;
typedef typename FieldMapBasedTagTraits<ImplementationType>::fieldType fieldType; typedef typename FieldMapBasedTagTraits<ImplementationType>::fieldType fieldType;
typedef typename FieldMapBasedTagTraits<ImplementationType>::fieldType::identifierType identifierType; typedef typename FieldMapBasedTagTraits<ImplementationType>::fieldType::identifierType identifierType;
@ -46,29 +45,37 @@ public:
TagType type() const; TagType type() const;
const char *typeName() const; const char *typeName() const;
TagTextEncoding proposedTextEncoding() const; TagTextEncoding proposedTextEncoding() const;
virtual const TagValue &value(const identifierType &id) const; // FIXME: use static polymorphism const TagValue &value(const identifierType &id) const;
const TagValue &value(KnownField field) const; const TagValue &value(KnownField field) const;
std::vector<const TagValue *> values(const identifierType &id) const; std::vector<const TagValue *> values(const identifierType &id) const;
std::vector<const TagValue *> values(KnownField field) const; std::vector<const TagValue *> values(KnownField field) const;
virtual bool setValue(const identifierType &id, const TagValue &value); // FIXME: use static polymorphism bool setValue(const identifierType &id, const TagValue &value);
bool setValue(KnownField field, const TagValue &value); bool setValue(KnownField field, const TagValue &value);
bool setValues(const identifierType &id, const std::vector<TagValue> &values); bool setValues(const identifierType &id, const std::vector<TagValue> &values);
bool setValues(KnownField field, const std::vector<TagValue> &values); bool setValues(KnownField field, const std::vector<TagValue> &values);
bool hasField(KnownField field) const; bool hasField(KnownField field) const;
virtual bool hasField(const identifierType &id) const; // FIXME: use static polymorphism bool hasField(const identifierType &id) const;
void removeAllFields(); void removeAllFields();
const std::multimap<identifierType, fieldType, compare> &fields() const; const std::multimap<identifierType, fieldType, compare> &fields() const;
std::multimap<identifierType, fieldType, compare> &fields(); std::multimap<identifierType, fieldType, compare> &fields();
unsigned int fieldCount() const; unsigned int fieldCount() const;
virtual identifierType fieldId(KnownField value) const = 0; // FIXME: use static polymorphism identifierType fieldId(KnownField value) const;
virtual KnownField knownField(const identifierType &id) const = 0; // FIXME: use static polymorphism KnownField knownField(const identifierType &id) const;
bool supportsField(KnownField field) const; bool supportsField(KnownField field) const;
using Tag::proposedDataType; using Tag::proposedDataType;
virtual TagDataType proposedDataType(const identifierType &id) const; // FIXME: use static polymorphism TagDataType proposedDataType(const identifierType &id) const;
int insertFields(const FieldMapBasedTag<ImplementationType> &from, bool overwrite); int insertFields(const FieldMapBasedTag<ImplementationType> &from, bool overwrite);
unsigned int insertValues(const Tag &from, bool overwrite); unsigned int insertValues(const Tag &from, bool overwrite);
void ensureTextValuesAreProperlyEncoded(); void ensureTextValuesAreProperlyEncoded();
protected:
const TagValue &internallyGetValue(const identifierType &id) const;
bool internallySetValue(const identifierType &id, const TagValue &value);
bool internallyHasField(const identifierType &id) const;
// no default implementation: identifierType internallyGetFieldId(KnownField field) const;
// no default implementation: KnownField internallyGetKnownField(const identifierType &id) const;
TagDataType internallyGetProposedDataType(const identifierType &id) const;
private: private:
std::multimap<identifierType, fieldType, compare> m_fields; std::multimap<identifierType, fieldType, compare> m_fields;
}; };
@ -112,6 +119,17 @@ TagTextEncoding FieldMapBasedTag<ImplementationType>::proposedTextEncoding() con
return ImplementationType::defaultTextEncoding; return ImplementationType::defaultTextEncoding;
} }
/*!
* \brief Default implementation for value().
* \remarks Shadow in subclass to provide custom implementation.
*/
template<class ImplementationType>
const TagValue &FieldMapBasedTag<ImplementationType>::internallyGetValue(const identifierType &id) const
{
auto i = m_fields.find(id);
return i != m_fields.end() ? i->second.value() : TagValue::empty();
}
/*! /*!
* \brief Returns the value of the field with the specified \a id. * \brief Returns the value of the field with the specified \a id.
* \sa Tag::value() * \sa Tag::value()
@ -119,8 +137,7 @@ TagTextEncoding FieldMapBasedTag<ImplementationType>::proposedTextEncoding() con
template <class ImplementationType> template <class ImplementationType>
inline const TagValue &FieldMapBasedTag<ImplementationType>::value(const identifierType &id) const inline const TagValue &FieldMapBasedTag<ImplementationType>::value(const identifierType &id) const
{ {
auto i = m_fields.find(id); return static_cast<const ImplementationType *>(this)->internallyGetValue(id);
return i != m_fields.end() ? i->second.value() : TagValue::empty();
} }
template <class ImplementationType> template <class ImplementationType>
@ -159,11 +176,11 @@ inline bool FieldMapBasedTag<ImplementationType>::setValue(KnownField field, con
} }
/*! /*!
* \brief Assigns the given \a value to the field with the specified \a id. * \brief Default implementation for setValue().
* \sa Tag::setValue() * \remarks Shadow in subclass to provide custom implementation.
*/ */
template <class ImplementationType> template<class ImplementationType>
bool FieldMapBasedTag<ImplementationType>::setValue(const identifierType &id, const Media::TagValue &value) bool FieldMapBasedTag<ImplementationType>::internallySetValue(const identifierType &id, const TagValue &value)
{ {
auto i = m_fields.find(id); auto i = m_fields.find(id);
if(i != m_fields.end()) { // field already exists -> set its value if(i != m_fields.end()) { // field already exists -> set its value
@ -176,6 +193,16 @@ bool FieldMapBasedTag<ImplementationType>::setValue(const identifierType &id, co
return true; return true;
} }
/*!
* \brief Assigns the given \a value to the field with the specified \a id.
* \sa Tag::setValue()
*/
template <class ImplementationType>
bool FieldMapBasedTag<ImplementationType>::setValue(const identifierType &id, const Media::TagValue &value)
{
return static_cast<ImplementationType *>(this)->internallySetValue(id, value);
}
/*! /*!
* \brief Assigns the given \a values to the field with the specified \a id. * \brief Assigns the given \a values to the field with the specified \a id.
* \remarks There might me more than one value assigned to an \a id. Whereas setValue() only alters the first value, this * \remarks There might me more than one value assigned to an \a id. Whereas setValue() only alters the first value, this
@ -225,10 +252,11 @@ inline bool FieldMapBasedTag<ImplementationType>::hasField(KnownField field) con
} }
/*! /*!
* \brief Returns an indication whether the field with the specified \a id is present. * \brief Default implementation for hasField().
* \remarks Shadow in subclass to provide custom implementation.
*/ */
template <class ImplementationType> template<class ImplementationType>
inline bool FieldMapBasedTag<ImplementationType>::hasField(const identifierType &id) const bool FieldMapBasedTag<ImplementationType>::internallyHasField(const identifierType &id) const
{ {
for (auto range = m_fields.equal_range(id); range.first != range.second; ++range.first) { for (auto range = m_fields.equal_range(id); range.first != range.second; ++range.first) {
if(!range.first->second.value().isEmpty()) { if(!range.first->second.value().isEmpty()) {
@ -238,6 +266,15 @@ inline bool FieldMapBasedTag<ImplementationType>::hasField(const identifierType
return false; return false;
} }
/*!
* \brief Returns an indication whether the field with the specified \a id is present.
*/
template <class ImplementationType>
inline bool FieldMapBasedTag<ImplementationType>::hasField(const identifierType &id) const
{
return static_cast<const ImplementationType *>(this)->internallyHasField(id);
}
template <class ImplementationType> template <class ImplementationType>
inline void FieldMapBasedTag<ImplementationType>::removeAllFields() inline void FieldMapBasedTag<ImplementationType>::removeAllFields()
{ {
@ -274,6 +311,26 @@ unsigned int FieldMapBasedTag<ImplementationType>::fieldCount() const
return count; return count;
} }
/*!
* \brief Returns the field ID for the specified \a value.
* \remarks Must be implemented in internallyGetFieldId() when creating subclass.
*/
template<class ImplementationType>
inline typename FieldMapBasedTag<ImplementationType>::identifierType FieldMapBasedTag<ImplementationType>::fieldId(KnownField value) const
{
return static_cast<const ImplementationType *>(this)->internallyGetFieldId(value);
}
/*!
* \brief Returns the KnownField for the specified \a id.
* \remarks Must be implemented in internallyGetKnownField() when creating subclass.
*/
template<class ImplementationType>
inline KnownField FieldMapBasedTag<ImplementationType>::knownField(const identifierType &id) const
{
return static_cast<FieldMapBasedTag<ImplementationType> *>(this)->internallyGetKnownField(id);
}
template <class ImplementationType> template <class ImplementationType>
inline bool FieldMapBasedTag<ImplementationType>::supportsField(KnownField field) const inline bool FieldMapBasedTag<ImplementationType>::supportsField(KnownField field) const
{ {
@ -281,13 +338,23 @@ inline bool FieldMapBasedTag<ImplementationType>::supportsField(KnownField field
return fieldId(field) != def; return fieldId(field) != def;
} }
/*!
* \brief Default implementation for proposedDataType().
* \remarks Shadow in subclass to provide custom implementation.
*/
template<class ImplementationType>
inline TagDataType FieldMapBasedTag<ImplementationType>::internallyGetProposedDataType(const identifierType &id) const
{
return Tag::proposedDataType(knownField(id));
}
/*! /*!
* \brief Returns the proposed data type for the field with the specified \a id. * \brief Returns the proposed data type for the field with the specified \a id.
*/ */
template <class ImplementationType> template <class ImplementationType>
inline TagDataType FieldMapBasedTag<ImplementationType>::proposedDataType(const identifierType &id) const inline TagDataType FieldMapBasedTag<ImplementationType>::proposedDataType(const identifierType &id) const
{ {
return Tag::proposedDataType(knownField(id)); return static_cast<ImplementationType *>(this)->determineProposedDataType(id);
} }
/*! /*!

View File

@ -108,8 +108,9 @@ class FileElementTraits
* \class Media::GenericFileElement * \class Media::GenericFileElement
* \brief The GenericFileElement class helps to parse binary files which consist * \brief The GenericFileElement class helps to parse binary files which consist
* of an arboreal element strucutre. * of an arboreal element strucutre.
*
* \tparam ImplementationType Specifies the type of the actual implementation. * \tparam ImplementationType Specifies the type of the actual implementation.
* \remarks This template class is intended to be subclassed using
* with the "Curiously recurring template pattern".
*/ */
template <class ImplementationType> template <class ImplementationType>
class TAG_PARSER_EXPORT GenericFileElement : public StatusProvider class TAG_PARSER_EXPORT GenericFileElement : public StatusProvider

View File

@ -25,7 +25,8 @@ class TagFieldTraits
* might be assigned as well. The usage of the type info depends on the * might be assigned as well. The usage of the type info depends on the
* particular tag implementation. * particular tag implementation.
* *
* \remarks This template class is intended to be used * \tparam ImplementationType Specifies the type of the actual implementation.
* \remarks This template class is intended to be subclassed using
* with the "Curiously recurring template pattern". * with the "Curiously recurring template pattern".
*/ */
template <class ImplementationType> template <class ImplementationType>

View File

@ -17,7 +17,7 @@ namespace Media {
* \brief Implementation of Media::Tag for ID3v2 tags. * \brief Implementation of Media::Tag for ID3v2 tags.
*/ */
uint32 Id3v2Tag::fieldId(KnownField field) const Id3v2Tag::identifierType Id3v2Tag::internallyGetFieldId(KnownField field) const
{ {
using namespace Id3v2FrameIds; using namespace Id3v2FrameIds;
if(m_majorVersion >= 3) { if(m_majorVersion >= 3) {
@ -78,7 +78,7 @@ uint32 Id3v2Tag::fieldId(KnownField field) const
return 0; return 0;
} }
KnownField Id3v2Tag::knownField(const uint32 &id) const KnownField Id3v2Tag::internallyGetKnownField(const identifierType &id) const
{ {
using namespace Id3v2FrameIds; using namespace Id3v2FrameIds;
switch(id) { switch(id) {
@ -125,7 +125,7 @@ KnownField Id3v2Tag::knownField(const uint32 &id) const
} }
} }
TagDataType Id3v2Tag::proposedDataType(const uint32 &id) const TagDataType Id3v2Tag::internallyGetProposedDataType(const uint32 &id) const
{ {
using namespace Id3v2FrameIds; using namespace Id3v2FrameIds;
switch(id) { switch(id) {

View File

@ -66,6 +66,8 @@ public:
class TAG_PARSER_EXPORT Id3v2Tag : public FieldMapBasedTag<Id3v2Tag> class TAG_PARSER_EXPORT Id3v2Tag : public FieldMapBasedTag<Id3v2Tag>
{ {
friend class FieldMapBasedTag<Id3v2Tag>;
public: public:
Id3v2Tag(); Id3v2Tag();
@ -74,11 +76,6 @@ public:
static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf16LittleEndian; static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf16LittleEndian;
TagTextEncoding proposedTextEncoding() const; TagTextEncoding proposedTextEncoding() const;
bool canEncodingBeUsed(TagTextEncoding encoding) const; bool canEncodingBeUsed(TagTextEncoding encoding) const;
uint32 fieldId(KnownField value) const;
KnownField knownField(const uint32 &id) const;
TagDataType proposedDataType(const uint32 &id) const;
using FieldMapBasedTag<Id3v2Tag>::value;
using FieldMapBasedTag<Id3v2Tag>::setValue;
bool supportsDescription(KnownField field) const; bool supportsDescription(KnownField field) const;
bool supportsMimeType(KnownField field) const; bool supportsMimeType(KnownField field) const;
@ -98,6 +95,11 @@ public:
uint32 extendedHeaderSize() const; uint32 extendedHeaderSize() const;
uint32 paddingSize() const; uint32 paddingSize() const;
protected:
identifierType internallyGetFieldId(KnownField field) const;
KnownField internallyGetKnownField(const identifierType &id) const;
TagDataType internallyGetProposedDataType(const uint32 &id) const;
private: private:
byte m_majorVersion; byte m_majorVersion;
byte m_revisionVersion; byte m_revisionVersion;

View File

@ -15,7 +15,7 @@ namespace Media {
* \brief Implementation of Media::Tag for the Matroska container. * \brief Implementation of Media::Tag for the Matroska container.
*/ */
std::string MatroskaTag::fieldId(KnownField field) const MatroskaTag::identifierType MatroskaTag::internallyGetFieldId(KnownField field) const
{ {
using namespace MatroskaTagIds; using namespace MatroskaTagIds;
switch(field) { switch(field) {
@ -45,7 +45,7 @@ std::string MatroskaTag::fieldId(KnownField field) const
} }
} }
KnownField MatroskaTag::knownField(const std::string &id) const KnownField MatroskaTag::internallyGetKnownField(const identifierType &id) const
{ {
using namespace MatroskaTagIds; using namespace MatroskaTagIds;
static const map<string, KnownField> map({ static const map<string, KnownField> map({

View File

@ -61,6 +61,8 @@ public:
class TAG_PARSER_EXPORT MatroskaTag : public FieldMapBasedTag<MatroskaTag> class TAG_PARSER_EXPORT MatroskaTag : public FieldMapBasedTag<MatroskaTag>
{ {
friend class FieldMapBasedTag<MatroskaTag>;
public: public:
MatroskaTag(); MatroskaTag();
@ -71,13 +73,14 @@ public:
bool supportsTarget() const; bool supportsTarget() const;
TagTargetLevel targetLevel() const; TagTargetLevel targetLevel() const;
std::string fieldId(KnownField field) const;
KnownField knownField(const std::string &id) const;
void parse(EbmlElement &tagElement); void parse(EbmlElement &tagElement);
MatroskaTagMaker prepareMaking(); MatroskaTagMaker prepareMaking();
void make(std::ostream &stream); void make(std::ostream &stream);
protected:
identifierType internallyGetFieldId(KnownField field) const;
KnownField internallyGetKnownField(const identifierType &id) const;
private: private:
void parseTargets(EbmlElement &targetsElement); void parseTargets(EbmlElement &targetsElement);
}; };

View File

@ -119,7 +119,7 @@ const TagValue &Mp4Tag::value(const string mean, const string name) const
return (this->*static_cast<const TagValue &(Mp4Tag::*)(const string &, const string &) const>(&Mp4Tag::value))(mean, name); return (this->*static_cast<const TagValue &(Mp4Tag::*)(const string &, const string &) const>(&Mp4Tag::value))(mean, name);
} }
uint32 Mp4Tag::fieldId(KnownField field) const Mp4Tag::identifierType Mp4Tag::internallyGetFieldId(KnownField field) const
{ {
using namespace Mp4TagAtomIds; using namespace Mp4TagAtomIds;
switch(field) { switch(field) {
@ -146,7 +146,7 @@ uint32 Mp4Tag::fieldId(KnownField field) const
} }
} }
KnownField Mp4Tag::knownField(const uint32 &id) const KnownField Mp4Tag::internallyGetKnownField(const identifierType &id) const
{ {
using namespace Mp4TagAtomIds; using namespace Mp4TagAtomIds;
switch(id) { switch(id) {

View File

@ -101,6 +101,8 @@ public:
class TAG_PARSER_EXPORT Mp4Tag : public FieldMapBasedTag<Mp4Tag> class TAG_PARSER_EXPORT Mp4Tag : public FieldMapBasedTag<Mp4Tag>
{ {
friend class FieldMapBasedTag<Mp4Tag>;
public: public:
Mp4Tag(); Mp4Tag();
@ -109,8 +111,6 @@ public:
static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf8; static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf8;
bool canEncodingBeUsed(TagTextEncoding encoding) const; bool canEncodingBeUsed(TagTextEncoding encoding) const;
uint32 fieldId(KnownField field) const;
KnownField knownField(const uint32 &id) const;
bool supportsField(KnownField field) const; bool supportsField(KnownField field) const;
using FieldMapBasedTag<Mp4Tag>::value; using FieldMapBasedTag<Mp4Tag>::value;
const TagValue &value(KnownField value) const; const TagValue &value(KnownField value) const;
@ -136,6 +136,11 @@ public:
void parse(Mp4Atom &metaAtom); void parse(Mp4Atom &metaAtom);
Mp4TagMaker prepareMaking(); Mp4TagMaker prepareMaking();
void make(std::ostream &stream); void make(std::ostream &stream);
protected:
identifierType internallyGetFieldId(KnownField field) const;
KnownField internallyGetKnownField(const identifierType &id) const;
}; };
/*! /*!

View File

@ -44,7 +44,7 @@ bool VorbisComment::setValue(KnownField field, const TagValue &value)
} }
} }
string VorbisComment::fieldId(KnownField field) const VorbisComment::identifierType VorbisComment::internallyGetFieldId(KnownField field) const
{ {
using namespace VorbisCommentIds; using namespace VorbisCommentIds;
switch(field) { switch(field) {
@ -70,7 +70,7 @@ string VorbisComment::fieldId(KnownField field) const
} }
} }
KnownField VorbisComment::knownField(const string &id) const KnownField VorbisComment::internallyGetKnownField(const identifierType &id) const
{ {
using namespace VorbisCommentIds; using namespace VorbisCommentIds;
static const map<string, KnownField> fieldMap({ static const map<string, KnownField> fieldMap({

View File

@ -26,6 +26,8 @@ public:
class TAG_PARSER_EXPORT VorbisComment : public FieldMapBasedTag<VorbisComment> class TAG_PARSER_EXPORT VorbisComment : public FieldMapBasedTag<VorbisComment>
{ {
friend class FieldMapBasedTag<VorbisComment>;
public: public:
VorbisComment(); VorbisComment();
@ -34,10 +36,10 @@ public:
static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf8; static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf8;
bool canEncodingBeUsed(TagTextEncoding encoding) const; bool canEncodingBeUsed(TagTextEncoding encoding) const;
using FieldMapBasedTag<VorbisComment>::value;
const TagValue &value(KnownField field) const; const TagValue &value(KnownField field) const;
using FieldMapBasedTag<VorbisComment>::setValue;
bool setValue(KnownField field, const TagValue &value); bool setValue(KnownField field, const TagValue &value);
std::string fieldId(KnownField field) const;
KnownField knownField(const std::string &id) const;
void parse(OggIterator &iterator, VorbisCommentFlags flags = VorbisCommentFlags::None); void parse(OggIterator &iterator, VorbisCommentFlags flags = VorbisCommentFlags::None);
void parse(std::istream &stream, uint64 maxSize, VorbisCommentFlags flags = VorbisCommentFlags::None); void parse(std::istream &stream, uint64 maxSize, VorbisCommentFlags flags = VorbisCommentFlags::None);
@ -46,6 +48,10 @@ public:
const TagValue &vendor() const; const TagValue &vendor() const;
void setVendor(const TagValue &vendor); void setVendor(const TagValue &vendor);
protected:
identifierType internallyGetFieldId(KnownField field) const;
KnownField internallyGetKnownField(const identifierType &id) const;
private: private:
template<class StreamType> template<class StreamType>
void internalParse(StreamType &stream, uint64 maxSize, VorbisCommentFlags flags); void internalParse(StreamType &stream, uint64 maxSize, VorbisCommentFlags flags);