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-05-15 18:21:54 Functions: 7 12 58.3 %

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

Generated by: LCOV version 1.14