LCOV - code coverage report
Current view: top level - apps - gdalalg_dispatcher.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 98 104 94.2 %
Date: 2025-08-01 10:10:57 Functions: 5 8 62.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal subcommand dispatcher
       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             : #ifndef GDALALG_DISPATCHER_INCLUDED
      14             : #define GDALALG_DISPATCHER_INCLUDED
      15             : 
      16             : #include "gdalalgorithm.h"
      17             : 
      18             : #include "gdal_priv.h"
      19             : #include "cpl_error.h"
      20             : 
      21             : //! @cond Doxygen_Suppress
      22             : 
      23             : /************************************************************************/
      24             : /*                       GDALDispatcherAlgorithm                        */
      25             : /************************************************************************/
      26             : 
      27             : template <class RasterDispatcher, class VectorDispatcher>
      28             : class GDALDispatcherAlgorithm : public GDALAlgorithm
      29             : {
      30             :   public:
      31          34 :     GDALDispatcherAlgorithm(const std::string &name,
      32             :                             const std::string &description,
      33             :                             const std::string &helpURL)
      34             :         : GDALAlgorithm(name, description, helpURL),
      35             :           m_rasterDispatcher(std::make_unique<RasterDispatcher>(
      36           0 :               /* standalone = */ true, /* openForMixedRasterVector = */ true)),
      37             :           m_vectorDispatcher(
      38          34 :               std::make_unique<VectorDispatcher>(/* standalone = */ true))
      39             :     {
      40             :         // A "info" dispacher command is a shortcut for something like
      41             :         // "raster info", "vector info". Best to expose the latter.
      42          34 :         SetDisplayInJSONUsage(false);
      43          34 :     }
      44             : 
      45             :     bool
      46             :     ParseCommandLineArguments(const std::vector<std::string> &args) override;
      47             : 
      48             :     std::string GetUsageForCLI(bool shortUsage,
      49             :                                const UsageOptions &usageOptions) const override;
      50             : 
      51             :   private:
      52             :     std::unique_ptr<RasterDispatcher> m_rasterDispatcher{};
      53             :     std::unique_ptr<VectorDispatcher> m_vectorDispatcher{};
      54             :     bool m_showUsage = true;
      55             : 
      56           0 :     bool RunImpl(GDALProgressFunc, void *) override
      57             :     {
      58           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      59             :                  "The Run() method should not be called directly on the \"gdal "
      60             :                  "%s\" program.",
      61           0 :                  GetName().c_str());
      62           0 :         return false;
      63             :     }
      64             : };
      65             : 
      66             : /************************************************************************/
      67             : /*         GDALDispatcherAlgorithm::ParseCommandLineArguments()         */
      68             : /************************************************************************/
      69             : 
      70             : template <class RasterDispatcher, class VectorDispatcher>
      71          21 : bool GDALDispatcherAlgorithm<RasterDispatcher, VectorDispatcher>::
      72             :     ParseCommandLineArguments(const std::vector<std::string> &args)
      73             : {
      74             :     // We first try to process with the raster specific algorithm (that has
      75             :     // been instantiated in a special way to accept both raster and vector
      76             :     // input datasets). If the raster specific algorithm can parse successfully
      77             :     // the arguments *and* the dataset is a raster one, then continue processing
      78             :     // with it. Otherwise try with the vector specific algorithm.
      79             : 
      80             :     bool ok;
      81          42 :     std::string osLastError;
      82          21 :     if (args.size() > 1)
      83             :     {
      84             :         // Silence errors as it might be rather for the vector algorithm
      85          30 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
      86          15 :         const auto nCounter = CPLGetErrorCounter();
      87          15 :         ok = m_rasterDispatcher->ParseCommandLineArguments(args);
      88          21 :         if (CPLGetErrorCounter() > nCounter &&
      89           6 :             CPLGetLastErrorType() == CE_Failure)
      90           6 :             osLastError = CPLGetLastErrorMsg();
      91             :     }
      92             :     else
      93             :     {
      94             :         // If there's just a single argument, we don't need to silence errors
      95             :         // as this will trigger a legitimate error message about the subcommand.
      96           6 :         ok = m_rasterDispatcher->ParseCommandLineArguments(args);
      97             :     }
      98             : 
      99          21 :     if (m_rasterDispatcher->PropagateSpecialActionTo(this))
     100             :     {
     101           0 :         return true;
     102             :     }
     103             : 
     104          21 :     if (ok)
     105             :     {
     106          14 :         auto poDS = m_rasterDispatcher->GetDatasetRef();
     107             :         // cppcheck-suppress knownConditionTrueFalse
     108          28 :         if (poDS &&
     109          14 :             (poDS->GetRasterCount() > 0 || poDS->GetMetadata("SUBDATASETS")))
     110             :         {
     111           8 :             if (poDS->GetLayerCount() != 0)
     112             :             {
     113           2 :                 m_showUsage = false;
     114           4 :                 CPLError(CE_Failure, CPLE_AppDefined,
     115             :                          "'%s' has both raster and vector content. "
     116             :                          "Please use 'gdal raster %s' or 'gdal vector %s'.",
     117           2 :                          poDS->GetDescription(), GetName().c_str(),
     118           2 :                          GetName().c_str());
     119           2 :                 return false;
     120             :             }
     121             : 
     122           6 :             m_selectedSubAlg = m_rasterDispatcher.get();
     123           6 :             std::vector<std::string> callPath(m_callPath);
     124           6 :             callPath.push_back("raster");
     125           6 :             m_selectedSubAlg->SetCallPath(callPath);
     126             : 
     127           6 :             return true;
     128             :         }
     129             :     }
     130           7 :     else if (args.size() <= 1)
     131             :     {
     132           1 :         return false;
     133             :     }
     134             : 
     135          12 :     auto poDSFromRaster = m_rasterDispatcher->GetDatasetRef();
     136             :     // cppcheck-suppress knownConditionTrueFalse
     137          12 :     if (poDSFromRaster)
     138             :     {
     139           6 :         m_vectorDispatcher->SetDataset(poDSFromRaster);
     140             :     }
     141             : 
     142          24 :     std::vector<std::string> argsWithoutInput;
     143          12 :     bool skipNext = false;
     144          44 :     for (const auto &arg : args)
     145             :     {
     146          32 :         if (arg == "-i" || arg == "--input")
     147             :         {
     148           2 :             skipNext = true;
     149             :         }
     150          30 :         else if (!skipNext)
     151             :         {
     152          37 :             if (!STARTS_WITH(arg.c_str(), "--input=") &&
     153           9 :                 !(poDSFromRaster && arg == poDSFromRaster->GetDescription()))
     154             :             {
     155          24 :                 argsWithoutInput.push_back(arg);
     156             :             }
     157             :         }
     158             :         else
     159             :         {
     160           2 :             skipNext = false;
     161             :         }
     162             :     }
     163             : 
     164             :     {
     165          12 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
     166          12 :         ok = m_vectorDispatcher->ParseCommandLineArguments(argsWithoutInput);
     167             :     }
     168          12 :     if (ok)
     169             :     {
     170           7 :         m_selectedSubAlg = m_vectorDispatcher.get();
     171           7 :         std::vector<std::string> callPath(m_callPath);
     172           7 :         callPath.push_back("vector");
     173           7 :         m_selectedSubAlg->SetCallPath(callPath);
     174             : 
     175           7 :         return true;
     176             :     }
     177             : 
     178           5 :     bool ret = false;
     179           5 :     bool managedToOpenDS = false;
     180          12 :     for (const auto &arg : args)
     181             :     {
     182             :         VSIStatBufL sStat;
     183          12 :         if (VSIStatL(arg.c_str(), &sStat) == 0)
     184             :         {
     185           5 :             auto poDS =
     186             :                 std::unique_ptr<GDALDataset>(GDALDataset::Open(arg.c_str()));
     187           5 :             if (poDS)
     188             :             {
     189           4 :                 managedToOpenDS = true;
     190           5 :                 if (poDS->GetRasterCount() > 0 ||
     191           1 :                     poDS->GetMetadata("SUBDATASETS"))
     192             :                 {
     193           3 :                     if (poDS->GetLayerCount() != 0)
     194             :                     {
     195           1 :                         m_showUsage = false;
     196           3 :                         CPLError(CE_Failure, CPLE_AppDefined,
     197             :                                  "'%s' has both raster and vector content. "
     198             :                                  "Please use 'gdal raster %s' or 'gdal "
     199             :                                  "vector %s'.",
     200           2 :                                  poDS->GetDescription(), GetName().c_str(),
     201           1 :                                  GetName().c_str());
     202           1 :                         return false;
     203             :                     }
     204           2 :                     m_rasterDispatcher = std::make_unique<RasterDispatcher>();
     205           2 :                     auto poDSRaw = poDS.get();
     206           2 :                     m_rasterDispatcher->SetDataset(poDS.release());
     207           2 :                     poDSRaw->Release();
     208           2 :                     m_selectedSubAlg = m_rasterDispatcher.get();
     209           2 :                     std::vector<std::string> callPath(m_callPath);
     210           2 :                     callPath.push_back("raster");
     211           2 :                     m_selectedSubAlg->SetCallPath(callPath);
     212           2 :                     ret = m_selectedSubAlg->ParseCommandLineArguments(
     213             :                         argsWithoutInput);
     214             :                 }
     215           1 :                 else if (poDS->GetLayerCount() != 0)
     216             :                 {
     217           1 :                     m_vectorDispatcher = std::make_unique<VectorDispatcher>();
     218           1 :                     auto poDSRaw = poDS.get();
     219           1 :                     m_vectorDispatcher->SetDataset(poDS.release());
     220           1 :                     poDSRaw->Release();
     221           1 :                     m_selectedSubAlg = m_vectorDispatcher.get();
     222           1 :                     std::vector<std::string> callPath(m_callPath);
     223           1 :                     callPath.push_back("vector");
     224           1 :                     m_selectedSubAlg->SetCallPath(callPath);
     225           1 :                     ret = m_selectedSubAlg->ParseCommandLineArguments(
     226             :                         argsWithoutInput);
     227             :                 }
     228             :             }
     229           4 :             break;
     230             :         }
     231             :     }
     232             : 
     233           5 :     if (!ret && !managedToOpenDS &&
     234           1 :         osLastError.find("not recognized") != std::string::npos)
     235             :     {
     236           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", osLastError.c_str());
     237             :     }
     238             : 
     239           4 :     return ret;
     240             : }
     241             : 
     242             : /************************************************************************/
     243             : /*                 GDALDispatcherAlgorithm::GetUsageForCLI()            */
     244             : /************************************************************************/
     245             : 
     246             : template <class RasterDispatcher, class VectorDispatcher>
     247             : std::string
     248           3 : GDALDispatcherAlgorithm<RasterDispatcher, VectorDispatcher>::GetUsageForCLI(
     249             :     bool shortUsage, const UsageOptions &usageOptions) const
     250             : {
     251           3 :     if (m_selectedSubAlg)
     252             :     {
     253           1 :         return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
     254             :     }
     255           2 :     if (m_showUsage)
     256             :     {
     257           1 :         return GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptions);
     258             :     }
     259           1 :     return std::string();
     260             : }
     261             : 
     262             : //! @endcond
     263             : 
     264             : #endif  // GDALALG_DISPATCHER_INCLUDED

Generated by: LCOV version 1.14