Ease dealing with native field IDs
In particular, this allow conversion from native field IDs to readible string representation and vice verca
This commit is contained in:
parent
c272ec315b
commit
b4e167bd71
|
@ -159,8 +159,8 @@ set(META_APP_AUTHOR "Martchus")
|
||||||
set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
|
set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
|
||||||
set(META_APP_DESCRIPTION "C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags")
|
set(META_APP_DESCRIPTION "C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags")
|
||||||
set(META_VERSION_MAJOR 6)
|
set(META_VERSION_MAJOR 6)
|
||||||
set(META_VERSION_MINOR 1)
|
set(META_VERSION_MINOR 2)
|
||||||
set(META_VERSION_PATCH 1)
|
set(META_VERSION_PATCH 0)
|
||||||
set(META_PUBLIC_SHARED_LIB_DEPENDS c++utilities)
|
set(META_PUBLIC_SHARED_LIB_DEPENDS c++utilities)
|
||||||
set(META_PUBLIC_STATIC_LIB_DEPENDS c++utilities_static)
|
set(META_PUBLIC_STATIC_LIB_DEPENDS c++utilities_static)
|
||||||
set(META_PRIVATE_COMPILE_DEFINITIONS LEGACY_API)
|
set(META_PRIVATE_COMPILE_DEFINITIONS LEGACY_API)
|
||||||
|
|
|
@ -27,25 +27,25 @@ class FieldMapBasedTag : public Tag
|
||||||
public:
|
public:
|
||||||
FieldMapBasedTag();
|
FieldMapBasedTag();
|
||||||
|
|
||||||
virtual const TagValue &value(const typename FieldType::identifierType &id) const;
|
virtual const TagValue &value(const typename FieldType::identifierType &id) const; // FIXME: use static polymorphism
|
||||||
const TagValue &value(KnownField field) const;
|
const TagValue &value(KnownField field) const;
|
||||||
std::vector<const TagValue *> values(const typename FieldType::identifierType &id) const;
|
std::vector<const TagValue *> values(const typename FieldType::identifierType &id) const;
|
||||||
std::vector<const TagValue *> values(KnownField field) const;
|
std::vector<const TagValue *> values(KnownField field) const;
|
||||||
virtual bool setValue(const typename FieldType::identifierType &id, const TagValue &value);
|
virtual bool setValue(const typename FieldType::identifierType &id, const TagValue &value); // FIXME: use static polymorphism
|
||||||
bool setValue(KnownField field, const TagValue &value);
|
bool setValue(KnownField field, const TagValue &value);
|
||||||
bool setValues(const typename FieldType::identifierType &id, const std::vector<TagValue> &values);
|
bool setValues(const typename FieldType::identifierType &id, const std::vector<TagValue> &values);
|
||||||
bool setValues(KnownField field, const std::vector<TagValue> &values);
|
bool setValues(KnownField field, const std::vector<TagValue> &values);
|
||||||
bool hasField(KnownField field) const;
|
bool hasField(KnownField field) const;
|
||||||
virtual bool hasField(const typename FieldType::identifierType &id) const;
|
virtual bool hasField(const typename FieldType::identifierType &id) const; // FIXME: use static polymorphism
|
||||||
void removeAllFields();
|
void removeAllFields();
|
||||||
const std::multimap<typename FieldType::identifierType, FieldType, Compare> &fields() const;
|
const std::multimap<typename FieldType::identifierType, FieldType, Compare> &fields() const;
|
||||||
std::multimap<typename FieldType::identifierType, FieldType, Compare> &fields();
|
std::multimap<typename FieldType::identifierType, FieldType, Compare> &fields();
|
||||||
unsigned int fieldCount() const;
|
unsigned int fieldCount() const;
|
||||||
virtual typename FieldType::identifierType fieldId(KnownField value) const = 0;
|
virtual typename FieldType::identifierType fieldId(KnownField value) const = 0; // FIXME: use static polymorphism
|
||||||
virtual KnownField knownField(const typename FieldType::identifierType &id) const = 0;
|
virtual KnownField knownField(const typename FieldType::identifierType &id) const = 0; // FIXME: use static polymorphism
|
||||||
bool supportsField(KnownField field) const;
|
bool supportsField(KnownField field) const;
|
||||||
using Tag::proposedDataType;
|
using Tag::proposedDataType;
|
||||||
virtual TagDataType proposedDataType(const typename FieldType::identifierType &id) const;
|
virtual TagDataType proposedDataType(const typename FieldType::identifierType &id) const; // FIXME: use static polymorphism
|
||||||
int insertFields(const FieldMapBasedTag<FieldType, Compare> &from, bool overwrite);
|
int insertFields(const FieldMapBasedTag<FieldType, Compare> &from, bool overwrite);
|
||||||
unsigned int insertValues(const Tag &from, bool overwrite);
|
unsigned int insertValues(const Tag &from, bool overwrite);
|
||||||
void ensureTextValuesAreProperlyEncoded();
|
void ensureTextValuesAreProperlyEncoded();
|
||||||
|
|
|
@ -42,6 +42,7 @@ public:
|
||||||
~TagField();
|
~TagField();
|
||||||
|
|
||||||
const identifierType &id() const;
|
const identifierType &id() const;
|
||||||
|
std::string idToString() const;
|
||||||
void setId(const identifierType &id);
|
void setId(const identifierType &id);
|
||||||
void clearId();
|
void clearId();
|
||||||
|
|
||||||
|
@ -118,6 +119,12 @@ inline const typename TagField<ImplementationType>::identifierType &TagField<Imp
|
||||||
return m_id;
|
return m_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class ImplementationType>
|
||||||
|
inline std::string TagField<ImplementationType>::idToString() const
|
||||||
|
{
|
||||||
|
return ImplementationType::fieldIdToString(m_id);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Sets the id of the current Tag Field.
|
* \brief Sets the id of the current Tag Field.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -11,6 +11,7 @@ class TAG_PARSER_EXPORT Id3v1Tag : public Tag
|
||||||
public:
|
public:
|
||||||
Id3v1Tag();
|
Id3v1Tag();
|
||||||
|
|
||||||
|
static constexpr TagType tagType = TagType::Id3v1Tag;
|
||||||
TagType type() const;
|
TagType type() const;
|
||||||
const char *typeName() const;
|
const char *typeName() const;
|
||||||
bool canEncodingBeUsed(TagTextEncoding encoding) const;
|
bool canEncodingBeUsed(TagTextEncoding encoding) const;
|
||||||
|
|
|
@ -150,6 +150,9 @@ public:
|
||||||
void makePicture(std::unique_ptr<char[]> &buffer, uint32 &bufferSize, const TagValue &picture, byte typeInfo);
|
void makePicture(std::unique_ptr<char[]> &buffer, uint32 &bufferSize, const TagValue &picture, byte typeInfo);
|
||||||
void makeComment(std::unique_ptr<char[]> &buffer, uint32 &bufferSize, const TagValue &comment);
|
void makeComment(std::unique_ptr<char[]> &buffer, uint32 &bufferSize, const TagValue &comment);
|
||||||
|
|
||||||
|
static identifierType fieldIdFromString(const char *idString, std::size_t idStringSize = std::string::npos);
|
||||||
|
static std::string fieldIdToString(identifierType id);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void cleared();
|
void cleared();
|
||||||
|
|
||||||
|
@ -188,14 +191,11 @@ inline bool Id3v2Frame::hasPaddingReached() const
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Returns the frame ID as string.
|
* \brief Returns the frame ID as string.
|
||||||
|
* \deprecated Will be removed in favour of generic idToString().
|
||||||
*/
|
*/
|
||||||
inline std::string Id3v2Frame::frameIdString() const
|
inline std::string Id3v2Frame::frameIdString() const
|
||||||
{
|
{
|
||||||
if(Id3v2FrameIds::isLongId(id())) {
|
return idToString();
|
||||||
return ConversionUtilities::interpretIntegerAsString<uint32>(id());
|
|
||||||
} else {
|
|
||||||
return ConversionUtilities::interpretIntegerAsString<uint32>(id(), 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -328,6 +328,29 @@ inline bool Id3v2Frame::supportsNestedFields() const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Converts the specified ID string representation to an actual ID.
|
||||||
|
*/
|
||||||
|
inline Id3v2Frame::identifierType Id3v2Frame::fieldIdFromString(const char *idString, std::size_t idStringSize)
|
||||||
|
{
|
||||||
|
switch(idStringSize != std::string::npos ? idStringSize : std::strlen(idString)) {
|
||||||
|
case 3:
|
||||||
|
return ConversionUtilities::BE::toUInt24(idString);
|
||||||
|
case 4:
|
||||||
|
return ConversionUtilities::BE::toUInt32(idString);
|
||||||
|
default:
|
||||||
|
throw ConversionUtilities::ConversionException("ID3v2 ID must be 3 or 4 chars");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns the string representation for the specified \a id.
|
||||||
|
*/
|
||||||
|
inline std::string Id3v2Frame::fieldIdToString(Id3v2Frame::identifierType id)
|
||||||
|
{
|
||||||
|
return ConversionUtilities::interpretIntegerAsString<uint32>(id, Id3v2FrameIds::isLongId(id) ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ID3V2FRAME_H
|
#endif // ID3V2FRAME_H
|
||||||
|
|
|
@ -57,6 +57,7 @@ class TAG_PARSER_EXPORT Id3v2Tag : public FieldMapBasedTag<Id3v2Frame, FrameComp
|
||||||
public:
|
public:
|
||||||
Id3v2Tag();
|
Id3v2Tag();
|
||||||
|
|
||||||
|
static constexpr TagType tagType = TagType::Id3v2Tag;
|
||||||
TagType type() const;
|
TagType type() const;
|
||||||
const char *typeName() const;
|
const char *typeName() const;
|
||||||
TagTextEncoding proposedTextEncoding() const;
|
TagTextEncoding proposedTextEncoding() const;
|
||||||
|
|
|
@ -52,6 +52,8 @@ class TAG_PARSER_EXPORT MatroskaTag : public FieldMapBasedTag<MatroskaTagField>
|
||||||
public:
|
public:
|
||||||
MatroskaTag();
|
MatroskaTag();
|
||||||
|
|
||||||
|
static constexpr TagType tagType = TagType::MatroskaTag;
|
||||||
|
// FIXME: implement type() and typeName() in FieldMapBasedTag
|
||||||
TagType type() const;
|
TagType type() const;
|
||||||
const char *typeName() const;
|
const char *typeName() const;
|
||||||
TagTextEncoding proposedTextEncoding() const;
|
TagTextEncoding proposedTextEncoding() const;
|
||||||
|
|
|
@ -94,6 +94,9 @@ public:
|
||||||
bool isAdditionalTypeInfoUsed() const;
|
bool isAdditionalTypeInfoUsed() const;
|
||||||
bool supportsNestedFields() const;
|
bool supportsNestedFields() const;
|
||||||
|
|
||||||
|
static typename std::string fieldIdFromString(const char *idString, std::size_t idStringSize = std::string::npos);
|
||||||
|
static std::string fieldIdToString(const std::string &id);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void cleared();
|
void cleared();
|
||||||
};
|
};
|
||||||
|
@ -114,6 +117,24 @@ inline bool MatroskaTagField::supportsNestedFields() const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Converts the specified ID string representation to an actual ID.
|
||||||
|
* \remarks As Matroska field IDs are text strings the string is just passed.
|
||||||
|
*/
|
||||||
|
inline std::string MatroskaTagField::fieldIdFromString(const char *idString, std::size_t idStringSize)
|
||||||
|
{
|
||||||
|
return idStringSize != std::string::npos ? std::string(idString, idStringSize) : std::string(idString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns the string representation for the specified \a id.
|
||||||
|
* \remarks As Matroska field IDs are text strings the string is just passed.
|
||||||
|
*/
|
||||||
|
inline std::string MatroskaTagField::fieldIdToString(const std::string &id)
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Ensures the field is cleared.
|
* \brief Ensures the field is cleared.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -92,6 +92,7 @@ class TAG_PARSER_EXPORT Mp4Tag : public FieldMapBasedTag<Mp4TagField>
|
||||||
public:
|
public:
|
||||||
Mp4Tag();
|
Mp4Tag();
|
||||||
|
|
||||||
|
static constexpr TagType tagType = TagType::Mp4Tag;
|
||||||
TagType type() const;
|
TagType type() const;
|
||||||
const char *typeName() const;
|
const char *typeName() const;
|
||||||
TagTextEncoding proposedTextEncoding() const;
|
TagTextEncoding proposedTextEncoding() const;
|
||||||
|
@ -102,6 +103,7 @@ public:
|
||||||
bool supportsField(KnownField field) const;
|
bool supportsField(KnownField field) const;
|
||||||
using FieldMapBasedTag<Mp4TagField>::value;
|
using FieldMapBasedTag<Mp4TagField>::value;
|
||||||
const TagValue &value(KnownField value) const;
|
const TagValue &value(KnownField value) const;
|
||||||
|
using FieldMapBasedTag<Mp4TagField>::values;
|
||||||
std::vector<const TagValue *> values(KnownField field) const;
|
std::vector<const TagValue *> values(KnownField field) const;
|
||||||
#ifdef LEGACY_API
|
#ifdef LEGACY_API
|
||||||
const TagValue &value(const std::string mean, const std::string name) const;
|
const TagValue &value(const std::string mean, const std::string name) const;
|
||||||
|
@ -110,6 +112,7 @@ public:
|
||||||
const TagValue &value(const char *mean, const char *name) const;
|
const TagValue &value(const char *mean, const char *name) const;
|
||||||
using FieldMapBasedTag<Mp4TagField>::setValue;
|
using FieldMapBasedTag<Mp4TagField>::setValue;
|
||||||
bool setValue(KnownField field, const TagValue &value);
|
bool setValue(KnownField field, const TagValue &value);
|
||||||
|
using FieldMapBasedTag<Mp4TagField>::setValues;
|
||||||
bool setValues(KnownField field, const std::vector<TagValue> &values);
|
bool setValues(KnownField field, const std::vector<TagValue> &values);
|
||||||
#ifdef LEGACY_API
|
#ifdef LEGACY_API
|
||||||
bool setValue(const std::string mean, const std::string name, const TagValue &value);
|
bool setValue(const std::string mean, const std::string name, const TagValue &value);
|
||||||
|
|
|
@ -103,7 +103,7 @@ void Mp4TagField::reparse(Mp4Atom &ilstChild)
|
||||||
}
|
}
|
||||||
setTypeInfo(m_parsedRawDataType = reader.readUInt24BE());
|
setTypeInfo(m_parsedRawDataType = reader.readUInt24BE());
|
||||||
try { // try to show warning if parsed raw data type differs from expected raw data type for this atom id
|
try { // try to show warning if parsed raw data type differs from expected raw data type for this atom id
|
||||||
vector<uint32> expectedRawDataTypes = this->expectedRawDataTypes();
|
const vector<uint32> expectedRawDataTypes = this->expectedRawDataTypes();
|
||||||
if(find(expectedRawDataTypes.cbegin(), expectedRawDataTypes.cend(), m_parsedRawDataType) == expectedRawDataTypes.cend()) {
|
if(find(expectedRawDataTypes.cbegin(), expectedRawDataTypes.cend(), m_parsedRawDataType) == expectedRawDataTypes.cend()) {
|
||||||
addNotification(NotificationType::Warning, "Unexpected data type indicator found.", context);
|
addNotification(NotificationType::Warning, "Unexpected data type indicator found.", context);
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ void Mp4TagField::reparse(Mp4Atom &ilstChild)
|
||||||
default:
|
default:
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
streamsize coverSize = dataAtom->dataSize() - 8;
|
const streamsize coverSize = dataAtom->dataSize() - 8;
|
||||||
unique_ptr<char []> coverData = make_unique<char []>(coverSize);
|
unique_ptr<char []> coverData = make_unique<char []>(coverSize);
|
||||||
stream.read(coverData.get(), coverSize);
|
stream.read(coverData.get(), coverSize);
|
||||||
value().assignData(move(coverData), coverSize, TagDataType::Picture);
|
value().assignData(move(coverData), coverSize, TagDataType::Picture);
|
||||||
|
@ -419,7 +419,7 @@ Mp4TagFieldMaker::Mp4TagFieldMaker(Mp4TagField &field) :
|
||||||
m_field.addNotification(NotificationType::Warning, "Invalid tag atom id.", "making MP4 tag field");
|
m_field.addNotification(NotificationType::Warning, "Invalid tag atom id.", "making MP4 tag field");
|
||||||
throw InvalidDataException();
|
throw InvalidDataException();
|
||||||
}
|
}
|
||||||
const string context("making MP4 tag field " + ConversionUtilities::interpretIntegerAsString<Mp4TagField::identifierType>(m_field.id()));
|
const string context("making MP4 tag field " + Mp4TagField::fieldIdToString(m_field.id()));
|
||||||
if(m_field.value().isEmpty() && (!m_field.mean().empty() || !m_field.name().empty())) {
|
if(m_field.value().isEmpty() && (!m_field.mean().empty() || !m_field.name().empty())) {
|
||||||
m_field.addNotification(NotificationType::Critical, "No tag value assigned.", context);
|
m_field.addNotification(NotificationType::Critical, "No tag value assigned.", context);
|
||||||
throw InvalidDataException();
|
throw InvalidDataException();
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "../statusprovider.h"
|
#include "../statusprovider.h"
|
||||||
|
|
||||||
#include <c++utilities/io/binarywriter.h>
|
#include <c++utilities/io/binarywriter.h>
|
||||||
|
#include <c++utilities/conversion/stringconversion.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
@ -135,6 +136,9 @@ public:
|
||||||
std::vector<uint32> expectedRawDataTypes() const;
|
std::vector<uint32> expectedRawDataTypes() const;
|
||||||
uint32 appropriateRawDataType() const;
|
uint32 appropriateRawDataType() const;
|
||||||
|
|
||||||
|
static identifierType fieldIdFromString(const char *idString, std::size_t idStringSize = std::string::npos);
|
||||||
|
static std::string fieldIdToString(identifierType id);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void cleared();
|
void cleared();
|
||||||
|
|
||||||
|
@ -218,6 +222,33 @@ inline bool Mp4TagField::supportsNestedFields() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Converts the specified ID string representation to an actual ID.
|
||||||
|
* \remarks The specified \a idString is assumed to be UTF-8 encoded. In order to get the ©-sign
|
||||||
|
* correctly, it is converted to Latin-1.
|
||||||
|
*/
|
||||||
|
inline Mp4TagField::identifierType Mp4TagField::fieldIdFromString(const char *idString, std::size_t idStringSize)
|
||||||
|
{
|
||||||
|
const auto latin1 = ConversionUtilities::convertUtf8ToLatin1(idString, idStringSize != std::string::npos ? idStringSize : std::strlen(idString));
|
||||||
|
switch(latin1.second) {
|
||||||
|
case 4:
|
||||||
|
return ConversionUtilities::BE::toUInt32(latin1.first.get());
|
||||||
|
default:
|
||||||
|
throw ConversionUtilities::ConversionException("MP4 ID must be exactly 4 chars");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns the string representation for the specified \a id.
|
||||||
|
* \remarks The specified \a id is considered Latin-1 encoded. In order to get the ©-sign
|
||||||
|
* correctly, it is converted to UTF-8.
|
||||||
|
*/
|
||||||
|
inline std::string Mp4TagField::fieldIdToString(Mp4TagField::identifierType id)
|
||||||
|
{
|
||||||
|
const auto utf8 = ConversionUtilities::convertLatin1ToUtf8(ConversionUtilities::interpretIntegerAsString<uint32>(id).data(), 4);
|
||||||
|
return std::string(utf8.first.get(), utf8.second);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // MP4TAGATOM_H
|
#endif // MP4TAGATOM_H
|
||||||
|
|
|
@ -17,6 +17,7 @@ class TAG_PARSER_EXPORT VorbisComment : public FieldMapBasedTag<VorbisCommentFie
|
||||||
public:
|
public:
|
||||||
VorbisComment();
|
VorbisComment();
|
||||||
|
|
||||||
|
static constexpr TagType tagType = TagType::VorbisComment;
|
||||||
TagType type() const;
|
TagType type() const;
|
||||||
const char *typeName() const;
|
const char *typeName() const;
|
||||||
TagTextEncoding proposedTextEncoding() const;
|
TagTextEncoding proposedTextEncoding() const;
|
||||||
|
|
|
@ -72,6 +72,9 @@ public:
|
||||||
bool isAdditionalTypeInfoUsed() const;
|
bool isAdditionalTypeInfoUsed() const;
|
||||||
bool supportsNestedFields() const;
|
bool supportsNestedFields() const;
|
||||||
|
|
||||||
|
static typename std::string fieldIdFromString(const char *idString, std::size_t idStringSize = std::string::npos);
|
||||||
|
static std::string fieldIdToString(const std::string &id);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void cleared();
|
void cleared();
|
||||||
|
|
||||||
|
@ -96,6 +99,24 @@ inline bool VorbisCommentField::supportsNestedFields() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Converts the specified ID string representation to an actual ID.
|
||||||
|
* \remarks As Vorbis field IDs are plain text the string is just passed.
|
||||||
|
*/
|
||||||
|
inline std::string VorbisCommentField::fieldIdFromString(const char *idString, std::size_t idStringSize)
|
||||||
|
{
|
||||||
|
return idStringSize != std::string::npos ? std::string(idString, idStringSize) : std::string(idString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns the string representation for the specified \a id.
|
||||||
|
* \remarks As Vorbis field IDs are plain text the string is just passed.
|
||||||
|
*/
|
||||||
|
inline std::string VorbisCommentField::fieldIdToString(const std::string &id)
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Ensures the field is cleared.
|
* \brief Ensures the field is cleared.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue