Coverage Report

Created: 2026-02-14 06:52

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
#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
}