1 // ==========================================================================
2 //                 SeqAn - The Library for Sequence Analysis
3 // ==========================================================================
4 // Copyright (c) 2006-2018, Knut Reinert, FU Berlin
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are met:
9 //
10 //     * Redistributions of source code must retain the above copyright
11 //       notice, this list of conditions and the following disclaimer.
12 //     * Redistributions in binary form must reproduce the above copyright
13 //       notice, this list of conditions and the following disclaimer in the
14 //       documentation and/or other materials provided with the distribution.
15 //     * Neither the name of Knut Reinert or the FU Berlin nor the names of
16 //       its contributors may be used to endorse or promote products derived
17 //       from this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 // ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
23 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29 // DAMAGE.
30 //
31 // ==========================================================================
32 // Author: Stephan Aiche <stephan.aiche@fu-berlin.de>
33 // ==========================================================================
34 
35 #ifndef SEQAN_INCLUDE_SEQAN_ARG_PARSE_ARG_PARSE_ARGUMENT_H_
36 #define SEQAN_INCLUDE_SEQAN_ARG_PARSE_ARG_PARSE_ARGUMENT_H_
37 
38 #include <seqan/arg_parse/arg_parse_exceptions.h>
39 #include <seqan/arg_parse/arg_parse_type_support.h>
40 
41 #include <string>
42 #include <vector>
43 #include <sstream>
44 
45 namespace seqan {
46 
47 // ==========================================================================
48 // Forwards
49 // ==========================================================================
50 
51 // Required for _checkStringRestrictions().
52 inline std::string getFileExtension(ArgParseArgument const & me, unsigned pos);
53 
54 // ==========================================================================
55 // Tags, Classes, Enums
56 // ==========================================================================
57 
58 // ----------------------------------------------------------------------------
59 // Class ArgParseArgument
60 // ----------------------------------------------------------------------------
61 
62 /*!
63  * @class ArgParseArgument
64  * @implements AssignableConcept
65  * @headerfile <seqan/arg_parse.h>
66  * @brief Information for a specific command line argument.
67  *
68  * @signature class ArgParseArgument
69  */
70 
71 /*!
72  * @enum ArgParseArgument::ArgumentType
73  * @headerfile <seqan/arg_parse.h>
74  * @brief Define the type of an @link ArgParseArgument @endlink.
75  *
76  * @signature enum ArgParseArgument::ArgumentType;
77  *
78  * @section Examples
79  *
80  * In the following example, the types <tt>INPUT_FILE</tt>, <tt>OUTPUT_FILE</tt>, and <tt>DOUBLE</tt> are used.
81  *
82  * @include demos/dox/arg_parse/argument_parser.cpp
83  */
84 
85 /*!
86  * @val ArgParseArgument::ArgumentType STRING
87  * @brief Argument is a string.
88  *
89  * @val ArgParseArgument::ArgumentType ArgParseArgument::INTEGER;
90  * @brief Argument is a signed 32 bit integer.
91  *
92  * @val ArgParseArgument::ArgumentType ArgParseArgument::INT64;
93  * @brief Argument is a signed 64 bit integer.
94  *
95  * @val ArgParseArgument::ArgumentType ArgParseArgument::DOUBLE;
96  * @brief Argument is a floating point number stored as double.
97  *
98  * @val ArgParseArgument::ArgumentType ArgParseArgument::INPUT_FILE;
99  * @brief Argument is an input file.
100  *
101  * @val ArgParseArgument::ArgumentType ArgParseArgument::OUTPUT_FILE;
102  * @brief Argument is an output file.
103  *
104  * @val ArgParseArgument::ArgumentType ArgParseArgument::INPUT_PREFIX;
105  * @brief Argument is a prefix to input file(s).
106  *
107  * @val ArgParseArgument::ArgumentType ArgParseArgument::OUTPUT_PREFIX;
108  * @brief Argument is a prefix to output file(s).
109  */
110 
111 /*!
112  * @fn ArgParseArgument::ArgParseArgument
113  * @brief Constructor
114  *
115  * @signature ArgParseArgument::ArgParseArgument(argumentType[, argumentLabel[, isListArgument[, numberOfArgument]]]);
116  *
117  * @param[in] argumentType      Type of the argument (<tt>ArgParseArgument::ArgumentType</tt>).
118  * @param[in] argumentLabel     Label for the argument (<tt>char const *</tt>).
119  * @param[in] isListArgument    Whether or not this argument can be given multiple times (<tt>bool</tt>).
120  * @param[in] numberOfArguments Number of times the argument must be given.  E.g. set to 2 for the parser to always
121  *                              expect two values (<tt>int</tt>, default is 1).
122  */
123 
124 class ArgParseArgument
125 {
126 public:
127     enum ArgumentType
128     {
129         // argument is
130         BOOL,
131         STRING,      // .. a string
132         INTEGER,     // .. an integer
133         INT64,       // .. a 64 bit integer
134         DOUBLE,      // .. a float
135         INPUT_FILE,   // .. an inputfile (implicitly also a string)
136         OUTPUT_FILE,  // .. an outputfile (implicitly also a string)
137         INPUT_PREFIX, // .. an inputprefix (implicitly also a string)
138         ///@deprecated use INPUT_PREFIX instead
139         INPUTPREFIX = INPUT_PREFIX,
140         OUTPUT_PREFIX, // .. an outoutprefix (implicitly also a string)
141         INPUT_DIRECTORY,
142         OUTPUT_DIRECTORY
143     };
144 
145 
146     // ----------------------------------------------------------------------------
147     // Members to store type information
148     // ----------------------------------------------------------------------------
149     ArgumentType _argumentType;
150     unsigned     _numberOfValues;
151     std::string  _argumentLabel;
152     bool         _isListArgument;
153 
154     // ----------------------------------------------------------------------------
155     // Members to store the values
156     // ----------------------------------------------------------------------------
157     std::vector<std::string>  defaultValue;
158     std::vector<std::string>  value;
159     // Override for the file extension;  only used for input/output file arguments.
160     std::vector<std::string> _fileExtensions;
161 
162     // ----------------------------------------------------------------------------
163     // Members for restrictions
164     // ----------------------------------------------------------------------------
165     std::string           minValue;
166     std::string           maxValue;
167     std::vector<std::string> validValues;
168 
169     // ----------------------------------------------------------------------------
170     // Tags
171     // ----------------------------------------------------------------------------
172     // Tags can be used to attach hints to the arguments (and options).  Currently,
173     // this is used for tagging the "-file-ext" arguments as "file-ext-override"
174     // and "gkn-ignore" for ignoring in GKN.
175     std::vector<std::string> tags;
176 
177     // ----------------------------------------------------------------------------
178     // Members to help text
179     // ----------------------------------------------------------------------------
180     std::string         _helpText;    // The help text shown on the command line
181 
182     // ----------------------------------------------------------------------------
183     // Constructors
184     // ----------------------------------------------------------------------------
185 
186     ArgParseArgument(ArgumentType argumentType,
187                      std::string const & argumentLabel = "",
188                      bool isListArgument = false,
189                      unsigned numberOfValues = 1) :
_argumentType(argumentType)190         _argumentType(argumentType),
191         _numberOfValues(numberOfValues),
192         _argumentLabel(argumentLabel),
193         _isListArgument(isListArgument),
194         minValue(""),
195         maxValue(""),
196         _helpText("")
197     {
198         if (argumentType == ArgParseArgument::BOOL)
199         {
200             copy(BooleanArgumentValues_<>::LIST_TRUE.begin(), BooleanArgumentValues_<>::LIST_TRUE.end(),
201                  std::back_inserter(validValues));
202             copy(BooleanArgumentValues_<>::LIST_FALSE.begin(), BooleanArgumentValues_<>::LIST_FALSE.end(),
203                  std::back_inserter(validValues));
204         }
205     }
206 };
207 
208 // ==========================================================================
209 // Metafunctions
210 // ==========================================================================
211 
212 // ==========================================================================
213 // Functions
214 // ==========================================================================
215 
216 // ----------------------------------------------------------------------------
217 // Helper Function _typeToString()
218 // ----------------------------------------------------------------------------
219 
_typeToString(ArgParseArgument const & me)220 inline std::string _typeToString(ArgParseArgument const & me)
221 {
222     switch (me._argumentType)
223     {
224         case ArgParseArgument::BOOL:
225             return "bool";
226 
227         case ArgParseArgument::DOUBLE:
228             return "double";
229 
230         case ArgParseArgument::INTEGER:
231             return "integer";
232 
233         case ArgParseArgument::INT64:
234             return "int64";
235 
236         case ArgParseArgument::STRING:
237             return "string";
238 
239         case ArgParseArgument::INPUT_FILE:
240             return "input_file";
241 
242         case ArgParseArgument::OUTPUT_FILE:
243             return "output_file";
244 
245         case ArgParseArgument::INPUT_PREFIX:
246             return "input_prefix";
247 
248         case ArgParseArgument::OUTPUT_PREFIX:
249             return "output_prefix";
250 
251         case ArgParseArgument::INPUT_DIRECTORY:
252             return "input_directory";
253 
254         case ArgParseArgument::OUTPUT_DIRECTORY:
255             return "output_directory";
256 
257         default:
258             return "unknown";
259     }
260 }
261 
262 // ----------------------------------------------------------------------------
263 // Function isListArgument()
264 // ----------------------------------------------------------------------------
265 
266 /*!
267  * @fn ArgParseArgument#isListArgument
268  * @headerfile <seqan/arg_parse.h>
269  *
270  * @brief Returns whether the argument can be given more than one time.
271  *
272  * @signature bool isListArgument(arg);
273  *
274  * @param[in] arg The ArgParseArgument to query.
275  *
276  * @return bool <tt>true</tt> if it can be given multiple times, <tt>false</tt> otherwise.
277  */
278 
isListArgument(ArgParseArgument const & me)279 inline bool isListArgument(ArgParseArgument const & me)
280 {
281     return me._isListArgument;
282 }
283 
284 // ----------------------------------------------------------------------------
285 // Function isBooleanArgument()
286 // ----------------------------------------------------------------------------
287 
288 /*!
289  * @fn ArgParseArgument#isBooleanArgument
290  * @headerfile <seqan/arg_parse.h>
291  * @brief Returns whether the argument is a bool.
292  *
293  * @signature bool isBooleanArgument(arg);
294  *
295  * @param[in] arg The ArgParseArgument to query.
296  *
297  * @return bool <tt>true</tt> if it is a bool, <tt>false</tt> otherwise.
298  */
299 
isBooleanArgument(ArgParseArgument const & me)300 inline bool isBooleanArgument(ArgParseArgument const & me)
301 {
302     return me._argumentType == ArgParseArgument::BOOL;
303 }
304 
305 // ----------------------------------------------------------------------------
306 // Function isStringArgument()
307 // ----------------------------------------------------------------------------
308 
309 /*!
310  * @fn ArgParseArgument#isStringArgument
311  * @headerfile <seqan/arg_parse.h>
312  *
313  * @brief Returns whether the argument is a string.
314  *
315  * @signature bool isStringArgument(arg);
316  *
317  * @param[in] arg The ArgParseArgument to query.
318  *
319  * @return bool <tt>true</tt> if it is a string, <tt>false</tt> otherwise.
320  */
321 
isStringArgument(ArgParseArgument const & me)322 inline bool isStringArgument(ArgParseArgument const & me)
323 {
324     return (me._argumentType == ArgParseArgument::STRING) ||
325            (me._argumentType == ArgParseArgument::INPUT_FILE) ||
326            (me._argumentType == ArgParseArgument::OUTPUT_FILE) ||
327            (me._argumentType == ArgParseArgument::INPUT_PREFIX) ||
328            (me._argumentType == ArgParseArgument::OUTPUT_PREFIX) ||
329            (me._argumentType == ArgParseArgument::INPUT_DIRECTORY) ||
330            (me._argumentType == ArgParseArgument::OUTPUT_DIRECTORY);
331 }
332 
333 // ----------------------------------------------------------------------------
334 // Function isIntegerArgument()
335 // ----------------------------------------------------------------------------
336 
337 /*!
338  * @fn ArgParseArgument#isIntegerArgument
339  * @headerfile <seqan/arg_parse.h>
340  * @brief Returns whether the argument is an integer.
341  *
342  * @signature bool isIntegerArgument(arg);
343  *
344  * @param[in] arg The ArgParseArgument to query.
345  *
346  * @return bool <tt>true</tt> if it is an integer, <tt>false</tt> otherwise.
347  */
348 
isIntegerArgument(ArgParseArgument const & me)349 inline bool isIntegerArgument(ArgParseArgument const & me)
350 {
351     return me._argumentType == ArgParseArgument::INTEGER;
352 }
353 
354 // ----------------------------------------------------------------------------
355 // Function isInt64Argument()
356 // ----------------------------------------------------------------------------
357 
358 /*!
359  * @fn ArgParseArgument#isInt64Argument
360  * @headerfile <seqan/arg_parse.h>
361  * @brief Returns whether the argument is a 64 bit integer.
362  *
363  * @signature bool isInt64Argument(arg);
364  *
365  * @param[in] arg The ArgParseArgument to query.
366  *
367  * @return bool <tt>true</tt> if it is a 64 bit integer, <tt>false</tt> otherwise.
368  */
369 
isInt64Argument(ArgParseArgument const & me)370 inline bool isInt64Argument(ArgParseArgument const & me)
371 {
372     return me._argumentType == ArgParseArgument::INT64;
373 }
374 
375 // ----------------------------------------------------------------------------
376 // Function isDoubleArgument()
377 // ----------------------------------------------------------------------------
378 
379 /*!
380  * @fn ArgParseArgument#isDoubleArgument
381  * @headerfile <seqan/arg_parse.h>
382  * @brief Returns whether the argument is a double integer.
383  *
384  * @signature bool isDoubleArgument(arg);
385  *
386  * @param[in] arg The ArgParseArgument to query.
387  *
388  * @return bool <tt>true</tt> if it is a double argument, <tt>false</tt> otherwise.
389  */
390 
isDoubleArgument(ArgParseArgument const & me)391 inline bool isDoubleArgument(ArgParseArgument const & me)
392 {
393     return me._argumentType == ArgParseArgument::DOUBLE;
394 }
395 
396 // ----------------------------------------------------------------------------
397 // Function isInputFileArgument()
398 // ----------------------------------------------------------------------------
399 
400 /*!
401  * @fn ArgParseArgument#isInputFileArgument
402  * @headerfile <seqan/arg_parse.h>
403  * @brief Returns whether the argument is a input file.
404  *
405  * @signature bool isInputFileArgument(arg);
406  *
407  * @param[in] arg The ArgParseArgument to query.
408  *
409  * @return bool <tt>true</tt> if it is a input file argument, <tt>false</tt> otherwise.
410  */
411 
isInputFileArgument(ArgParseArgument const & me)412 inline bool isInputFileArgument(ArgParseArgument const & me)
413 {
414     return me._argumentType == ArgParseArgument::INPUT_FILE ||
415            me._argumentType == ArgParseArgument::INPUT_DIRECTORY;
416 }
417 
418 // ----------------------------------------------------------------------------
419 // Function isOutputFileArgument()
420 // ----------------------------------------------------------------------------
421 
422 /*!
423  * @fn ArgParseArgument#isOutputFileArgument
424  * @headerfile <seqan/arg_parse.h>
425  * @brief Returns whether the argument is a output file.
426  *
427  * @signature bool isOutputFileArgument(arg);
428  *
429  * @param[in] arg The ArgParseArgument to query.
430  *
431  * @return bool <tt>true</tt> if it is a output file argument, <tt>false</tt> otherwise.
432  */
433 
isOutputFileArgument(ArgParseArgument const & me)434 inline bool isOutputFileArgument(ArgParseArgument const & me)
435 {
436     return me._argumentType == ArgParseArgument::OUTPUT_FILE ||
437            me._argumentType == ArgParseArgument::OUTPUT_DIRECTORY;
438 }
439 
440 // ----------------------------------------------------------------------------
441 // Function isDirectoryArgument()
442 // ----------------------------------------------------------------------------
443 
444 /*!
445  * @fn ArgParseArgument#isDirectoryArgument
446  * @headerfile <seqan/arg_parse.h>
447  * @brief Returns whether the argument is a directorz argument.
448  *
449  * @signature bool isDirectoryArgument(arg);
450  *
451  * @param[in] arg The ArgParseArgument to query.
452  *
453  * @return bool <tt>true</tt> if it is a directory argument, <tt>false</tt> otherwise.
454  */
455 
isDirectoryArgument(ArgParseArgument const & me)456 inline bool isDirectoryArgument(ArgParseArgument const & me)
457 {
458     return me._argumentType == ArgParseArgument::INPUT_DIRECTORY ||
459            me._argumentType == ArgParseArgument::OUTPUT_DIRECTORY;
460 }
461 
462 // ----------------------------------------------------------------------------
463 // Function isOutputPrefixArgument()
464 // ----------------------------------------------------------------------------
465 
466 /*!
467  * @fn ArgParseArgument#isOutputPrefixArgument
468  * @headerfile <seqan/arg_parse.h>
469  * @brief Returns whether the argument is an output prefix.
470  *
471  * @signature bool isOutputPrefixArgument(arg);
472  *
473  * @param[in] arg The ArgParseArgument to query.
474  *
475  * @return bool <tt>true</tt> if it is an output prefix argument, <tt>false</tt> otherwise.
476  */
477 
isOutputPrefixArgument(ArgParseArgument const & me)478 inline bool isOutputPrefixArgument(ArgParseArgument const & me)
479 {
480     return me._argumentType == ArgParseArgument::OUTPUT_PREFIX;
481 }
482 
483 // ----------------------------------------------------------------------------
484 // Function isOutputFileArgument()
485 // ----------------------------------------------------------------------------
486 
487 /*!
488  * @fn ArgParseArgument#isInputPrefixArgument
489  * @headerfile <seqan/arg_parse.h>
490  * @brief Returns whether the argument is an input prefix argument.
491  *
492  * @signature bool isInputPrefixArgument(arg);
493  *
494  * @param[in] arg The ArgParseArgument to query.
495  *
496  * @return bool <tt>true</tt> if it is an input prefix argument, <tt>false</tt> otherwise.
497  */
498 
isInputPrefixArgument(ArgParseArgument const & me)499 inline bool isInputPrefixArgument(ArgParseArgument const & me)
500 {
501     return me._argumentType == ArgParseArgument::INPUT_PREFIX;
502 }
503 
504 // ----------------------------------------------------------------------------
505 // Function getArgumentType()
506 // ----------------------------------------------------------------------------
507 
508 /*!
509  * @fn ArgParseArgument#getArgumentType
510  * @headerfile <seqan/arg_parse.h>
511  * @brief Return the <tt>ArgParseArgument::ArgumentType</tt>.
512  *
513  * @signature std::string getArgumentType(arg);
514  *
515  * @param[in] arg The ArgParseArgument to query.
516  *
517  * @return ArgumentType The argument type.
518  */
519 
getArgumentType(ArgParseArgument const & me)520 inline ArgParseArgument::ArgumentType getArgumentType(ArgParseArgument const & me)
521 {
522     return me._argumentType;
523 }
524 
525 // ----------------------------------------------------------------------------
526 // Function getArgumentTypeAsString()
527 // ----------------------------------------------------------------------------
528 
529 /*!
530  * @fn ArgParseArgument#getArgumentTypeAsString
531  * @headerfile <seqan/arg_parse.h>
532  * @brief Return argument type As a string.
533  *
534  * @signature std::string getArgumentTypeAsString(arg);
535  *
536  * @param[in] arg The ArgParseArgument to query.
537  *
538  * @return std::string The argument type as a STL string.
539  */
540 
getArgumentTypeAsString(ArgParseArgument const & me)541 inline std::string getArgumentTypeAsString(ArgParseArgument const & me)
542 {
543     return _typeToString(me._argumentType);
544 }
545 
546 // ----------------------------------------------------------------------------
547 // Function getArgumentLabel()
548 // ----------------------------------------------------------------------------
549 
550 /*!
551  * @fn ArgParseArgument#getArgumentLabel
552  * @headerfile <seqan/arg_parse.h>
553  * @brief Return argument label.
554  *
555  * @signature std::string getArgumentLabel(arg);
556  *
557  * @param[in] arg The ArgParseArgument to query.
558  *
559  * @return std::string The argument label as a STL string.
560  */
561 
getArgumentLabel(ArgParseArgument const & me)562 inline std::string getArgumentLabel(ArgParseArgument const & me)
563 {
564     return me._argumentLabel;
565 }
566 
567 // ----------------------------------------------------------------------------
568 // Helper Function _intervalAssert()
569 // ----------------------------------------------------------------------------
570 
571 // this methods ensures that the given arguments define a valid interval
572 // otherwise it will trigger a SEQAN_CHECK failure
573 template <typename TIntervalBorder>
_intervalAssert(const std::string minValueAsString,const std::string maxValueAsString)574 inline void _intervalAssert(const std::string minValueAsString, const std::string maxValueAsString)
575 {
576     if (minValueAsString != "" && maxValueAsString != "")
577         SEQAN_CHECK(_cast<TIntervalBorder>(minValueAsString) <= _cast<TIntervalBorder>(maxValueAsString),
578                     "The interval [%s:%s] is invalid. Please specify a valid interval.",
579                     minValueAsString.c_str(),
580                     maxValueAsString.c_str());
581 }
582 
583 // ----------------------------------------------------------------------------
584 // Function setMinValue()
585 // ----------------------------------------------------------------------------
586 
587 /*!
588  * @fn ArgParseArgument#setMinValue
589  * @headerfile <seqan/arg_parse.h>
590  * @brief Set smallest allowed value for the argument.
591  *
592  * @signature void setMinValue(arg, minValue);
593  *
594  * @param[in,out] arg      The ArgParseArgument to set the smallest value of.
595  * @param[in]     minValue The smallest value to set (<tt>std::string</tt>).
596  */
597 
setMinValue(ArgParseArgument & me,const std::string minValue)598 inline void setMinValue(ArgParseArgument & me, const std::string minValue)
599 {
600     if (isDoubleArgument(me))
601     {
602         SEQAN_CHECK(_isCastable<double>(minValue), "The maximal value for a double argument must be double.");
603         _intervalAssert<double>(minValue, me.maxValue);
604         me.minValue = minValue;
605     }
606     else if (isIntegerArgument(me))
607     {
608         SEQAN_CHECK(_isCastable<int>(minValue), "The maximal value for an integer argument must be an integer");
609         _intervalAssert<int>(minValue, me.maxValue);
610         me.minValue = minValue;
611     }
612     else if (isInt64Argument(me))
613     {
614         SEQAN_CHECK(_isCastable<int64_t>(minValue), "The maximal value for a 64 integer argument must be a 64 bit integer");
615         _intervalAssert<int64_t>(minValue, me.maxValue);
616         me.minValue = minValue;
617     }
618     else
619         SEQAN_FAIL("min/max values are not applicable to non numeric arguments");
620 }
621 
622 // ----------------------------------------------------------------------------
623 // Function setMaxValue()
624 // ----------------------------------------------------------------------------
625 
626 /*!
627  * @fn ArgParseArgument#setMaxValue
628  * @headerfile <seqan/arg_parse.h>
629  * @brief Set smallest allowed value for the argument.
630  *
631  * @signature void setMaxValue(arg, maxValue);
632  *
633  * @param[in,out] arg      The ArgParseArgument to set the smallest value of.
634  * @param[in]     maxValue The largest value to set (<tt>std::string</tt>).
635  */
636 
setMaxValue(ArgParseArgument & me,const std::string maxValue)637 inline void setMaxValue(ArgParseArgument & me, const std::string maxValue)
638 {
639     if (isDoubleArgument(me))
640     {
641         SEQAN_CHECK(_isCastable<double>(maxValue), "The maximal value for a double argument must be double.");
642         _intervalAssert<double>(me.minValue, maxValue);
643         me.maxValue = maxValue;
644     }
645     else if (isIntegerArgument(me))
646     {
647         SEQAN_CHECK(_isCastable<int>(maxValue), "The maximal value for an integer argument must be an integer");
648         _intervalAssert<int>(me.minValue, maxValue);
649         me.maxValue = maxValue;
650     }
651     else if (isInt64Argument(me))
652     {
653         SEQAN_CHECK(_isCastable<int>(maxValue), "The maximal value for a 64 bit integer argument must be an 64 bit integer");
654         _intervalAssert<int>(me.minValue, maxValue);
655         me.maxValue = maxValue;
656     }
657     else
658         SEQAN_FAIL("min/max values are not applicable to non numeric arguments");
659 }
660 
661 // ----------------------------------------------------------------------------
662 // Function setValidValues()
663 // ----------------------------------------------------------------------------
664 
665 /*!
666  * @fn ArgParseArgument#setValidValues
667  * @headerfile <seqan/arg_parse.h>
668  * @brief Set list of valid values.
669  *
670  * @signature void setValidValues(arg, values);
671  *
672  * @param[in,out] arg    The ArgParseArgument to set the valid values for.
673  * @param[in]     values Either a <tt>std::string</tt> containing all valid entries, separated by spaces or a
674  *                       <tt>std::vector&lt;std::string&gt;</tt> with the valid entries.
675  *
676  * If the argument is of type string then the list of valid values is the case-sensitive list of string values
677  * allowed for this argument.  If it is an input or output file then the list of valid values is a list of
678  * case-insentive file extensions identifying the allowed types.
679  *
680  * @section Examples
681  *
682  * An example of setting allowed values for a string option.
683  *
684  * @code{.cpp}
685  * seqan::ArgParseArgument stringArg(seqan::ArgParseArgument::STRING);
686  * setValidValues(stringArg, "one two three");  // one of {"one", "two", "three"}
687  *
688  * std::vector<std::string> values;
689  * values.push_back("four");
690  * values.push_back("five");
691  * setValidValues(stringArg, values);  // one of {"four", "five"}
692  * @endcode
693  *
694  * An example for an input file option.  Note that by changing <tt>INPUT_FILE</tt> to <tt>OUTPUT_FILE</tt> below,
695  * the example would be the same for output files.
696  *
697  * @code{.cpp}
698  * seqan::ArgParseArgument fileArg(seqan::ArgParseArgument::INPUT_FILE);
699  * setValidValues(fileArg, "fq fastq");  // file must end in ".fq" or ".fastq"
700  *
701  * std::vector<std::string> values;
702  * values.push_back("sam");
703  * values.push_back("bam");
704  * setValidValues(fileArg, values);  // file must end in ".sam" or ".bam"
705  * @endcode
706  */
707 
setValidValues(ArgParseArgument & me,std::vector<std::string> const & values)708 inline void setValidValues(ArgParseArgument & me, std::vector<std::string> const & values)
709 {
710     if (isDoubleArgument(me) || isIntegerArgument(me) || isBooleanArgument(me))
711         SEQAN_FAIL("ArgParseArgument does not support setting valid values for numeric or boolean arguments.");
712 
713     me.validValues = values;
714 }
715 
setValidValues(ArgParseArgument & me,std::string const & valuesString)716 inline void setValidValues(ArgParseArgument & me, std::string const & valuesString)
717 {
718     if (isDoubleArgument(me) || isIntegerArgument(me) || isBooleanArgument(me))
719         SEQAN_FAIL("ArgParseArgument does not support setting valid values for numeric or boolean arguments.");
720 
721     // convert array to String<std::string>
722     std::vector<std::string> values;
723     std::string current_argument;
724 
725     for (std::string::const_iterator ch  = valuesString.begin(); ch != valuesString.end(); ++ch)
726     {
727         if (*ch == ' ')
728         {
729             appendValue(values, current_argument);
730             current_argument = "";
731         }
732         else
733         {
734             append(current_argument, *ch);
735         }
736     }
737     if (current_argument != "")
738         appendValue(values, current_argument);
739 
740     setValidValues(me, values);
741 }
742 
743 // ----------------------------------------------------------------------------
744 // Function setHelpText()
745 // ----------------------------------------------------------------------------
746 
747 /*!
748  * @fn ArgParseArgument#setHelpText
749  * @headerfile <seqan/arg_parse.h>
750  * @brief Set the help text for an ArgParseArgument.
751  *
752  * @signature void setHelpText(arg, text);
753  *
754  * @param[in,out] arg  The ArgParseArgument to set the help text for.
755  * @param[in]     text The text to display as the description of the argument (<tt>std::string</tt>).
756  */
757 
setHelpText(ArgParseArgument & me,std::string const & text)758 inline void setHelpText(ArgParseArgument & me, std::string const & text)
759 {
760     me._helpText = text;
761 }
762 
763 // ----------------------------------------------------------------------------
764 // Helper Function _isInInterval()
765 // ----------------------------------------------------------------------------
766 
767 // check if the given value is in the provided interval
768 template <typename TTarget, typename TString>
_isInInterval(TString value,TString lowerIntervalBound,TString upperIntervalBound)769 inline bool _isInInterval(TString value, TString lowerIntervalBound, TString upperIntervalBound)
770 {
771     bool isInInterval = true;
772 
773     if (lowerIntervalBound != "")
774         isInInterval &= (_cast<TTarget>(lowerIntervalBound) <= _cast<TTarget>(value));
775     if (upperIntervalBound != "")
776         isInInterval &= (_cast<TTarget>(value) <= _cast<TTarget>(upperIntervalBound));
777 
778     return isInInterval;
779 }
780 
781 // ----------------------------------------------------------------------------
782 // Helper Function _checkNumericArgument()
783 // ----------------------------------------------------------------------------
784 
785 // test if the values can be assigned to the option and is in the given boundaries
786 template <typename TNumerical>
_checkNumericArgument(ArgParseArgument const & me,std::string const & value)787 inline void _checkNumericArgument(ArgParseArgument const & me, std::string const & value)
788 {
789     if (!_isCastable<TNumerical>(value))
790     {
791         std::stringstream what;
792         what << "the given value '" << value << "' cannot be casted to " << _typeToString(me);
793         SEQAN_THROW(ParseError(what.str()));
794     }
795 
796     if (!_isInInterval<TNumerical>(value, me.minValue, me.maxValue))
797     {
798         std::stringstream what;
799         what << "the given value '" << value << "' is not in the interval ["
800              << (me.minValue != "" ? me.minValue : "-inf") << ":"
801              << (me.maxValue != "" ? me.maxValue : "+inf") << "]";
802 
803         SEQAN_THROW(ParseError(what.str()));
804     }
805 }
806 
807 // ----------------------------------------------------------------------------
808 // Helper Function _compareExtension()
809 // ----------------------------------------------------------------------------
810 
_compareExtension(std::string const & str,std::string const & ext)811 inline bool _compareExtension(std::string const & str, std::string const & ext)
812 {
813     std::string str_ext = str.substr(str.size() - ext.size());
814     for (size_t i = 0; i < str_ext.size() && i < ext.size(); ++i)
815     {
816         if (tolower(str_ext[i]) != tolower(ext[i]))
817             return false;
818     }
819     return true;
820 }
821 
822 // ----------------------------------------------------------------------------
823 // Helper Function _checkStringRestrictions()
824 // ----------------------------------------------------------------------------
825 
826 // The parameter i gives the index of the value in the argument.
827 
_checkStringRestrictions(ArgParseArgument const & me,std::string const & value,unsigned i)828 inline void _checkStringRestrictions(ArgParseArgument const & me, std::string const &value,
829                                      unsigned i)
830 {
831     typedef std::vector<std::string>::const_iterator TVectorIterator;
832 
833     // we only check valid values for files and string arguments, but not for prefix arguments
834     if (!empty(me.validValues) && !(isInputPrefixArgument(me) || isOutputPrefixArgument(me)))
835     {
836         // The file name "-" is reserved for stdin or stdout
837         if ((isInputFileArgument(me) || isOutputFileArgument(me)) && value == "-")
838             return;
839 
840         // Allow the filename to be a pipe (without checking its extension)
841         if (isInputFileArgument(me) && _isPipe(value.c_str()))
842             return;
843 
844         bool isContained = false;
845         for (TVectorIterator validValue = me.validValues.begin();
846              validValue != me.validValues.end();
847              ++validValue)
848         {
849             // if it is an input or output file, we only check the file endings
850             if (isInputFileArgument(me) || isOutputFileArgument(me))
851             {
852                 if (length(*validValue) > length(getFileExtension(me, i)))
853                     continue;
854                 else
855                     isContained |= _compareExtension(getFileExtension(me, i), *validValue);
856             }
857             else
858             {
859                 isContained |= (*validValue == value);
860             }
861             if (isContained)
862                 break;
863         }
864         if (!isContained)
865         {
866             std::stringstream what;
867             if (isInputFileArgument(me) || isOutputFileArgument(me))
868                 what << "the given path '" << value << "' does not have one of the valid file extensions [";
869             else
870                 what << "the given value '" << value << "' is not in the list of allowed values [";
871             for (TVectorIterator validValue = me.validValues.begin();
872                  validValue != me.validValues.end();
873                  ++validValue)
874             {
875                 if (validValue != me.validValues.begin())
876                     what << ", ";
877                 what << ((isInputFileArgument(me) || isOutputFileArgument(me)) ? "*" : "") << *validValue;
878             }
879             what << "]";
880             if (i < me._fileExtensions.size())
881                 what << "; the file extension was overridden to be '" << getFileExtension(me, i) << "'";
882             SEQAN_THROW(ParseError(what.str()));
883         }
884     }
885 }
886 
887 // ----------------------------------------------------------------------------
888 // Helper Function _checkBooleanValidValues()
889 // ----------------------------------------------------------------------------
890 
_checkBooleanValidValues(ArgParseArgument const & me,std::string const & value)891 inline void _checkBooleanValidValues(ArgParseArgument const & me, std::string const & value)
892 {
893     SEQAN_ASSERT(isBooleanArgument(me));
894 
895     std::string value_up{value};
896     std::transform(value.begin(), value.end(), value_up.begin(), ::toupper); // allow for lowercase letters
897     bool isContained = (std::find(me.validValues.begin(), me.validValues.end(), value_up)
898                         != me.validValues.end());
899 
900     if (!isContained)
901     {
902         std::stringstream what;
903         what << "the given value '" << value << "' is not in the list of allowed values [";
904 
905         for (auto validValue = me.validValues.begin(); validValue != me.validValues.end(); ++validValue)
906         {
907             if (validValue != me.validValues.begin())
908                 what << ", ";
909             what << *validValue;
910         }
911         what << "]";
912         SEQAN_THROW(ParseError(what.str()));
913     }
914 }
915 
916 // ----------------------------------------------------------------------------
917 // Function _checkValue()
918 // ----------------------------------------------------------------------------
919 
920 // Check value before or after assignment.
921 //
922 // The parameter i gives the index of the value to check for overriding the extension in case of file arguments.
923 
924 inline void _checkValue(ArgParseArgument const & me, std::string val, unsigned i = 0)
925 {
926     // type checks
927     if (isIntegerArgument(me))
928         _checkNumericArgument<int>(me, val);
929 
930     if (isInt64Argument(me))
931         _checkNumericArgument<int64_t>(me, val);
932 
933     if (isDoubleArgument(me))
934         _checkNumericArgument<double>(me, val);
935 
936     // check valid values
937     if (isBooleanArgument(me))
938         _checkBooleanValidValues(me, val);
939 
940     if (isStringArgument(me))
941         _checkStringRestrictions(me, val, i);
942 }
943 
_checkValue(ArgParseArgument const & me)944 inline void _checkValue(ArgParseArgument const & me)
945 {
946     unsigned i = 0;
947     for (std::vector<std::string>::const_iterator it = me.value.begin(); it != me.value.end(); ++it, ++i)
948     {
949         auto val = *it;
950 
951         if (isDirectoryArgument(me)) // strip trailing slash for directories
952             if (val[length(val) - 1] == '/')
953                 val.resize(length(val) - 1);
954 
955         _checkValue(me, val, i);
956     }
957 }
958 
959 // ----------------------------------------------------------------------------
960 // Function _assignArgumentValue()
961 // ----------------------------------------------------------------------------
962 
_assignArgumentValue(ArgParseArgument & me,std::string const & value)963 inline void _assignArgumentValue(ArgParseArgument & me, std::string const & value)
964 {
965     // assignment
966     if (isListArgument(me)) // just append
967     {
968         appendValue(me.value, value, Exact());
969     }
970     else
971     {
972         // check if we already set all expected arguments
973         if (length(me.value) == me._numberOfValues)
974             clear(me.value);
975         appendValue(me.value, value, Exact());
976     }
977 }
978 
979 // ----------------------------------------------------------------------------
980 // Function getArgumentValue()
981 // ----------------------------------------------------------------------------
982 
983 /*!
984  * @fn ArgParseArgument#getArgumentValue
985  * @headerfile <seqan/arg_parse.h>
986  * @brief Return the value of the argument.
987  *
988  * @signature std::string getArgumentValue(arg[, argNo]);
989  *
990  * @param[in,out] arg   The ArgParseArgument to query.
991  * @param[in]     argNo In case that the ArgParseArgument allowed multiple values, give the index of the argument
992  *                      that you want to retrieve (<tt>unsigned</tt>, starts at 0).
993  *
994  * @return std::string Const-reference to the argument value.
995  */
996 
getArgumentValue(ArgParseArgument const & me,unsigned argNo)997 inline std::string const & getArgumentValue(ArgParseArgument const & me, unsigned argNo)
998 {
999     SEQAN_CHECK(argNo < me.value.size() || argNo < me.defaultValue.size(),
1000                 "ArgParseArgument: No value set for index %d", argNo);
1001 
1002     if (argNo < me.value.size())
1003         return me.value[argNo];
1004     else
1005         return me.defaultValue[argNo];
1006 }
1007 
getArgumentValue(ArgParseArgument const & me)1008 inline std::string const & getArgumentValue(ArgParseArgument const & me)
1009 {
1010     return getArgumentValue(me, 0);
1011 }
1012 
1013 // ----------------------------------------------------------------------------
1014 // Function getArgumentValues()
1015 // ----------------------------------------------------------------------------
1016 
1017 /*!
1018  * @fn ArgParseArgument#getArgumentValues
1019  * @headerfile <seqan/arg_parse.h>
1020  * @brief Return all values of the argument.
1021  *
1022  * @signature std::vector<std::string> getArgumentValue(arg);
1023  *
1024  * @param[in] arg   The ArgParseArgument to query.
1025  *
1026  * @return std::vector<std::string> Const-reference to the argument values.
1027  */
1028 
getArgumentValues(ArgParseArgument const & me)1029 inline std::vector<std::string> const & getArgumentValues(ArgParseArgument const & me)
1030 {
1031     if (!me.value.empty())
1032         return me.value;
1033     else
1034         return me.defaultValue;
1035 }
1036 
1037 // ----------------------------------------------------------------------------
1038 // Function hasArgumentValue()
1039 // ----------------------------------------------------------------------------
1040 
1041 /*!
1042  * @fn ArgParseArgument#hasArgumentValue
1043  * @headerfile <seqan/arg_parse.h>
1044  * @brief Return whether a value is available.
1045  *
1046  * @signature bool hasValue(arg[, pos]);
1047  *
1048  * @param[in] arg The ArgParseArgument to query.
1049  * @param[in] pos The position of the argument in case of being a list (<tt>unsigned</tt>, 0-based, default is 0).
1050  *
1051  * @return bool <tt>true</tt> if <tt>pos</tt> is less than the size and the argument is non-empty.
1052  */
1053 
hasValue(ArgParseArgument const & arg,unsigned position)1054 inline bool hasValue(ArgParseArgument const & arg, unsigned position)
1055 {
1056     return arg.value.size() > position || arg.defaultValue.size() > position;
1057 }
1058 
hasValue(ArgParseArgument const & arg)1059 inline bool hasValue(ArgParseArgument const & arg)
1060 {
1061     return hasValue(arg, 0);
1062 }
1063 
1064 // ----------------------------------------------------------------------------
1065 // Function isSet()
1066 // ----------------------------------------------------------------------------
1067 
1068 /*!
1069  * @fn ArgParseArgument#isSet
1070  * @headerfile <seqan/arg_parse.h>
1071  * @brief Returns true if a value was assigned to the argument.
1072  *
1073  * @signature bool isSet(arg):
1074  *
1075  * @param[in] arg The ArgParseArgument to query.
1076  *
1077  * @return bool <tt>true</tt> if a value was assigned, <tt>false</tt> otherwise.
1078  */
1079 
isSet(ArgParseArgument const & me)1080 inline bool isSet(ArgParseArgument const & me)
1081 {
1082     return !me.value.empty();
1083 }
1084 
1085 // ----------------------------------------------------------------------------
1086 // Function hasDefault()
1087 // ----------------------------------------------------------------------------
1088 
1089 /*!
1090  * @fn ArgParseArgument#hasDefault
1091  * @headerfile <seqan/arg_parse.h>
1092  * @brief Returns whether the argument has a default value.
1093  *
1094  * @signature bool hasDefault(arg);
1095  *
1096  * @param[in] arg The argument to query.
1097  *
1098  * @return bool <tt>true</tt> if the argument has a default value and <tt>false</tt> if not.
1099  */
1100 
hasDefault(ArgParseArgument const & me)1101 inline bool hasDefault(ArgParseArgument const & me)
1102 {
1103     return !me.defaultValue.empty();
1104 }
1105 
1106 // ----------------------------------------------------------------------------
1107 // Function numberOfArguments
1108 // ----------------------------------------------------------------------------
1109 
1110 /*!
1111  * @fn ArgParseArgument#numberOfAllowedValues
1112  * @headerfile <seqan/arg_parse.h>
1113  * @brief Returns the number of allowed values for this ArgParseArgument.
1114  *
1115  * @signature unsigned numberOfAllowedValues(arg);
1116  *
1117  * @param[in] arg The ArgParseArgument to query.
1118  *
1119  * @return unsigned The number of allowed values.
1120  */
1121 
numberOfAllowedValues(ArgParseArgument const & me)1122 inline unsigned numberOfAllowedValues(ArgParseArgument const & me)
1123 {
1124     return me._numberOfValues;
1125 }
1126 
1127 // ----------------------------------------------------------------------------
1128 // Function getFileExtension()
1129 // ----------------------------------------------------------------------------
1130 
1131 /*!
1132  * @fn ArgParseArgument#getFileExtension
1133  * @headerfile <seqan/arg_parse.h>
1134  * @brief Returns the file extension for the given file argument.
1135  *
1136  * Only valid when argument is an INPUT_FILE or OUTPUT_FILE.
1137  *
1138  * Halts the program if not an input or output file argument.
1139  *
1140  * Can be overridden with special hidden options.
1141  * For arguments, you can pass <tt>--arg-&lt;num&gt;-file-ext</tt> for argument <tt>num</tt>.
1142  * For parameters, you can pass <tt>--&lt;param-name&gt;-file-ext</tt> for the option named <tt>param-name</tt>.
1143  *
1144  * @signature std::string getFileExtension(arg[, pos]);
1145  *
1146  * @param[in] arg The ArgParseArgument to query.
1147  * @param[in] pos The position of the value to retrieve if multiple values (<tt>unsigned</tt>).
1148  *
1149  * @return std::string The file extension, empty if no extension or not set.
1150  */
1151 
1152 inline std::string getFileExtension(ArgParseArgument const & me, unsigned pos = 0)
1153 {
1154     if (!isInputFileArgument(me) && !isOutputFileArgument(me))
1155         SEQAN_FAIL("Cannot get file extension from non-file argument/option.");
1156 
1157     // Short-circuit to override file extension if set.
1158     if (pos < me._fileExtensions.size())
1159     {
1160         std::string result = me._fileExtensions[pos];
1161         if (!result.empty() && result[0] != '.')
1162             result.insert(0, ".");
1163         return result;
1164     }
1165 
1166     // Get argument value and break if empty.
1167     std::string value = getArgumentValue(me, pos);
1168     if (value.empty())
1169         return "";
1170 
1171     if (isDirectoryArgument(me)) // strip trailing slash for directories
1172         if (value[length(value) - 1] == '/')
1173             value.resize(length(value) - 1);
1174 
1175     // If there is a list of valid values then we look for each of these in the path.
1176     if (!me.validValues.empty())
1177     {
1178         for (unsigned i = 0; i < length(me.validValues); ++i)
1179         {
1180             unsigned len = std::min(me.validValues[i].size(), value.size());
1181             std::string tmp = value.substr(value.size() - len);
1182             std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
1183             if (tmp == me.validValues[i])
1184                 return me.validValues[i];
1185         }
1186         return "";
1187     }
1188 
1189     // Fall back to finding position of last dot.  Return suffix if found and empty string if not.
1190     size_t dotPos = value.find_last_of('.');
1191     if (dotPos == std::string::npos)
1192         return "";
1193     return value.substr(dotPos + 1);
1194 }
1195 
1196 } // namespace seqan
1197 
1198 #endif // SEQAN_INCLUDE_SEQAN_ARG_PARSE_ARG_PARSE_ARGUMENT_H_
1199