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 130082 : bool GDALAlgorithmRegistry::Register(const GDALAlgorithmRegistry::AlgInfo &info) 33 : { 34 130082 : 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 160007 : for (const std::string &alias : info.m_aliases) 42 : { 43 59855 : if (cpl::contains(m_mapAliasToInfo, alias) || 44 29927 : 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 130079 : m_mapNameToInfo[info.m_name] = info; 53 130079 : bool hidden = false; 54 160004 : for (const std::string &alias : info.m_aliases) 55 : { 56 29925 : if (alias == HIDDEN_ALIAS_SEPARATOR) 57 11421 : hidden = true; 58 18504 : else if (hidden) 59 15646 : m_mapAliasToInfo[alias] = info; 60 : else 61 2858 : m_mapHiddenAliasToInfo[alias] = info; 62 : } 63 130079 : return true; 64 : } 65 : 66 : /************************************************************************/ 67 : /* GDALAlgorithmRegistry::InstantiateTopLevel() */ 68 : /************************************************************************/ 69 : 70 : std::unique_ptr<GDALAlgorithm> 71 9340 : GDALAlgorithmRegistry::InstantiateTopLevel(const std::string &name) const 72 : { 73 9340 : auto iter = m_mapNameToInfo.find(name); 74 9340 : if (iter == m_mapNameToInfo.end()) 75 : { 76 524 : iter = m_mapAliasToInfo.find(name); 77 524 : if (iter == m_mapAliasToInfo.end()) 78 : { 79 518 : iter = m_mapHiddenAliasToInfo.find(name); 80 518 : if (iter == m_mapHiddenAliasToInfo.end()) 81 : { 82 518 : return nullptr; 83 : } 84 : } 85 : } 86 17644 : auto alg = iter->second.m_creationFunc(); 87 8822 : alg->m_aliases = iter->second.m_aliases; 88 8822 : return alg; 89 : } 90 : 91 : /************************************************************************/ 92 : /* GDALAlgorithmRegistry::Instantiate() */ 93 : /************************************************************************/ 94 : 95 : std::unique_ptr<GDALAlgorithm> 96 3310 : GDALAlgorithmRegistry::Instantiate(const std::vector<std::string> &path) const 97 : { 98 3310 : if (path.empty()) 99 1 : return nullptr; 100 6618 : auto alg = Instantiate(path[0]); 101 3312 : for (size_t i = 1; i < path.size() && alg; ++i) 102 : { 103 3 : alg = alg->InstantiateSubAlgorithm(path[i]); 104 : } 105 3309 : return alg; 106 : } 107 : 108 : /************************************************************************/ 109 : /* GDALAlgorithmRegistry::GetNames() */ 110 : /************************************************************************/ 111 : 112 1002 : std::vector<std::string> GDALAlgorithmRegistry::GetNames() const 113 : { 114 1002 : std::vector<std::string> res; 115 7353 : for (const auto &iter : m_mapNameToInfo) 116 : { 117 6351 : res.push_back(iter.first); 118 : } 119 1002 : return res; 120 : } 121 : 122 : /************************************************************************/ 123 : /* GDALAlgorithmRegistry::Instantiate() */ 124 : /************************************************************************/ 125 : 126 : std::unique_ptr<GDALAlgorithm> 127 9688 : GDALAlgorithmRegistry::Instantiate(const std::string &name) const 128 : { 129 9688 : return InstantiateTopLevel(name); 130 : } 131 : 132 : std::unique_ptr<GDALAlgorithm> 133 3308 : GDALAlgorithmRegistry::InstantiateInternal(std::vector<std::string> &path) 134 : { 135 3308 : 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 29980 : GDALGlobalAlgorithmRegistry::GetSingleton() 148 : { 149 29980 : static GDALGlobalAlgorithmRegistry singleton; 150 29980 : return singleton; 151 : } 152 : 153 : /************************************************************************/ 154 : /* GDALGlobalAlgorithmRegistry::InstantiateTopLevel() */ 155 : /************************************************************************/ 156 : 157 : std::unique_ptr<GDALAlgorithm> 158 2495 : GDALGlobalAlgorithmRegistry::InstantiateTopLevel(const std::string &name) const 159 : { 160 2495 : if (name == GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME) 161 348 : return std::make_unique<GDALMainAlgorithm>(); 162 4294 : auto alg = GDALAlgorithmRegistry::InstantiateTopLevel(name); 163 2147 : if (!alg) 164 : { 165 116 : alg = InstantiateDeclaredSubAlgorithm( 166 87 : {GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME, name}); 167 : } 168 2147 : if (alg) 169 : { 170 6423 : alg->SetCallPath({GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME, name}); 171 : } 172 2147 : return alg; 173 : } 174 : 175 : /************************************************************************/ 176 : /* GDALGlobalAlgorithmRegistry::DeclareAlgorithm() */ 177 : /************************************************************************/ 178 : 179 14832 : void GDALGlobalAlgorithmRegistry::DeclareAlgorithm( 180 : const std::vector<std::string> &path, InstantiateFunc instantiateFunc) 181 : { 182 14832 : Node *curNode = &m_root; 183 49984 : for (size_t i = 0; i < path.size(); ++i) 184 : { 185 35152 : const std::string &name = path[i]; 186 35152 : auto iter = curNode->children.find(name); 187 35152 : if (iter == curNode->children.end()) 188 : { 189 14015 : Node newNode; 190 14015 : if (i + 1 == path.size()) 191 : { 192 14014 : 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 14015 : curNode = 204 28030 : &(curNode->children.insert(std::pair(name, std::move(newNode))) 205 14015 : .first->second); 206 : } 207 : else 208 : { 209 21137 : curNode = &(iter->second); 210 : } 211 : } 212 14832 : } 213 : 214 : /************************************************************************/ 215 : /* GDALGlobalAlgorithmRegistry::GetNodeFromPath() */ 216 : /************************************************************************/ 217 : 218 : const GDALGlobalAlgorithmRegistry::Node * 219 18696 : GDALGlobalAlgorithmRegistry::GetNodeFromPath( 220 : const std::vector<std::string> &path) const 221 : { 222 18696 : if (!path.empty()) 223 : { 224 18000 : const Node *curNode = &m_root; 225 18000 : bool first = true; 226 33981 : for (const std::string &name : path) 227 : { 228 27433 : if (first && name == GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME) 229 : { 230 2180 : first = false; 231 2180 : continue; 232 : } 233 25253 : first = false; 234 25253 : auto iter = curNode->children.find(name); 235 25253 : if (iter == curNode->children.end()) 236 11452 : return nullptr; 237 13801 : curNode = &(iter->second); 238 : } 239 6548 : return curNode; 240 : } 241 696 : return nullptr; 242 : } 243 : 244 : /************************************************************************/ 245 : /* GDALGlobalAlgorithmRegistry::GetDeclaredSubAlgorithmNames() */ 246 : /************************************************************************/ 247 : 248 : std::vector<std::string> 249 4367 : GDALGlobalAlgorithmRegistry::GetDeclaredSubAlgorithmNames( 250 : const std::vector<std::string> &path) const 251 : { 252 4367 : const GDALGlobalAlgorithmRegistry::Node *node = GetNodeFromPath(path); 253 4367 : std::vector<std::string> ret; 254 4367 : if (node) 255 : { 256 354 : 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 216 : if (!subnode.instantiateFunc || subnode.instantiateFunc()) 262 : { 263 216 : ret.push_back(name); 264 : } 265 : } 266 : } 267 4367 : return ret; 268 : } 269 : 270 : /************************************************************************/ 271 : /* GDALGlobalAlgorithmRegistry::HasDeclaredSubAlgorithm() */ 272 : /************************************************************************/ 273 : 274 14090 : bool GDALGlobalAlgorithmRegistry::HasDeclaredSubAlgorithm( 275 : const std::vector<std::string> &path) const 276 : { 277 14090 : return GetNodeFromPath(path) != nullptr; 278 : } 279 : 280 : /************************************************************************/ 281 : /* GDALGlobalAlgorithmRegistry::InstantiateDeclaredSubAlgorithm() */ 282 : /************************************************************************/ 283 : 284 : std::unique_ptr<GDALAlgorithm> 285 239 : GDALGlobalAlgorithmRegistry::InstantiateDeclaredSubAlgorithm( 286 : const std::vector<std::string> &path) const 287 : { 288 239 : const GDALGlobalAlgorithmRegistry::Node *node = GetNodeFromPath(path); 289 239 : if (node && node->instantiateFunc) 290 : { 291 210 : auto alg = node->instantiateFunc(); 292 105 : if (alg) 293 : { 294 210 : auto callPath = path; 295 105 : if (path[0] != GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME) 296 0 : callPath.insert(callPath.begin(), 297 0 : GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME); 298 105 : alg->SetCallPath(callPath); 299 : } 300 105 : 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 2132 : GDALAlgorithmRegistryH GDALGetGlobalAlgorithmRegistry() 327 : { 328 4264 : auto ret = std::make_unique<GDALAlgorithmRegistryHS>(); 329 2132 : ret->ptr = &(GDALGlobalAlgorithmRegistry::GetSingleton()); 330 4264 : 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 2132 : void GDALAlgorithmRegistryRelease(GDALAlgorithmRegistryH hReg) 343 : { 344 2132 : delete hReg; 345 2132 : } 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 2134 : GDALAlgorithmH GDALAlgorithmRegistryInstantiateAlg(GDALAlgorithmRegistryH hReg, 380 : const char *pszAlgName) 381 : { 382 2134 : VALIDATE_POINTER1(hReg, __func__, nullptr); 383 2134 : VALIDATE_POINTER1(pszAlgName, __func__, nullptr); 384 4268 : auto alg = hReg->ptr->Instantiate(pszAlgName); 385 4268 : return alg ? std::make_unique<GDALAlgorithmHS>(std::move(alg)).release() 386 4268 : : 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 : }