tagparser/fieldbasedtag.h

419 lines
14 KiB
C
Raw Normal View History

#ifndef TAG_PARSER_FIELDBASEDTAG_H
#define TAG_PARSER_FIELDBASEDTAG_H
2015-04-22 19:22:01 +02:00
2015-09-06 19:57:33 +02:00
#include "./tag.h"
2015-04-22 19:22:01 +02:00
#include <map>
#include <functional>
namespace TagParser {
2015-04-22 19:22:01 +02:00
/*!
* \class Media::FieldMapBasedTagTraits
* \brief Defines traits for the specified \a ImplementationType.
*
* A template specialization for each FieldMapBasedTag subclass must be provided.
*/
template<typename ImplementationType>
class FieldMapBasedTagTraits
{};
2015-04-22 19:22:01 +02:00
/*!
* \class Media::FieldMapBasedTag
* \brief The FieldMapBasedTag provides a generic implementation of Tag which stores
* the tag fields using std::multimap.
*
* The FieldMapBasedTag class only provides the interface and common functionality.
* It is meant to be subclassed using CRTP pattern.
2015-04-22 19:22:01 +02:00
*
* \remarks This template class is intended to be subclassed using
* with the "Curiously recurring template pattern".
2015-04-22 19:22:01 +02:00
*/
template <class ImplementationType>
2015-04-22 19:22:01 +02:00
class FieldMapBasedTag : public Tag
{
friend class FieldMapBasedTagTraits<ImplementationType>;
public:
typedef typename FieldMapBasedTagTraits<ImplementationType>::FieldType FieldType;
typedef typename FieldMapBasedTagTraits<ImplementationType>::FieldType::IdentifierType IdentifierType;
typedef typename FieldMapBasedTagTraits<ImplementationType>::Compare Compare;
2015-04-22 19:22:01 +02:00
FieldMapBasedTag();
TagType type() const;
const char *typeName() const;
TagTextEncoding proposedTextEncoding() const;
const TagValue &value(const IdentifierType &id) const;
2016-08-05 01:46:31 +02:00
const TagValue &value(KnownField field) const;
std::vector<const TagValue *> values(const IdentifierType &id) const;
std::vector<const TagValue *> values(KnownField field) const;
bool setValue(const IdentifierType &id, const TagValue &value);
2016-08-05 01:46:31 +02:00
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);
2016-08-05 01:46:31 +02:00
bool hasField(KnownField field) const;
bool hasField(const IdentifierType &id) const;
2016-08-05 01:46:31 +02:00
void removeAllFields();
const std::multimap<IdentifierType, FieldType, Compare> &fields() const;
std::multimap<IdentifierType, FieldType, Compare> &fields();
2016-08-05 01:46:31 +02:00
unsigned int fieldCount() const;
IdentifierType fieldId(KnownField value) const;
KnownField knownField(const IdentifierType &id) const;
2016-08-05 01:46:31 +02:00
bool supportsField(KnownField field) const;
2015-04-22 19:22:01 +02:00
using Tag::proposedDataType;
TagDataType proposedDataType(const IdentifierType &id) const;
int insertFields(const FieldMapBasedTag<ImplementationType> &from, bool overwrite);
2016-08-05 01:46:31 +02:00
unsigned int insertValues(const Tag &from, bool overwrite);
void ensureTextValuesAreProperlyEncoded();
2015-04-22 19:22:01 +02:00
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;
2015-04-22 19:22:01 +02:00
private:
std::multimap<IdentifierType, FieldType, Compare> m_fields;
2015-04-22 19:22:01 +02:00
};
/*!
* \fn FieldMapBasedTag::fieldId()
* \brief Returns the ID for the specified \a field.
*
* Needs to be implemented when subclassing.
*/
/*!
* \fn FieldMapBasedTag::knownField()
* \brief Returns the field for the specified \a ID.
*
* Needs to be implemented when subclassing.
*/
/*!
* \brief Constructs a new FieldMapBasedTag.
*/
template <class ImplementationType>
FieldMapBasedTag<ImplementationType>::FieldMapBasedTag()
2015-04-22 19:22:01 +02:00
{}
template <class ImplementationType>
TagType FieldMapBasedTag<ImplementationType>::type() const
{
return ImplementationType::tagType;
}
template <class ImplementationType>
const char *FieldMapBasedTag<ImplementationType>::typeName() const
{
return ImplementationType::tagName;
}
template<class ImplementationType>
TagTextEncoding FieldMapBasedTag<ImplementationType>::proposedTextEncoding() const
{
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();
}
2015-04-22 19:22:01 +02:00
/*!
* \brief Returns the value of the field with the specified \a id.
2016-08-04 00:16:19 +02:00
* \sa Tag::value()
2015-04-22 19:22:01 +02:00
*/
template <class ImplementationType>
inline const TagValue &FieldMapBasedTag<ImplementationType>::value(const IdentifierType &id) const
2015-04-22 19:22:01 +02:00
{
return static_cast<const ImplementationType *>(this)->internallyGetValue(id);
2015-04-22 19:22:01 +02:00
}
template <class ImplementationType>
inline const TagValue &FieldMapBasedTag<ImplementationType>::value(KnownField field) const
2016-08-04 00:16:19 +02:00
{
return value(fieldId(field));
}
2015-04-22 19:22:01 +02:00
/*!
* \brief Returns the values of the field with the specified \a id.
2016-08-04 00:16:19 +02:00
* \sa Tag::values()
2015-04-22 19:22:01 +02:00
*/
template <class ImplementationType>
inline std::vector<const TagValue *> FieldMapBasedTag<ImplementationType>::values(const IdentifierType &id) const
2015-04-22 19:22:01 +02:00
{
auto range = m_fields.equal_range(id);
std::vector<const TagValue *> values;
2015-04-22 19:22:01 +02:00
for(auto i = range.first; i != range.second; ++i) {
2016-08-04 00:16:19 +02:00
if(!i->second.value().isEmpty()) {
values.push_back(&i->second.value());
}
2015-04-22 19:22:01 +02:00
}
return values;
}
template <class ImplementationType>
inline std::vector<const TagValue *> FieldMapBasedTag<ImplementationType>::values(KnownField field) const
2016-08-04 00:16:19 +02:00
{
return values(fieldId(field));
}
template <class ImplementationType>
inline bool FieldMapBasedTag<ImplementationType>::setValue(KnownField field, const TagValue &value)
2015-04-22 19:22:01 +02:00
{
return setValue(fieldId(field), value);
}
/*!
* \brief Default implementation for setValue().
* \remarks Shadow in subclass to provide custom implementation.
2015-04-22 19:22:01 +02:00
*/
template<class ImplementationType>
bool FieldMapBasedTag<ImplementationType>::internallySetValue(const IdentifierType &id, const TagValue &value)
2015-04-22 19:22:01 +02:00
{
auto i = m_fields.find(id);
if(i != m_fields.end()) { // field already exists -> set its value
i->second.setValue(value);
2016-08-04 00:16:19 +02:00
} else if(!value.isEmpty()) { // field doesn't exist -> create new one if value is not null
m_fields.insert(std::make_pair(id, FieldType(id, value)));
2015-04-22 19:22:01 +02:00
} else { // otherwise return false
return false;
}
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 TagParser::TagValue &value)
{
return static_cast<ImplementationType *>(this)->internallySetValue(id, value);
}
2016-08-04 00:16:19 +02:00
/*!
* \brief Assigns the given \a values to the field with the specified \a id.
2017-03-01 18:21:00 +01:00
* \remarks There might me more than one value assigned to an \a id. Whereas setValue() only alters the first value, this
2016-12-27 23:40:36 +01:00
* method will replace all currently assigned values with the specified \a values.
2016-08-04 00:16:19 +02:00
* \sa Tag::setValues()
*/
template <class ImplementationType>
bool FieldMapBasedTag<ImplementationType>::setValues(const IdentifierType &id, const std::vector<TagValue> &values)
2016-08-04 00:16:19 +02:00
{
auto valuesIterator = values.cbegin();
2016-08-04 00:16:19 +02:00
auto range = m_fields.equal_range(id);
// iterate through all specified and all existing values
for(; valuesIterator != values.cend() && range.first != range.second; ++valuesIterator) {
// replace existing value with non-empty specified value
2016-08-04 00:16:19 +02:00
if(!valuesIterator->isEmpty()) {
range.first->second.setValue(*valuesIterator);
++range.first;
}
}
// add remaining specified values (there are more specified values than existing ones)
for(; valuesIterator != values.cend(); ++valuesIterator) {
m_fields.insert(std::make_pair(id, FieldType(id, *valuesIterator)));
2016-08-04 00:16:19 +02:00
}
// remove remaining existing values (there are more existing values than specified ones)
2016-08-04 00:16:19 +02:00
for(; range.first != range.second; ++range.first) {
range.first->second.setValue(TagValue());
}
return true;
}
/*!
* \brief Assigns the given \a values to the field with the specified \a id.
2017-03-01 18:21:00 +01:00
* \remarks There might me more than one value assigned to a \a field. Whereas setValue() only alters the first value, this
2016-08-04 00:16:19 +02:00
* method will replace all currently assigned values with the specified \a values.
2016-12-27 23:40:36 +01:00
* \sa Tag::setValues()
2016-08-04 00:16:19 +02:00
*/
template <class ImplementationType>
bool FieldMapBasedTag<ImplementationType>::setValues(KnownField field, const std::vector<TagValue> &values)
2016-08-04 00:16:19 +02:00
{
return setValues(fieldId(field), values);
}
template <class ImplementationType>
inline bool FieldMapBasedTag<ImplementationType>::hasField(KnownField field) const
2015-04-22 19:22:01 +02:00
{
return hasField(fieldId(field));
}
/*!
* \brief Default implementation for hasField().
* \remarks Shadow in subclass to provide custom implementation.
2015-04-22 19:22:01 +02:00
*/
template<class ImplementationType>
bool FieldMapBasedTag<ImplementationType>::internallyHasField(const IdentifierType &id) const
2015-04-22 19:22:01 +02:00
{
for (auto range = m_fields.equal_range(id); range.first != range.second; ++range.first) {
if(!range.first->second.value().isEmpty()) {
return true;
}
}
return false;
2015-04-22 19:22:01 +02:00
}
/*!
* \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()
2015-04-22 19:22:01 +02:00
{
m_fields.clear();
}
/*!
2016-08-04 00:16:19 +02:00
* \brief Returns the fields of the tag by providing direct access to the field map of the tag.
2015-04-22 19:22:01 +02:00
*/
template <class ImplementationType>
inline auto FieldMapBasedTag<ImplementationType>::fields() const -> const std::multimap<IdentifierType, FieldType, Compare> &
2015-04-22 19:22:01 +02:00
{
return m_fields;
}
/*!
2016-08-04 00:16:19 +02:00
* \brief Returns the fields of the tag by providing direct access to the field map of the tag.
2015-04-22 19:22:01 +02:00
*/
template <class ImplementationType>
inline auto FieldMapBasedTag<ImplementationType>::fields() -> std::multimap<IdentifierType, FieldType, Compare> &
2015-04-22 19:22:01 +02:00
{
return m_fields;
}
template <class ImplementationType>
unsigned int FieldMapBasedTag<ImplementationType>::fieldCount() const
2015-04-22 19:22:01 +02:00
{
2016-08-04 00:16:19 +02:00
unsigned int count = 0;
2015-08-16 23:39:42 +02:00
for(const auto &field : m_fields) {
if(!field.second.value().isEmpty()) {
++count;
}
}
return count;
2015-04-22 19:22:01 +02:00
}
/*!
* \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
2015-04-22 19:22:01 +02:00
{
static IdentifierType def;
2015-04-22 19:22:01 +02:00
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));
}
2015-04-22 19:22:01 +02:00
/*!
* \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
2015-04-22 19:22:01 +02:00
{
return static_cast<ImplementationType *>(this)->determineProposedDataType(id);
2015-04-22 19:22:01 +02:00
}
/*!
* \brief Inserts all fields \a from another tag of the same field type and compare function.
* \param from Specifies the tag the fields should be inserted from.
* \param overwrite Indicates whether existing fields should be overwritten.
* \return Returns the number of fields that have been inserted.
*/
template <class ImplementationType>
int FieldMapBasedTag<ImplementationType>::insertFields(const FieldMapBasedTag<ImplementationType> &from, bool overwrite)
2015-04-22 19:22:01 +02:00
{
int fieldsInserted = 0;
for(const auto &pair : from.fields()) {
const FieldType &fromField = pair.second;
2018-03-07 01:11:42 +01:00
if(fromField.value().isEmpty()) {
2015-04-22 19:22:01 +02:00
continue;
2018-03-07 01:11:42 +01:00
}
2015-04-22 19:22:01 +02:00
bool fieldInserted = false;
auto range = fields().equal_range(fromField.id());
for(auto i = range.first; i != range.second; ++i) {
FieldType &ownField = i->second;
2015-04-22 19:22:01 +02:00
if((fromField.isTypeInfoAssigned() && ownField.isTypeInfoAssigned()
&& fromField.typeInfo() == ownField.typeInfo())
|| (!fromField.isTypeInfoAssigned() && ! ownField.isTypeInfoAssigned())) {
if(overwrite || ownField.value().isEmpty()) {
ownField = fromField;
++fieldsInserted;
}
fieldInserted = true;
continue;
}
}
if(!fieldInserted) {
2016-08-04 00:16:19 +02:00
fields().insert(std::make_pair(fromField.id(), fromField));
2015-04-22 19:22:01 +02:00
++fieldsInserted;
}
}
return fieldsInserted;
}
template <class ImplementationType>
unsigned int FieldMapBasedTag<ImplementationType>::insertValues(const Tag &from, bool overwrite)
2015-04-22 19:22:01 +02:00
{
if(type() == from.type()) {
// the tags are of the same type, we can insert the fields directly
return insertFields(static_cast<const FieldMapBasedTag<ImplementationType> &>(from), overwrite);
2015-04-22 19:22:01 +02:00
} else {
return Tag::insertValues(from, overwrite);
}
}
template <class ImplementationType>
void FieldMapBasedTag<ImplementationType>::ensureTextValuesAreProperlyEncoded()
2016-08-05 01:46:31 +02:00
{
for(auto &field : fields()) {
field.second.value().convertDataEncodingForTag(this);
}
}
2015-04-22 19:22:01 +02:00
}
#endif // TAG_PARSER_FIELDBASEDTAG_H