source: src/UIElements/CommandLineUI/CommandLineParser.cpp@ 23b6cf

Last change on this file since 23b6cf was 06b5df, checked in by Frederik Heber <heber@…>, 11 years ago

FIX: CommandLineParser causes idling thread when parsing goes wrong.

  • CommandLineParser::Parse() now returns bool to give status of parsing, only when true is scanForArguments() called, which fills the ActionQueue's queue which is not executed anymore because the run thread was already stopped by the caught exception from the parse failure.
  • Property mode set to 100644
File size: 21.6 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2010-2012 University of Bonn. All rights reserved.
5 *
6 *
7 * This file is part of MoleCuilder.
8 *
9 * MoleCuilder is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * MoleCuilder is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/*
24 * CommandLineParser.cpp
25 *
26 * Created on: May 8, 2010
27 * Author: heber
28 */
29
30// include config.h
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34
35#include "CodePatterns/MemDebug.hpp"
36
37#include <boost/filesystem.hpp>
38#include <boost/program_options.hpp>
39#include <fstream>
40#include <iostream>
41#include <set>
42#include <map>
43
44#include "Actions/Action.hpp"
45#include "Actions/ActionQueue.hpp"
46#include "Actions/ActionTrait.hpp"
47#include "Actions/OptionRegistry.hpp"
48#include "Actions/OptionTrait.hpp"
49#include "Actions/Values.hpp"
50#include "CodePatterns/Log.hpp"
51#include "CodePatterns/Verbose.hpp"
52#include "CommandLineParser.hpp"
53#include "CommandLineParser_validate.hpp"
54#include "World.hpp"
55
56#include "CodePatterns/Singleton_impl.hpp"
57
58using namespace MoleCuilder;
59
60class element;
61
62/** Constructor of class CommandLineParser.
63 *
64 */
65CommandLineParser::CommandLineParser() :
66 analysis("Analysis options"),
67 atom("Atom options"),
68 bond("Bond options"),
69 command("Command options"),
70 fill("fill options"),
71 shape("shape options"),
72 fragmentation("Fragmentation options"),
73 graph("Graph options"),
74 molecule("Molecule options"),
75 options("Secondary options"),
76 parser("Parser options"),
77 potential("Potential options"),
78 selection("Selection options"),
79 tesselation("Tesselation options"),
80 world("World options")
81{
82 // put all options lists into a lookup
83 CmdParserLookup["analysis"] = &analysis;
84 CmdParserLookup["atom"] = &atom;
85 CmdParserLookup["bond"] = &bond;
86 CmdParserLookup["command"] = &command;
87 CmdParserLookup["edit"] = &edit;
88 CmdParserLookup["fill"] = &fill;
89 CmdParserLookup["shape"] = &shape;
90 CmdParserLookup["fragmentation"] = &fragmentation;
91 CmdParserLookup["graph"] = &graph;
92 CmdParserLookup["options"] = &options;
93 CmdParserLookup["molecule"] = &molecule;
94 CmdParserLookup["parser"] = &parser;
95 CmdParserLookup["potential"] = &potential;
96 CmdParserLookup["selection"] = &selection;
97 CmdParserLookup["tesselation"] = &tesselation;
98 CmdParserLookup["world"] = &world;
99}
100
101/** Destructor of class CommandLineParser.
102 *
103 */
104CommandLineParser::~CommandLineParser()
105{}
106
107/** Initializes command arguments to accept.
108 * Goes through ActionRegistry and puts all actions therein into the map.
109 */
110void CommandLineParser::InitializeCommandArguments()
111{
112 // we need a list of already added options, otherwise we get ambigious exceptions
113 std::set<std::string> AlreadyAddedOptionNames;
114
115 bool ActionAlreadyAdded_flag = false;
116 ActionQueue &AQ = ActionQueue::getInstance();
117 ActionQueue::ActionTokens_t tokens = AQ.getListOfActions();
118 for (ActionQueue::ActionTokens_t::const_iterator iter = tokens.begin();
119 iter != tokens.end(); ++iter) {
120 const ActionTrait &CurrentTrait = AQ.getActionsTrait(*iter);
121 ActionAlreadyAdded_flag = false;
122 //std::cout << "Current Action to initialize is: " << actioniter->first << std::endl;
123
124 for (ActionTrait::options_const_iterator optioniter = CurrentTrait.getBeginIter();
125 optioniter != CurrentTrait.getEndIter();
126 ++optioniter) {
127 if (optioniter->first == *iter)
128 ActionAlreadyAdded_flag = true;
129 ASSERT( OptionRegistry::getInstance().isOptionPresentByName(optioniter->first),
130 "CommandLineParser::Init() - Option "+optioniter->first+" not present in OptionRegistry." );
131 const OptionTrait* const currentOption = OptionRegistry::getInstance().getOptionByName(optioniter->first);
132
133 if (AlreadyAddedOptionNames.find(optioniter->first) == AlreadyAddedOptionNames.end()) {
134 // add the option
135// std::cout << "Registering Option "
136// << currentOption->getName()
137// << " with type '" << currentOption->getTypeName() << "' "
138// << " with description '" << currentOption->getDescription() << "' ";
139// if (currentOption->hasShortForm())
140// std::cout << ", with short form " << currentOption->getShortForm();
141// else
142// std::cout << ", with no short form ";
143// if (currentOption->hasDefaultValue())
144// std::cout << ", with default value " << currentOption->getDefaultValue();
145// else
146// std::cout << ", with no default value ";
147// std::cout << std::endl;
148
149 AddOptionToParser(currentOption, (CmdParserLookup["options"]));
150
151 AlreadyAddedOptionNames.insert(optioniter->first);
152 } else {
153// std::cout << "Option " << currentOption->getName() << " already registered." << std::endl;
154 }
155 }
156
157 if (!ActionAlreadyAdded_flag) {
158 // add the action
159// std::cout << "Registering Action "
160// << currentAction->Traits.getName()
161// << " in menu " << currentAction->Traits.getMenuName()
162// << " with type '" << currentAction->Traits.getTypeName() << "' "
163// << " with description '" << currentAction->Traits.getDescription() << "' ";
164// if (currentAction->Traits.hasShortForm())
165// std::cout << ", with short form " << currentAction->Traits.getShortForm();
166// else
167// std::cout << ", with no short form ";
168// if (currentAction->Traits.hasDefaultValue())
169// std::cout << ", with default value " << currentAction->Traits.getDefaultValue();
170// else
171// std::cout << ", with no default value ";
172// std::cout << std::endl;
173
174 ASSERT(CmdParserLookup.find(CurrentTrait.getMenuName()) != CmdParserLookup.end(),
175 "CommandLineParser: boost::program_options::options_description for this Action not present.");
176 AddOptionToParser(dynamic_cast<const OptionTrait * const>(&CurrentTrait),(CmdParserLookup[CurrentTrait.getMenuName()]));
177 }
178 }
179 // note: positioning is not important on the command line
180}
181
182/** Adds an Action or Option to the CommandLineParser.
183 * Note that Action is derived from Option(Trait)
184 *
185 * This ugly switch function is necessary because of the compile-time problem:
186 * po::value<T> has to be instantiated at compile-time however we do know the type not until run-time.
187 * Not even a templated function like po::value<T> getProgramOptionValuefromType() does help, specialized
188 * to each available type, as the signatures of all the functions differ. Hence, they cannot not put into
189 * one type_info -> po::value<T> map ...
190 *
191 * \param *currentOption pointer to Action/Option to add
192 * \param *OptionList program_options list to add to
193 */
194void CommandLineParser::AddOptionToParser(const OptionTrait * const currentOption, po::options_description* OptionList)
195{
196 // check whether dynamic_cast in Init() suceeded
197 ASSERT(currentOption != NULL, "CommandLineParser::AddOptionToParser() - currentOption is NULL!");
198 // add other options
199// std::cout << "Adding Action " << currentOption->getName() << " with type "
200// << currentOption->getType()->name() << ", " << (currentOption->hasDefaultValue() ? "with" : "without")
201// << " default value, and KeyandShortform " << currentOption->getKeyAndShortForm()
202// << " to CommandLineParser." << std::endl;
203 switch(TypeToEnums.getEnumforType(currentOption->getType())) {
204 default:
205 case TypeEnumContainer::NoneType:
206 OptionList->add_options()
207 (currentOption->getKeyAndShortForm().c_str(), currentOption->getDescription().c_str())
208 ;
209 break;
210 case TypeEnumContainer::BooleanType:
211 OptionList->add_options()
212 (currentOption->getKeyAndShortForm().c_str(),
213 currentOption->hasDefaultValue() ?
214 po::value < bool >()->default_value(boost::lexical_cast<int>(currentOption->getDefaultValue().c_str())) :
215 po::value < bool >(),
216 currentOption->getDescription().c_str())
217 ;
218 break;
219 case TypeEnumContainer::FileType:
220 OptionList->add_options()
221 (currentOption->getKeyAndShortForm().c_str(),
222// currentOption->hasDefaultValue() ?
223// po::value < boost::filesystem::path >()->default_value(boost::lexical_cast<boost::filesystem::path>(currentOption->getDefaultValue().c_str())) :
224 po::value < boost::filesystem::path >(),
225 currentOption->getDescription().c_str())
226 ;
227 break;
228 case TypeEnumContainer::ListOfFilesType:
229 OptionList->add_options()
230 (currentOption->getKeyAndShortForm().c_str(),
231// currentOption->hasDefaultValue() ?
232// po::value < std::vector<boost::filesystem::path> >()->default_value(boost::lexical_cast< std::vector<boost::filesystem::path> >(currentOption->getDefaultValue().c_str())) :
233 po::value < std::vector<boost::filesystem::path> >()->multitoken(),
234 currentOption->getDescription().c_str())
235 ;
236 break;
237 case TypeEnumContainer::IntegerType:
238 OptionList->add_options()
239 (currentOption->getKeyAndShortForm().c_str(),
240 currentOption->hasDefaultValue() ?
241 po::value < int >()->default_value(boost::lexical_cast<int>(currentOption->getDefaultValue().c_str())) :
242 po::value < int >(),
243 currentOption->getDescription().c_str())
244 ;
245 break;
246 case TypeEnumContainer::ListOfIntegersType:
247 OptionList->add_options()
248 (currentOption->getKeyAndShortForm().c_str(),
249// currentOption->hasDefaultValue() ?
250// po::value < std::vector<int> >()->default_value(boost::lexical_cast< std::vector<int> >(currentOption->getDefaultValue().c_str())) :
251 po::value < std::vector<int> >()->multitoken(),
252 currentOption->getDescription().c_str())
253 ;
254 break;
255 case TypeEnumContainer::UnsignedIntegerType:
256 OptionList->add_options()
257 (currentOption->getKeyAndShortForm().c_str(),
258 currentOption->hasDefaultValue() ?
259 po::value < unsigned int >()->default_value(boost::lexical_cast<unsigned int>(currentOption->getDefaultValue().c_str())) :
260 po::value < unsigned int >(),
261 currentOption->getDescription().c_str())
262 ;
263 break;
264 case TypeEnumContainer::ListOfUnsignedIntegersType:
265 OptionList->add_options()
266 (currentOption->getKeyAndShortForm().c_str(),
267// currentOption->hasDefaultValue() ?
268// po::value < std::vector<unsigned int> >()->default_value(boost::lexical_cast< std::vector<unsigned int> >(currentOption->getDefaultValue().c_str())) :
269 po::value < std::vector<unsigned int> >()->multitoken(),
270 currentOption->getDescription().c_str())
271 ;
272 break;
273 case TypeEnumContainer::DoubleType:
274 OptionList->add_options()
275 (currentOption->getKeyAndShortForm().c_str(),
276 currentOption->hasDefaultValue() ?
277 po::value < double >()->default_value(boost::lexical_cast<double>(currentOption->getDefaultValue().c_str())) :
278 po::value < double >(),
279 currentOption->getDescription().c_str())
280 ;
281 break;
282 case TypeEnumContainer::ListOfDoublesType:
283 OptionList->add_options()
284 (currentOption->getKeyAndShortForm().c_str(),
285// currentOption->hasDefaultValue() ?
286// po::value < std::vector<double> >()->default_value(boost::lexical_cast< std::vector<double> >(currentOption->getDefaultValue().c_str())) :
287 po::value < std::vector<double> >()->multitoken(),
288 currentOption->getDescription().c_str())
289 ;
290 break;
291 case TypeEnumContainer::StringType:
292 OptionList->add_options()
293 (currentOption->getKeyAndShortForm().c_str(),
294 currentOption->hasDefaultValue() ?
295 po::value < std::string >()->default_value(currentOption->getDefaultValue()) :
296 po::value < std::string >(),
297 currentOption->getDescription().c_str())
298 ;
299 break;
300 case TypeEnumContainer::ListOfStringsType:
301 OptionList->add_options()
302 (currentOption->getKeyAndShortForm().c_str(),
303// currentOption->hasDefaultValue() ?
304// po::value < std::vector<std::string> >()->default_value(boost::lexical_cast< std::vector<std::string> >(currentOption->getDefaultValue().c_str())) :
305 po::value < std::vector<std::string> >()->multitoken(),
306 currentOption->getDescription().c_str())
307 ;
308 break;
309 case TypeEnumContainer::VectorType:
310 OptionList->add_options()
311 (currentOption->getKeyAndShortForm().c_str(),
312// currentOption->hasDefaultValue() ?
313// po::value < VectorValue >()->default_value(boost::lexical_cast<VectorValue>(currentOption->getDefaultValue().c_str())) :
314 po::value < VectorValue >(),
315 currentOption->getDescription().c_str())
316 ;
317 break;
318 case TypeEnumContainer::ListOfVectorsType:
319 OptionList->add_options()
320 (currentOption->getKeyAndShortForm().c_str(),
321// currentOption->hasDefaultValue() ?
322// po::value < std::vector<VectorValue> >()->default_value(boost::lexical_cast< std::vector<VectorValue> >(currentOption->getDefaultValue().c_str())) :
323 po::value < std::vector<VectorValue> >()->multitoken(),
324 currentOption->getDescription().c_str())
325 ;
326 break;
327 case TypeEnumContainer::MoleculeType:
328 OptionList->add_options()
329 (currentOption->getKeyAndShortForm().c_str(),
330// currentOption->hasDefaultValue() ?
331// po::value < const molecule * >()->default_value(boost::lexical_cast<const molecule *>(currentOption->getDefaultValue().c_str())) :
332 po::value < int >(),
333 currentOption->getDescription().c_str())
334 ;
335 break;
336 case TypeEnumContainer::ListOfMoleculesType:
337 OptionList->add_options()
338 (currentOption->getKeyAndShortForm().c_str(),
339// currentOption->hasDefaultValue() ?
340// po::value < std::vector<const molecule *> >()->default_value(boost::lexical_cast< std::vector<const molecule *> >(currentOption->getDefaultValue().c_str())) :
341 po::value < std::vector<int> >()->multitoken(),
342 currentOption->getDescription().c_str())
343 ;
344 break;
345 case TypeEnumContainer::AtomType:
346 OptionList->add_options()
347 (currentOption->getKeyAndShortForm().c_str(),
348 currentOption->hasDefaultValue() ?
349 po::value < int >()->default_value(boost::lexical_cast<int>(currentOption->getDefaultValue().c_str())) :
350 po::value < int >(),
351 currentOption->getDescription().c_str())
352 ;
353 break;
354 case TypeEnumContainer::ListOfAtomsType:
355 OptionList->add_options()
356 (currentOption->getKeyAndShortForm().c_str(),
357// currentOption->hasDefaultValue() ?
358// po::value < std::vector<const atom *> >()->default_value(boost::lexical_cast< std::vector<const atom *> >(currentOption->getDefaultValue().c_str())) :
359 po::value < std::vector<int> >()->multitoken(),
360 currentOption->getDescription().c_str())
361 ;
362 break;
363 case TypeEnumContainer::ElementType:
364 OptionList->add_options()
365 (currentOption->getKeyAndShortForm().c_str(),
366// currentOption->hasDefaultValue() ?
367// po::value < const element * >()->default_value(boost::lexical_cast<const element *>(currentOption->getDefaultValue().c_str())) :
368 po::value < int >(),
369 currentOption->getDescription().c_str())
370 ;
371 break;
372 case TypeEnumContainer::ListOfElementsType:
373 OptionList->add_options()
374 (currentOption->getKeyAndShortForm().c_str(),
375// currentOption->hasDefaultValue() ?
376// po::value < std::vector<const element *> >()->default_value(boost::lexical_cast< std::vector<const element *> >(currentOption->getDefaultValue().c_str())) :
377 po::value < std::vector<int> >()->multitoken(),
378 currentOption->getDescription().c_str())
379 ;
380 break;
381 case TypeEnumContainer::RandomNumberDistribution_ParametersType:
382 OptionList->add_options()
383 (currentOption->getKeyAndShortForm().c_str(),
384 currentOption->hasDefaultValue() ?
385 po::value < std::string >()->default_value(boost::lexical_cast< std::string >(currentOption->getDefaultValue().c_str())) :
386 po::value < std::string >(),
387 currentOption->getDescription().c_str())
388 ;
389 break;
390 case TypeEnumContainer::RealSpaceMatrixType:
391 OptionList->add_options()
392 (currentOption->getKeyAndShortForm().c_str(),
393// currentOption->hasDefaultValue() ?
394// po::value < RealSpaceMatrixValue >()->default_value(boost::lexical_cast<BoxValue>(currentOption->getDefaultValue().c_str())) :
395 po::value < RealSpaceMatrixValue >(),
396 currentOption->getDescription().c_str())
397 ;
398 break;
399 }
400}
401
402/** States whether there are command line arguments.
403 * \return true - there are none, false - there is at least one command line argument
404 */
405bool CommandLineParser::isEmpty()
406{
407 return vm.empty();
408}
409
410/** Sets the options.
411 * \param _argc arg count from main()
412 * \param **_argv argument array from main()
413 */
414void CommandLineParser::setOptions(int _argc, char **_argv)
415{
416 argc = _argc;
417 argv = _argv;
418 config_file_options.add(options);
419 // append all option_descriptions to both cmdline_options and visible
420 for (CmdParserLookupMap::iterator iter = CmdParserLookup.begin();
421 iter != CmdParserLookup.end();
422 ++iter) {
423 cmdline_options.add(*(iter->second));
424 visible.add(*(iter->second));
425 }
426}
427
428/** Parses the command line arguments.
429 * Calls program_options::store() and program_options::notify()
430 *
431 * @return true - all is ok, false - command-line options could not be parsed
432 * correctly
433 */
434bool CommandLineParser::Parse()
435{
436 bool status = true;
437 try {
438 po::store(po::command_line_parser(argc,argv).options(cmdline_options).run(), vm);
439 } catch (std::exception &e) {
440 std::cerr << "Something went wrong with parsing the command-line arguments." << std::endl;
441 World::getInstance().setExitFlag(134);
442#ifdef HAVE_ACTION_THREAD
443 // force action queue to stop thread
444 ActionQueue::getInstance().stop();
445#endif
446 ActionQueue::getInstance().clearQueue();
447 status = false;
448 }
449 if (status) {
450 std::ifstream input;
451 input.open("example.cfg");
452 if (!input.fail())
453 po::store(po::parse_config_file(input, config_file_options), vm);
454 input.close();
455 po::notify(vm);
456 }
457 return status;
458}
459
460/** Scan the argument list for -a or --arguments and store their order for later use.
461 */
462void CommandLineParser::scanforSequenceOfArguments()
463{
464 std::map <std::string, std::string> ShortFormToActionMap = getShortFormToActionMap();
465 LOG(0, "Scanning command line arguments and recognizing Actions.");
466 // go through all arguments
467 for (int i=1;i<argc;i++) {
468 LOG(2, "Checking on " << argv[i]);
469 // check whether they
470 if (argv[i][0] == '-') { // .. begin with -
471 LOG(2, "Possible argument: " << argv[i]);
472 if (argv[i][1] == '-') { // .. or --
473 LOG(1, "Putting " << argv[i] << " into the sequence.");
474 SequenceOfActions.push_back(&(argv[i][2]));
475 // .. and check that next letter is not numeric, if so insert
476 } else if (((argv[i][1] < '0') || (argv[i][1] > '9')) && ((argv[i][1] != '.'))) {
477 std::map <std::string, std::string>::iterator iter = ShortFormToActionMap.find(&(argv[i][1]));
478 if (iter != ShortFormToActionMap.end()) {
479 LOG(1, "Putting " << iter->second << " for " << iter->first << " into the sequence.");
480 SequenceOfActions.push_back(iter->second);
481 }
482 }
483 }
484 }
485}
486
487/** Makes the Parser parse the command line options with current known options.
488 * \param _argc arg count from main()
489 * \param **_argv argument array from main()
490 */
491void CommandLineParser::Run(int _argc, char **_argv)
492{
493 setOptions(_argc,_argv);
494 const bool status = Parse();
495 if (status)
496 scanforSequenceOfArguments();
497}
498
499/** Go through all Actions and create a map from short form to their token.
500 * \return map from Action's ShortForm to token.
501 */
502std::map <std::string, std::string> CommandLineParser::getShortFormToActionMap() const
503{
504 std::map <std::string, std::string> result;
505
506 ActionQueue &AQ = ActionQueue::getInstance();
507 ActionQueue::ActionTokens_t tokens = AQ.getListOfActions();
508 for (ActionQueue::ActionTokens_t::const_iterator iter = tokens.begin();
509 iter != tokens.end(); ++iter) {
510 const ActionTrait &CurrentTrait = AQ.getActionsTrait(*iter);
511 if (CurrentTrait.hasShortForm()) {
512 ASSERT(result.find(CurrentTrait.getShortForm()) == result.end(),
513 "Short form "+toString(CurrentTrait.getShortForm())+
514 " for action "+toString(*iter)+" already present from "+
515 std::string(result[CurrentTrait.getShortForm()])+"!");
516 result[CurrentTrait.getShortForm()] = *iter;
517 }
518 }
519
520 return result;
521}
522
523CONSTRUCT_SINGLETON(CommandLineParser)
Note: See TracBrowser for help on using the repository browser.