diff --git a/autotest/gcore/misc.py b/autotest/gcore/misc.py index 5d0cc0395344..54fd91bfde6e 100755 --- a/autotest/gcore/misc.py +++ b/autotest/gcore/misc.py @@ -1030,6 +1030,24 @@ def test_misc_general_cmd_line_processor(tmp_path): assert processed == ["program", "2", str(tmp_path / "a_path"), "a_string"] +############################################################################### +# Test GDALDriverHasOpenOption() + + +@pytest.mark.require_driver("GTiff") +@pytest.mark.parametrize( + "driver_name,open_option,expected", + [ + ("GTiff", "XXXX", False), + ("GTiff", "GEOTIFF_KEYS_FLAVOR", True), + ], +) +def test_misc_gdal_driver_has_open_option(driver_name, open_option, expected): + driver = gdal.GetDriverByName(driver_name) + assert driver is not None + assert driver.HasOpenOption(open_option) == expected + + ############################################################################### diff --git a/gcore/gdal.h b/gcore/gdal.h index 944ea2b3d1c3..c8d00745832c 100644 --- a/gcore/gdal.h +++ b/gcore/gdal.h @@ -1152,6 +1152,9 @@ char CPL_DLL **GDALGetOutputDriversForDatasetName(const char *pszDestFilename, bool bSingleMatch, bool bEmitWarning); +bool CPL_DLL GDALDriverHasOpenOption(GDALDriverH, + const char *pszOpenOptionName); + /* The following are deprecated */ const char CPL_DLL *CPL_STDCALL GDALGetDriverShortName(GDALDriverH); const char CPL_DLL *CPL_STDCALL GDALGetDriverLongName(GDALDriverH); diff --git a/gcore/gdal_priv.h b/gcore/gdal_priv.h index f5501e1a91d6..269b8789f300 100644 --- a/gcore/gdal_priv.h +++ b/gcore/gdal_priv.h @@ -2157,6 +2157,14 @@ class CPL_DLL GDALDriver : public GDALMajorObject CSLConstList papszVectorTranslateArguments, char ***ppapszFailureReasons); + /** + * \brief Returns TRUE if the given open option is supported by the driver. + * @param pszOpenOptionName name of the open option to be checked + * @return TRUE if the driver supports the open option + * @since GDAL 3.11 + */ + bool HasOpenOption(const char *pszOpenOptionName) const; + GDALDataset * VectorTranslateFrom(const char *pszDestName, GDALDataset *poSourceDS, CSLConstList papszVectorTranslateArguments, diff --git a/gcore/gdaldriver.cpp b/gcore/gdaldriver.cpp index cc7f96c17f93..02974ac4e946 100644 --- a/gcore/gdaldriver.cpp +++ b/gcore/gdaldriver.cpp @@ -1478,6 +1478,28 @@ bool GDALDriver::CanVectorTranslateFrom( return bRet; } +bool GDALDriver::HasOpenOption(const char *pszOpenOptionName) const +{ + if (pszOpenOptionName == nullptr) + return false; + + // Const cast is safe here since we are only reading the metadata + auto pszOOMd{const_cast(this)->GetMetadataItem( + GDAL_DMD_OPENOPTIONLIST)}; + if (pszOOMd == nullptr) + return false; + + const CPLXMLTreeCloser oXml{CPLParseXMLString(pszOOMd)}; + for (CPLXMLNode *option = oXml->psChild; option != nullptr; + option = option->psNext) + { + if (EQUAL(CPLGetXMLValue(CPLGetXMLNode(option, "name"), nullptr, ""), + pszOpenOptionName)) + return true; + } + return false; +} + /************************************************************************/ /* VectorTranslateFrom() */ /************************************************************************/ @@ -1985,6 +2007,23 @@ CPLErr CPL_STDCALL GDALCopyDatasetFiles(GDALDriverH hDriver, return GDALDriver::FromHandle(hDriver)->CopyFiles(pszNewName, pszOldName); } +/************************************************************************/ +/* GDALDriverHasOpenOption() */ +/************************************************************************/ + +/** + * \brief Returns TRUE if the given open option is supported by the driver. + * @param hDriver the handle of the driver + * @param pszOpenOptionName name of the open option to be checked + * @return TRUE if the driver supports the open option + * @since GDAL 3.11 + */ +bool GDALDriverHasOpenOption(GDALDriverH hDriver, const char *pszOpenOptionName) +{ + VALIDATE_POINTER1(hDriver, "GDALDriverHasOpenOption", false); + return GDALDriver::FromHandle(hDriver)->HasOpenOption(pszOpenOptionName); +} + /************************************************************************/ /* GDALGetDriverShortName() */ /************************************************************************/ diff --git a/swig/include/Driver.i b/swig/include/Driver.i index 43ad651f8258..fbad4d68af5f 100644 --- a/swig/include/Driver.i +++ b/swig/include/Driver.i @@ -109,6 +109,10 @@ public: return GDALCopyDatasetFiles( self, newName, oldName ); } + bool HasOpenOption( const char *openOptionName ) { + return GDALDriverHasOpenOption( self, openOptionName ); + } + #ifdef SWIGPYTHON bool TestCapability(const char* cap) { // TODO: should this also check DCAP entries in driver metadata?