LCOV - code coverage report
Current view: top level - apps - gdalalg_dispatcher.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 106 114 93.0 %
Date: 2025-09-10 17:48:50 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          37 :     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          37 :               std::make_unique<VectorDispatcher>(/* standalone = */ true))
      39             :     {
      40             :         // A "info" dispatcher command is a shortcut for something like
      41             :         // "raster info", "vector info". Best to expose the latter.
      42          37 :         SetDisplayInJSONUsage(false);
      43          37 :     }
      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          22 : bool GDALDispatcherAlgorithm<RasterDispatcher, VectorDispatcher>::
      72             :     ParseCommandLineArguments(const std::vector<std::string> &args)
      73             : {
      74          22 :     if (args.size() == 1 && (args[0] == "-h" || args[0] == "--help"))
      75           1 :         return GDALAlgorithm::ParseCommandLineArguments(args);
      76             : 
      77             :     // We first try to process with the raster specific algorithm (that has
      78             :     // been instantiated in a special way to accept both raster and vector
      79             :     // input datasets). If the raster specific algorithm can parse successfully
      80             :     // the arguments *and* the dataset is a raster one, then continue processing
      81             :     // with it. Otherwise try with the vector specific algorithm.
      82             : 
      83             :     bool ok;
      84          42 :     std::string osLastError;
      85          21 :     if (args.size() > 1)
      86             :     {
      87             :         // Silence errors as it might be rather for the vector algorithm
      88          30 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
      89          15 :         const auto nCounter = CPLGetErrorCounter();
      90          15 :         ok = m_rasterDispatcher->ParseCommandLineArguments(args);
      91          21 :         if (CPLGetErrorCounter() > nCounter &&
      92           6 :             CPLGetLastErrorType() == CE_Failure)
      93           6 :             osLastError = CPLGetLastErrorMsg();
      94             :     }
      95             :     else
      96             :     {
      97             :         // If there's just a single argument, we don't need to silence errors
      98             :         // as this will trigger a legitimate error message about the subcommand.
      99           6 :         ok = m_rasterDispatcher->ParseCommandLineArguments(args);
     100             :     }
     101             : 
     102          21 :     if (m_rasterDispatcher->PropagateSpecialActionTo(this))
     103             :     {
     104           0 :         return true;
     105             :     }
     106             : 
     107          21 :     if (ok)
     108             :     {
     109          14 :         auto poDS = m_rasterDispatcher->GetInputDatasetRef();
     110             :         // cppcheck-suppress knownConditionTrueFalse
     111          28 :         if (poDS &&
     112          14 :             (poDS->GetRasterCount() > 0 || poDS->GetMetadata("SUBDATASETS")))
     113             :         {
     114           8 :             if (poDS->GetLayerCount() != 0)
     115             :             {
     116           2 :                 m_showUsage = false;
     117           4 :                 CPLError(CE_Failure, CPLE_AppDefined,
     118             :                          "'%s' has both raster and vector content. "
     119             :                          "Please use 'gdal raster %s' or 'gdal vector %s'.",
     120           2 :                          poDS->GetDescription(), GetName().c_str(),
     121           2 :                          GetName().c_str());
     122           2 :                 return false;
     123             :             }
     124             : 
     125           6 :             m_selectedSubAlg = m_rasterDispatcher.get();
     126           6 :             std::vector<std::string> callPath(m_callPath);
     127           6 :             callPath.push_back("raster");
     128           6 :             m_selectedSubAlg->SetCallPath(callPath);
     129             : 
     130           6 :             return true;
     131             :         }
     132             :     }
     133           7 :     else if (args.size() <= 1)
     134             :     {
     135           1 :         return false;
     136             :     }
     137             : 
     138          12 :     auto poDSFromRaster = m_rasterDispatcher->GetInputDatasetRef();
     139             :     // cppcheck-suppress knownConditionTrueFalse
     140          12 :     if (poDSFromRaster)
     141             :     {
     142           6 :         m_vectorDispatcher->SetInputDataset(poDSFromRaster);
     143             :     }
     144             : 
     145          24 :     std::vector<std::string> argsWithoutInput;
     146          12 :     bool skipNext = false;
     147          24 :     std::string osLikelyDatasetName;
     148          12 :     size_t nCountLikelyDatasetName = 0;
     149          44 :     for (const auto &arg : args)
     150             :     {
     151          93 :         if (arg == "-i" || arg == "--input" || arg == "-f" || arg == "--of" ||
     152          93 :             arg == "--output-format" || arg == "--format")
     153             :         {
     154           2 :             skipNext = true;
     155             :         }
     156          30 :         else if (!skipNext)
     157             :         {
     158          37 :             if (!STARTS_WITH(arg.c_str(), "--input=") &&
     159           9 :                 !(poDSFromRaster && arg == poDSFromRaster->GetDescription()))
     160             :             {
     161          24 :                 if (!arg.empty() && arg[0] != '-')
     162             :                 {
     163           9 :                     ++nCountLikelyDatasetName;
     164           9 :                     osLikelyDatasetName = arg;
     165             :                 }
     166          24 :                 argsWithoutInput.push_back(arg);
     167             :             }
     168             :         }
     169             :         else
     170             :         {
     171           2 :             skipNext = false;
     172             :         }
     173             :     }
     174             : 
     175             :     {
     176          12 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
     177          12 :         ok = m_vectorDispatcher->ParseCommandLineArguments(argsWithoutInput);
     178             :     }
     179          12 :     if (ok)
     180             :     {
     181           7 :         m_selectedSubAlg = m_vectorDispatcher.get();
     182           7 :         std::vector<std::string> callPath(m_callPath);
     183           7 :         callPath.push_back("vector");
     184           7 :         m_selectedSubAlg->SetCallPath(callPath);
     185             : 
     186           7 :         return true;
     187             :     }
     188             : 
     189           5 :     bool ret = false;
     190           5 :     bool managedToOpenDS = false;
     191          12 :     for (const auto &arg : args)
     192             :     {
     193             :         VSIStatBufL sStat;
     194          12 :         if (VSIStatL(arg.c_str(), &sStat) == 0)
     195             :         {
     196           5 :             auto poDS =
     197             :                 std::unique_ptr<GDALDataset>(GDALDataset::Open(arg.c_str()));
     198           5 :             if (poDS)
     199             :             {
     200           4 :                 managedToOpenDS = true;
     201           5 :                 if (poDS->GetRasterCount() > 0 ||
     202           1 :                     poDS->GetMetadata("SUBDATASETS"))
     203             :                 {
     204           3 :                     if (poDS->GetLayerCount() != 0)
     205             :                     {
     206           1 :                         m_showUsage = false;
     207           3 :                         CPLError(CE_Failure, CPLE_AppDefined,
     208             :                                  "'%s' has both raster and vector content. "
     209             :                                  "Please use 'gdal raster %s' or 'gdal "
     210             :                                  "vector %s'.",
     211           2 :                                  poDS->GetDescription(), GetName().c_str(),
     212           1 :                                  GetName().c_str());
     213           1 :                         return false;
     214             :                     }
     215           2 :                     m_rasterDispatcher = std::make_unique<RasterDispatcher>();
     216           2 :                     auto poDSRaw = poDS.get();
     217           2 :                     m_rasterDispatcher->SetInputDataset(poDS.release());
     218           2 :                     poDSRaw->Release();
     219           2 :                     m_selectedSubAlg = m_rasterDispatcher.get();
     220           2 :                     std::vector<std::string> callPath(m_callPath);
     221           2 :                     callPath.push_back("raster");
     222           2 :                     m_selectedSubAlg->SetCallPath(callPath);
     223           2 :                     ret = m_selectedSubAlg->ParseCommandLineArguments(
     224             :                         argsWithoutInput);
     225             :                 }
     226           1 :                 else if (poDS->GetLayerCount() != 0)
     227             :                 {
     228           1 :                     m_vectorDispatcher = std::make_unique<VectorDispatcher>();
     229           1 :                     auto poDSRaw = poDS.get();
     230           1 :                     m_vectorDispatcher->SetInputDataset(poDS.release());
     231           1 :                     poDSRaw->Release();
     232           1 :                     m_selectedSubAlg = m_vectorDispatcher.get();
     233           1 :                     std::vector<std::string> callPath(m_callPath);
     234           1 :                     callPath.push_back("vector");
     235           1 :                     m_selectedSubAlg->SetCallPath(callPath);
     236           1 :                     ret = m_selectedSubAlg->ParseCommandLineArguments(
     237             :                         argsWithoutInput);
     238             :                 }
     239             :             }
     240           4 :             break;
     241             :         }
     242             :     }
     243             : 
     244           5 :     if (!ret && !managedToOpenDS &&
     245           1 :         (osLastError.find("not recognized") != std::string::npos ||
     246           0 :          (nCountLikelyDatasetName == 1 &&
     247           0 :           cpl::starts_with(osLastError, osLikelyDatasetName))))
     248             :     {
     249           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", osLastError.c_str());
     250             :     }
     251             : 
     252           4 :     return ret;
     253             : }
     254             : 
     255             : /************************************************************************/
     256             : /*                 GDALDispatcherAlgorithm::GetUsageForCLI()            */
     257             : /************************************************************************/
     258             : 
     259             : template <class RasterDispatcher, class VectorDispatcher>
     260             : std::string
     261           4 : GDALDispatcherAlgorithm<RasterDispatcher, VectorDispatcher>::GetUsageForCLI(
     262             :     bool shortUsage, const UsageOptions &usageOptions) const
     263             : {
     264           4 :     if (m_selectedSubAlg)
     265             :     {
     266           1 :         return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
     267             :     }
     268           3 :     if (m_showUsage)
     269             :     {
     270           2 :         return GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptions);
     271             :     }
     272           1 :     return std::string();
     273             : }
     274             : 
     275             : //! @endcond
     276             : 
     277             : #endif  // GDALALG_DISPATCHER_INCLUDED

Generated by: LCOV version 1.14