67935fb77ed7667a62ebabb66904802f4d6d103c
[occt.git] / src / ViewerTest / ViewerTest_CmdParser.cxx
1 // Created on: 2015-03-15
2 // Created by: Danila ULYANOV
3 // Copyright (c) 2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <ViewerTest_CmdParser.hxx>
17
18 #include <Draw.hxx>
19 #include <Message.hxx>
20 #include <ViewerTest.hxx>
21
22 #include <algorithm>
23 #include <iostream>
24
25 namespace
26 {
27
28   //! Converts the given string to lowercase
29   //! @param theString the string to be converted
30   //! @return a converted string (a string in lowercase)
31   static std::string toLowerCase (std::string theString)
32   {
33     std::transform (theString.begin(), theString.end(), theString.begin(), ::LowerCase);
34     return theString;
35   }
36
37   //! Converts the vector of std::strings to a vector of pointers to its data
38   //! @param theStringList the vector of strings to be converted
39   //! @return a vector of pointers to the data of given strings
40   static std::vector<const char*> convertToRawStringList (const std::vector<std::string>& theStringList)
41   {
42     const std::size_t        aListSize = theStringList.size();
43     std::vector<const char*> aRawStringList (aListSize);
44     for (std::size_t anIndex = 0; anIndex < aListSize; ++anIndex)
45     {
46       aRawStringList[anIndex] = theStringList[anIndex].c_str();
47     }
48     return aRawStringList;
49   }
50
51 } // namespace
52
53 const std::size_t ViewerTest_CmdParser::THE_UNNAMED_COMMAND_OPTION_KEY = (std::numeric_limits<std::size_t>::max)();
54
55 const std::size_t ViewerTest_CmdParser::THE_HELP_COMMAND_OPTION_KEY = 0;
56
57 //===============================================================================================
58 // function : ViewerTest_CmdParser
59 // purpose  :
60 //===============================================================================================
61 ViewerTest_CmdParser::ViewerTest_CmdParser (const std::string& theDescription) : myDescription (theDescription)
62 {
63   AddOption ("help|h", "Prints a short description of the command and its options.");
64 }
65
66 //===============================================================================================
67 // function : AddOption
68 // purpose  :
69 //===============================================================================================
70 ViewerTest_CommandOptionKey ViewerTest_CmdParser::AddOption (const std::string& theOptionNames,
71                                                              const std::string& theOptionDescription)
72 {
73   CommandOption aNewOption;
74
75   // extract option names
76   std::vector<std::string> aNames;
77   std::stringstream        aStream (theOptionNames);
78   std::string              anItem;
79   while (std::getline (aStream, anItem, '|'))
80   {
81     if (!anItem.empty())
82     {
83       aNames.push_back (anItem);
84     }
85   }
86
87   aNewOption.Name = aNames.front();
88   if (aNames.size() > 1)
89   {
90     const std::size_t aNumberOfAliases = aNames.size() - 1;
91
92     aNewOption.Aliases.reserve (aNumberOfAliases);
93     std::copy (aNames.begin() + 1, aNames.end(), std::back_inserter (aNewOption.Aliases));
94   }
95   aNewOption.Description = theOptionDescription;
96
97   const ViewerTest_CommandOptionKey aNewOptionKey = myOptionStorage.size();
98
99   myOptionStorage.push_back (aNewOption);
100
101   std::vector<std::string>::const_iterator anIt = aNames.begin();
102   for (; anIt != aNames.end(); ++anIt)
103   {
104     const std::string aNameInLowerCase = toLowerCase (*anIt);
105
106     myOptionMap[aNameInLowerCase] = aNewOptionKey;
107   }
108
109   return aNewOptionKey;
110 }
111
112 //===============================================================================================
113 // function : PrintHelp
114 // purpose  :
115 //===============================================================================================
116 void ViewerTest_CmdParser::PrintHelp() const
117 {
118   std::cout << myDescription << std::endl;
119   std::vector<CommandOption>::const_iterator anIt = myOptionStorage.begin();
120   for (++anIt; anIt != myOptionStorage.end(); ++anIt)
121   {
122     const CommandOption& aCommandOption = *anIt;
123     std::cout << "\n\t-" << aCommandOption.Name;
124     const OptionAliases& anAliases = aCommandOption.Aliases;
125     if (!anAliases.empty())
126     {
127       std::cout << " (-" << anAliases.front();
128       for (OptionAliases::const_iterator anAliasIterator = anAliases.begin() + 1; anAliasIterator != anAliases.end();
129            ++anAliasIterator)
130       {
131         std::cout << ", -" << *anAliasIterator;
132       }
133       std::cout << ")";
134     }
135     std::cout << " : " << aCommandOption.Description;
136   }
137   std::cout << std::endl;
138 }
139
140 //===============================================================================================
141 // function : Parse
142 // purpose  :
143 //===============================================================================================
144 void ViewerTest_CmdParser::Parse (const Standard_Integer theArgsNb, const char* const* const theArgVec)
145 {
146   std::size_t aCurrentUsedOptionIndex = 0;
147   for (Standard_Integer anIter = 1; anIter < theArgsNb; ++anIter)
148   {
149     const char* const anArgument = theArgVec[anIter];
150     if (anArgument[0] == '-' && !std::isdigit (anArgument[1]))
151     {
152       const std::string   anOptionName = toLowerCase (anArgument + 1);
153       OptionMap::iterator aMapIter     = myOptionMap.find (anOptionName);
154       if (aMapIter != myOptionMap.end())
155       {
156         const ViewerTest_CommandOptionKey aCurrentUsedOptionKey = aMapIter->second;
157         aCurrentUsedOptionIndex                                 = addUsedOption (aCurrentUsedOptionKey);
158       }
159       else
160       {
161         Message::SendFail() << "Error: unknown argument '" << anOptionName << "'";
162         return;
163       }
164     }
165     else
166     {
167       if (anIter == 1)
168       {
169         aCurrentUsedOptionIndex = addUsedOption (THE_UNNAMED_COMMAND_OPTION_KEY);
170       }
171       myOptionArgumentStorage[aCurrentUsedOptionIndex].push_back (anArgument);
172     }
173   }
174 }
175
176 //===============================================================================================
177 // function : GetOptionNameByKey
178 // purpose  :
179 //===============================================================================================
180 std::string ViewerTest_CmdParser::GetOptionNameByKey (const ViewerTest_CommandOptionKey theOptionKey) const
181 {
182   if (theOptionKey == THE_UNNAMED_COMMAND_OPTION_KEY)
183   {
184     return "Unnamed";
185   }
186   return myOptionStorage[theOptionKey].Name;
187 }
188
189 //===============================================================================================
190 // function : GetUsedOptions
191 // purpose  :
192 //===============================================================================================
193 ViewerTest_CommandOptionKeySet ViewerTest_CmdParser::GetUsedOptions() const
194 {
195   ViewerTest_CommandOptionKeySet aUsedOptions;
196   for (UsedOptionMap::const_iterator aUsedOptionMapIterator = myUsedOptionMap.begin();
197        aUsedOptionMapIterator != myUsedOptionMap.end();
198        ++aUsedOptionMapIterator)
199   {
200     aUsedOptions.insert (aUsedOptionMapIterator->first);
201   }
202   return aUsedOptions;
203 }
204
205 //===============================================================================================
206 // function : HasNoOption
207 // purpose  :
208 //===============================================================================================
209 bool ViewerTest_CmdParser::HasNoOption() const
210 {
211   return myUsedOptionMap.empty();
212 }
213
214 //===============================================================================================
215 // function : HasUnnamedOption
216 // purpose  :
217 //===============================================================================================
218 bool ViewerTest_CmdParser::HasUnnamedOption() const
219 {
220   return myUsedOptionMap.find (THE_UNNAMED_COMMAND_OPTION_KEY) != myUsedOptionMap.end();
221 }
222
223 //===============================================================================================
224 // function : HasNoUnnamedOption
225 // purpose  :
226 //===============================================================================================
227 bool ViewerTest_CmdParser::HasOnlyUnnamedOption() const
228 {
229   return HasUnnamedOption() && (myUsedOptionMap.size() == 1);
230 }
231
232 //===============================================================================================
233 // function : HasOption
234 // purpose  :
235 //===============================================================================================
236 bool ViewerTest_CmdParser::HasOption (const std::string& theOptionName,
237                                       const std::size_t  theMandatoryArgsNb /* = 0 */,
238                                       const bool         isFatal /* = false */) const
239 {
240   ViewerTest_CommandOptionKey anOptionKey;
241   if (!findOptionKey (theOptionName, anOptionKey))
242   {
243     return false;
244   }
245   return HasOption (anOptionKey, theMandatoryArgsNb, isFatal);
246 }
247
248 //===============================================================================================
249 // function : HasOption
250 // purpose  :
251 //===============================================================================================
252 bool ViewerTest_CmdParser::HasOption (const ViewerTest_CommandOptionKey theOptionKey,
253                                       const std::size_t                 theMandatoryArgsNb /* = 0 */,
254                                       const bool                        isFatal /* = false */) const
255 {
256   std::size_t aUsedOptionIndex = 0;
257   if (!findUsedOptionIndex (theOptionKey, aUsedOptionIndex))
258   {
259     return false;
260   }
261   const OptionArguments& anOptionArguments = myOptionArgumentStorage[aUsedOptionIndex];
262   const bool             aResult           = (anOptionArguments.size() >= theMandatoryArgsNb);
263   if (isFatal && !aResult)
264   {
265     Message::SendFail() << "Error: wrong syntax at option '" << myOptionStorage[theOptionKey].Name << "'\n"
266                         << "At least " << theMandatoryArgsNb << "expected, but only " << anOptionArguments.size()
267                         << "provided.";
268   }
269   return aResult;
270 }
271
272 //===============================================================================================
273 // function : GetNumberOfOptionArguments
274 // purpose  :
275 //===============================================================================================
276 Standard_Integer ViewerTest_CmdParser::GetNumberOfOptionArguments (const std::string& theOptionName) const
277 {
278   ViewerTest_CommandOptionKey anOptionKey = THE_UNNAMED_COMMAND_OPTION_KEY;
279   if (!findOptionKey (theOptionName, anOptionKey))
280   {
281     return 0;
282   }
283   return GetNumberOfOptionArguments (anOptionKey);
284 }
285
286 //===============================================================================================
287 // function : GetNumberOfOptionArguments
288 // purpose  :
289 //===============================================================================================
290 Standard_Integer ViewerTest_CmdParser::GetNumberOfOptionArguments (const ViewerTest_CommandOptionKey theOptionKey) const
291 {
292   std::size_t aUsedOptionIndex = 0;
293   if (!findUsedOptionIndex (theOptionKey, aUsedOptionIndex))
294   {
295     return false;
296   }
297   return static_cast<Standard_Integer> (myOptionArgumentStorage[aUsedOptionIndex].size());
298 }
299
300 //===============================================================================================
301 // function : Arg
302 // purpose  :
303 //===============================================================================================
304 bool ViewerTest_CmdParser::Arg (const std::string&     theOptionName,
305                                 const Standard_Integer theArgumentIndex,
306                                 std::string&           theOptionArgument) const
307 {
308   Standard_ASSERT_RETURN (theArgumentIndex >= 0,
309                           "'theArgumentIndex' must be greater than or equal to zero.",
310                           false);
311   ViewerTest_CommandOptionKey anOptionKey = THE_UNNAMED_COMMAND_OPTION_KEY;
312   if (!theOptionName.empty() && !findOptionKey (theOptionName, anOptionKey))
313   {
314     return false;
315   }
316   return Arg (anOptionKey, theArgumentIndex, theOptionArgument);
317 }
318
319 //===============================================================================================
320 // function : Arg
321 // purpose  :
322 //===============================================================================================
323 bool ViewerTest_CmdParser::Arg (const ViewerTest_CommandOptionKey theOptionKey,
324                                 const Standard_Integer            theArgumentIndex,
325                                 std::string&                      theOptionArgument) const
326 {
327   Standard_ASSERT_RETURN (theArgumentIndex >= 0,
328                           "'theArgumentIndex' must be greater than or equal to zero.",
329                           false);
330   std::size_t aUsedOptionIndex = 0;
331   if (!findUsedOptionIndex (theOptionKey, aUsedOptionIndex))
332   {
333     return false;
334   }
335   const OptionArguments& anOptionArguments = myOptionArgumentStorage[aUsedOptionIndex];
336   if (static_cast<std::size_t> (theArgumentIndex) >= anOptionArguments.size())
337   {
338     return false;
339   }
340   theOptionArgument = anOptionArguments[theArgumentIndex];
341   return true;
342 }
343
344 //===============================================================================================
345 // function : Arg
346 // purpose  :
347 //===============================================================================================
348 std::string ViewerTest_CmdParser::Arg (const std::string& theOptionName, const Standard_Integer theArgumentIndex) const
349 {
350   Standard_ASSERT_RETURN (theArgumentIndex >= 0,
351                           "'theArgumentIndex' must be greater than or equal to zero.",
352                           std::string());
353   std::string anOptionArgument;
354   if (!Arg (theOptionName, theArgumentIndex, anOptionArgument))
355   {
356     return std::string();
357   }
358   return anOptionArgument;
359 }
360
361 //===============================================================================================
362 // function : Arg
363 // purpose  :
364 //===============================================================================================
365 std::string ViewerTest_CmdParser::Arg (const ViewerTest_CommandOptionKey theOptionKey,
366                                        const Standard_Integer            theArgumentIndex) const
367 {
368   std::string anOptionArgument;
369   if (!Arg (theOptionKey, theArgumentIndex, anOptionArgument))
370   {
371     return std::string();
372   }
373   return anOptionArgument;
374 }
375
376 //===============================================================================================
377 // function : ArgVec3f
378 // purpose  :
379 //===============================================================================================
380 Graphic3d_Vec3 ViewerTest_CmdParser::ArgVec3f (const std::string& theOptionName,
381                                                Standard_Integer   theArgumentIndex) const
382 {
383   return Graphic3d_Vec3 (
384     static_cast<Standard_ShortReal> (Draw::Atof (Arg (theOptionName, theArgumentIndex).c_str())),
385     static_cast<Standard_ShortReal> (Draw::Atof (Arg (theOptionName, theArgumentIndex + 1).c_str())),
386     static_cast<Standard_ShortReal> (Draw::Atof (Arg (theOptionName, theArgumentIndex + 2).c_str())));
387 }
388
389 //===============================================================================================
390 // function : ArgVec3d
391 // purpose  :
392 //===============================================================================================
393 Graphic3d_Vec3d ViewerTest_CmdParser::ArgVec3d (const std::string& theOptionName,
394                                                 Standard_Integer   theArgumentIndex) const
395 {
396   return Graphic3d_Vec3d (Draw::Atof (Arg (theOptionName, theArgumentIndex).c_str()),
397                           Draw::Atof (Arg (theOptionName, theArgumentIndex + 1).c_str()),
398                           Draw::Atof (Arg (theOptionName, theArgumentIndex + 2).c_str()));
399 }
400
401 //===============================================================================================
402 // function : ArgVec
403 // purpose  :
404 //===============================================================================================
405 gp_Vec ViewerTest_CmdParser::ArgVec (const std::string& theOptionName, Standard_Integer theArgumentIndex) const
406 {
407   return gp_Vec (Draw::Atof (Arg (theOptionName, theArgumentIndex).c_str()),
408                  Draw::Atof (Arg (theOptionName, theArgumentIndex + 1).c_str()),
409                  Draw::Atof (Arg (theOptionName, theArgumentIndex + 2).c_str()));
410 }
411
412 //===============================================================================================
413 // function : ArgPnt
414 // purpose  :
415 //===============================================================================================
416 gp_Pnt ViewerTest_CmdParser::ArgPnt (const std::string& theOptionName, Standard_Integer theArgumentIndex) const
417 {
418   return gp_Pnt (Draw::Atof (Arg (theOptionName, theArgumentIndex).c_str()),
419                  Draw::Atof (Arg (theOptionName, theArgumentIndex + 1).c_str()),
420                  Draw::Atof (Arg (theOptionName, theArgumentIndex + 2).c_str()));
421 }
422
423 //===============================================================================================
424 // function : ArgDouble
425 // purpose  :
426 //===============================================================================================
427 Standard_Real ViewerTest_CmdParser::ArgDouble (const std::string& theOptionName,
428                                                Standard_Integer   theArgumentIndex) const
429 {
430   return Draw::Atof (Arg (theOptionName, theArgumentIndex).c_str());
431 }
432
433 //===============================================================================================
434 // function : ArgFloat
435 // purpose  :
436 //===============================================================================================
437 Standard_ShortReal ViewerTest_CmdParser::ArgFloat (const std::string& theOptionName,
438                                                    Standard_Integer   theArgumentIndex) const
439 {
440   return static_cast<Standard_ShortReal> (Draw::Atof (Arg (theOptionName, theArgumentIndex).c_str()));
441 }
442
443 //===============================================================================================
444 // function : ArgInt
445 // purpose  :
446 //===============================================================================================
447 Standard_Integer ViewerTest_CmdParser::ArgInt (const std::string&     theOptionName,
448                                                const Standard_Integer theArgumentIndex) const
449 {
450   return static_cast<Standard_Integer> (Draw::Atoi (Arg (theOptionName, theArgumentIndex).c_str()));
451 }
452
453 //===============================================================================================
454 // function : ArgBool
455 // purpose  :
456 //===============================================================================================
457 bool ViewerTest_CmdParser::ArgBool (const std::string& theOptionName, const Standard_Integer theArgumentIndex) const
458 {
459   return Draw::Atoi (Arg (theOptionName, theArgumentIndex).c_str()) != 0;
460 }
461
462 //===============================================================================================
463 // function : ArgColor
464 // purpose  :
465 //===============================================================================================
466 template <typename TheColor>
467 bool ViewerTest_CmdParser::ArgColor (const std::string& theOptionName,
468                                      Standard_Integer&  theArgumentIndex,
469                                      TheColor&          theColor) const
470 {
471   ViewerTest_CommandOptionKey anOptionKey;
472   if (!findOptionKey (theOptionName, anOptionKey))
473   {
474     return false;
475   }
476   return ArgColor (anOptionKey, theArgumentIndex, theColor);
477 }
478
479 //! ViewerTest_CmdParser::ArgColor() explicit template instantiation definitions
480 template bool ViewerTest_CmdParser::ArgColor (const std::string& theOptionName,
481                                               Standard_Integer&  theArgumentIndex,
482                                               Quantity_Color&    theColor) const;
483
484 template bool ViewerTest_CmdParser::ArgColor (const std::string&  theOptionName,
485                                               Standard_Integer&   theArgumentIndex,
486                                               Quantity_ColorRGBA& theColor) const;
487
488 //===============================================================================================
489 // function : ArgColor
490 // purpose  :
491 //===============================================================================================
492 template <typename TheColor>
493 bool ViewerTest_CmdParser::ArgColor (const ViewerTest_CommandOptionKey theOptionKey,
494                                      Standard_Integer&                 theArgumentIndex,
495                                      TheColor&                         theColor) const
496 {
497   std::size_t aUsedOptionIndex = 0;
498   if (!findUsedOptionIndex (theOptionKey, aUsedOptionIndex))
499   {
500     return false;
501   }
502   const RawStringArguments aRawStringArguments = getRawStringArguments (aUsedOptionIndex);
503   const Standard_Integer   aNumberOfArguments  = static_cast<Standard_Integer> (aRawStringArguments.size());
504   Standard_ASSERT_RETURN (theArgumentIndex < aNumberOfArguments,
505                           "'theArgumentIndex' must be less than the number of command-line arguments "
506                           "passed with the option which access key is 'theOptionKey'.",
507                           false);
508   const Standard_Integer aNumberOfAvailableArguments = aNumberOfArguments - theArgumentIndex;
509   TheColor               aColor;
510   const Standard_Integer aNumberOfParsedArguments = ViewerTest::ParseColor (aNumberOfAvailableArguments,
511                                                                             &aRawStringArguments[theArgumentIndex],
512                                                                             aColor);
513   if (aNumberOfParsedArguments == 0)
514   {
515     return false;
516   }
517   theArgumentIndex += aNumberOfParsedArguments;
518   theColor = aColor;
519   return true;
520 }
521
522 //! ViewerTest_CmdParser::ArgColor() explicit template instantiation definitions
523 template bool ViewerTest_CmdParser::ArgColor (ViewerTest_CommandOptionKey theOptionKey,
524                                               Standard_Integer&           theArgumentIndex,
525                                               Quantity_Color&             theColor) const;
526
527 template bool ViewerTest_CmdParser::ArgColor (ViewerTest_CommandOptionKey theOptionKey,
528                                               Standard_Integer&           theArgumentIndex,
529                                               Quantity_ColorRGBA&         theColor) const;
530
531 //===============================================================================================
532 // function : findUsedOptionKey
533 // purpose  :
534 //===============================================================================================
535 bool ViewerTest_CmdParser::findOptionKey (const std::string&           theOptionName,
536                                           ViewerTest_CommandOptionKey& theOptionKey) const
537 {
538   const std::string               anOptionNameInLowercase = toLowerCase (theOptionName);
539   const OptionMap::const_iterator aMapIter                = myOptionMap.find (anOptionNameInLowercase);
540   if (aMapIter == myOptionMap.end())
541   {
542     return false;
543   }
544   theOptionKey = aMapIter->second;
545   return true;
546 }
547
548 //===============================================================================================
549 // function : findUsedOptionKey
550 // purpose  :
551 //===============================================================================================
552 bool ViewerTest_CmdParser::findUsedOptionIndex (const ViewerTest_CommandOptionKey theOptionKey,
553                                                 std::size_t&                      theUsedOptionIndex) const
554 {
555   const UsedOptionMap::const_iterator aUsedOptionIterator = myUsedOptionMap.find (theOptionKey);
556   if (aUsedOptionIterator == myUsedOptionMap.end())
557   {
558     return false;
559   }
560   theUsedOptionIndex = aUsedOptionIterator->second;
561   return true;
562 }
563
564 //===============================================================================================
565 // function : findUsedOptionIndex
566 // purpose  :
567 //===============================================================================================
568 bool ViewerTest_CmdParser::findUsedOptionIndex (const std::string& theOptionName, std::size_t& theUsedOptionIndex) const
569 {
570   ViewerTest_CommandOptionKey anOptionKey = THE_UNNAMED_COMMAND_OPTION_KEY;
571   if (!findOptionKey (theOptionName, anOptionKey))
572   {
573     return false;
574   }
575   std::size_t aUsedOptionIndex = 0;
576   if (!findUsedOptionIndex (anOptionKey, aUsedOptionIndex))
577   {
578     return false;
579   }
580   theUsedOptionIndex = aUsedOptionIndex;
581   return true;
582 }
583
584 //===============================================================================================
585 // function : addUsedOption
586 // purpose  :
587 //===============================================================================================
588 std::size_t ViewerTest_CmdParser::addUsedOption (const ViewerTest_CommandOptionKey theNewUsedOptionKey)
589 {
590   const std::size_t aNewUsedOptionIndex = myOptionArgumentStorage.size();
591   myOptionArgumentStorage.push_back (OptionArguments());
592   myUsedOptionMap[theNewUsedOptionKey] = aNewUsedOptionIndex;
593   return aNewUsedOptionIndex;
594 }
595
596 //===============================================================================================
597 // function : getRawStringArguments
598 // purpose  :
599 //===============================================================================================
600 ViewerTest_CmdParser::RawStringArguments ViewerTest_CmdParser::getRawStringArguments (
601   const std::size_t theUsedOptionIndex) const
602 {
603   Standard_ASSERT_RETURN (
604     theUsedOptionIndex < myOptionArgumentStorage.size(),
605     "'theUsedOptionIndex' must be less than the size of 'myOptionArgumentStorage'.",
606     RawStringArguments());
607   const OptionArguments& anOptionArguments = myOptionArgumentStorage[theUsedOptionIndex];
608   return convertToRawStringList (anOptionArguments);
609 }