#ifndef TAG_PARSER_GENERICCONTAINER_H #define TAG_PARSER_GENERICCONTAINER_H #include "./abstractcontainer.h" #include #include #include namespace TagParser { /*! * \class TagParser::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 TagParser::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 TAG_PARSER_EXPORT GenericContainer : public AbstractContainer { friend FileInfoType; public: GenericContainer(FileInfoType &fileInfo, std::uint64_t startOffset); ~GenericContainer() override; void validateElementStructure(Diagnostics &diag, AbortableProgressFeedback &progress, std::uint64_t *paddingSize = nullptr); FileInfoType &fileInfo() const; ElementType *firstElement() const; const std::vector> &additionalElements() const; std::vector> &additionalElements(); TagType *tag(std::size_t index) override; std::size_t tagCount() const override; TrackType *track(std::size_t index) override; TrackType *trackById(std::uint64_t id); std::size_t trackCount() const override; const std::vector> &tags() const; std::vector> &tags(); const std::vector> &tracks() const; std::vector> &tracks(); TagType *createTag(const TagTarget &target = TagTarget()) override; bool removeTag(Tag *tag) override; void removeAllTags() override; bool addTrack(TrackType *track); bool removeTrack(AbstractTrack *track) override; void removeAllTracks() override; void reset() override; using ContainerFileInfoType = FileInfoType; using ContainerTagType = TagType; using ContainerTrackType = TrackType; using ContainerElementType = ElementType; protected: std::unique_ptr m_firstElement; std::vector> m_additionalElements; std::vector> m_tags; std::vector> m_tracks; private: FileInfoType *m_fileInfo; }; /*! * \brief Constructs a new container for the specified \a fileInfo at the specified \a startOffset. */ template GenericContainer::GenericContainer(FileInfoType &fileInfo, std::uint64_t 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 GenericContainer::~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 inline void GenericContainer::validateElementStructure( Diagnostics &diag, AbortableProgressFeedback &progress, std::uint64_t *paddingSize) { parseHeader(diag, progress); if (m_firstElement) { m_firstElement->validateSubsequentElementStructure(diag, paddingSize, &progress); } } /*! * \brief Returns the related file info. * * The related file info has been specified when constructing the container. */ template inline FileInfoType &GenericContainer::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 inline ElementType *GenericContainer::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 achieve 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 inline const std::vector> &GenericContainer::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 achieve 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 inline std::vector> &GenericContainer::additionalElements() { return m_additionalElements; } template inline TagType *GenericContainer::tag(std::size_t index) { return m_tags[index].get(); } template inline std::size_t GenericContainer::tagCount() const { return m_tags.size(); } template inline TrackType *GenericContainer::track(std::size_t index) { return m_tracks[index].get(); } template inline TrackType *GenericContainer::trackById(std::uint64_t id) { for (auto &track : m_tracks) { if (track->id() == id) { return track.get(); } } return nullptr; } template inline std::size_t GenericContainer::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 inline const std::vector> &GenericContainer::tags() const { return m_tags; } /*! * \brief Returns the tags of the file. * * The tags need to be parsed before (see parseTags()). * * The container keeps ownership over the returned tags. Do not push or remove elements to the returned vector. * * \sa areTagsParsed() */ template inline std::vector> &GenericContainer::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 inline const std::vector> &GenericContainer::tracks() const { return m_tracks; } /*! * \brief Returns the tracks of the file. * * The tags need to be parsed before (see parseTracks()). * * The container keeps ownership over the returned tracks. Do not push or remove elements to the returned vector. * * \sa areTracksParsed() */ template inline std::vector> &GenericContainer::tracks() { return m_tracks; } template TagType *GenericContainer::createTag(const TagTarget &target) { // 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(); } } } else { return m_tags.front().get(); } } // a new tag must be created const auto &tag = m_tags.emplace_back(std::make_unique()); tag->setTarget(target); return tag.get(); } template bool GenericContainer::removeTag(Tag *tag) { if (const auto size = m_tags.size()) { m_tags.erase(std::remove_if(m_tags.begin(), m_tags.end(), [tag](const std::unique_ptr &existingTag) -> bool { return static_cast(existingTag.get()) == tag; }), m_tags.end()); return size != m_tags.size(); } return false; } template inline void GenericContainer::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() * * \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. * * \returns Returns an indication whether the \a track could be added. */ template bool GenericContainer::addTrack(TrackType *track) { if (!areTracksParsed() || !supportsTrackModifications()) { return false; } // ensure ID is unique auto id = track->id(); ensureIdIsUnique: for (const auto &existingTrack : m_tracks) { if (existingTrack->id() == id) { ++id; goto ensureIdIsUnique; } } track->setId(id); m_tracks.emplace_back(track); return m_tracksAltered = true; } template bool GenericContainer::removeTrack(AbstractTrack *track) { if (!areTracksParsed() || !supportsTrackModifications() || m_tracks.empty()) { return false; } auto removed = false; for (auto i = m_tracks.end() - 1, begin = m_tracks.begin();; --i) { if (static_cast(i->get()) == track) { i->release(); m_tracks.erase(i); removed = true; } if (i == begin) { break; } } if (removed) { m_tracksAltered = true; } return removed; } template void GenericContainer::removeAllTracks() { if (areTracksParsed() && supportsTrackModifications() && m_tracks.size()) { m_tracks.clear(); m_tracksAltered = true; } } template void GenericContainer::reset() { AbstractContainer::reset(); m_firstElement.reset(); m_additionalElements.clear(); m_tracks.clear(); m_tags.clear(); } } // namespace TagParser #endif // TAG_PARSER_GENERICCONTAINER_H