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