tagparser/genericcontainer.h

380 lines
13 KiB
C
Raw Normal View History

#ifndef TAG_PARSER_GENERICCONTAINER_H
#define TAG_PARSER_GENERICCONTAINER_H
2015-04-22 19:22:01 +02:00
2015-09-06 19:57:33 +02:00
#include "./abstractcontainer.h"
2015-04-22 19:22:01 +02:00
#include <algorithm>
#include <memory>
#include <vector>
2017-02-05 21:02:40 +01:00
#include <memory>
2015-04-22 19:22:01 +02:00
namespace TagParser {
2015-04-22 19:22:01 +02:00
/*!
* \class Media::GenericContainer
* \brief The GenericContainer class helps parsing header, track, tag and chapter information
* of a file.
*
* \tparam FileInfoType Specifies the file info class (a class derived from Media::BasicFileInfo) which is used to specify the related file.
* \tparam TagType Specifies the class which is used to deal with the tag information of the file.
* \tparam TrackType Specifies the class which is used to deal with the track of the file.
* \tparam ElementType Specifies the class which is used to deal with the elements the file consists of.
*/
template <class FileInfoType, class TagType, class TrackType, class ElementType>
2016-08-29 15:43:05 +02:00
class TAG_PARSER_EXPORT GenericContainer : public AbstractContainer
2015-04-22 19:22:01 +02:00
{
friend FileInfoType;
public:
GenericContainer(FileInfoType &fileInfo, uint64 startOffset);
2018-03-07 01:11:42 +01:00
~GenericContainer() override;
2015-04-22 19:22:01 +02:00
void validateElementStructure(Diagnostics &diag, uint64 *paddingSize = nullptr);
2015-04-22 19:22:01 +02:00
FileInfoType &fileInfo() const;
ElementType *firstElement() const;
const std::vector<std::unique_ptr<ElementType> > &additionalElements() const;
std::vector<std::unique_ptr<ElementType> > &additionalElements();
2018-03-07 01:11:42 +01:00
TagType *tag(std::size_t index) override;
std::size_t tagCount() const override;
TrackType *track(std::size_t index) override;
TrackType *trackById(uint64 id);
2018-03-07 01:11:42 +01:00
std::size_t trackCount() const override;
2015-04-22 19:22:01 +02:00
const std::vector<std::unique_ptr<TagType> > &tags() const;
std::vector<std::unique_ptr<TagType> > &tags();
const std::vector<std::unique_ptr<TrackType> > &tracks() const;
std::vector<std::unique_ptr<TrackType> > &tracks();
2018-03-07 01:11:42 +01:00
TagType *createTag(const TagTarget &target = TagTarget()) override;
bool removeTag(Tag *tag) override;
void removeAllTags() override;
2015-04-22 19:22:01 +02:00
bool addTrack(TrackType *track);
2018-03-07 01:11:42 +01:00
bool removeTrack(AbstractTrack *track) override;
void removeAllTracks() override;
void reset() override;
2015-04-22 19:22:01 +02:00
typedef FileInfoType ContainerFileInfoType;
typedef TagType ContainerTagType;
typedef TrackType ContainerTrackType;
typedef ElementType ContainerElementType;
2015-04-22 19:22:01 +02:00
protected:
std::unique_ptr<ElementType> m_firstElement;
std::vector<std::unique_ptr<ElementType> > m_additionalElements;
std::vector<std::unique_ptr<TagType> > m_tags;
std::vector<std::unique_ptr<TrackType> > m_tracks;
private:
FileInfoType *m_fileInfo;
};
/*!
* \brief Constructs a new container for the specified \a fileInfo at the specified \a startOffset.
*/
template <class FileInfoType, class TagType, class TrackType, class ElementType>
GenericContainer<FileInfoType, TagType, TrackType, ElementType>::GenericContainer(FileInfoType &fileInfo, uint64 startOffset) :
AbstractContainer(fileInfo.stream(), startOffset),
m_fileInfo(&fileInfo)
{}
/*!
* \brief Destroys the container.
*
* Destroys the reader, the writer and track, tag, chapter and attachment objects as well.
* Does NOT destroy the stream which has been specified when constructing the object.
*/
template <class FileInfoType, class TagType, class TrackType, class ElementType>
GenericContainer<FileInfoType, TagType, TrackType, ElementType>::~GenericContainer()
{}
/*!
* \brief Parses all elements the file consists of.
*
* All parsing notifications will be stored in \a gatheredNotifications.
* The size of padding/void elements will be accumulated and stored in
* at \a paddingSize if it is not a null pointer.
*
* \throws Throws Failure or a derived class when a parsing error occurs.
* \throws Throws std::ios_base::failure when an IO error occurs.
*/
template <class FileInfoType, class TagType, class TrackType, class ElementType>
inline void GenericContainer<FileInfoType, TagType, TrackType, ElementType>::validateElementStructure(Diagnostics &diag, uint64 *paddingSize)
2015-04-22 19:22:01 +02:00
{
parseHeader(diag);
2015-04-22 19:22:01 +02:00
if(m_firstElement) {
m_firstElement->validateSubsequentElementStructure(diag, paddingSize);
2015-04-22 19:22:01 +02:00
}
}
/*!
* \brief Returns the related file info.
*
* The related file info has been specefied when constructing the container.
*/
template <class FileInfoType, class TagType, class TrackType, class ElementType>
inline FileInfoType &GenericContainer<FileInfoType, TagType, TrackType, ElementType>::fileInfo() const
{
return *m_fileInfo;
}
/*!
* \brief Returns the first element of the file if available; otherwiese returns nullptr.
*
* This method gives access to the element structure of the container - the entire element tree
* can be looked up using the nextSibling() and firstChild() methods of the returned element.
*
* The header needs to be parsed before (see parseHeader()).
*
* The container keeps ownership over the returned element.
*
* \sa isHeaderParsed()
*/
template <class FileInfoType, class TagType, class TrackType, class ElementType>
inline ElementType *GenericContainer<FileInfoType, TagType, TrackType, ElementType>::firstElement() const
{
return m_firstElement.get();
}
/*!
* \brief Returns all available additional elements.
*
* The parser might decide to split up a file's element tree to skip irrelevant elements to achive better performance.
* This method gives access to those sub element trees. Each of the returned elements represents an independent element
* tree within the file.
*/
template <class FileInfoType, class TagType, class TrackType, class ElementType>
inline const std::vector<std::unique_ptr<ElementType> > &GenericContainer<FileInfoType, TagType, TrackType, ElementType>::additionalElements() const
{
return m_additionalElements;
}
/*!
* \brief Returns all available additional elements.
*
* The parser might decide to split up a file's element tree to skip irrelevant elements to achive better performance.
* This method gives access to those sub element trees. Each of the returned elements represents an independent element
* tree within the file.
*/
template <class FileInfoType, class TagType, class TrackType, class ElementType>
inline std::vector<std::unique_ptr<ElementType> > &GenericContainer<FileInfoType, TagType, TrackType, ElementType>::additionalElements()
{
return m_additionalElements;
}
template <class FileInfoType, class TagType, class TrackType, class ElementType>
inline TagType *GenericContainer<FileInfoType, TagType, TrackType, ElementType>::tag(std::size_t index)
{
return m_tags[index].get();
}
template <class FileInfoType, class TagType, class TrackType, class ElementType>
inline std::size_t GenericContainer<FileInfoType, TagType, TrackType, ElementType>::tagCount() const
{
return m_tags.size();
}
template <class FileInfoType, class TagType, class TrackType, class ElementType>
inline TrackType *GenericContainer<FileInfoType, TagType, TrackType, ElementType>::track(std::size_t index)
{
return m_tracks[index].get();
}
template<class FileInfoType, class TagType, class TrackType, class ElementType>
inline TrackType *GenericContainer<FileInfoType, TagType, TrackType, ElementType>::trackById(uint64 id)
{
for (auto &track : m_tracks) {
if(track->id() == id) {
return track.get();
}
}
return nullptr;
}
2015-04-22 19:22:01 +02:00
template <class FileInfoType, class TagType, class TrackType, class ElementType>
inline std::size_t GenericContainer<FileInfoType, TagType, TrackType, ElementType>::trackCount() const
{
return m_tracks.size();
}
/*!
* \brief Returns the tags of the file.
*
* The tags need to be parsed before (see parseTags()).
*
* The container keeps ownership over the returned tags.
*
* \sa areTagsParsed()
*/
template <class FileInfoType, class TagType, class TrackType, class ElementType>
inline const std::vector<std::unique_ptr<TagType> > &GenericContainer<FileInfoType, TagType, TrackType, ElementType>::tags() const
{
return m_tags;
}
/*!
* \brief Returns the tags of the file.
*
* The tags need to be parsed before (see parseTags()).
*
2016-03-22 22:52:36 +01:00
* The container keeps ownership over the returned tags. Do not push or remove elements to the returned vector.
2015-04-22 19:22:01 +02:00
*
* \sa areTagsParsed()
*/
template <class FileInfoType, class TagType, class TrackType, class ElementType>
inline std::vector<std::unique_ptr<TagType> > &GenericContainer<FileInfoType, TagType, TrackType, ElementType>::tags()
{
return m_tags;
}
/*!
* \brief Returns the tracks of the file.
*
* The tags need to be parsed before (see parseTracks()).
*
* The container keeps ownership over the returned tracks.
*
* \sa areTracksParsed()
*/
template <class FileInfoType, class TagType, class TrackType, class ElementType>
inline const std::vector<std::unique_ptr<TrackType> > &GenericContainer<FileInfoType, TagType, TrackType, ElementType>::tracks() const
{
return m_tracks;
}
/*!
* \brief Returns the tracks of the file.
*
* The tags need to be parsed before (see parseTracks()).
*
2016-03-22 22:52:36 +01:00
* The container keeps ownership over the returned tracks. Do not push or remove elements to the returned vector.
2015-04-22 19:22:01 +02:00
*
* \sa areTracksParsed()
*/
template <class FileInfoType, class TagType, class TrackType, class ElementType>
inline std::vector<std::unique_ptr<TrackType> > &GenericContainer<FileInfoType, TagType, TrackType, ElementType>::tracks()
{
return m_tracks;
}
template <class FileInfoType, class TagType, class TrackType, class ElementType>
2016-03-22 22:52:36 +01:00
TagType *GenericContainer<FileInfoType, TagType, TrackType, ElementType>::createTag(const TagTarget &target)
2015-04-22 19:22:01 +02:00
{
2016-05-14 00:24:01 +02:00
// check whether a tag matching the specified target is already assigned
if(!m_tags.empty()) {
if(!target.isEmpty() && m_tags.front()->supportsTarget()) {
for(auto &tag : m_tags) {
if(tag->target() == target) {
return tag.get();
}
2015-04-22 19:22:01 +02:00
}
2016-05-14 00:24:01 +02:00
} else {
return m_tags.front().get();
2015-04-22 19:22:01 +02:00
}
}
2016-05-14 00:24:01 +02:00
// a new tag must be created
2016-03-22 22:52:36 +01:00
m_tags.emplace_back(std::make_unique<TagType>());
2015-04-22 19:22:01 +02:00
auto &tag = m_tags.back();
tag->setTarget(target);
return tag.get();
}
template <class FileInfoType, class TagType, class TrackType, class ElementType>
bool GenericContainer<FileInfoType, TagType, TrackType, ElementType>::removeTag(Tag *tag)
{
if(auto size = m_tags.size()) {
m_tags.erase(std::remove_if(m_tags.begin(), m_tags.end(), [tag] (const std::unique_ptr<TagType> &existingTag) -> bool {
return static_cast<Tag *>(existingTag.get()) == tag;
}), m_tags.end());
return size != m_tags.size();
}
return false;
}
template <class FileInfoType, class TagType, class TrackType, class ElementType>
inline void GenericContainer<FileInfoType, TagType, TrackType, ElementType>::removeAllTags()
{
m_tags.clear();
}
/*!
* \brief Adds the specified \a track to the container.
*
* Adding tracks might be not supported by the implementation.
* \sa supportsTrackModifications()
*
* The tracks needs to be parsed before additional tracks can be added.
* \sa areTracksParsed()
* \sa parseTracks()
*
2017-05-28 20:56:15 +02:00
* \remarks The container takes ownership over the specified \a track if it was possible
* to add the track. This makes adding a track from another container impossible
* without removing it from the other container first.
2015-04-22 19:22:01 +02:00
*
* \returns Returns an indication whether the \a track could be added.
*/
template <class FileInfoType, class TagType, class TrackType, class ElementType>
bool GenericContainer<FileInfoType, TagType, TrackType, ElementType>::addTrack(TrackType *track)
{
if(areTracksParsed() && supportsTrackModifications()) {
2017-05-28 20:56:15 +02:00
// ensure ID is unique
auto id = track->id();
ensureIdIsUnique:
for(const auto &track : m_tracks) {
if(track->id() == id) {
++id;
goto ensureIdIsUnique;
}
}
track->setId(id);
2015-04-22 19:22:01 +02:00
m_tracks.emplace_back(track);
return m_tracksAltered = true;
}
return false;
}
template <class FileInfoType, class TagType, class TrackType, class ElementType>
bool GenericContainer<FileInfoType, TagType, TrackType, ElementType>::removeTrack(AbstractTrack *track)
{
bool removed = false;
if(areTracksParsed() && supportsTrackModifications() && !m_tracks.empty()) {
for(auto i = m_tracks.end() - 1, begin = m_tracks.begin(); ; --i) {
2016-03-22 22:52:36 +01:00
if(static_cast<AbstractTrack *>(i->get()) == track) {
2015-04-22 19:22:01 +02:00
i->release();
m_tracks.erase(i);
removed = true;
}
if(i == begin) {
break;
}
}
if(removed) {
m_tracksAltered = true;
}
}
return removed;
}
template <class FileInfoType, class TagType, class TrackType, class ElementType>
void GenericContainer<FileInfoType, TagType, TrackType, ElementType>::removeAllTracks()
{
if(areTracksParsed() && supportsTrackModifications() && m_tracks.size()) {
m_tracks.clear();
m_tracksAltered = true;
}
}
template <class FileInfoType, class TagType, class TrackType, class ElementType>
void GenericContainer<FileInfoType, TagType, TrackType, ElementType>::reset()
{
AbstractContainer::reset();
m_firstElement.reset();
m_additionalElements.clear();
m_tracks.clear();
m_tags.clear();
}
} // namespace Media
#endif // TAG_PARSER_GENERICCONTAINER_H