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