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<std::string></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-<num>-file-ext</tt> for argument <tt>num</tt>.
1142 * For parameters, you can pass <tt>--<param-name>-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