Tag Parser 12.4.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
Loading...
Searching...
No Matches
fieldbasedtag.h
Go to the documentation of this file.
1#ifndef TAG_PARSER_FIELDBASEDTAG_H
2#define TAG_PARSER_FIELDBASEDTAG_H
3
4#include "./tag.h"
5
6#include <functional>
7#include <map>
8
9namespace TagParser {
10
17template <typename ImplementationType> class FieldMapBasedTagTraits {};
18
30template <class ImplementationType> class FieldMapBasedTag : public Tag {
31 friend class FieldMapBasedTagTraits<ImplementationType>;
32
33public:
37
39
40 TagType type() const;
41 std::string_view typeName() const;
43 const TagValue &value(const IdentifierType &id) const;
44 const TagValue &value(KnownField field) const;
45 std::vector<const TagValue *> values(const IdentifierType &id) const;
46 std::vector<const TagValue *> values(KnownField field) const;
47 bool setValue(const IdentifierType &id, const TagValue &value);
48 bool setValue(KnownField field, const TagValue &value);
49 bool setValues(const IdentifierType &id, const std::vector<TagValue> &values);
50 bool setValues(KnownField field, const std::vector<TagValue> &values);
51 bool hasField(KnownField field) const;
52 bool hasField(const IdentifierType &id) const;
54 const std::multimap<IdentifierType, FieldType, Compare> &fields() const;
55 std::multimap<IdentifierType, FieldType, Compare> &fields();
56 std::size_t fieldCount() const;
59 bool supportsField(KnownField field) const;
62 std::size_t insertFields(const FieldMapBasedTag<ImplementationType> &from, bool overwrite);
63 std::size_t insertValues(const Tag &from, bool overwrite);
65
66protected:
68
70 void internallyGetValuesFromField(const FieldType &field, std::vector<const TagValue *> &values) const;
71 std::vector<const TagValue *> internallyGetValues(const IdentifierType &id) const;
73 bool internallySetValues(const IdentifierType &id, const std::vector<TagValue> &values);
74 bool internallyHasField(const IdentifierType &id) const;
75 // no default implementation: IdentifierType internallyGetFieldId(KnownField field) const;
76 // no default implementation: KnownField internallyGetKnownField(const IdentifierType &id) const;
78
79private:
80 std::multimap<IdentifierType, FieldType, Compare> m_fields;
81};
82
101{
102}
103
104template <class ImplementationType> TagType FieldMapBasedTag<ImplementationType>::type() const
105{
106 return ImplementationType::tagType;
107}
108
109template <class ImplementationType> std::string_view FieldMapBasedTag<ImplementationType>::typeName() const
110{
111 return ImplementationType::tagName;
112}
113
115{
116 return ImplementationType::defaultTextEncoding;
117}
118
123template <class ImplementationType> const TagValue &FieldMapBasedTag<ImplementationType>::internallyGetValue(const IdentifierType &id) const
124{
125 auto i = m_fields.find(id);
126 return i != m_fields.end() ? i->second.value() : TagValue::empty();
127}
128
133template <class ImplementationType>
135 const FieldMapBasedTag<ImplementationType>::FieldType &field, std::vector<const TagValue *> &values) const
136{
137 if (!field.value().isEmpty()) {
138 values.emplace_back(&field.value());
139 }
140}
141
146template <class ImplementationType>
148{
149 auto range = m_fields.equal_range(id);
150 std::vector<const TagValue *> values;
151 for (auto i = range.first; i != range.second; ++i) {
152 static_cast<const ImplementationType *>(this)->internallyGetValuesFromField(i->second, values);
153 }
154 return values;
155}
156
161template <class ImplementationType> inline const TagValue &FieldMapBasedTag<ImplementationType>::value(const IdentifierType &id) const
162{
163 return static_cast<const ImplementationType *>(this)->internallyGetValue(id);
164}
165
166template <class ImplementationType> inline const TagValue &FieldMapBasedTag<ImplementationType>::value(KnownField field) const
167{
168 return value(fieldId(field));
169}
170
175template <class ImplementationType> inline std::vector<const TagValue *> FieldMapBasedTag<ImplementationType>::values(const IdentifierType &id) const
176{
177 return static_cast<const ImplementationType *>(this)->internallyGetValues(id);
178}
179
180template <class ImplementationType> inline std::vector<const TagValue *> FieldMapBasedTag<ImplementationType>::values(KnownField field) const
181{
182 return static_cast<const ImplementationType *>(this)->values(fieldId(field));
183}
184
185template <class ImplementationType> inline bool FieldMapBasedTag<ImplementationType>::setValue(KnownField field, const TagValue &value)
186{
187 const auto id = fieldId(field);
188 if constexpr (std::is_arithmetic_v<IdentifierType>) {
189 if (!id) {
190 return false;
191 }
192 } else {
193 if (id.empty()) {
194 return false;
195 }
196 }
197 return setValue(id, value);
198}
199
204template <class ImplementationType> bool FieldMapBasedTag<ImplementationType>::internallySetValue(const IdentifierType &id, const TagValue &value)
205{
206 auto i = m_fields.find(id);
207 if (i != m_fields.end()) { // field already exists -> set its value
208 i->second.setValue(value);
209 } else if (!value.isEmpty()) { // field doesn't exist -> create new one if value is not null
210 m_fields.insert(std::make_pair(id, FieldType(id, value)));
211 } else { // otherwise return false
212 return false;
213 }
214 return true;
215}
216
221template <class ImplementationType>
223{
224 auto valuesIterator = values.cbegin();
225 auto range = m_fields.equal_range(id);
226 // iterate through all specified and all existing values
227 for (; valuesIterator != values.cend() && range.first != range.second; ++valuesIterator) {
228 // replace existing value with non-empty specified value
229 if (!valuesIterator->isEmpty()) {
230 auto &field = range.first->second;
231 field.clearValue();
232 field.setValue(*valuesIterator);
233 ++range.first;
234 }
235 }
236 // add remaining specified values (there are more specified values than existing ones)
237 for (; valuesIterator != values.cend(); ++valuesIterator) {
238 if (!valuesIterator->isEmpty()) {
239 m_fields.insert(std::make_pair(id, FieldType(id, *valuesIterator)));
240 }
241 }
242 // remove remaining existing values (there are more existing values than specified ones)
243 for (; range.first != range.second; ++range.first) {
244 range.first->second.clearValue();
245 }
246 return true;
247}
248
253template <class ImplementationType> bool FieldMapBasedTag<ImplementationType>::setValue(const IdentifierType &id, const TagParser::TagValue &value)
254{
255 return static_cast<ImplementationType *>(this)->internallySetValue(id, value);
256}
257
264template <class ImplementationType>
265bool FieldMapBasedTag<ImplementationType>::setValues(const IdentifierType &id, const std::vector<TagValue> &values)
266{
267 return static_cast<ImplementationType *>(this)->internallySetValues(id, values);
268}
269
276template <class ImplementationType> bool FieldMapBasedTag<ImplementationType>::setValues(KnownField field, const std::vector<TagValue> &values)
277{
278 const auto id = fieldId(field);
279 if constexpr (std::is_arithmetic_v<IdentifierType>) {
280 if (!id) {
281 return false;
282 }
283 } else {
284 if (id.empty()) {
285 return false;
286 }
287 }
288 return setValues(id, values);
289}
290
291template <class ImplementationType> inline bool FieldMapBasedTag<ImplementationType>::hasField(KnownField field) const
292{
293 return hasField(fieldId(field));
294}
295
300template <class ImplementationType> bool FieldMapBasedTag<ImplementationType>::internallyHasField(const IdentifierType &id) const
301{
302 for (auto range = m_fields.equal_range(id); range.first != range.second; ++range.first) {
303 if (!range.first->second.value().isEmpty()) {
304 return true;
305 }
306 }
307 return false;
308}
309
313template <class ImplementationType> inline bool FieldMapBasedTag<ImplementationType>::hasField(const IdentifierType &id) const
314{
315 return static_cast<const ImplementationType *>(this)->internallyHasField(id);
316}
317
318template <class ImplementationType> inline void FieldMapBasedTag<ImplementationType>::removeAllFields()
319{
320 m_fields.clear();
321}
322
326template <class ImplementationType>
328{
329 return m_fields;
330}
331
335template <class ImplementationType> inline auto FieldMapBasedTag<ImplementationType>::fields() -> std::multimap<IdentifierType, FieldType, Compare> &
336{
337 return m_fields;
338}
339
340template <class ImplementationType> std::size_t FieldMapBasedTag<ImplementationType>::fieldCount() const
341{
342 auto count = std::size_t(0);
343 for (const auto &field : m_fields) {
344 if (!field.second.value().isEmpty()) {
345 ++count;
346 }
347 }
348 return count;
349}
350
355template <class ImplementationType>
357{
358 return static_cast<const ImplementationType *>(this)->internallyGetFieldId(value);
359}
360
365template <class ImplementationType> inline KnownField FieldMapBasedTag<ImplementationType>::knownField(const IdentifierType &id) const
366{
367 return static_cast<const ImplementationType *>(this)->internallyGetKnownField(id);
368}
369
370template <class ImplementationType> inline bool FieldMapBasedTag<ImplementationType>::supportsField(KnownField field) const
371{
372 static const auto def = IdentifierType();
373 return fieldId(field) != def;
374}
375
380template <class ImplementationType>
385
389template <class ImplementationType> inline TagDataType FieldMapBasedTag<ImplementationType>::proposedDataType(const IdentifierType &id) const
390{
391 return static_cast<ImplementationType *>(this)->determineProposedDataType(id);
392}
393
400template <class ImplementationType>
402{
403 auto fieldsInserted = std::size_t(0);
404 for (const auto &pair : from.fields()) {
405 const FieldType &fromField = pair.second;
406 if (fromField.value().isEmpty()) {
407 continue;
408 }
409 bool fieldInserted = false;
410 auto range = fields().equal_range(fromField.id());
411 for (auto i = range.first; i != range.second; ++i) {
412 FieldType &ownField = i->second;
413 if ((fromField.isTypeInfoAssigned() && ownField.isTypeInfoAssigned() && fromField.typeInfo() == ownField.typeInfo())
414 || (!fromField.isTypeInfoAssigned() && !ownField.isTypeInfoAssigned())) {
415 if (overwrite || ownField.value().isEmpty()) {
416 ownField = fromField;
417 ++fieldsInserted;
418 }
419 fieldInserted = true;
420 continue;
421 }
422 }
423 if (!fieldInserted) {
424 fields().insert(std::make_pair(fromField.id(), fromField));
425 ++fieldsInserted;
426 }
427 }
428 return fieldsInserted;
429}
430
431template <class ImplementationType> std::size_t FieldMapBasedTag<ImplementationType>::insertValues(const Tag &from, bool overwrite)
432{
433 if (type() == from.type()) {
434 // the tags are of the same type, we can insert the fields directly
435 return insertFields(static_cast<const FieldMapBasedTag<ImplementationType> &>(from), overwrite);
436 } else {
437 return Tag::insertValues(from, overwrite);
438 }
439}
440
442{
443 for (auto &field : fields()) {
444 field.second.value().convertDataEncodingForTag(this);
445 }
446}
447
448} // namespace TagParser
449
450#endif // TAG_PARSER_FIELDBASEDTAG_H
Defines traits for the specified ImplementationType.
The FieldMapBasedTag provides a generic implementation of Tag which stores the tag fields using std::...
bool hasField(const IdentifierType &id) const
Returns an indication whether the field with the specified id is present.
bool setValue(const IdentifierType &id, const TagValue &value)
Assigns the given value to the field with the specified id.
void ensureTextValuesAreProperlyEncoded()
Ensures the encoding of all assigned text values is supported by the tag by converting the character ...
bool setValue(KnownField field, const TagValue &value)
Assigns the given value to the specified field.
bool hasField(KnownField field) const
Returns an indication whether the specified field is present.
std::vector< const TagValue * > values(KnownField field) const
Returns the values of the specified field.
std::multimap< IdentifierType, FieldType, Compare > & fields()
Returns the fields of the tag by providing direct access to the field map of the tag.
std::vector< const TagValue * > internallyGetValues(const IdentifierType &id) const
Default implementation for values().
typename FieldMapBasedTagTraits< ImplementationType >::Compare Compare
std::string_view typeName() const
Returns the type name of the tag as C-style string.
typename FieldMapBasedTagTraits< ImplementationType >::FieldType::IdentifierType IdentifierType
const TagValue & value(KnownField field) const
Returns the value of the specified field.
FieldMapBasedTag()
Constructs a new FieldMapBasedTag.
TagTextEncoding proposedTextEncoding() const
Returns the proposed text encoding.
const TagValue & value(const IdentifierType &id) const
Returns the value of the field with the specified id.
void removeAllFields()
Removes all fields from the tag.
const std::multimap< IdentifierType, FieldType, Compare > & fields() const
Returns the fields of the tag by providing direct access to the field map of the tag.
TagDataType proposedDataType(const IdentifierType &id) const
Returns the proposed data type for the field with the specified id.
bool internallySetValues(const IdentifierType &id, const std::vector< TagValue > &values)
Default implementation for setValues().
TagType type() const
Returns the type of the tag as TagParser::TagType.
std::size_t insertFields(const FieldMapBasedTag< ImplementationType > &from, bool overwrite)
Inserts all fields from another tag of the same field type and compare function.
typename FieldMapBasedTagTraits< ImplementationType >::FieldType FieldType
TagDataType internallyGetProposedDataType(const IdentifierType &id) const
Default implementation for proposedDataType().
void internallyGetValuesFromField(const FieldType &field, std::vector< const TagValue * > &values) const
Default way to gather values from a field in internallyGetValues().
KnownField knownField(const IdentifierType &id) const
Returns the field for the specified ID.
std::vector< const TagValue * > values(const IdentifierType &id) const
Returns the values of the field with the specified id.
const TagValue & internallyGetValue(const IdentifierType &id) const
Default implementation for value().
bool internallyHasField(const IdentifierType &id) const
Default implementation for hasField().
bool setValues(const IdentifierType &id, const std::vector< TagValue > &values)
Assigns the given values to the field with the specified id.
std::size_t fieldCount() const
Returns the number of present fields.
bool supportsField(KnownField field) const
Returns an indication whether the specified field is supported by the tag.
bool setValues(KnownField field, const std::vector< TagValue > &values)
Assigns the given values to the field with the specified id.
IdentifierType fieldId(KnownField value) const
Returns the ID for the specified field.
std::size_t insertValues(const Tag &from, bool overwrite)
Inserts all compatible values from another Tag.
bool internallySetValue(const IdentifierType &id, const TagValue &value)
Default implementation for setValue().
The TagValue class wraps values of different types.
static const TagValue & empty()
Returns a default-constructed TagValue where TagValue::isNull() and TagValue::isEmpty() both return t...
bool isEmpty() const
Returns whether no or an empty value is assigned.
Definition tagvalue.h:490
The Tag class is used to store, read and write tag information.
virtual std::size_t insertValues(const Tag &from, bool overwrite)
Inserts all compatible values from another Tag.
Definition tag.cpp:89
virtual TagType type() const
Returns the type of the tag as TagParser::TagType.
Definition tag.h:222
virtual TagDataType proposedDataType(KnownField field) const
Returns the proposed data type for the specified field as TagDataType.
Definition tag.h:292
Contains all classes and functions of the TagInfo library.
Definition aaccodebook.h:10
KnownField
Specifies the field.
Definition tag.h:37
TagTextEncoding
Specifies the text encoding.
Definition tagvalue.h:29
TagType
Specifies the tag type.
Definition tagtype.h:11
TagDataType
Specifies the data type.
Definition tagvalue.h:119