Line data Source code
1 : /****************************************************************************** 2 : * 3 : * Project: GDAL 4 : * Purpose: GDALAlgorithm class 5 : * Author: Even Rouault <even dot rouault at spatialys.com> 6 : * 7 : ****************************************************************************** 8 : * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com> 9 : * 10 : * SPDX-License-Identifier: MIT 11 : ****************************************************************************/ 12 : 13 : #include "gdalalgorithm.h" 14 : #include "gdalalg_main.h" 15 : #ifdef GDAL_ENABLE_ALGORITHMS 16 : #include "gdalalg_raster.h" 17 : #include "gdalalg_vector.h" 18 : #include "gdalalg_dataset.h" 19 : #include "gdalalg_mdim.h" 20 : #include "gdalalg_convert.h" 21 : #include "gdalalg_info.h" 22 : #include "gdalalg_pipeline.h" 23 : #include "gdalalg_vsi.h" 24 : #include "gdalalg_crs.h" 25 : #endif 26 : 27 : #include "cpl_vsi.h" 28 : 29 : #include "gdal_priv.h" 30 : 31 : #include <cassert> 32 : 33 : /************************************************************************/ 34 : /* GDALAlgorithmRegistry::~GDALAlgorithmRegistry() */ 35 : /************************************************************************/ 36 : 37 : GDALAlgorithmRegistry::~GDALAlgorithmRegistry() = default; 38 : 39 : /************************************************************************/ 40 : /* GDALAlgorithmRegistry::Register() */ 41 : /************************************************************************/ 42 : 43 220209 : bool GDALAlgorithmRegistry::Register(const GDALAlgorithmRegistry::AlgInfo &info) 44 : { 45 220209 : if (cpl::contains(m_mapNameToInfo, info.m_name)) 46 : { 47 1 : CPLError(CE_Failure, CPLE_AppDefined, 48 : "GDAL algorithm '%s' already registered!", 49 : info.m_name.c_str()); 50 1 : return false; 51 : } 52 262564 : for (const std::string &alias : info.m_aliases) 53 : { 54 84715 : if (cpl::contains(m_mapAliasToInfo, alias) || 55 42357 : cpl::contains(m_mapHiddenAliasToInfo, alias)) 56 : { 57 2 : CPLError(CE_Failure, CPLE_AppDefined, 58 : "An algorithm with alias '%s' is already registered!", 59 : alias.c_str()); 60 2 : return false; 61 : } 62 : } 63 220206 : m_mapNameToInfo[info.m_name] = info; 64 220206 : bool hidden = false; 65 262561 : for (const std::string &alias : info.m_aliases) 66 : { 67 42355 : if (alias == HIDDEN_ALIAS_SEPARATOR) 68 15994 : hidden = true; 69 26361 : else if (hidden) 70 21597 : m_mapAliasToInfo[alias] = info; 71 : else 72 4764 : m_mapHiddenAliasToInfo[alias] = info; 73 : } 74 220206 : return true; 75 : } 76 : 77 : /************************************************************************/ 78 : /* GDALAlgorithmRegistry::InstantiateTopLevel() */ 79 : /************************************************************************/ 80 : 81 : std::unique_ptr<GDALAlgorithm> 82 20137 : GDALAlgorithmRegistry::InstantiateTopLevel(const std::string &name) const 83 : { 84 20137 : auto iter = m_mapNameToInfo.find(name); 85 20137 : if (iter == m_mapNameToInfo.end()) 86 : { 87 1708 : iter = m_mapAliasToInfo.find(name); 88 1708 : if (iter == m_mapAliasToInfo.end()) 89 : { 90 1702 : iter = m_mapHiddenAliasToInfo.find(name); 91 1702 : if (iter == m_mapHiddenAliasToInfo.end()) 92 : { 93 1699 : return nullptr; 94 : } 95 : } 96 : } 97 36876 : auto alg = iter->second.m_creationFunc(); 98 18438 : alg->m_aliases = iter->second.m_aliases; 99 18438 : return alg; 100 : } 101 : 102 : /************************************************************************/ 103 : /* GDALAlgorithmRegistry::Instantiate() */ 104 : /************************************************************************/ 105 : 106 : std::unique_ptr<GDALAlgorithm> 107 4933 : GDALAlgorithmRegistry::Instantiate(const std::vector<std::string> &path) const 108 : { 109 4933 : if (path.empty()) 110 1 : return nullptr; 111 9864 : auto alg = Instantiate(path[0]); 112 4949 : for (size_t i = 1; i < path.size() && alg; ++i) 113 : { 114 17 : alg = alg->InstantiateSubAlgorithm(path[i]); 115 : } 116 4932 : return alg; 117 : } 118 : 119 : /************************************************************************/ 120 : /* GDALAlgorithmRegistry::GetNames() */ 121 : /************************************************************************/ 122 : 123 3174 : std::vector<std::string> GDALAlgorithmRegistry::GetNames() const 124 : { 125 3174 : std::vector<std::string> res; 126 17912 : for (const auto &iter : m_mapNameToInfo) 127 : { 128 14738 : res.push_back(iter.first); 129 : } 130 3174 : return res; 131 : } 132 : 133 : /************************************************************************/ 134 : /* GDALAlgorithmRegistry::Instantiate() */ 135 : /************************************************************************/ 136 : 137 : std::unique_ptr<GDALAlgorithm> 138 20627 : GDALAlgorithmRegistry::Instantiate(const std::string &name) const 139 : { 140 20627 : return InstantiateTopLevel(name); 141 : } 142 : 143 : std::unique_ptr<GDALAlgorithm> 144 4931 : GDALAlgorithmRegistry::InstantiateInternal(std::vector<std::string> &path) 145 : { 146 4931 : return Instantiate(path); 147 : } 148 : 149 : /************************************************************************/ 150 : /* GDALGlobalAlgorithmRegistry::GDALGlobalAlgorithmRegistry() */ 151 : /************************************************************************/ 152 : 153 1610 : GDALGlobalAlgorithmRegistry::GDALGlobalAlgorithmRegistry() 154 : { 155 : #ifdef GDAL_ENABLE_ALGORITHMS 156 1610 : Register<GDALRasterAlgorithm>(); 157 1610 : Register<GDALVectorAlgorithm>(); 158 1610 : Register<GDALDatasetAlgorithm>(); 159 1610 : Register<GDALMdimAlgorithm>(); 160 1610 : Register<GDALConvertAlgorithm>(); 161 1610 : Register<GDALInfoAlgorithm>(); 162 1610 : Register<GDALPipelineAlgorithm>(); 163 1610 : Register<GDALVSIAlgorithm>(); 164 1610 : Register<GDALCRSAlgorithm>(); 165 : #endif 166 1610 : } 167 : 168 : GDALGlobalAlgorithmRegistry::~GDALGlobalAlgorithmRegistry() = default; 169 : 170 : /************************************************************************/ 171 : /* GDALGlobalAlgorithmRegistry::GetSingleton() */ 172 : /************************************************************************/ 173 : 174 : /* static */ GDALGlobalAlgorithmRegistry & 175 43513 : GDALGlobalAlgorithmRegistry::GetSingleton() 176 : { 177 43513 : static GDALGlobalAlgorithmRegistry singleton; 178 43513 : return singleton; 179 : } 180 : 181 : /************************************************************************/ 182 : /* GDALGlobalAlgorithmRegistry::InstantiateTopLevel() */ 183 : /************************************************************************/ 184 : 185 : std::unique_ptr<GDALAlgorithm> 186 3860 : GDALGlobalAlgorithmRegistry::InstantiateTopLevel(const std::string &name) const 187 : { 188 3860 : if (name == GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME) 189 490 : return std::make_unique<GDALMainAlgorithm>(); 190 6740 : auto alg = GDALAlgorithmRegistry::InstantiateTopLevel(name); 191 3370 : if (!alg) 192 : { 193 260 : alg = InstantiateDeclaredSubAlgorithm( 194 195 : {GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME, name}); 195 : } 196 3370 : if (alg) 197 : { 198 10092 : alg->SetCallPath({GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME, name}); 199 : } 200 3370 : return alg; 201 : } 202 : 203 : /************************************************************************/ 204 : /* GDALGlobalAlgorithmRegistry::DeclareAlgorithm() */ 205 : /************************************************************************/ 206 : 207 35925 : void GDALGlobalAlgorithmRegistry::DeclareAlgorithm( 208 : const std::vector<std::string> &path, InstantiateFunc instantiateFunc) 209 : { 210 35925 : Node *curNode = &m_root; 211 126098 : for (size_t i = 0; i < path.size(); ++i) 212 : { 213 90173 : const std::string &name = path[i]; 214 90173 : auto iter = curNode->children.find(name); 215 90173 : if (iter == curNode->children.end()) 216 : { 217 33602 : Node newNode; 218 33602 : if (i + 1 == path.size()) 219 : { 220 33601 : newNode.instantiateFunc = instantiateFunc; 221 : } 222 : else 223 : { 224 : newNode.instantiateFunc = 225 4 : [name]() -> std::unique_ptr<GDALAlgorithm> 226 : { 227 4 : return std::make_unique<GDALContainerAlgorithm>( 228 8 : name, std::string("Command for ").append(name)); 229 1 : }; 230 : } 231 33602 : curNode = 232 67204 : &(curNode->children.insert(std::pair(name, std::move(newNode))) 233 33602 : .first->second); 234 : } 235 : else 236 : { 237 56571 : curNode = &(iter->second); 238 : } 239 : } 240 35925 : } 241 : 242 : /************************************************************************/ 243 : /* GDALGlobalAlgorithmRegistry::GetNodeFromPath() */ 244 : /************************************************************************/ 245 : 246 : const GDALGlobalAlgorithmRegistry::Node * 247 54757 : GDALGlobalAlgorithmRegistry::GetNodeFromPath( 248 : const std::vector<std::string> &path) const 249 : { 250 54757 : if (!path.empty()) 251 : { 252 53571 : const Node *curNode = &m_root; 253 53571 : bool first = true; 254 114619 : for (const std::string &name : path) 255 : { 256 87918 : if (first && name == GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME) 257 : { 258 10829 : first = false; 259 10829 : continue; 260 : } 261 77089 : first = false; 262 77089 : auto iter = curNode->children.find(name); 263 77089 : if (iter == curNode->children.end()) 264 26870 : return nullptr; 265 50219 : curNode = &(iter->second); 266 : } 267 26701 : return curNode; 268 : } 269 1186 : return nullptr; 270 : } 271 : 272 : /************************************************************************/ 273 : /* GDALGlobalAlgorithmRegistry::GetDeclaredSubAlgorithmNames() */ 274 : /************************************************************************/ 275 : 276 : std::vector<std::string> 277 13556 : GDALGlobalAlgorithmRegistry::GetDeclaredSubAlgorithmNames( 278 : const std::vector<std::string> &path) const 279 : { 280 13556 : const GDALGlobalAlgorithmRegistry::Node *node = GetNodeFromPath(path); 281 13556 : std::vector<std::string> ret; 282 13556 : if (node) 283 : { 284 4419 : for (const auto &[name, subnode] : node->children) 285 : { 286 : // If there is an instantiation function, run it, to avoid 287 : // reporting algorithms that might be in drivers built as 288 : // deferred loaded plugins, but not available at runtime. 289 2731 : if (!subnode.instantiateFunc || subnode.instantiateFunc()) 290 : { 291 2731 : ret.push_back(name); 292 : } 293 : } 294 : } 295 13556 : return ret; 296 : } 297 : 298 : /************************************************************************/ 299 : /* GDALGlobalAlgorithmRegistry::HasDeclaredSubAlgorithm() */ 300 : /************************************************************************/ 301 : 302 39846 : bool GDALGlobalAlgorithmRegistry::HasDeclaredSubAlgorithm( 303 : const std::vector<std::string> &path) const 304 : { 305 39846 : return GetNodeFromPath(path) != nullptr; 306 : } 307 : 308 : /************************************************************************/ 309 : /* GDALGlobalAlgorithmRegistry::InstantiateDeclaredSubAlgorithm() */ 310 : /************************************************************************/ 311 : 312 : std::unique_ptr<GDALAlgorithm> 313 1355 : GDALGlobalAlgorithmRegistry::InstantiateDeclaredSubAlgorithm( 314 : const std::vector<std::string> &path) const 315 : { 316 1355 : const GDALGlobalAlgorithmRegistry::Node *node = GetNodeFromPath(path); 317 1355 : if (node && node->instantiateFunc) 318 : { 319 2334 : auto alg = node->instantiateFunc(); 320 1167 : if (alg) 321 : { 322 2334 : auto callPath = path; 323 1167 : if (path[0] != GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME) 324 0 : callPath.insert(callPath.begin(), 325 0 : GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME); 326 1167 : alg->SetCallPath(callPath); 327 : } 328 1167 : return alg; 329 : } 330 188 : return nullptr; 331 : } 332 : 333 : /************************************************************************/ 334 : /* struct GDALAlgorithmRegistryHS */ 335 : /************************************************************************/ 336 : 337 : struct GDALAlgorithmRegistryHS 338 : { 339 : GDALAlgorithmRegistry *ptr = nullptr; 340 : }; 341 : 342 : /************************************************************************/ 343 : /* GDALGetGlobalAlgorithmRegistry() */ 344 : /************************************************************************/ 345 : 346 : /** Gets a handle to the GDALGetGlobalAlgorithmRegistry which references 347 : * all available top-level GDAL algorithms ("raster", "vector", etc.) 348 : * 349 : * The handle must be released with GDALAlgorithmRegistryRelease() (but 350 : * this does not destroy the GDALAlgorithmRegistryRelease singleton). 351 : * 352 : * @since 3.11 353 : */ 354 3383 : GDALAlgorithmRegistryH GDALGetGlobalAlgorithmRegistry() 355 : { 356 6766 : auto ret = std::make_unique<GDALAlgorithmRegistryHS>(); 357 3383 : ret->ptr = &(GDALGlobalAlgorithmRegistry::GetSingleton()); 358 6766 : return ret.release(); 359 : } 360 : 361 : /************************************************************************/ 362 : /* GDALAlgorithmRegistryRelease() */ 363 : /************************************************************************/ 364 : 365 : /** Release a handle to an algorithm registry, but this does not destroy the 366 : * registry itself. 367 : * 368 : * @since 3.11 369 : */ 370 3383 : void GDALAlgorithmRegistryRelease(GDALAlgorithmRegistryH hReg) 371 : { 372 3383 : delete hReg; 373 3383 : } 374 : 375 : /************************************************************************/ 376 : /* GDALAlgorithmRegistryGetAlgNames() */ 377 : /************************************************************************/ 378 : 379 : /** Return the names of the algorithms registered in the registry passed as 380 : * parameter. 381 : * 382 : * @param hReg Handle to a registry. Must NOT be null. 383 : * @return a NULL terminated list of names, which must be destroyed with 384 : * CSLDestroy() 385 : * 386 : * @since 3.11 387 : */ 388 2 : char **GDALAlgorithmRegistryGetAlgNames(GDALAlgorithmRegistryH hReg) 389 : { 390 2 : VALIDATE_POINTER1(hReg, __func__, nullptr); 391 2 : return CPLStringList(hReg->ptr->GetNames()).StealList(); 392 : } 393 : 394 : /************************************************************************/ 395 : /* GDALAlgorithmRegistryInstantiateAlg() */ 396 : /************************************************************************/ 397 : 398 : /** Instantiate an algorithm available in a registry from its name. 399 : * 400 : * @param hReg Handle to a registry. Must NOT be null. 401 : * @param pszAlgName Algorithm name. Must NOT be null. 402 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease), 403 : * or NULL if the algorithm does not exist or another error occurred. 404 : * 405 : * @since 3.11 406 : */ 407 3388 : GDALAlgorithmH GDALAlgorithmRegistryInstantiateAlg(GDALAlgorithmRegistryH hReg, 408 : const char *pszAlgName) 409 : { 410 3388 : VALIDATE_POINTER1(hReg, __func__, nullptr); 411 3388 : VALIDATE_POINTER1(pszAlgName, __func__, nullptr); 412 6776 : auto alg = hReg->ptr->Instantiate(pszAlgName); 413 6776 : return alg ? std::make_unique<GDALAlgorithmHS>(std::move(alg)).release() 414 6776 : : nullptr; 415 : } 416 : 417 : /************************************************************************/ 418 : /* GDALAlgorithmRegistryInstantiateAlgFromPath() */ 419 : /************************************************************************/ 420 : 421 : /** Instantiate an algorithm available in a registry from its path. 422 : * 423 : * @param hReg Handle to a registry. Must NOT be null. 424 : * @param papszAlgPath Algorithm path. Must NOT be null. 425 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease), 426 : * or NULL if the algorithm does not exist or another error occurred. 427 : * 428 : * @since 3.12 429 : */ 430 : GDALAlgorithmH 431 1 : GDALAlgorithmRegistryInstantiateAlgFromPath(GDALAlgorithmRegistryH hReg, 432 : const char *const *papszAlgPath) 433 : { 434 1 : VALIDATE_POINTER1(hReg, __func__, nullptr); 435 1 : VALIDATE_POINTER1(papszAlgPath, __func__, nullptr); 436 1 : auto alg = hReg->ptr->Instantiate( 437 2 : static_cast<std::vector<std::string>>(CPLStringList(papszAlgPath))); 438 2 : return alg ? std::make_unique<GDALAlgorithmHS>(std::move(alg)).release() 439 2 : : nullptr; 440 : }