Blue Brain BioExplorer
ParametersManager.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015-2024, EPFL/Blue Brain Project
3  *
4  * The Blue Brain BioExplorer is a tool for scientists to extract and analyse
5  * scientific data from visualization
6  *
7  * This file is part of Blue Brain BioExplorer <https://github.com/BlueBrain/BioExplorer>
8  *
9  * This library is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU Lesser General Public License version 3.0 as published
11  * by the Free Software Foundation.
12  *
13  * This library is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this library; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 
23 #include "ParametersManager.h"
24 
25 #include <Version.h>
26 
28 
29 namespace
30 {
31 // https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C++
32 unsigned int levenshtein_distance(const std::string& s1, const std::string& s2)
33 {
34  const std::size_t len1 = s1.size(), len2 = s2.size();
35  std::vector<unsigned int> col(len2 + 1), prevCol(len2 + 1);
36 
37  for (unsigned int i = 0; i < prevCol.size(); i++)
38  prevCol[i] = i;
39  for (unsigned int i = 0; i < len1; i++)
40  {
41  col[0] = i + 1;
42  for (unsigned int j = 0; j < len2; j++)
43  col[j + 1] = std::min({prevCol[1 + j] + 1, col[j] + 1, prevCol[j] + (s1[i] == s2[j] ? 0 : 1)});
44  col.swap(prevCol);
45  }
46  return prevCol[len2];
47 }
48 
49 std::vector<std::string> findSimilarOptions(const std::string& name, const std::vector<std::string>& options)
50 {
51  constexpr size_t MAX_SUGGESTIONS = 7;
52 
53  std::vector<std::string> subStringOptions;
54  std::vector<std::string> levenshteinOptions;
55 
56  // Collect substring options
57  {
58  // Strip dashes
59  auto nameStrip = name;
60  nameStrip.erase(std::remove(nameStrip.begin(), nameStrip.end(), '-'), nameStrip.end());
61 
62  // Suggest options containing the substring
63  for (const auto& optionName : options)
64  {
65  if (subStringOptions.size() >= MAX_SUGGESTIONS)
66  break;
67 
68  if (optionName.find(nameStrip) != std::string::npos)
69  subStringOptions.push_back(optionName);
70  }
71  }
72 
73  // Collect best levenshtein distance options
74  {
75  size_t bestDist = UINT_MAX;
76 
77  for (const auto& optionName : options)
78  {
79  if (levenshteinOptions.size() >= MAX_SUGGESTIONS)
80  break;
81 
82  const auto dist = levenshtein_distance(name, optionName);
83  if (dist < bestDist)
84  {
85  levenshteinOptions.clear();
86  bestDist = dist;
87  }
88 
89  if (dist == bestDist &&
90  std::find(subStringOptions.begin(), subStringOptions.end(), optionName) == subStringOptions.end())
91  levenshteinOptions.push_back(optionName);
92  }
93  }
94 
95  // Merge suggestions giving precedence to substrings
96  auto output = subStringOptions;
97  for (const auto& option : levenshteinOptions)
98  output.push_back(option);
99  output.resize(std::min(output.size(), MAX_SUGGESTIONS));
100 
101  return output;
102 }
103 
104 void _printVersion()
105 {
106  CORE_INFO("Core " << PACKAGE_VERSION_STRING);
107 }
108 } // namespace
109 
110 namespace core
111 {
112 ParametersManager::ParametersManager(const int argc, const char** argv)
113 {
114  registerParameters(&_animationParameters);
115  registerParameters(&_applicationParameters);
116  registerParameters(&_geometryParameters);
117  registerParameters(&_renderingParameters);
118  registerParameters(&_volumeParameters);
119 
120  for (auto parameters : _parameterSets)
121  _allOptions.add(parameters->parameters());
122 
123  _parse(argc, argv);
124 }
125 
127 {
128  _parameterSets.push_back(parameters);
129 }
130 
131 void ParametersManager::_parse(int argc, const char** argv)
132 {
133  try
134  {
135  po::options_description generalOptions("General options");
136  generalOptions.add_options()("help", "Print this help")("version",
137  "Print the Core version")("verbose",
138  "Print parsed parameters");
139 
140  _allOptions.add(generalOptions);
141 
142  po::variables_map vm;
143  po::parsed_options parsedOptions =
144  po::command_line_parser(argc, argv)
145  .options(_allOptions)
146  .allow_unregistered()
147  .positional(_applicationParameters.posArgs())
148  .style(po::command_line_style::unix_style & ~po::command_line_style::allow_short &
149  ~po::command_line_style::allow_guessing)
150  .run();
151 
152  const auto unrecognizedOptions = po::collect_unrecognized(parsedOptions.options, po::exclude_positional);
153 
154  _processUnrecognizedOptions(unrecognizedOptions);
155 
156  po::store(parsedOptions, vm);
157  po::notify(vm);
158 
159  if (vm.count("help"))
160  {
161  usage();
162  exit(EXIT_SUCCESS);
163  }
164 
165  _printVersion();
166  if (vm.count("version"))
167  exit(EXIT_SUCCESS);
168 
169  for (auto parameters : _parameterSets)
170  parameters->parse(vm);
171 
172  if (vm.count("verbose"))
173  print();
174  }
175  catch (const po::error& e)
176  {
177  CORE_ERROR(e.what());
178  exit(EXIT_FAILURE);
179  }
180 }
181 
183 {
184  std::cout << _allOptions << std::endl;
185 }
186 
188 {
189  for (AbstractParameters* parameters : _parameterSets)
190  parameters->print();
191 }
192 
194 {
195  for (AbstractParameters* parameters : _parameterSets)
196  parameters->resetModified();
197 }
198 
200 {
201  for (AbstractParameters* parameters : _parameterSets)
202  {
203  if (parameters->isModified())
204  return true;
205  }
206  return false;
207 }
208 
210 {
211  return _animationParameters;
212 }
213 
215 {
216  return _animationParameters;
217 }
218 
220 {
221  return _applicationParameters;
222 }
223 
225 {
226  return _applicationParameters;
227 }
228 
230 {
231  return _renderingParameters;
232 }
233 
235 {
236  return _renderingParameters;
237 }
238 
240 {
241  return _geometryParameters;
242 }
243 
245 {
246  return _geometryParameters;
247 }
248 
250 {
251  return _volumeParameters;
252 }
253 
255 {
256  return _volumeParameters;
257 }
258 
260 {
261  return _fieldParameters;
262 }
263 
265 {
266  return _fieldParameters;
267 }
268 
269 void ParametersManager::_processUnrecognizedOptions(const std::vector<std::string>& unrecognizedOptions) const
270 {
271  if (unrecognizedOptions.empty())
272  return;
273 
274  const auto& unrecognized = unrecognizedOptions.front();
275 
276  std::vector<std::string> availableOptions;
277  for (auto option : _allOptions.options())
278  availableOptions.push_back(option->format_name());
279 
280  const auto suggestions = findSimilarOptions(unrecognized, availableOptions);
281 
282  std::string errorMessage = "Unrecognized option '" + unrecognized + "'.\n\nMost similar options are:";
283 
284  for (const auto& suggestion : suggestions)
285  errorMessage += "\n\t" + suggestion;
286 
287  throw po::error(errorMessage);
288 }
289 } // namespace core
po::positional_options_description & posArgs()
PLATFORM_API GeometryParameters & getGeometryParameters()
PLATFORM_API ApplicationParameters & getApplicationParameters()
PLATFORM_API void print()
PLATFORM_API void registerParameters(AbstractParameters *parameters)
PLATFORM_API AnimationParameters & getAnimationParameters()
PLATFORM_API void usage()
PLATFORM_API RenderingParameters & getRenderingParameters()
PLATFORM_API FieldParameters & getFieldParameters()
PLATFORM_API VolumeParameters & getVolumeParameters()
ParametersManager(int argc, const char **argv)
_UNIT_TESTING_ENABLED Unit testing ON option(${NAME}_DEBUG_JSON_ENABLED "Catch JSON exceptions during runtime" OFF) if($
Definition: CMakeLists.txt:29
#define CORE_INFO(__msg)
Definition: Logs.h:33
#define CORE_ERROR(__msg)
Definition: Logs.h:31