From 392eaac69a19d5ca2dd227c93cbb92182b6d0f11 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 25 Jun 2024 13:04:19 +1000 Subject: [PATCH] Refactor out method for creating layer data items from file-like paths Provide a method so that external classes can use this same logic to create their own layer items containing sublayers for file-like paths. --- .../browser/qgsfilebaseddataitemprovider.cpp | 79 +++++++++++++++++-- .../browser/qgsfilebaseddataitemprovider.h | 32 +++++++- 2 files changed, 103 insertions(+), 8 deletions(-) diff --git a/src/core/browser/qgsfilebaseddataitemprovider.cpp b/src/core/browser/qgsfilebaseddataitemprovider.cpp index 0258e9792ce..53acd34d16f 100644 --- a/src/core/browser/qgsfilebaseddataitemprovider.cpp +++ b/src/core/browser/qgsfilebaseddataitemprovider.cpp @@ -213,9 +213,10 @@ QgsMimeDataUtils::UriList QgsFileDataCollectionGroupItem::mimeUris() const // QgsFileDataCollectionItem // -QgsFileDataCollectionItem::QgsFileDataCollectionItem( QgsDataItem *parent, const QString &name, const QString &path, const QList &sublayers ) +QgsFileDataCollectionItem::QgsFileDataCollectionItem( QgsDataItem *parent, const QString &name, const QString &path, const QList &sublayers, const QVariantMap &extraUriParts ) : QgsDataCollectionItem( parent, name, path ) , mSublayers( sublayers ) + , mExtraUriParts( extraUriParts ) { if ( QgsProviderUtils::sublayerDetailsAreIncomplete( mSublayers, QgsProviderUtils::SublayerCompletenessFlag::IgnoreUnknownFeatureCount ) ) setCapabilities( Qgis::BrowserItemCapability::Fertile ); @@ -231,8 +232,37 @@ QgsFileDataCollectionItem::QgsFileDataCollectionItem( QgsDataItem *parent, const QVector QgsFileDataCollectionItem::createChildren() { QList< QgsProviderSublayerDetails> sublayers; - if ( QgsProviderUtils::sublayerDetailsAreIncomplete( mSublayers, QgsProviderUtils::SublayerCompletenessFlag::IgnoreUnknownFeatureCount ) - || mSublayers.empty() ) + if ( QgsProviderUtils::sublayerDetailsAreIncomplete( mSublayers, QgsProviderUtils::SublayerCompletenessFlag::IgnoreUnknownFeatureCount ) ) + { + QSet< QString > providers; + for ( const QgsProviderSublayerDetails &details : std::as_const( mSublayers ) ) + { + providers.insert( details.providerKey() ); + } + + for ( const QString &provider : std::as_const( providers ) ) + { + if ( QgsProviderMetadata *metadata = QgsProviderRegistry::instance()->providerMetadata( provider ) ) + { + if ( !mExtraUriParts.empty() ) + { + QVariantMap uriParts = metadata->decodeUri( path() ); + for ( auto it = mExtraUriParts.constBegin(); it != mExtraUriParts.constEnd(); ++it ) + { + uriParts.insert( it.key(), it.value() ); + } + QString updatedUri = metadata->encodeUri( uriParts ); + + sublayers.append( metadata->querySublayers( updatedUri.isEmpty() ? path() : updatedUri, Qgis::SublayerQueryFlag::ResolveGeometryType ) ); + } + else + { + sublayers.append( metadata->querySublayers( path(), Qgis::SublayerQueryFlag::ResolveGeometryType ) ); + } + } + } + } + else if ( mSublayers.empty() ) { sublayers = QgsProviderRegistry::instance()->querySublayers( path(), Qgis::SublayerQueryFlag::ResolveGeometryType ); } @@ -526,6 +556,16 @@ Qgis::DataItemProviderCapabilities QgsFileBasedDataItemProvider::capabilities() } QgsDataItem *QgsFileBasedDataItemProvider::createDataItem( const QString &path, QgsDataItem *parentItem ) +{ + return createDataItemForPathPrivate( path, parentItem, nullptr, Qgis::SublayerQueryFlags(), QVariantMap() ); +} + +QgsDataItem *QgsFileBasedDataItemProvider::createLayerItemForPath( const QString &path, QgsDataItem *parentItem, const QStringList &allowedProviders, const QVariantMap &extraUriParts, Qgis::SublayerQueryFlags queryFlags ) +{ + return createDataItemForPathPrivate( path, parentItem, &allowedProviders, queryFlags, extraUriParts ); +} + +QgsDataItem *QgsFileBasedDataItemProvider::createDataItemForPathPrivate( const QString &path, QgsDataItem *parentItem, const QStringList *allowedProviders, Qgis::SublayerQueryFlags queryFlags, const QVariantMap &extraUriParts ) { if ( path.isEmpty() ) return nullptr; @@ -579,8 +619,6 @@ QgsDataItem *QgsFileBasedDataItemProvider::createDataItem( const QString &path, QgsSettings settings; - Qgis::SublayerQueryFlags queryFlags = Qgis::SublayerQueryFlags(); - // should we fast scan only? if ( ( settings.value( QStringLiteral( "qgis/scanItemsInBrowser2" ), "extension" ).toString() == QLatin1String( "extension" ) ) || @@ -590,7 +628,34 @@ QgsDataItem *QgsFileBasedDataItemProvider::createDataItem( const QString &path, queryFlags |= Qgis::SublayerQueryFlag::FastScan; } - const QList sublayers = QgsProviderRegistry::instance()->querySublayers( path, queryFlags ); + QList sublayers; + if ( !allowedProviders ) + { + sublayers = QgsProviderRegistry::instance()->querySublayers( path, queryFlags ); + } + else + { + for ( const QString &provider : *allowedProviders ) + { + if ( QgsProviderMetadata *metadata = QgsProviderRegistry::instance()->providerMetadata( provider ) ) + { + if ( !extraUriParts.empty() ) + { + QVariantMap uriParts = metadata->decodeUri( path ); + for ( auto it = extraUriParts.constBegin(); it != extraUriParts.constEnd(); ++it ) + { + uriParts.insert( it.key(), it.value() ); + } + + sublayers.append( metadata->querySublayers( metadata->encodeUri( uriParts ), queryFlags ) ); + } + else + { + sublayers.append( metadata->querySublayers( path, queryFlags ) ); + } + } + } + } if ( sublayers.size() == 1 && ( ( ( queryFlags & Qgis::SublayerQueryFlag::FastScan ) && !QgsProviderUtils::sublayerDetailsAreIncomplete( sublayers, QgsProviderUtils::SublayerCompletenessFlag::IgnoreUnknownFeatureCount | QgsProviderUtils::SublayerCompletenessFlag::IgnoreUnknownGeometryType ) ) @@ -603,7 +668,7 @@ QgsDataItem *QgsFileBasedDataItemProvider::createDataItem( const QString &path, } else if ( !sublayers.empty() ) { - QgsFileDataCollectionItem *item = new QgsFileDataCollectionItem( parentItem, name, path, sublayers ); + QgsFileDataCollectionItem *item = new QgsFileDataCollectionItem( parentItem, name, path, sublayers, extraUriParts ); item->setCapabilities( item->capabilities2() | Qgis::BrowserItemCapability::ItemRepresentsFile ); return item; } diff --git a/src/core/browser/qgsfilebaseddataitemprovider.h b/src/core/browser/qgsfilebaseddataitemprovider.h index 79fbcabbcc1..29bd9ba8067 100644 --- a/src/core/browser/qgsfilebaseddataitemprovider.h +++ b/src/core/browser/qgsfilebaseddataitemprovider.h @@ -129,8 +129,13 @@ class CORE_EXPORT QgsFileDataCollectionItem final: public QgsDataCollectionItem * \param sublayers list of sublayers to initially populate the item with. If the sublayer details are incomplete * (see QgsProviderUtils::sublayerDetailsAreIncomplete()) then the item will be populated in a background thread when * expanded. + * \param extraUriParts optional map of extra components to append to URIs generated for the \a path. The provider-specific encodeUri methods will be used to handle these URI additions. Since QGIS 3.40. */ - QgsFileDataCollectionItem( QgsDataItem *parent, const QString &name, const QString &path, const QList< QgsProviderSublayerDetails> &sublayers ); + QgsFileDataCollectionItem( QgsDataItem *parent, + const QString &name, + const QString &path, + const QList< QgsProviderSublayerDetails> &sublayers, + const QVariantMap &extraUriParts = QVariantMap() ); QVector createChildren() override; bool hasDragEnabled() const override; @@ -180,6 +185,7 @@ class CORE_EXPORT QgsFileDataCollectionItem final: public QgsDataCollectionItem private: QList< QgsProviderSublayerDetails> mSublayers; + QVariantMap mExtraUriParts; mutable bool mHasCachedCapabilities = false; mutable QgsAbstractDatabaseProviderConnection::Capabilities mCachedCapabilities; mutable Qgis::DatabaseProviderConnectionCapabilities2 mCachedCapabilities2; @@ -204,7 +210,31 @@ class CORE_EXPORT QgsFileBasedDataItemProvider : public QgsDataItemProvider QString name() override; Qgis::DataItemProviderCapabilities capabilities() const override; QgsDataItem *createDataItem( const QString &path, QgsDataItem *parentItem ) override SIP_FACTORY; + + /** + * Static method to create a data item for sublayers corresponding to a file-like \a path. + * + * \param path file like path to create item for + * \param parentItem parent data item + * \param providers list of data providers to include when scanning for sublayers for the path. Must be populated. + * \param extraUriParts map of optional extra components to append to URIs generated for the \a path. The provider-specific encodeUri methods will be used to handle these URI additions. + * \param queryFlags flags controlling sublayer querying + * + * \returns data item, if \a path corresponds to a layer or an item with multiple sublayers + * + * \since QGIS 3.40 + */ + static QgsDataItem *createLayerItemForPath( const QString &path, QgsDataItem *parentItem, const QStringList &providers, + const QVariantMap &extraUriParts, + Qgis::SublayerQueryFlags queryFlags ); + bool handlesDirectoryPath( const QString &path ) override; + + private: + + static QgsDataItem *createDataItemForPathPrivate( const QString &path, QgsDataItem *parentItem, const QStringList *allowedProviders, + Qgis::SublayerQueryFlags queryFlags, + const QVariantMap &extraUriParts ); }; #endif // QGSFILEBASEDDATAITEMPROVIDER_H