diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -166,6 +166,15 @@ LINK_LIBRARIES Qt5::Test KF5::FileMetaData ) +set(writercollection_SRCS + writercollectiontest.cpp +) + +ecm_add_test(${writercollection_SRCS} + TEST_NAME "writercollectiontest" + LINK_LIBRARIES Qt5::Test KF5::FileMetaData +) + # # XML # diff --git a/autotests/writercollectiontest.cpp b/autotests/writercollectiontest.cpp new file mode 100644 --- /dev/null +++ b/autotests/writercollectiontest.cpp @@ -0,0 +1,49 @@ +/* + * This file is part of the KDE KFileMetaData project + * Copyright (C) 2014 Vishesh Handa + * 2017 Igor Poboiko + * 2019 Alexander Stippich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include + +#include "writercollection.h" + +namespace KFileMetaData { + +class WriterCollectionTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void testFetchWriters() + { + QCoreApplication::setLibraryPaths({QCoreApplication::applicationDirPath()}); + WriterCollection collection; + QVERIFY(collection.fetchWriters(QStringLiteral("unknown/mimetype")).isEmpty()); + QVERIFY(!collection.fetchWriters(QStringLiteral("audio/mpeg3")).isEmpty()); + } + +}; + +} + +QTEST_GUILESS_MAIN(KFileMetaData::WriterCollectionTest) + +#include "writercollectiontest.moc" diff --git a/src/writer.h b/src/writer.h --- a/src/writer.h +++ b/src/writer.h @@ -38,7 +38,15 @@ */ class KFILEMETADATA_EXPORT Writer { + class WriterPrivate; + + enum WriterPluginOwnership { + AutoDeletePlugin, + DoNotDeletePlugin, + }; + public: + Writer(Writer&&); virtual ~Writer(); void write(const WriteData& data); @@ -50,10 +58,9 @@ void operator =(const Writer&); - class WriterPrivate; - WriterPrivate *d_ptr; - Q_DECLARE_PRIVATE(Writer) + void setAutoDeletePlugin(WriterPluginOwnership autoDelete); + WriterPrivate *d; friend class WriterCollection; }; } diff --git a/src/writer.cpp b/src/writer.cpp --- a/src/writer.cpp +++ b/src/writer.cpp @@ -28,25 +28,33 @@ using namespace KFileMetaData; Writer::Writer() - : d_ptr(new WriterPrivate) + : d(new WriterPrivate) { } Writer::~Writer() { - Q_D(Writer); - delete d->m_plugin; - delete d_ptr; + delete d; } +Writer::Writer(Writer&& other) +{ + d = other.d; + other.d = nullptr; +} + + void Writer::write(const WriteData& data) { - Q_D(Writer); d->m_plugin->write(data); } QStringList Writer::mimetypes() const { - Q_D(const Writer); return d->m_plugin->writeMimetypes(); } + +void Writer::setAutoDeletePlugin(WriterPluginOwnership autoDelete) +{ + d->m_autoDeletePlugin = autoDelete; +} diff --git a/src/writer_p.h b/src/writer_p.h --- a/src/writer_p.h +++ b/src/writer_p.h @@ -24,14 +24,24 @@ #ifndef KFILEMETADATA_WRITER_P_H #define KFILEMETADATA_WRITER_P_H +#include "writerplugin.h" + namespace KFileMetaData { class WriterPlugin; -class Q_DECL_HIDDEN Writer::WriterPrivate +class Writer::WriterPrivate { public: + ~WriterPrivate() { + if (m_autoDeletePlugin == AutoDeletePlugin) { + delete m_plugin; + } + } + WriterPlugin *m_plugin = nullptr; + + WriterPluginOwnership m_autoDeletePlugin = AutoDeletePlugin; }; } diff --git a/src/writercollection.h b/src/writercollection.h --- a/src/writercollection.h +++ b/src/writercollection.h @@ -45,8 +45,7 @@ private: class WriterCollectionPrivate; - WriterCollectionPrivate* d_ptr; - Q_DECLARE_PRIVATE(WriterCollection) + WriterCollectionPrivate* d; }; } diff --git a/src/writercollection.cpp b/src/writercollection.cpp --- a/src/writercollection.cpp +++ b/src/writercollection.cpp @@ -32,40 +32,33 @@ #include #include #include +#include +#include using namespace KFileMetaData; class Q_DECL_HIDDEN WriterCollection::WriterCollectionPrivate { public: QHash m_writers; - QList allWriters() const; + std::vector m_allWriters; + + void findWriters(); }; WriterCollection::WriterCollection() - : d_ptr(new WriterCollectionPrivate) + : d(new WriterCollectionPrivate) { - Q_D(WriterCollection); - const QList all = d->allWriters(); - - for (Writer* writer : all) { - const QStringList lst = writer->mimetypes(); - for (const QString& type : lst) { - d->m_writers.insertMulti(type, writer); - } - } + d->findWriters(); } WriterCollection::~WriterCollection() { - Q_D(WriterCollection); - qDeleteAll(d->m_writers.begin(), d->m_writers.end()); delete d; } - -QList WriterCollection::WriterCollectionPrivate::allWriters() const +void WriterCollection::WriterCollectionPrivate::findWriters() { QStringList plugins; QStringList pluginPaths; @@ -106,7 +99,6 @@ } externalPlugins.clear(); - QList writers; for (const QString& pluginPath : qAsConst(pluginPaths)) { QPluginLoader loader(pluginPath); @@ -120,10 +112,11 @@ if (obj) { WriterPlugin* plugin = qobject_cast(obj); if (plugin) { - Writer* writer = new Writer; - writer->d_ptr->m_plugin = plugin; + Writer writer; + writer.d->m_plugin = plugin; + writer.setAutoDeletePlugin(Writer::DoNotDeletePlugin); - writers << writer; + m_allWriters.push_back(std::move(writer)); } else { qCDebug(KFILEMETADATA_LOG) << "Plugin could not be converted to a WriterPlugin"; qCDebug(KFILEMETADATA_LOG) << pluginPath; @@ -136,24 +129,41 @@ for (const QString& externalPluginPath : qAsConst(externalPluginPaths)) { ExternalWriter *plugin = new ExternalWriter(externalPluginPath); - Writer* writer = new Writer; - writer->d_ptr->m_plugin = plugin; + Writer writer; + writer.d->m_plugin = plugin; + writer.setAutoDeletePlugin(Writer::AutoDeletePlugin); - writers << writer; + m_allWriters.push_back(std::move(writer)); } - return writers; + for (Writer& writer : m_allWriters) { + const QStringList lst = writer.mimetypes(); + for (const QString& type : lst) { + m_writers.insertMulti(type, &writer); + } + } } QList WriterCollection::fetchWriters(const QString& mimetype) const { - Q_D(const WriterCollection); QList plugins = d->m_writers.values(mimetype); - if (plugins.isEmpty()) { - auto it = d->m_writers.constBegin(); - for (; it != d->m_writers.constEnd(); ++it) { - if (mimetype.startsWith(it.key())) - plugins << it.value(); + if (!plugins.isEmpty()) { + return plugins; + } + + // try to find the best matching more generic writer by mimetype inheritance + QMimeDatabase db; + auto type = db.mimeTypeForName(mimetype); + const QStringList ancestors = type.allAncestors(); + + for (const auto &ancestor : ancestors) { + if (ancestor == QLatin1String("application/octet-stream")) { + continue; + } + QList plugins = d->m_writers.values(ancestor); + if (!plugins.isEmpty()) { + qCDebug(KFILEMETADATA_LOG) << "Using inherited mimetype" << ancestor << "for" << mimetype; + return plugins; } } diff --git a/src/writers/CMakeLists.txt b/src/writers/CMakeLists.txt --- a/src/writers/CMakeLists.txt +++ b/src/writers/CMakeLists.txt @@ -7,6 +7,7 @@ ${TAGLIB_LIBRARIES} ) + set_target_properties(kfilemetadata_taglibwriter PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/kf5/kfilemetadata/writers") install( TARGETS kfilemetadata_taglibwriter DESTINATION ${PLUGIN_INSTALL_DIR}/kf5/kfilemetadata/writers)