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