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