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