diff --git a/fieldbasedtag.h b/fieldbasedtag.h index c3d63e2..6172d07 100644 --- a/fieldbasedtag.h +++ b/fieldbasedtag.h @@ -24,18 +24,17 @@ class FieldMapBasedTagTraits * the tag fields using std::multimap. * * 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 - * of TagField. - * - * \tparam Compare Specifies the key comparsion function. Default is std::less. + * \remarks This template class is intended to be subclassed using + * with the "Curiously recurring template pattern". */ template class FieldMapBasedTag : public Tag { -public: friend class FieldMapBasedTagTraits; + +public: typedef typename FieldMapBasedTagTraits::implementationType implementationType; typedef typename FieldMapBasedTagTraits::fieldType fieldType; typedef typename FieldMapBasedTagTraits::fieldType::identifierType identifierType; @@ -46,29 +45,37 @@ public: TagType type() const; const char *typeName() 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; std::vector values(const identifierType &id) const; std::vector 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 setValues(const identifierType &id, const std::vector &values); bool setValues(KnownField field, const std::vector &values); bool hasField(KnownField field) const; - virtual bool hasField(const identifierType &id) const; // FIXME: use static polymorphism + bool hasField(const identifierType &id) const; void removeAllFields(); const std::multimap &fields() const; std::multimap &fields(); unsigned int fieldCount() const; - virtual identifierType fieldId(KnownField value) const = 0; // FIXME: use static polymorphism - virtual KnownField knownField(const identifierType &id) const = 0; // FIXME: use static polymorphism + identifierType fieldId(KnownField value) const; + KnownField knownField(const identifierType &id) const; bool supportsField(KnownField field) const; using Tag::proposedDataType; - virtual TagDataType proposedDataType(const identifierType &id) const; // FIXME: use static polymorphism + TagDataType proposedDataType(const identifierType &id) const; int insertFields(const FieldMapBasedTag &from, bool overwrite); unsigned int insertValues(const Tag &from, bool overwrite); 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: std::multimap m_fields; }; @@ -112,6 +119,17 @@ TagTextEncoding FieldMapBasedTag::proposedTextEncoding() con return ImplementationType::defaultTextEncoding; } +/*! + * \brief Default implementation for value(). + * \remarks Shadow in subclass to provide custom implementation. + */ +template +const TagValue &FieldMapBasedTag::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. * \sa Tag::value() @@ -119,8 +137,7 @@ TagTextEncoding FieldMapBasedTag::proposedTextEncoding() con template inline const TagValue &FieldMapBasedTag::value(const identifierType &id) const { - auto i = m_fields.find(id); - return i != m_fields.end() ? i->second.value() : TagValue::empty(); + return static_cast(this)->internallyGetValue(id); } template @@ -159,11 +176,11 @@ inline bool FieldMapBasedTag::setValue(KnownField field, con } /*! - * \brief Assigns the given \a value to the field with the specified \a id. - * \sa Tag::setValue() + * \brief Default implementation for setValue(). + * \remarks Shadow in subclass to provide custom implementation. */ -template -bool FieldMapBasedTag::setValue(const identifierType &id, const Media::TagValue &value) +template +bool FieldMapBasedTag::internallySetValue(const identifierType &id, const TagValue &value) { auto i = m_fields.find(id); if(i != m_fields.end()) { // field already exists -> set its value @@ -176,6 +193,16 @@ bool FieldMapBasedTag::setValue(const identifierType &id, co return true; } +/*! + * \brief Assigns the given \a value to the field with the specified \a id. + * \sa Tag::setValue() + */ +template +bool FieldMapBasedTag::setValue(const identifierType &id, const Media::TagValue &value) +{ + return static_cast(this)->internallySetValue(id, value); +} + /*! * \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 @@ -225,10 +252,11 @@ inline bool FieldMapBasedTag::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 -inline bool FieldMapBasedTag::hasField(const identifierType &id) const +template +bool FieldMapBasedTag::internallyHasField(const identifierType &id) const { for (auto range = m_fields.equal_range(id); range.first != range.second; ++range.first) { if(!range.first->second.value().isEmpty()) { @@ -238,6 +266,15 @@ inline bool FieldMapBasedTag::hasField(const identifierType return false; } +/*! + * \brief Returns an indication whether the field with the specified \a id is present. + */ +template +inline bool FieldMapBasedTag::hasField(const identifierType &id) const +{ + return static_cast(this)->internallyHasField(id); +} + template inline void FieldMapBasedTag::removeAllFields() { @@ -274,6 +311,26 @@ unsigned int FieldMapBasedTag::fieldCount() const return count; } +/*! + * \brief Returns the field ID for the specified \a value. + * \remarks Must be implemented in internallyGetFieldId() when creating subclass. + */ +template +inline typename FieldMapBasedTag::identifierType FieldMapBasedTag::fieldId(KnownField value) const +{ + return static_cast(this)->internallyGetFieldId(value); +} + +/*! + * \brief Returns the KnownField for the specified \a id. + * \remarks Must be implemented in internallyGetKnownField() when creating subclass. + */ +template +inline KnownField FieldMapBasedTag::knownField(const identifierType &id) const +{ + return static_cast *>(this)->internallyGetKnownField(id); +} + template inline bool FieldMapBasedTag::supportsField(KnownField field) const { @@ -281,13 +338,23 @@ inline bool FieldMapBasedTag::supportsField(KnownField field return fieldId(field) != def; } +/*! + * \brief Default implementation for proposedDataType(). + * \remarks Shadow in subclass to provide custom implementation. + */ +template +inline TagDataType FieldMapBasedTag::internallyGetProposedDataType(const identifierType &id) const +{ + return Tag::proposedDataType(knownField(id)); +} + /*! * \brief Returns the proposed data type for the field with the specified \a id. */ template inline TagDataType FieldMapBasedTag::proposedDataType(const identifierType &id) const { - return Tag::proposedDataType(knownField(id)); + return static_cast(this)->determineProposedDataType(id); } /*! diff --git a/genericfileelement.h b/genericfileelement.h index 8b9f473..0e1253e 100644 --- a/genericfileelement.h +++ b/genericfileelement.h @@ -108,8 +108,9 @@ class FileElementTraits * \class Media::GenericFileElement * \brief The GenericFileElement class helps to parse binary files which consist * of an arboreal element strucutre. - * * \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 TAG_PARSER_EXPORT GenericFileElement : public StatusProvider diff --git a/generictagfield.h b/generictagfield.h index b422c84..45c03ee 100644 --- a/generictagfield.h +++ b/generictagfield.h @@ -25,7 +25,8 @@ class TagFieldTraits * might be assigned as well. The usage of the type info depends on the * 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". */ template diff --git a/id3/id3v2tag.cpp b/id3/id3v2tag.cpp index f1380d6..62adf94 100644 --- a/id3/id3v2tag.cpp +++ b/id3/id3v2tag.cpp @@ -17,7 +17,7 @@ namespace Media { * \brief Implementation of Media::Tag for ID3v2 tags. */ -uint32 Id3v2Tag::fieldId(KnownField field) const +Id3v2Tag::identifierType Id3v2Tag::internallyGetFieldId(KnownField field) const { using namespace Id3v2FrameIds; if(m_majorVersion >= 3) { @@ -78,7 +78,7 @@ uint32 Id3v2Tag::fieldId(KnownField field) const return 0; } -KnownField Id3v2Tag::knownField(const uint32 &id) const +KnownField Id3v2Tag::internallyGetKnownField(const identifierType &id) const { using namespace Id3v2FrameIds; 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; switch(id) { diff --git a/id3/id3v2tag.h b/id3/id3v2tag.h index fcf56f6..4aa0fa1 100644 --- a/id3/id3v2tag.h +++ b/id3/id3v2tag.h @@ -66,6 +66,8 @@ public: class TAG_PARSER_EXPORT Id3v2Tag : public FieldMapBasedTag { + friend class FieldMapBasedTag; + public: Id3v2Tag(); @@ -74,11 +76,6 @@ public: static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf16LittleEndian; TagTextEncoding proposedTextEncoding() 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::value; - using FieldMapBasedTag::setValue; bool supportsDescription(KnownField field) const; bool supportsMimeType(KnownField field) const; @@ -98,6 +95,11 @@ public: uint32 extendedHeaderSize() const; uint32 paddingSize() const; +protected: + identifierType internallyGetFieldId(KnownField field) const; + KnownField internallyGetKnownField(const identifierType &id) const; + TagDataType internallyGetProposedDataType(const uint32 &id) const; + private: byte m_majorVersion; byte m_revisionVersion; diff --git a/matroska/matroskatag.cpp b/matroska/matroskatag.cpp index e358554..68e10f1 100644 --- a/matroska/matroskatag.cpp +++ b/matroska/matroskatag.cpp @@ -15,7 +15,7 @@ namespace Media { * \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; 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; static const map map({ diff --git a/matroska/matroskatag.h b/matroska/matroskatag.h index c13b489..9d58e17 100644 --- a/matroska/matroskatag.h +++ b/matroska/matroskatag.h @@ -61,6 +61,8 @@ public: class TAG_PARSER_EXPORT MatroskaTag : public FieldMapBasedTag { + friend class FieldMapBasedTag; + public: MatroskaTag(); @@ -71,13 +73,14 @@ public: bool supportsTarget() const; TagTargetLevel targetLevel() const; - std::string fieldId(KnownField field) const; - KnownField knownField(const std::string &id) const; - void parse(EbmlElement &tagElement); MatroskaTagMaker prepareMaking(); void make(std::ostream &stream); +protected: + identifierType internallyGetFieldId(KnownField field) const; + KnownField internallyGetKnownField(const identifierType &id) const; + private: void parseTargets(EbmlElement &targetsElement); }; diff --git a/mp4/mp4tag.cpp b/mp4/mp4tag.cpp index d0852d8..689bfb2 100644 --- a/mp4/mp4tag.cpp +++ b/mp4/mp4tag.cpp @@ -119,7 +119,7 @@ const TagValue &Mp4Tag::value(const string mean, const string name) const return (this->*static_cast(&Mp4Tag::value))(mean, name); } -uint32 Mp4Tag::fieldId(KnownField field) const +Mp4Tag::identifierType Mp4Tag::internallyGetFieldId(KnownField field) const { using namespace Mp4TagAtomIds; 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; switch(id) { diff --git a/mp4/mp4tag.h b/mp4/mp4tag.h index d75711f..0208318 100644 --- a/mp4/mp4tag.h +++ b/mp4/mp4tag.h @@ -101,6 +101,8 @@ public: class TAG_PARSER_EXPORT Mp4Tag : public FieldMapBasedTag { + friend class FieldMapBasedTag; + public: Mp4Tag(); @@ -109,8 +111,6 @@ public: static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf8; bool canEncodingBeUsed(TagTextEncoding encoding) const; - uint32 fieldId(KnownField field) const; - KnownField knownField(const uint32 &id) const; bool supportsField(KnownField field) const; using FieldMapBasedTag::value; const TagValue &value(KnownField value) const; @@ -136,6 +136,11 @@ public: void parse(Mp4Atom &metaAtom); Mp4TagMaker prepareMaking(); void make(std::ostream &stream); + +protected: + identifierType internallyGetFieldId(KnownField field) const; + KnownField internallyGetKnownField(const identifierType &id) const; + }; /*! diff --git a/vorbis/vorbiscomment.cpp b/vorbis/vorbiscomment.cpp index e835e1d..e493e04 100644 --- a/vorbis/vorbiscomment.cpp +++ b/vorbis/vorbiscomment.cpp @@ -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; 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; static const map fieldMap({ diff --git a/vorbis/vorbiscomment.h b/vorbis/vorbiscomment.h index 725b089..388901d 100644 --- a/vorbis/vorbiscomment.h +++ b/vorbis/vorbiscomment.h @@ -26,6 +26,8 @@ public: class TAG_PARSER_EXPORT VorbisComment : public FieldMapBasedTag { + friend class FieldMapBasedTag; + public: VorbisComment(); @@ -34,10 +36,10 @@ public: static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf8; bool canEncodingBeUsed(TagTextEncoding encoding) const; + using FieldMapBasedTag::value; const TagValue &value(KnownField field) const; + using FieldMapBasedTag::setValue; 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(std::istream &stream, uint64 maxSize, VorbisCommentFlags flags = VorbisCommentFlags::None); @@ -46,6 +48,10 @@ public: const TagValue &vendor() const; void setVendor(const TagValue &vendor); +protected: + identifierType internallyGetFieldId(KnownField field) const; + KnownField internallyGetKnownField(const identifierType &id) const; + private: template void internalParse(StreamType &stream, uint64 maxSize, VorbisCommentFlags flags);