Coverage Report

Created: 2026-04-01 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}