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