575 lines
19 KiB
C++
575 lines
19 KiB
C++
#ifndef TAG_PARSER_TAGVALUE_H
|
|
#define TAG_PARSER_TAGVALUE_H
|
|
|
|
#include "./positioninset.h"
|
|
|
|
#include <c++utilities/chrono/datetime.h>
|
|
#include <c++utilities/chrono/timespan.h>
|
|
#include <c++utilities/conversion/binaryconversion.h>
|
|
#include <c++utilities/misc/traits.h>
|
|
|
|
#include <cstring>
|
|
#include <iosfwd>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
namespace TagParser {
|
|
|
|
class Tag;
|
|
class Id3v2Frame;
|
|
|
|
/*!
|
|
* \brief Specifies the text encoding.
|
|
*/
|
|
enum class TagTextEncoding : unsigned int {
|
|
Latin1, /**< ISO/IEC 8859-1 aka "Latin 1" */
|
|
Utf8, /**< UTF-8 */
|
|
Utf16LittleEndian, /**< UTF-16 (little endian) */
|
|
Utf16BigEndian, /**< UTF-16 (big endian) */
|
|
Unspecified /**< unspecified encoding */
|
|
};
|
|
|
|
/*!
|
|
* \brief Returns the size of one character for the specified \a encoding in bytes.
|
|
* \remarks For variable-width encoding the minimum size is returned.
|
|
*/
|
|
constexpr int characterSize(TagTextEncoding encoding)
|
|
{
|
|
switch (encoding) {
|
|
case TagTextEncoding::Latin1:
|
|
case TagTextEncoding::Utf8:
|
|
return 1;
|
|
case TagTextEncoding::Utf16LittleEndian:
|
|
case TagTextEncoding::Utf16BigEndian:
|
|
return 2;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* \brief Specifies the data type.
|
|
*/
|
|
enum class TagDataType : unsigned int {
|
|
Text, /**< text/string */
|
|
Integer, /**< integer */
|
|
PositionInSet, /**< position in set, see TagParser::PositionInSet */
|
|
StandardGenreIndex, /**< pre-defined genre name denoted by numerical code */
|
|
TimeSpan, /**< time span, see ChronoUtils::TimeSpan */
|
|
DateTime, /**< date time, see ChronoUtils::DateTime */
|
|
Picture, /**< picture file */
|
|
Binary, /**< unspecified binary data */
|
|
Undefined /**< undefined/invalid data type */
|
|
};
|
|
|
|
class TAG_PARSER_EXPORT TagValue {
|
|
public:
|
|
// constructor, destructor
|
|
TagValue();
|
|
TagValue(const char *text, std::size_t textSize, TagTextEncoding textEncoding = TagTextEncoding::Latin1,
|
|
TagTextEncoding convertTo = TagTextEncoding::Unspecified);
|
|
TagValue(const char *text, TagTextEncoding textEncoding = TagTextEncoding::Latin1, TagTextEncoding convertTo = TagTextEncoding::Unspecified);
|
|
TagValue(
|
|
const std::string &text, TagTextEncoding textEncoding = TagTextEncoding::Latin1, TagTextEncoding convertTo = TagTextEncoding::Unspecified);
|
|
TagValue(int value);
|
|
TagValue(const char *data, std::size_t length, TagDataType type = TagDataType::Undefined, TagTextEncoding encoding = TagTextEncoding::Latin1);
|
|
TagValue(std::unique_ptr<char[]> &&data, std::size_t length, TagDataType type = TagDataType::Binary,
|
|
TagTextEncoding encoding = TagTextEncoding::Latin1);
|
|
TagValue(const PositionInSet &value);
|
|
TagValue(const TagValue &other);
|
|
TagValue(TagValue &&other) = default;
|
|
~TagValue();
|
|
|
|
// operators
|
|
TagValue &operator=(const TagValue &other);
|
|
TagValue &operator=(TagValue &&other) = default;
|
|
bool operator==(const TagValue &other) const;
|
|
bool operator!=(const TagValue &other) const;
|
|
|
|
// methods
|
|
bool isEmpty() const;
|
|
void clearData();
|
|
void clearMetadata();
|
|
void clearDataAndMetadata();
|
|
TagDataType type() const;
|
|
std::string toString(TagTextEncoding encoding = TagTextEncoding::Unspecified) const;
|
|
void toString(std::string &result, TagTextEncoding encoding = TagTextEncoding::Unspecified) const;
|
|
std::u16string toWString(TagTextEncoding encoding = TagTextEncoding::Unspecified) const;
|
|
void toWString(std::u16string &result, TagTextEncoding encoding = TagTextEncoding::Unspecified) const;
|
|
int32 toInteger() const;
|
|
int toStandardGenreIndex() const;
|
|
PositionInSet toPositionInSet() const;
|
|
ChronoUtilities::TimeSpan toTimeSpan() const;
|
|
ChronoUtilities::DateTime toDateTime() const;
|
|
std::size_t dataSize() const;
|
|
char *dataPointer();
|
|
const char *dataPointer() const;
|
|
const std::string &description() const;
|
|
void setDescription(const std::string &value, TagTextEncoding encoding = TagTextEncoding::Latin1);
|
|
const std::string &mimeType() const;
|
|
void setMimeType(const std::string &mimeType);
|
|
const std::string &language() const;
|
|
void setLanguage(const std::string &language);
|
|
bool isLabeledAsReadonly() const;
|
|
void setReadonly(bool readOnly);
|
|
TagTextEncoding dataEncoding() const;
|
|
void convertDataEncoding(TagTextEncoding encoding);
|
|
void convertDataEncodingForTag(const Tag *tag);
|
|
TagTextEncoding descriptionEncoding() const;
|
|
static const TagValue &empty();
|
|
|
|
void assignText(const char *text, std::size_t textSize, TagTextEncoding textEncoding = TagTextEncoding::Latin1,
|
|
TagTextEncoding convertTo = TagTextEncoding::Unspecified);
|
|
void assignText(
|
|
const std::string &text, TagTextEncoding textEncoding = TagTextEncoding::Latin1, TagTextEncoding convertTo = TagTextEncoding::Unspecified);
|
|
void assignInteger(int value);
|
|
void assignStandardGenreIndex(int index);
|
|
void assignData(const char *data, std::size_t length, TagDataType type = TagDataType::Binary, TagTextEncoding encoding = TagTextEncoding::Latin1);
|
|
void assignData(std::unique_ptr<char[]> &&data, std::size_t length, TagDataType type = TagDataType::Binary,
|
|
TagTextEncoding encoding = TagTextEncoding::Latin1);
|
|
void assignPosition(PositionInSet value);
|
|
void assignTimeSpan(ChronoUtilities::TimeSpan value);
|
|
void assignDateTime(ChronoUtilities::DateTime value);
|
|
|
|
static void stripBom(const char *&text, std::size_t &length, TagTextEncoding encoding);
|
|
static void ensureHostByteOrder(std::u16string &u16str, TagTextEncoding currentEncoding);
|
|
template <typename ContainerType,
|
|
Traits::EnableIf<Traits::IsIteratable<ContainerType>,
|
|
std::is_same<typename std::add_const<typename std::remove_pointer<typename ContainerType::value_type>::type>::type, const TagValue>>
|
|
* = nullptr>
|
|
static std::vector<std::string> toStrings(const ContainerType &values, TagTextEncoding encoding = TagTextEncoding::Utf8);
|
|
|
|
private:
|
|
std::unique_ptr<char[]> m_ptr;
|
|
std::size_t m_size;
|
|
std::string m_desc;
|
|
std::string m_mimeType;
|
|
std::string m_language;
|
|
TagDataType m_type;
|
|
TagTextEncoding m_encoding;
|
|
TagTextEncoding m_descEncoding;
|
|
bool m_labeledAsReadonly;
|
|
};
|
|
|
|
/*!
|
|
* \brief Constructs an empty TagValue.
|
|
*/
|
|
inline TagValue::TagValue()
|
|
: m_size(0)
|
|
, m_type(TagDataType::Undefined)
|
|
, m_encoding(TagTextEncoding::Latin1)
|
|
, m_descEncoding(TagTextEncoding::Latin1)
|
|
, m_labeledAsReadonly(false)
|
|
{
|
|
}
|
|
|
|
/*!
|
|
* \brief Destroys the TagValue.
|
|
*/
|
|
inline TagValue::~TagValue()
|
|
{
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a new TagValue holding a copy of the given \a text.
|
|
* \param text Specifies the text to be assigned.
|
|
* \param textSize Specifies the size of \a text. (The actual number of bytes, not the number of characters.)
|
|
* \param textEncoding Specifies the encoding of the given \a text.
|
|
* \param convertTo Specifies the encoding to convert \a text to; set to TagTextEncoding::Unspecified to
|
|
* use \a textEncoding without any character set conversions.
|
|
* \throws Throws a ConversionException if the conversion the specified character set fails.
|
|
* \remarks Strips the BOM of the specified \a text.
|
|
*/
|
|
inline TagValue::TagValue(const char *text, std::size_t textSize, TagTextEncoding textEncoding, TagTextEncoding convertTo)
|
|
: m_descEncoding(TagTextEncoding::Latin1)
|
|
, m_labeledAsReadonly(false)
|
|
{
|
|
assignText(text, textSize, textEncoding, convertTo);
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a new TagValue holding a copy of the given \a text.
|
|
* \param text Specifies the text to be assigned. This string must be null-terminated.
|
|
* \param textEncoding Specifies the encoding of the given \a text.
|
|
* \param convertTo Specifies the encoding to convert \a text to; set to TagTextEncoding::Unspecified to
|
|
* use \a textEncoding without any character set conversions.
|
|
* \throws Throws a ConversionException if the conversion the specified character set fails.
|
|
* \remarks Strips the BOM of the specified \a text.
|
|
*/
|
|
inline TagValue::TagValue(const char *text, TagTextEncoding textEncoding, TagTextEncoding convertTo)
|
|
{
|
|
assignText(text, std::strlen(text), textEncoding, convertTo);
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a new TagValue holding a copy of the given \a text.
|
|
* \param text Specifies the text to be assigned.
|
|
* \param textEncoding Specifies the encoding of the given \a text.
|
|
* \param convertTo Specifies the encoding to convert \a text to; set to TagTextEncoding::Unspecified to
|
|
* use \a textEncoding without any character set conversions.
|
|
* \throws Throws a ConversionException if the conversion the specified character set fails.
|
|
* \remarks Strips the BOM of the specified \a text.
|
|
*/
|
|
inline TagValue::TagValue(const std::string &text, TagTextEncoding textEncoding, TagTextEncoding convertTo)
|
|
: m_descEncoding(TagTextEncoding::Latin1)
|
|
, m_labeledAsReadonly(false)
|
|
{
|
|
assignText(text, textEncoding, convertTo);
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a new TagValue holding the given integer \a value.
|
|
*/
|
|
inline TagValue::TagValue(int value)
|
|
: TagValue(reinterpret_cast<const char *>(&value), sizeof(value), TagDataType::Integer)
|
|
{
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a new TagValue with a copy of the given \a data.
|
|
*
|
|
* \param data Specifies a pointer to the data.
|
|
* \param length Specifies the length of the data.
|
|
* \param type Specifies the type of the data as TagDataType.
|
|
* \param encoding Specifies the encoding of the data as TagTextEncoding. The
|
|
* encoding will only be considered if a text is assigned.
|
|
* \remarks Strips the BOM of the specified \a data if \a type is TagDataType::Text.
|
|
*/
|
|
inline TagValue::TagValue(const char *data, std::size_t length, TagDataType type, TagTextEncoding encoding)
|
|
: m_size(length)
|
|
, m_type(type)
|
|
, m_encoding(encoding)
|
|
, m_descEncoding(TagTextEncoding::Latin1)
|
|
, m_labeledAsReadonly(false)
|
|
{
|
|
if (length) {
|
|
if (type == TagDataType::Text) {
|
|
stripBom(data, m_size, encoding);
|
|
}
|
|
m_ptr = std::make_unique<char[]>(m_size);
|
|
std::copy(data, data + m_size, m_ptr.get());
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a new TagValue holding with the given \a data.
|
|
*
|
|
* The \a data is not copied. It is moved.
|
|
*
|
|
* \param data Specifies a pointer to the data.
|
|
* \param length Specifies the length of the data.
|
|
* \param type Specifies the type of the data as TagDataType.
|
|
* \param encoding Specifies the encoding of the data as TagTextEncoding. The
|
|
* encoding will only be considered if a text is assigned.
|
|
* \remarks Does not strip the BOM so for consistency the caller must ensure there is no BOM present.
|
|
*/
|
|
inline TagValue::TagValue(std::unique_ptr<char[]> &&data, std::size_t length, TagDataType type, TagTextEncoding encoding)
|
|
: m_size(length)
|
|
, m_type(type)
|
|
, m_encoding(encoding)
|
|
, m_descEncoding(TagTextEncoding::Latin1)
|
|
, m_labeledAsReadonly(false)
|
|
{
|
|
if (length) {
|
|
m_ptr = move(data);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* \brief Constructs a new TagValue holding a copy of the given PositionInSet \a value.
|
|
* \param value Specifies the PositionInSet.
|
|
* \todo Pass \a value by value in v8.
|
|
*/
|
|
inline TagValue::TagValue(const PositionInSet &value)
|
|
: TagValue(reinterpret_cast<const char *>(&value), sizeof(value), TagDataType::PositionInSet)
|
|
{
|
|
}
|
|
|
|
/*!
|
|
* \brief Returns whether both instances are not equal.
|
|
* \remarks Simply the negation of operator==() so check there for details.
|
|
*/
|
|
inline bool TagValue::operator!=(const TagValue &other) const
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
|
|
/*!
|
|
* \brief Assigns a copy of the given \a text.
|
|
* \param text Specifies the text to be assigned.
|
|
* \param textEncoding Specifies the encoding of the given \a text.
|
|
* \param convertTo Specifies the encoding to convert \a text to; set to TagTextEncoding::Unspecified to
|
|
* use \a textEncoding without any character set conversions.
|
|
* \throws Throws a ConversionException if the conversion the specified character set fails.
|
|
* \remarks Strips the BOM of the specified \a text.
|
|
*/
|
|
inline void TagValue::assignText(const std::string &text, TagTextEncoding textEncoding, TagTextEncoding convertTo)
|
|
{
|
|
assignText(text.data(), text.size(), textEncoding, convertTo);
|
|
}
|
|
|
|
/*!
|
|
* \brief Assigns the given PositionInSet \a value.
|
|
*/
|
|
inline void TagValue::assignPosition(PositionInSet value)
|
|
{
|
|
if (value.isNull()) {
|
|
m_type = TagDataType::PositionInSet;
|
|
clearData();
|
|
} else {
|
|
assignData(reinterpret_cast<const char *>(&value), sizeof(value), TagDataType::PositionInSet);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* \brief Assigns the given TimeSpan \a value.
|
|
*/
|
|
inline void TagValue::assignTimeSpan(ChronoUtilities::TimeSpan value)
|
|
{
|
|
assignData(reinterpret_cast<const char *>(&value), sizeof(value), TagDataType::TimeSpan);
|
|
}
|
|
|
|
/*!
|
|
* \brief Assigns the given DateTime \a value.
|
|
*/
|
|
inline void TagValue::assignDateTime(ChronoUtilities::DateTime value)
|
|
{
|
|
assignData(reinterpret_cast<const char *>(&value), sizeof(value), TagDataType::DateTime);
|
|
}
|
|
|
|
/*!
|
|
* \brief Assigns the given standard genre \a index to be assigned.
|
|
* \param index Specifies the index to be assigned.
|
|
* \sa <a href="http://en.wikipedia.org/wiki/ID3#List_of_genres">List of genres - Wikipedia</a>
|
|
*/
|
|
inline void TagValue::assignStandardGenreIndex(int index)
|
|
{
|
|
assignInteger(index);
|
|
m_type = TagDataType::StandardGenreIndex;
|
|
}
|
|
|
|
/*!
|
|
* \brief Returns the type of the assigned value.
|
|
*/
|
|
inline TagDataType TagValue::type() const
|
|
{
|
|
return m_type;
|
|
}
|
|
|
|
/*!
|
|
* \brief Converts the value of the current TagValue object to its equivalent
|
|
* std::string representation.
|
|
* \param encoding Specifies the encoding to to be used; set to TagTextEncoding::Unspecified to use the
|
|
* present encoding without any character set conversion.
|
|
* \throws Throws ConversionException on failure.
|
|
*/
|
|
inline std::string TagValue::toString(TagTextEncoding encoding) const
|
|
{
|
|
std::string res;
|
|
toString(res, encoding);
|
|
return res;
|
|
}
|
|
|
|
/*!
|
|
* \brief Converts the value of the current TagValue object to its equivalent
|
|
* std::wstring representation.
|
|
* \throws Throws ConversionException on failure.
|
|
* \remarks Use this only, if \a encoding is an UTF-16 encoding.
|
|
*/
|
|
inline std::u16string TagValue::toWString(TagTextEncoding encoding) const
|
|
{
|
|
std::u16string res;
|
|
toWString(res, encoding);
|
|
return res;
|
|
}
|
|
|
|
/*!
|
|
* \brief Returns an indication whether an value is assigned.
|
|
* \remarks Meta data such as description and MIME type is not considered as an assigned value.
|
|
*/
|
|
inline bool TagValue::isEmpty() const
|
|
{
|
|
return m_ptr == nullptr || m_size == 0;
|
|
}
|
|
|
|
/*!
|
|
* \brief Clears the assigned data.
|
|
* \remarks Meta data such as description and MIME type remains unaffected.
|
|
* \sa clearMetadata()
|
|
* \sa clearDataAndMetadata()
|
|
*/
|
|
inline void TagValue::clearData()
|
|
{
|
|
m_size = 0;
|
|
m_ptr.reset();
|
|
}
|
|
|
|
/*!
|
|
* \brief Wipes assigned data including meta data.
|
|
* \sa clearData()
|
|
* \sa clearMetadata()
|
|
*/
|
|
inline void TagValue::clearDataAndMetadata()
|
|
{
|
|
clearData();
|
|
clearMetadata();
|
|
}
|
|
|
|
/*!
|
|
* \brief Returns the size of the assigned value in bytes.
|
|
* \remarks Meta data such as description and MIME type is not considered as part of the assigned value.
|
|
*/
|
|
inline std::size_t TagValue::dataSize() const
|
|
{
|
|
return m_size;
|
|
}
|
|
|
|
/*!
|
|
* \brief Returns a pointer to the raw data assigned to the current instance.
|
|
* \remarks The instance keeps ownership over the data which will be invalidated when the
|
|
* TagValue gets destroyed or another value is assigned.
|
|
* \remarks The raw data is not null terminated. See dataSize().
|
|
*/
|
|
inline char *TagValue::dataPointer()
|
|
{
|
|
return m_ptr.get();
|
|
}
|
|
|
|
inline const char *TagValue::dataPointer() const
|
|
{
|
|
return m_ptr.get();
|
|
}
|
|
|
|
/*!
|
|
* \brief Returns the description.
|
|
* \remarks The usage of this meta information depends on the tag implementation.
|
|
* \sa descriptionEncoding()
|
|
* \sa setDescription()
|
|
*/
|
|
inline const std::string &TagValue::description() const
|
|
{
|
|
return m_desc;
|
|
}
|
|
|
|
/*!
|
|
* \brief Sets the description.
|
|
* \param value Specifies the description.
|
|
* \param encoding Specifies the encoding used to provide the description.
|
|
* \remarks The usage of this meta information depends on the tag implementation.
|
|
* \sa description()
|
|
* \sa descriptionEncoding()
|
|
*/
|
|
inline void TagValue::setDescription(const std::string &value, TagTextEncoding encoding)
|
|
{
|
|
m_desc = value;
|
|
m_descEncoding = encoding;
|
|
}
|
|
|
|
/*!
|
|
* \brief Returns the MIME type.
|
|
* \remarks The usage of this meta information depends on the tag implementation.
|
|
* \sa setMimeType()
|
|
*/
|
|
inline const std::string &TagValue::mimeType() const
|
|
{
|
|
return m_mimeType;
|
|
}
|
|
|
|
/*!
|
|
* \brief Sets the MIME type.
|
|
* \remarks The usage of this meta information depends on the tag implementation.
|
|
* \sa mimeType()
|
|
*/
|
|
inline void TagValue::setMimeType(const std::string &mimeType)
|
|
{
|
|
m_mimeType = mimeType;
|
|
}
|
|
|
|
/*!
|
|
* \brief Returns the language.
|
|
* \remarks The usage of this meta information depends on the tag implementation.
|
|
* \sa setLanguage()
|
|
*/
|
|
inline const std::string &TagValue::language() const
|
|
{
|
|
return m_language;
|
|
}
|
|
|
|
/*!
|
|
* \brief Sets the language.
|
|
* \remarks The usage of this meta information depends on the tag implementation.
|
|
* \sa language()
|
|
*/
|
|
inline void TagValue::setLanguage(const std::string &language)
|
|
{
|
|
m_language = language;
|
|
}
|
|
|
|
/*!
|
|
* \brief Returns an indication whether the value is labeled as read-only.
|
|
* \remarks The usage of this meta information depends on the tag implementation.
|
|
* \remarks This is just an additional information. It has no effect on the behavior
|
|
* of the TagValue thus assignments can still be performed (to prohibit
|
|
* assignments simply use the "const" keyword).
|
|
* \sa setReadonly()
|
|
*/
|
|
inline bool TagValue::isLabeledAsReadonly() const
|
|
{
|
|
return m_labeledAsReadonly;
|
|
}
|
|
|
|
/*!
|
|
* \brief Sets whether the TagValue is labeled as read-only.
|
|
* \remarks The usage of this meta information depends on the tag implementation.
|
|
* \remarks This is just an additional information. It has no effect on the behavior
|
|
* of the TagValue thus assignments can still be performed (to prohibit
|
|
* assignments simply use the "const" keyword).
|
|
* \sa isLabeledAsReadonly()
|
|
*/
|
|
inline void TagValue::setReadonly(bool readOnly)
|
|
{
|
|
m_labeledAsReadonly = readOnly;
|
|
}
|
|
|
|
/*!
|
|
* \brief Returns the data encoding.
|
|
* \remarks This value is only relevant if type() equals TagDataType::Text.
|
|
* \sa assignText()
|
|
*/
|
|
inline TagTextEncoding TagValue::dataEncoding() const
|
|
{
|
|
return m_encoding;
|
|
}
|
|
|
|
/*!
|
|
* \brief Returns the description encoding.
|
|
* \remarks This value is only relevant if a description is assigned.
|
|
* \sa description(), setDescription()
|
|
*/
|
|
inline TagTextEncoding TagValue::descriptionEncoding() const
|
|
{
|
|
return m_descEncoding;
|
|
}
|
|
|
|
/*!
|
|
* \brief Converts the specified \a values to string using the specified \a encoding.
|
|
* \throws Throws ConversionException on failure.
|
|
* \sa toString()
|
|
*/
|
|
template <typename ContainerType,
|
|
Traits::EnableIf<Traits::IsIteratable<ContainerType>,
|
|
std::is_same<typename std::add_const<typename std::remove_pointer<typename ContainerType::value_type>::type>::type, const TagValue>> *>
|
|
std::vector<std::string> TagValue::toStrings(const ContainerType &values, TagTextEncoding encoding)
|
|
{
|
|
std::vector<std::string> res;
|
|
res.reserve(values.size());
|
|
for (const auto &value : values) {
|
|
res.emplace_back(Traits::dereferenceMaybe(value).toString(encoding));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
} // namespace TagParser
|
|
|
|
#endif // TAG_PARSER_TAGVALUE_H
|