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 : 16 : #include "cpl_vsi.h" 17 : 18 : #include "gdal_priv.h" 19 : 20 : #include <cassert> 21 : 22 : /************************************************************************/ 23 : /* GDALAlgorithmRegistry::~GDALAlgorithmRegistry() */ 24 : /************************************************************************/ 25 : 26 : GDALAlgorithmRegistry::~GDALAlgorithmRegistry() = default; 27 : 28 : /************************************************************************/ 29 : /* GDALAlgorithmRegistry::Register() */ 30 : /************************************************************************/ 31 : 32 127995 : bool GDALAlgorithmRegistry::Register(const GDALAlgorithmRegistry::AlgInfo &info) 33 : { 34 127995 : if (cpl::contains(m_mapNameToInfo, info.m_name)) 35 : { 36 1 : CPLError(CE_Failure, CPLE_AppDefined, 37 : "GDAL algorithm '%s' already registered!", 38 : info.m_name.c_str()); 39 1 : return false; 40 : } 41 157609 : for (const std::string &alias : info.m_aliases) 42 : { 43 59233 : if (cpl::contains(m_mapAliasToInfo, alias) || 44 29616 : cpl::contains(m_mapHiddenAliasToInfo, alias)) 45 : { 46 2 : CPLError(CE_Failure, CPLE_AppDefined, 47 : "An algorithm with alias '%s' is already registered!", 48 : alias.c_str()); 49 2 : return false; 50 : } 51 : } 52 127992 : m_mapNameToInfo[info.m_name] = info; 53 127992 : bool hidden = false; 54 157606 : for (const std::string &alias : info.m_aliases) 55 : { 56 29614 : if (alias == HIDDEN_ALIAS_SEPARATOR) 57 11318 : hidden = true; 58 18296 : else if (hidden) 59 15520 : m_mapAliasToInfo[alias] = info; 60 : else 61 2776 : m_mapHiddenAliasToInfo[alias] = info; 62 : } 63 127992 : return true; 64 : } 65 : 66 : /************************************************************************/ 67 : /* GDALAlgorithmRegistry::InstantiateTopLevel() */ 68 : /************************************************************************/ 69 : 70 : std::unique_ptr<GDALAlgorithm> 71 8855 : GDALAlgorithmRegistry::InstantiateTopLevel(const std::string &name) const 72 : { 73 8855 : auto iter = m_mapNameToInfo.find(name); 74 8855 : if (iter == m_mapNameToInfo.end()) 75 : { 76 460 : iter = m_mapAliasToInfo.find(name); 77 460 : if (iter == m_mapAliasToInfo.end()) 78 : { 79 454 : iter = m_mapHiddenAliasToInfo.find(name); 80 454 : if (iter == m_mapHiddenAliasToInfo.end()) 81 : { 82 454 : return nullptr; 83 : } 84 : } 85 : } 86 16802 : auto alg = iter->second.m_creationFunc(); 87 8401 : alg->m_aliases = iter->second.m_aliases; 88 8401 : return alg; 89 : } 90 : 91 : /************************************************************************/ 92 : /* GDALAlgorithmRegistry::Instantiate() */ 93 : /************************************************************************/ 94 : 95 : std::unique_ptr<GDALAlgorithm> 96 3288 : GDALAlgorithmRegistry::Instantiate(const std::vector<std::string> &path) const 97 : { 98 3288 : if (path.empty()) 99 1 : return nullptr; 100 6574 : auto alg = Instantiate(path[0]); 101 3290 : for (size_t i = 1; i < path.size() && alg; ++i) 102 : { 103 3 : alg = alg->InstantiateSubAlgorithm(path[i]); 104 : } 105 3287 : return alg; 106 : } 107 : 108 : /************************************************************************/ 109 : /* GDALAlgorithmRegistry::GetNames() */ 110 : /************************************************************************/ 111 : 112 952 : std::vector<std::string> GDALAlgorithmRegistry::GetNames() const 113 : { 114 952 : std::vector<std::string> res; 115 6906 : for (const auto &iter : m_mapNameToInfo) 116 : { 117 5954 : res.push_back(iter.first); 118 : } 119 952 : return res; 120 : } 121 : 122 : /************************************************************************/ 123 : /* GDALAlgorithmRegistry::Instantiate() */ 124 : /************************************************************************/ 125 : 126 : std::unique_ptr<GDALAlgorithm> 127 9198 : GDALAlgorithmRegistry::Instantiate(const std::string &name) const 128 : { 129 9198 : return InstantiateTopLevel(name); 130 : } 131 : 132 : std::unique_ptr<GDALAlgorithm> 133 3286 : GDALAlgorithmRegistry::InstantiateInternal(std::vector<std::string> &path) 134 : { 135 3286 : return Instantiate(path); 136 : } 137 : 138 : GDALGlobalAlgorithmRegistry::GDALGlobalAlgorithmRegistry() = default; 139 : 140 : GDALGlobalAlgorithmRegistry::~GDALGlobalAlgorithmRegistry() = default; 141 : 142 : /************************************************************************/ 143 : /* GDALGlobalAlgorithmRegistry::GetSingleton() */ 144 : /************************************************************************/ 145 : 146 : /* static */ GDALGlobalAlgorithmRegistry & 147 29407 : GDALGlobalAlgorithmRegistry::GetSingleton() 148 : { 149 29407 : static GDALGlobalAlgorithmRegistry singleton; 150 29407 : return singleton; 151 : } 152 : 153 : /************************************************************************/ 154 : /* GDALGlobalAlgorithmRegistry::InstantiateTopLevel() */ 155 : /************************************************************************/ 156 : 157 : std::unique_ptr<GDALAlgorithm> 158 2473 : GDALGlobalAlgorithmRegistry::InstantiateTopLevel(const std::string &name) const 159 : { 160 2473 : if (name == GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME) 161 343 : return std::make_unique<GDALMainAlgorithm>(); 162 4260 : auto alg = GDALAlgorithmRegistry::InstantiateTopLevel(name); 163 2130 : if (!alg) 164 : { 165 116 : alg = InstantiateDeclaredSubAlgorithm( 166 87 : {GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME, name}); 167 : } 168 2130 : if (alg) 169 : { 170 6372 : alg->SetCallPath({GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME, name}); 171 : } 172 2130 : return alg; 173 : } 174 : 175 : /************************************************************************/ 176 : /* GDALGlobalAlgorithmRegistry::DeclareAlgorithm() */ 177 : /************************************************************************/ 178 : 179 14813 : void GDALGlobalAlgorithmRegistry::DeclareAlgorithm( 180 : const std::vector<std::string> &path, InstantiateFunc instantiateFunc) 181 : { 182 14813 : Node *curNode = &m_root; 183 49920 : for (size_t i = 0; i < path.size(); ++i) 184 : { 185 35107 : const std::string &name = path[i]; 186 35107 : auto iter = curNode->children.find(name); 187 35107 : if (iter == curNode->children.end()) 188 : { 189 13997 : Node newNode; 190 13997 : if (i + 1 == path.size()) 191 : { 192 13996 : newNode.instantiateFunc = instantiateFunc; 193 : } 194 : else 195 : { 196 : newNode.instantiateFunc = 197 4 : [name]() -> std::unique_ptr<GDALAlgorithm> 198 : { 199 4 : return std::make_unique<GDALContainerAlgorithm>( 200 8 : name, std::string("Command for ").append(name)); 201 1 : }; 202 : } 203 13997 : curNode = 204 27994 : &(curNode->children.insert(std::pair(name, std::move(newNode))) 205 13997 : .first->second); 206 : } 207 : else 208 : { 209 21110 : curNode = &(iter->second); 210 : } 211 : } 212 14813 : } 213 : 214 : /************************************************************************/ 215 : /* GDALGlobalAlgorithmRegistry::GetNodeFromPath() */ 216 : /************************************************************************/ 217 : 218 : const GDALGlobalAlgorithmRegistry::Node * 219 18197 : GDALGlobalAlgorithmRegistry::GetNodeFromPath( 220 : const std::vector<std::string> &path) const 221 : { 222 18197 : if (!path.empty()) 223 : { 224 17514 : const Node *curNode = &m_root; 225 17514 : bool first = true; 226 32895 : for (const std::string &name : path) 227 : { 228 26427 : if (first && name == GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME) 229 : { 230 1750 : first = false; 231 1750 : continue; 232 : } 233 24677 : first = false; 234 24677 : auto iter = curNode->children.find(name); 235 24677 : if (iter == curNode->children.end()) 236 11046 : return nullptr; 237 13631 : curNode = &(iter->second); 238 : } 239 6468 : return curNode; 240 : } 241 683 : return nullptr; 242 : } 243 : 244 : /************************************************************************/ 245 : /* GDALGlobalAlgorithmRegistry::GetDeclaredSubAlgorithmNames() */ 246 : /************************************************************************/ 247 : 248 : std::vector<std::string> 249 3913 : GDALGlobalAlgorithmRegistry::GetDeclaredSubAlgorithmNames( 250 : const std::vector<std::string> &path) const 251 : { 252 3913 : const GDALGlobalAlgorithmRegistry::Node *node = GetNodeFromPath(path); 253 3913 : std::vector<std::string> ret; 254 3913 : if (node) 255 : { 256 258 : for (const auto &[name, subnode] : node->children) 257 : { 258 : // If there is an instantiation function, run it, to avoid 259 : // reporting algorithms that might be in drivers built as 260 : // deferred loaded plugins, but not available at runtime. 261 165 : if (!subnode.instantiateFunc || subnode.instantiateFunc()) 262 : { 263 165 : ret.push_back(name); 264 : } 265 : } 266 : } 267 3913 : return ret; 268 : } 269 : 270 : /************************************************************************/ 271 : /* GDALGlobalAlgorithmRegistry::HasDeclaredSubAlgorithm() */ 272 : /************************************************************************/ 273 : 274 14072 : bool GDALGlobalAlgorithmRegistry::HasDeclaredSubAlgorithm( 275 : const std::vector<std::string> &path) const 276 : { 277 14072 : return GetNodeFromPath(path) != nullptr; 278 : } 279 : 280 : /************************************************************************/ 281 : /* GDALGlobalAlgorithmRegistry::InstantiateDeclaredSubAlgorithm() */ 282 : /************************************************************************/ 283 : 284 : std::unique_ptr<GDALAlgorithm> 285 212 : GDALGlobalAlgorithmRegistry::InstantiateDeclaredSubAlgorithm( 286 : const std::vector<std::string> &path) const 287 : { 288 212 : const GDALGlobalAlgorithmRegistry::Node *node = GetNodeFromPath(path); 289 212 : if (node && node->instantiateFunc) 290 : { 291 156 : auto alg = node->instantiateFunc(); 292 78 : if (alg) 293 : { 294 156 : auto callPath = path; 295 78 : if (path[0] != GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME) 296 0 : callPath.insert(callPath.begin(), 297 0 : GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME); 298 78 : alg->SetCallPath(callPath); 299 : } 300 78 : return alg; 301 : } 302 134 : return nullptr; 303 : } 304 : 305 : /************************************************************************/ 306 : /* struct GDALAlgorithmRegistryHS */ 307 : /************************************************************************/ 308 : 309 : struct GDALAlgorithmRegistryHS 310 : { 311 : GDALAlgorithmRegistry *ptr = nullptr; 312 : }; 313 : 314 : /************************************************************************/ 315 : /* GDALGetGlobalAlgorithmRegistry() */ 316 : /************************************************************************/ 317 : 318 : /** Gets a handle to the GDALGetGlobalAlgorithmRegistry which references 319 : * all available top-level GDAL algorithms ("raster", "vector", etc.) 320 : * 321 : * The handle must be released with GDALAlgorithmRegistryRelease() (but 322 : * this does not destroy the GDALAlgorithmRegistryRelease singleton). 323 : * 324 : * @since 3.11 325 : */ 326 2112 : GDALAlgorithmRegistryH GDALGetGlobalAlgorithmRegistry() 327 : { 328 4224 : auto ret = std::make_unique<GDALAlgorithmRegistryHS>(); 329 2112 : ret->ptr = &(GDALGlobalAlgorithmRegistry::GetSingleton()); 330 4224 : return ret.release(); 331 : } 332 : 333 : /************************************************************************/ 334 : /* GDALAlgorithmRegistryRelease() */ 335 : /************************************************************************/ 336 : 337 : /** Release a handle to an algorithm registry, but this does not destroy the 338 : * registry itself. 339 : * 340 : * @since 3.11 341 : */ 342 2112 : void GDALAlgorithmRegistryRelease(GDALAlgorithmRegistryH hReg) 343 : { 344 2112 : delete hReg; 345 2112 : } 346 : 347 : /************************************************************************/ 348 : /* GDALAlgorithmRegistryGetAlgNames() */ 349 : /************************************************************************/ 350 : 351 : /** Return the names of the algorithms registered in the registry passed as 352 : * parameter. 353 : * 354 : * @param hReg Handle to a registry. Must NOT be null. 355 : * @return a NULL terminated list of names, which must be destroyed with 356 : * CSLDestroy() 357 : * 358 : * @since 3.11 359 : */ 360 2 : char **GDALAlgorithmRegistryGetAlgNames(GDALAlgorithmRegistryH hReg) 361 : { 362 2 : VALIDATE_POINTER1(hReg, __func__, nullptr); 363 2 : return CPLStringList(hReg->ptr->GetNames()).StealList(); 364 : } 365 : 366 : /************************************************************************/ 367 : /* GDALAlgorithmRegistryInstantiateAlg() */ 368 : /************************************************************************/ 369 : 370 : /** Instantiate an algorithm available in a registry from its name. 371 : * 372 : * @param hReg Handle to a registry. Must NOT be null. 373 : * @param pszAlgName Algorithm name. Must NOT be null. 374 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease), 375 : * or NULL if the algorithm does not exist or another error occurred. 376 : * 377 : * @since 3.11 378 : */ 379 2114 : GDALAlgorithmH GDALAlgorithmRegistryInstantiateAlg(GDALAlgorithmRegistryH hReg, 380 : const char *pszAlgName) 381 : { 382 2114 : VALIDATE_POINTER1(hReg, __func__, nullptr); 383 2114 : VALIDATE_POINTER1(pszAlgName, __func__, nullptr); 384 4228 : auto alg = hReg->ptr->Instantiate(pszAlgName); 385 4228 : return alg ? std::make_unique<GDALAlgorithmHS>(std::move(alg)).release() 386 4228 : : nullptr; 387 : } 388 : 389 : /************************************************************************/ 390 : /* GDALAlgorithmRegistryInstantiateAlgFromPath() */ 391 : /************************************************************************/ 392 : 393 : /** Instantiate an algorithm available in a registry from its path. 394 : * 395 : * @param hReg Handle to a registry. Must NOT be null. 396 : * @param papszAlgPath Algorithm path. Must NOT be null. 397 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease), 398 : * or NULL if the algorithm does not exist or another error occurred. 399 : * 400 : * @since 3.12 401 : */ 402 : GDALAlgorithmH 403 1 : GDALAlgorithmRegistryInstantiateAlgFromPath(GDALAlgorithmRegistryH hReg, 404 : const char *const *papszAlgPath) 405 : { 406 1 : VALIDATE_POINTER1(hReg, __func__, nullptr); 407 1 : VALIDATE_POINTER1(papszAlgPath, __func__, nullptr); 408 1 : auto alg = hReg->ptr->Instantiate( 409 2 : static_cast<std::vector<std::string>>(CPLStringList(papszAlgPath))); 410 2 : return alg ? std::make_unique<GDALAlgorithmHS>(std::move(alg)).release() 411 2 : : nullptr; 412 : }