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