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 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 ImplementationType>
class FieldMapBasedTag : public Tag
{
public:
friend class FieldMapBasedTagTraits<ImplementationType>;
public:
typedef typename FieldMapBasedTagTraits<ImplementationType>::implementationType implementationType;
typedef typename FieldMapBasedTagTraits<ImplementationType>::fieldType fieldType;
typedef typename FieldMapBasedTagTraits<ImplementationType>::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<const TagValue *> values(const identifierType &id) 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 setValues(const identifierType &id, const std::vector<TagValue> &values);
bool setValues(KnownField field, const std::vector<TagValue> &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<identifierType, fieldType, compare> &fields() const;
std::multimap<identifierType, fieldType, compare> &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<ImplementationType> &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<identifierType, fieldType, compare> m_fields;
};
@ -112,6 +119,17 @@ TagTextEncoding FieldMapBasedTag<ImplementationType>::proposedTextEncoding() con
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.
* \sa Tag::value()
@ -119,8 +137,7 @@ TagTextEncoding FieldMapBasedTag<ImplementationType>::proposedTextEncoding() con
template <class ImplementationType>
inline const TagValue &FieldMapBasedTag<ImplementationType>::value(const identifierType &id) const
{
auto i = m_fields.find(id);
return i != m_fields.end() ? i->second.value() : TagValue::empty();
return static_cast<const ImplementationType *>(this)->internallyGetValue(id);
}
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.
* \sa Tag::setValue()
* \brief Default implementation for setValue().
* \remarks Shadow in subclass to provide custom implementation.
*/
template <class ImplementationType>
bool FieldMapBasedTag<ImplementationType>::setValue(const identifierType &id, const Media::TagValue &value)
template<class ImplementationType>
bool FieldMapBasedTag<ImplementationType>::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<ImplementationType>::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 <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.
* \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>
inline bool FieldMapBasedTag<ImplementationType>::hasField(const identifierType &id) const
template<class ImplementationType>
bool FieldMapBasedTag<ImplementationType>::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<ImplementationType>::hasField(const identifierType
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>
inline void FieldMapBasedTag<ImplementationType>::removeAllFields()
{
@ -274,6 +311,26 @@ unsigned int FieldMapBasedTag<ImplementationType>::fieldCount() const
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>
inline bool FieldMapBasedTag<ImplementationType>::supportsField(KnownField field) const
{
@ -281,13 +338,23 @@ inline bool FieldMapBasedTag<ImplementationType>::supportsField(KnownField field
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.
*/
template <class ImplementationType>
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
* \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 ImplementationType>
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
* 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 <class ImplementationType>

View File

@ -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) {

View File

@ -66,6 +66,8 @@ public:
class TAG_PARSER_EXPORT Id3v2Tag : public FieldMapBasedTag<Id3v2Tag>
{
friend class FieldMapBasedTag<Id3v2Tag>;
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<Id3v2Tag>::value;
using FieldMapBasedTag<Id3v2Tag>::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;

View File

@ -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<string, KnownField> map({

View File

@ -61,6 +61,8 @@ public:
class TAG_PARSER_EXPORT MatroskaTag : public FieldMapBasedTag<MatroskaTag>
{
friend class FieldMapBasedTag<MatroskaTag>;
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);
};

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);
}
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) {

View File

@ -101,6 +101,8 @@ public:
class TAG_PARSER_EXPORT Mp4Tag : public FieldMapBasedTag<Mp4Tag>
{
friend class FieldMapBasedTag<Mp4Tag>;
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<Mp4Tag>::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;
};
/*!

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;
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<string, KnownField> fieldMap({

View File

@ -26,6 +26,8 @@ public:
class TAG_PARSER_EXPORT VorbisComment : public FieldMapBasedTag<VorbisComment>
{
friend class FieldMapBasedTag<VorbisComment>;
public:
VorbisComment();
@ -34,10 +36,10 @@ public:
static constexpr TagTextEncoding defaultTextEncoding = TagTextEncoding::Utf8;
bool canEncodingBeUsed(TagTextEncoding encoding) const;
using FieldMapBasedTag<VorbisComment>::value;
const TagValue &value(KnownField field) const;
using FieldMapBasedTag<VorbisComment>::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<class StreamType>
void internalParse(StreamType &stream, uint64 maxSize, VorbisCommentFlags flags);