Coverage Report

Created: 2025-11-16 06:25

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
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::InstantiateTopLevel()             */
68
/************************************************************************/
69
70
std::unique_ptr<GDALAlgorithm>
71
GDALAlgorithmRegistry::InstantiateTopLevel(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::Instantiate()               */
93
/************************************************************************/
94
95
std::unique_ptr<GDALAlgorithm>
96
GDALAlgorithmRegistry::Instantiate(const std::vector<std::string> &path) const
97
0
{
98
0
    if (path.empty())
99
0
        return nullptr;
100
0
    auto alg = Instantiate(path[0]);
101
0
    for (size_t i = 1; i < path.size() && alg; ++i)
102
0
    {
103
0
        alg = alg->InstantiateSubAlgorithm(path[i]);
104
0
    }
105
0
    return alg;
106
0
}
107
108
/************************************************************************/
109
/*                GDALAlgorithmRegistry::GetNames()                     */
110
/************************************************************************/
111
112
std::vector<std::string> GDALAlgorithmRegistry::GetNames() const
113
0
{
114
0
    std::vector<std::string> res;
115
0
    for (const auto &iter : m_mapNameToInfo)
116
0
    {
117
0
        res.push_back(iter.first);
118
0
    }
119
0
    return res;
120
0
}
121
122
/************************************************************************/
123
/*                GDALAlgorithmRegistry::Instantiate()                  */
124
/************************************************************************/
125
126
std::unique_ptr<GDALAlgorithm>
127
GDALAlgorithmRegistry::Instantiate(const std::string &name) const
128
0
{
129
0
    return InstantiateTopLevel(name);
130
0
}
131
132
std::unique_ptr<GDALAlgorithm>
133
GDALAlgorithmRegistry::InstantiateInternal(std::vector<std::string> &path)
134
0
{
135
0
    return Instantiate(path);
136
0
}
137
138
0
GDALGlobalAlgorithmRegistry::GDALGlobalAlgorithmRegistry() = default;
139
140
0
GDALGlobalAlgorithmRegistry::~GDALGlobalAlgorithmRegistry() = default;
141
142
/************************************************************************/
143
/*              GDALGlobalAlgorithmRegistry::GetSingleton()             */
144
/************************************************************************/
145
146
/* static */ GDALGlobalAlgorithmRegistry &
147
GDALGlobalAlgorithmRegistry::GetSingleton()
148
0
{
149
0
    static GDALGlobalAlgorithmRegistry singleton;
150
0
    return singleton;
151
0
}
152
153
/************************************************************************/
154
/*           GDALGlobalAlgorithmRegistry::InstantiateTopLevel()         */
155
/************************************************************************/
156
157
std::unique_ptr<GDALAlgorithm>
158
GDALGlobalAlgorithmRegistry::InstantiateTopLevel(const std::string &name) const
159
0
{
160
0
    if (name == GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME)
161
0
        return std::make_unique<GDALMainAlgorithm>();
162
0
    auto alg = GDALAlgorithmRegistry::InstantiateTopLevel(name);
163
0
    if (!alg)
164
0
    {
165
0
        alg = InstantiateDeclaredSubAlgorithm(
166
0
            {GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME, name});
167
0
    }
168
0
    if (alg)
169
0
    {
170
0
        alg->SetCallPath({GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME, name});
171
0
    }
172
0
    return alg;
173
0
}
174
175
/************************************************************************/
176
/*             GDALGlobalAlgorithmRegistry::DeclareAlgorithm()          */
177
/************************************************************************/
178
179
void GDALGlobalAlgorithmRegistry::DeclareAlgorithm(
180
    const std::vector<std::string> &path, InstantiateFunc instantiateFunc)
181
0
{
182
0
    Node *curNode = &m_root;
183
0
    for (size_t i = 0; i < path.size(); ++i)
184
0
    {
185
0
        const std::string &name = path[i];
186
0
        auto iter = curNode->children.find(name);
187
0
        if (iter == curNode->children.end())
188
0
        {
189
0
            Node newNode;
190
0
            if (i + 1 == path.size())
191
0
            {
192
0
                newNode.instantiateFunc = instantiateFunc;
193
0
            }
194
0
            else
195
0
            {
196
0
                newNode.instantiateFunc =
197
0
                    [name]() -> std::unique_ptr<GDALAlgorithm>
198
0
                {
199
0
                    return std::make_unique<GDALContainerAlgorithm>(
200
0
                        name, std::string("Command for ").append(name));
201
0
                };
202
0
            }
203
0
            curNode =
204
0
                &(curNode->children.insert(std::pair(name, std::move(newNode)))
205
0
                      .first->second);
206
0
        }
207
0
        else
208
0
        {
209
0
            curNode = &(iter->second);
210
0
        }
211
0
    }
212
0
}
213
214
/************************************************************************/
215
/*            GDALGlobalAlgorithmRegistry::GetNodeFromPath()            */
216
/************************************************************************/
217
218
const GDALGlobalAlgorithmRegistry::Node *
219
GDALGlobalAlgorithmRegistry::GetNodeFromPath(
220
    const std::vector<std::string> &path) const
221
0
{
222
0
    if (!path.empty())
223
0
    {
224
0
        const Node *curNode = &m_root;
225
0
        bool first = true;
226
0
        for (const std::string &name : path)
227
0
        {
228
0
            if (first && name == GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME)
229
0
            {
230
0
                first = false;
231
0
                continue;
232
0
            }
233
0
            first = false;
234
0
            auto iter = curNode->children.find(name);
235
0
            if (iter == curNode->children.end())
236
0
                return nullptr;
237
0
            curNode = &(iter->second);
238
0
        }
239
0
        return curNode;
240
0
    }
241
0
    return nullptr;
242
0
}
243
244
/************************************************************************/
245
/*     GDALGlobalAlgorithmRegistry::GetDeclaredSubAlgorithmNames()      */
246
/************************************************************************/
247
248
std::vector<std::string>
249
GDALGlobalAlgorithmRegistry::GetDeclaredSubAlgorithmNames(
250
    const std::vector<std::string> &path) const
251
0
{
252
0
    const GDALGlobalAlgorithmRegistry::Node *node = GetNodeFromPath(path);
253
0
    std::vector<std::string> ret;
254
0
    if (node)
255
0
    {
256
0
        for (const auto &[name, subnode] : node->children)
257
0
        {
258
            // If there is an instantiation function, run it, to avoid
259
            // reporting algorithms that might be in drivers built as
260
            // deferred loaded plugins, but not available at runtime.
261
0
            if (!subnode.instantiateFunc || subnode.instantiateFunc())
262
0
            {
263
0
                ret.push_back(name);
264
0
            }
265
0
        }
266
0
    }
267
0
    return ret;
268
0
}
269
270
/************************************************************************/
271
/*       GDALGlobalAlgorithmRegistry::HasDeclaredSubAlgorithm()         */
272
/************************************************************************/
273
274
bool GDALGlobalAlgorithmRegistry::HasDeclaredSubAlgorithm(
275
    const std::vector<std::string> &path) const
276
0
{
277
0
    return GetNodeFromPath(path) != nullptr;
278
0
}
279
280
/************************************************************************/
281
/*     GDALGlobalAlgorithmRegistry::InstantiateDeclaredSubAlgorithm()   */
282
/************************************************************************/
283
284
std::unique_ptr<GDALAlgorithm>
285
GDALGlobalAlgorithmRegistry::InstantiateDeclaredSubAlgorithm(
286
    const std::vector<std::string> &path) const
287
0
{
288
0
    const GDALGlobalAlgorithmRegistry::Node *node = GetNodeFromPath(path);
289
0
    if (node && node->instantiateFunc)
290
0
    {
291
0
        auto alg = node->instantiateFunc();
292
0
        if (alg)
293
0
        {
294
0
            auto callPath = path;
295
0
            if (path[0] != GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME)
296
0
                callPath.insert(callPath.begin(),
297
0
                                GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME);
298
0
            alg->SetCallPath(callPath);
299
0
        }
300
0
        return alg;
301
0
    }
302
0
    return nullptr;
303
0
}
304
305
/************************************************************************/
306
/*                    struct GDALAlgorithmRegistryHS                    */
307
/************************************************************************/
308
309
struct GDALAlgorithmRegistryHS
310
{
311
    GDALAlgorithmRegistry *ptr = nullptr;
312
};
313
314
/************************************************************************/
315
/*                   GDALGetGlobalAlgorithmRegistry()                   */
316
/************************************************************************/
317
318
/** Gets a handle to the GDALGetGlobalAlgorithmRegistry which references
319
 * all available top-level GDAL algorithms ("raster", "vector", etc.)
320
 *
321
 * The handle must be released with GDALAlgorithmRegistryRelease() (but
322
 * this does not destroy the GDALAlgorithmRegistryRelease singleton).
323
 *
324
 * @since 3.11
325
 */
326
GDALAlgorithmRegistryH GDALGetGlobalAlgorithmRegistry()
327
0
{
328
0
    auto ret = std::make_unique<GDALAlgorithmRegistryHS>();
329
0
    ret->ptr = &(GDALGlobalAlgorithmRegistry::GetSingleton());
330
0
    return ret.release();
331
0
}
332
333
/************************************************************************/
334
/*                   GDALAlgorithmRegistryRelease()                     */
335
/************************************************************************/
336
337
/** Release a handle to an algorithm registry, but this does not destroy the
338
 * registry itself.
339
 *
340
 * @since 3.11
341
 */
342
void GDALAlgorithmRegistryRelease(GDALAlgorithmRegistryH hReg)
343
0
{
344
0
    delete hReg;
345
0
}
346
347
/************************************************************************/
348
/*                   GDALAlgorithmRegistryGetAlgNames()                 */
349
/************************************************************************/
350
351
/** Return the names of the algorithms registered in the registry passed as
352
 * parameter.
353
 *
354
 * @param hReg Handle to a registry. Must NOT be null.
355
 * @return a NULL terminated list of names, which must be destroyed with
356
 * CSLDestroy()
357
 *
358
 * @since 3.11
359
 */
360
char **GDALAlgorithmRegistryGetAlgNames(GDALAlgorithmRegistryH hReg)
361
0
{
362
0
    VALIDATE_POINTER1(hReg, __func__, nullptr);
363
0
    return CPLStringList(hReg->ptr->GetNames()).StealList();
364
0
}
365
366
/************************************************************************/
367
/*                  GDALAlgorithmRegistryInstantiateAlg()               */
368
/************************************************************************/
369
370
/** Instantiate an algorithm available in a registry from its name.
371
 *
372
 * @param hReg Handle to a registry. Must NOT be null.
373
 * @param pszAlgName Algorithm name. Must NOT be null.
374
 * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
375
 * or NULL if the algorithm does not exist or another error occurred.
376
 *
377
 * @since 3.11
378
 */
379
GDALAlgorithmH GDALAlgorithmRegistryInstantiateAlg(GDALAlgorithmRegistryH hReg,
380
                                                   const char *pszAlgName)
381
0
{
382
0
    VALIDATE_POINTER1(hReg, __func__, nullptr);
383
0
    VALIDATE_POINTER1(pszAlgName, __func__, nullptr);
384
0
    auto alg = hReg->ptr->Instantiate(pszAlgName);
385
0
    return alg ? std::make_unique<GDALAlgorithmHS>(std::move(alg)).release()
386
0
               : nullptr;
387
0
}
388
389
/************************************************************************/
390
/*             GDALAlgorithmRegistryInstantiateAlgFromPath()            */
391
/************************************************************************/
392
393
/** Instantiate an algorithm available in a registry from its path.
394
 *
395
 * @param hReg Handle to a registry. Must NOT be null.
396
 * @param papszAlgPath Algorithm path. Must NOT be null.
397
 * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
398
 * or NULL if the algorithm does not exist or another error occurred.
399
 *
400
 * @since 3.12
401
 */
402
GDALAlgorithmH
403
GDALAlgorithmRegistryInstantiateAlgFromPath(GDALAlgorithmRegistryH hReg,
404
                                            const char *const *papszAlgPath)
405
0
{
406
0
    VALIDATE_POINTER1(hReg, __func__, nullptr);
407
0
    VALIDATE_POINTER1(papszAlgPath, __func__, nullptr);
408
0
    auto alg = hReg->ptr->Instantiate(
409
0
        static_cast<std::vector<std::string>>(CPLStringList(papszAlgPath)));
410
0
    return alg ? std::make_unique<GDALAlgorithmHS>(std::move(alg)).release()
411
0
               : nullptr;
412
0
}