1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
|
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtDebug>
#include <QTextBoundaryFinder>
#include <QCoreApplication>
#include <QHash>
#include <QPair>
#include <QStringList>
#include <QTextStream>
#include <QUrl>
#include "qapplicationargument_p.h"
#include "qapplicationargumentparser_p.h"
QT_BEGIN_NAMESPACE
/*!
\class QApplicationArgumentParser
\brief The QApplicationArgumentParser class parses the command
line arguments for an application.
\reentrant
\internal
\since 4.4
QApplicationArgumentParser simplifies writing command line applications by taking care of:
\list
\o Generating help and version arguments
\o Taking care of converting arguments to QVariant types, since each argument
has a type: QApplicationArgument::type()
\o Validates the command line such that the user operates on well-defined input. For instance,
that the argument is a valid integer if that is the case, that an argument does not
occur more times than allowed, and so on.
\o Allows customization through sub-classing.
\endlist
The user declares what arguments that can be given to the application with QApplicationArgument. Provided
with that information, QApplicationArgumentParser takes care of parsing the actual
command line, appropriately flag errors, generate help messages, and provide
convenient access to the values of the arguments.
The way to use it is to create a set of QApplicationArgument by ones choosing, call
addArgument() for each, and subsequently call parse(). If parse() returns \c false,
the caller should exit and return exitCode().
If parse() returns \c true the command line was successfully parsed, its
values are well-defined, and they can be spectated with count(),
has(), value() and values().
\snippet doc/src/snippets/code/tools_patternist_qapplicationargumentparser.cpp 0
For arguments without a name(such as filename passed to the \c ls utility on Linux) add a
QApplicationArgument that does not have a name. The minimum and maximum occurrences will be
respected as usual and the type applies too.
QApplicationArgumentParser always has two options builtin: \c version and \c help.
\section1 Changing Parsing Convention
QApplicationArgumentParser by default parses the command line in the style
of Qt's utilities, where arguments are preceded by a single dash, and identified
by a single name. However, in some cases it might be of interest to parse
another style, such as the well-established UNIX \c getopt convention(\c -l
and \c --long).
This can be achieved by sub-classing QApplicationArgumentParser and reimplementing
parse(). It would do the following:
\list
\o Call input() to retrieve the strings the user specified on the command line.
\o Call declaredArguments() to retrieve the arguments that the implementor has
decided can be specified.
\o Parse and validate the input. Salt and pepper as per taste.
\o If an error occurred, call setExitCode() and return \c false.
\o Otherwise, call setExitCode(Success), provide access to the
arguments by calling setUsedArguments(), and return \c true. If a
help message was requested, call setExitCode(Success) and return \c false.
\endlist
\sa QApplicationArgument, QCoreApplication
*/
class QApplicationArgumentParserPrivate
{
Q_DECLARE_TR_FUNCTIONS(QApplicationArgumentParserPrivate)
public:
// TODO Isn't it like ten times better with QHash<QApplicationArgument, QList<QVariant> >?
// TODO test QApplicationArgument::nameless()
typedef QList<QPair<QApplicationArgument, QVariant> > UsedList;
/*!
We initialize exitCode to ParseError such that we consciously flag success.
*/
inline QApplicationArgumentParserPrivate(QApplicationArgumentParser *const master,
const QStringList &aInput) : exitCode(QApplicationArgumentParser::ParseError)
, input(aInput)
, q_ptr(master)
{
Q_ASSERT(!aInput.isEmpty());
}
QApplicationArgument nextNamelessArgument() const;
static QStringList argumentsFromLocal(const int argc, const char *const *const argv);
bool error(const QString &message);
static bool errorMessage(const QString &message);
static inline bool isSwitch(const QApplicationArgument &arg);
static inline QVariant conversionError(const QString &typeName,
const QString &input);
int count(const QApplicationArgument &arg) const;
bool contains(const QApplicationArgument &arg) const;
static inline bool isBuiltinVariant(const int type);
void displayVersion() const;
void displayHelp() const;
void parseNameless();
bool parseNamelessArguments(const QString &in);
QApplicationArgumentParser::ExitCode exitCode;
const QStringList input;
/*!
Since the QString is QApplicationArgument::name() anyway, why
not use a QSet?
*/
QHash<QString, QApplicationArgument> declaredArguments;
QList<QApplicationArgument> declaredNamelessArguments;
UsedList usedArguments;
QString applicationDescription;
QString applicationVersion;
private:
QApplicationArgumentParser *const q_ptr;
Q_DECLARE_PUBLIC(QApplicationArgumentParser)
static QString lineWrap(const QString &input,
const int leftIndent,
const int width);
static QList<QApplicationArgument> builtinArguments();
};
QApplicationArgument QApplicationArgumentParserPrivate::nextNamelessArgument() const
{
/* Count how many nameless arguments we have so far. */
int count = 0;
for(int i = 0; i < usedArguments.count(); ++i)
{
if(usedArguments.at(i).first.isNameless())
++count;
}
/* TODO this doesn't work for arguments that have more than one
* mandatory value(e.g nameless ones), since several values should
* then only count for one argument. */
for(int i = 0; i < declaredNamelessArguments.count(); ++i)
{
if(count)
{
/* Skip the ones we already have processed. */
--count;
continue;
}
if(declaredNamelessArguments.at(i).isNameless())
return declaredNamelessArguments.at(i);
}
return QApplicationArgument();
}
int QApplicationArgumentParserPrivate::count(const QApplicationArgument &arg) const
{
const int len = usedArguments.count();
int count = 0;
for(int i = 0; i < len; ++i)
{
if(usedArguments.at(i).first == arg)
++count;
}
return count;
}
/*!
Returns \c true if \a arg has appeared on the command line, not whether it has been declared.
*/
bool QApplicationArgumentParserPrivate::contains(const QApplicationArgument &arg) const
{
const int len = usedArguments.count();
for(int i = 0; i < len; ++i)
{
if(usedArguments.at(i).first == arg)
return true;
}
return false;
}
/*!
Returns always \c false.
*/
bool QApplicationArgumentParserPrivate::error(const QString &message)
{
exitCode = QApplicationArgumentParser::ParseError;
errorMessage(message);
return errorMessage(tr("Pass -help for information about the command line."));
}
/*!
Returns always \c false.
*/
bool QApplicationArgumentParserPrivate::errorMessage(const QString &message)
{
QTextStream out(stderr, QIODevice::WriteOnly);
out << message << endl;
return false;
}
/*!
\internal
Determines whether \a arg carries a value or is on/off.
*/
bool QApplicationArgumentParserPrivate::isSwitch(const QApplicationArgument &arg)
{
return arg.type() == QVariant::Invalid;
}
QVariant QApplicationArgumentParserPrivate::conversionError(const QString &typeName,
const QString &input)
{
errorMessage(tr("Cannot convert %1 to type %2.").arg(input, typeName));
return QVariant();
}
bool QApplicationArgumentParserPrivate::isBuiltinVariant(const int type)
{
return type < int(QVariant::UserType);
}
/*!
TODO Temporary, replace with a function in QCoreApplication.
*/
QStringList QApplicationArgumentParserPrivate::argumentsFromLocal(const int argc, const char *const *const argv)
{
Q_ASSERT(argc >= 1);
Q_ASSERT(argv);
QStringList result;
for(int i = 0; i < argc; ++i)
result.append(QString::fromLocal8Bit(argv[i]));
return result;
}
void QApplicationArgumentParserPrivate::displayVersion() const
{
QTextStream out(stderr);
out << tr("%1 version %2 using Qt %3").arg(QCoreApplication::applicationName(), applicationVersion, QString::fromAscii(qVersion()))
<< endl;
}
/*!
\internal
\relates QApplicationArgument
qLess() functor for QApplicationArgument that considers the name.
*/
template<>
class qLess <QApplicationArgument>
{
public:
inline bool operator()(const QApplicationArgument &o1,
const QApplicationArgument &o2) const
{
return o1.name().compare(o2.name()) < 0;
}
};
void QApplicationArgumentParserPrivate::displayHelp() const
{
enum Constants
{
/**
* When we want to line wrap, 80 minus a couple of characters. This should
* be suitable for vt100 compatible terminals.
*/
LineWrapAt = 78,
/**
* The initial " -" for each option.
*/
IndentPadding = 3,
/**
* Pad for the brackets and space we use when we have a type.
*/
ValueArgumentPadding = 4
};
QList<QApplicationArgument> args(declaredArguments.values());
args += builtinArguments();
/* Sort them, such that we get the nameless options at the end, and it
* generally looks tidy. */
qSort(args);
/* This is the basic approach:
* Switches:
* -name description
* Value arguments:
* -name <name-of-value-type> description
*
* Nameless arguments
* name <type> description
*
* It all line-wraps at OutputWidth and the description is indented,
* where the highest indent is the length of the name plus length of the name
* of the type. */
/* First we find the name with the largest width. */
int maxWidth = 0;
QList<QApplicationArgument> nameless(declaredNamelessArguments);
qSort(nameless);
/* Note, here the nameless arguments appear last, but are sorted
* with themselves. */
QList<QApplicationArgument> allArgs(args + nameless);
const int allArgsCount = allArgs.count();
for(int i = 0; i < allArgsCount; ++i)
{
const QApplicationArgument &at = allArgs.at(i);
const int nameLength = at.name().length();
const QString typeName(q_ptr->typeToName(at));
const int typeNameLength = typeName.length();
const int padding = at.type() == QVariant::Invalid ? 0 : ValueArgumentPadding;
maxWidth = qMax(maxWidth, nameLength + typeNameLength + padding);
}
QTextStream out(stderr);
out << endl
<< QString(IndentPadding, QLatin1Char(' '))
<< QCoreApplication::applicationName()
<< QLatin1String(" -- ")
<< applicationDescription
<< endl;
// TODO synopsis
/* One extra so we get some space between the overview and the options. */
out << endl;
const int indentWidth = maxWidth + 3;
/* Ok, print them out. */
for(int i = 0; i < allArgsCount; ++i)
{
const QApplicationArgument &at = allArgs.at(i);
/* " -name ". Indent a bit first, inspired by Qt's moc. */
const QString &name = at.name();
QString prolog(QLatin1String(" "));
/* We have a special case for the single dash. */
if(name == QChar::fromLatin1('-'))
prolog.append(name);
else
{
if(!at.isNameless())
prolog.append(QLatin1Char('-'));
prolog.append(name + QLatin1Char(' '));
}
if(at.type() != QVariant::Invalid)
{
/* It's not a switch, it has a value. */
/* Do we have a default value? If so, the argument is optional. */
const QString typeName(q_ptr->typeToName(at));
if(at.defaultValue().isValid())
prolog.append(QLatin1Char('[') + typeName + QLatin1Char(']'));
else
prolog.append(QLatin1Char('<') + typeName + QLatin1Char('>'));
// TODO Don't we want to display the default value?
prolog.append(QLatin1Char(' '));
}
prolog = prolog.leftJustified(indentWidth);
out << prolog
<< lineWrap(at.description(), indentWidth, LineWrapAt)
<< endl;
}
}
/*!
Line wraps \a input and indents each line with \a leftIndent spaces, such that
the width does not go beyond \a maxWidth.
The addition of line endings is accounted for by the caller.
With QTextBoundaryFinder our line wrapping is relatively fancy, since it
does it the Unicode-way.
*/
QString QApplicationArgumentParserPrivate::lineWrap(const QString &input,
const int leftIndent,
const int maxWidth)
{
const QString indent(QString(leftIndent, QLatin1Char(' ')));
const int len = input.length();
const int textWidth = maxWidth - leftIndent;
QString output;
QTextBoundaryFinder wrapFinder(QTextBoundaryFinder::Line, input);
wrapFinder.setPosition(textWidth);
if(input.length() + leftIndent <= maxWidth)
return input;
int from = wrapFinder.toPreviousBoundary();
output.append(input.left(from));
while(true)
{
if((len - from) + leftIndent > maxWidth)
{
/* We need to line wrap. */
wrapFinder.setPosition(from + textWidth);
const int currentWidthPos = wrapFinder.toPreviousBoundary();
output.append(QLatin1Char('\n'));
output.append(indent);
output.append(input.mid(from, currentWidthPos - from).trimmed());
from += (currentWidthPos - from);
}
else
{
/* Append the remains. */
output.append(QLatin1Char('\n'));
output.append(indent);
output.append(input.mid(from).trimmed());
break;
}
}
return output;
}
/*!
Returns a list with the builtin options that the parser has
*/
QList<QApplicationArgument> QApplicationArgumentParserPrivate::builtinArguments()
{
QList<QApplicationArgument> result;
result.append(QApplicationArgument(QLatin1String("help"),
QLatin1String("Displays this help.")));
result.append(QApplicationArgument(QLatin1String("version"),
QLatin1String("Displays version information.")));
result.append(QApplicationArgument(QLatin1String("-"),
QLatin1String("When appearing, any following options are not interpreted as switches.")));
return result;
}
/* TODO, I don't think we want this function in a public API. Add it first when there is a demand. */
/*!
Creates a QApplicationArgumentParser that will parse the input in \a argc and \a argv.
These arguments should be passed directly from the \c main() function, and the decoding
of the input will be taken care of appropriately, depending on platform.
It is preferred to use the QStringList overload, in case the input is in the form of QStrings.
*/
QApplicationArgumentParser::QApplicationArgumentParser(int argc, char **argv) : d(new QApplicationArgumentParserPrivate(this, QApplicationArgumentParserPrivate::argumentsFromLocal(argc, argv)))
{
Q_ASSERT_X(argv, Q_FUNC_INFO, "Argv cannot be null.");
Q_ASSERT_X(argc >= 1, Q_FUNC_INFO,
"argc must at least contain the application name. "
"Use the QStringList overload instead.");
}
/*!
\overload
Creates a QApplicationArgumentParser that will parse \a input. That is, instead of passing in \c argc
and \c argv, one can pass in a QStringList.
The caller guarantees that the first string in \a input is the name of the application.
*/
QApplicationArgumentParser::QApplicationArgumentParser(const QStringList &input) : d(new QApplicationArgumentParserPrivate(this, input))
{
Q_ASSERT_X(input.count() >= 1, Q_FUNC_INFO,
"The input must at least contain the application name.");
}
/*!
This function is only of interest when subclassing.
Returns the strings that the user specified when starting the application. The first string
in the list is always the application name.
*/
QStringList QApplicationArgumentParser::input() const
{
Q_ASSERT_X(d->input.count() >= 1, Q_FUNC_INFO, "Internal error, this should always hold true");
return d->input;
}
/*!
This function is only of interest when subclassing.
Sets the arguments that the user actually used on the command line to \a arguments.
The parse() function should call this, such that the result afterwards can be inspected
with for instance has() or count().
\sa usedArguments()
*/
void QApplicationArgumentParser::setUsedArguments(const QList<QPair<QApplicationArgument, QVariant> > &arguments)
{
d->usedArguments = arguments;
}
/*!
This function is only of interest when subclassing.
Returns the arguments that the user used on the command line.
\sa setUsedArguments()
*/
QList<QPair<QApplicationArgument, QVariant> > QApplicationArgumentParser::usedArguments() const
{
return d->usedArguments;
}
/*!
Destructs this QApplicationArgumentParser instance.
*/
QApplicationArgumentParser::~QApplicationArgumentParser()
{
delete d;
}
/*!
Adds \a argument to this parser.
This function is provided for convenience. It is equivalent to creating a QList
containing \a argument, append the existing arguments, and then call setDeclaredArguments() with the list.
\sa setDeclaredArguments()
*/
void QApplicationArgumentParser::addArgument(const QApplicationArgument &argument)
{
if(argument.isNameless())
d->declaredNamelessArguments.append(argument);
else
d->declaredArguments.insert(argument.name(), argument);
}
/*!
Makes the parser recognize all arguments in \a arguments.
Any arguments previously set, are discarded.
\sa addArgument(), declaredArguments()
*/
void QApplicationArgumentParser::setDeclaredArguments(const QList<QApplicationArgument> &arguments)
{
// TODO If we have a QHash internally, why not use it in the public API too?
const int len = arguments.count();
for(int i = 0; i < len; ++i)
d->declaredArguments.insert(arguments.at(i).name(), arguments.at(i));
}
/*!
Returns the arguments that this parser recognizes.
\sa addArgument(), setDeclaredArguments()
*/
QList<QApplicationArgument> QApplicationArgumentParser::declaredArguments() const
{
return d->declaredArguments.values();
}
bool QApplicationArgumentParserPrivate::parseNamelessArguments(const QString &in)
{
/* It's a nameless options, such as simply "value". */
const QApplicationArgument nameless(nextNamelessArgument());
const QVariant val(q_ptr->convertToValue(nameless, in));
if(val.isValid())
{
usedArguments.append(qMakePair(nameless, val));
return true;
}
else
return false; // TODO error msg?
}
/*!
Parses input() together with declaredArguments() and returns \c false if the caller
should exit immediately, which is the case of which an error was encountered or
help or the version was requested.
In the case of \c true was returned, valid arguments were supplied, and they can
be requested with functions like value(), values(), count() and has().
parse() must only be called once per QApplicationArgumentParser instance. The
second time it's called, the effects and return value are undefined.
\sa convertToValue(), typeToName()
*/
bool QApplicationArgumentParser::parse()
{
const QChar sep(QLatin1Char('-'));
const int inputCount = d->input.count();
/* We skip the first entry, which is the application name. */
int i = 1;
for(; i < inputCount; ++i)
{
const QString &in = d->input.at(i);
/* We have a single '-', signalling that the succeeding are not options. */
if(in == sep)
{
++i;
for(; i < inputCount; ++i)
{
if(!d->parseNamelessArguments(d->input.at(i)))
return false;
/* Process nameless options. Have code for this elsewhere, factor it out. */
}
break;
}
if(in.startsWith(sep)) /* It is "-name". */
{
const QString name(in.mid(1));
if(name == QLatin1String("help"))
{
setExitCode(Success);
d->displayHelp();
return false;
}
else if(name == QLatin1String("version"))
{
setExitCode(Success);
d->displayVersion();
return false;
}
if(!d->declaredArguments.contains(name))
return d->error(QApplicationArgumentParserPrivate::tr("\"%1\" is an unknown argument.").arg(name));
const QApplicationArgument &arg = d->declaredArguments.value(name);
const int argCount = d->count(arg) + 1;
const int max = arg.maximumOccurrence();
if(argCount > max && max != -1)
{
/* Let's tailor the message for a common case. */
if(max == 1)
return d->error(QApplicationArgumentParserPrivate::tr("\"%1\" can only be used once.").arg(name));
else
return d->error(QApplicationArgumentParserPrivate::tr("\"%1\" can only be used %2 times.").arg(name, QString::number(max)));
}
if(QApplicationArgumentParserPrivate::isSwitch(arg))
{
d->usedArguments.append(qMakePair(arg, QVariant()));
continue;
}
else
{
++i;
if(i == inputCount)
return d->error(QApplicationArgumentParserPrivate::tr("\"%1\" must be followed by a value.").arg(name));
/* Okidoki, got a value, always something. Let's
* see if it validates. */
const QString &value = d->input.at(i);
const QVariant val(convertToValue(arg, value));
if(val.isValid())
{
d->usedArguments.append(qMakePair(arg, val));
continue;
}
else
return false; // TODO error msg?
}
}
else
{
if(!d->parseNamelessArguments(in))
return false;
}
}
/* Check that all arguments that have been declared as mandatory, are actually
* specified. */
const QList<QApplicationArgument> declaredArguments(d->declaredArguments.values() + d->declaredNamelessArguments);
const int len = declaredArguments.count();
for(int i = 0; i < len; ++i)
{
const QApplicationArgument &at = declaredArguments.at(i);
const int min = at.minimumOccurrence();
const int max = at.maximumOccurrence(); // TODO What about infinite? -1
if(min == 0)
continue;
else
{
const int usedLen = d->usedArguments.count();
int useCount = 0;
for(int u = 0; u < usedLen; ++u)
{
const QPair<QApplicationArgument, QVariant> &used = d->usedArguments.at(u);
if(used.first == at)
++useCount;
}
const QString originalName(at.name());
const QString effectiveName(originalName.isEmpty() ? QLatin1Char('<') + typeToName(at) + QLatin1Char('>') : originalName);
if(useCount < min)
{
/* For nameless options, we use the type as the name. Looks better. */
return d->error(QApplicationArgumentParserPrivate::tr("%1 must occur at least %2 times, therefore %3 times is insufficient.", "The number is for %2.", min)
.arg(effectiveName, QString::number(min), QString::number(useCount)));
}
else if(useCount > max)
return d->error(QApplicationArgumentParserPrivate::tr("%1 can occur at most %2 times", "", max).arg(effectiveName, QString::number(max)));
}
}
d->exitCode = Success;
return true;
}
/*!
This function is only of interest when subclassing.
parse() calls this function each time a value, that is \a input, on the command line needs to be
validated and subsequently converted to the type of \a argument. A descriptive error message will
be outputted if \a input cannot be converted to the required type.
The default implementation uses QVariant::canConvert() and QVariant::convert() for doing conversions.
QApplicationArgumentParser can be subclassed and this function subsequently overridden, to handle custom types.
If \a input isn't valid input for \a argument, this function returns a default constructed
QVariant.
\sa typeToName(), parse()
*/
QVariant QApplicationArgumentParser::convertToValue(const QApplicationArgument &argument,
const QString &input) const
{
const int type = argument.type();
switch(type)
{
case QVariant::Bool:
{
if(input == QLatin1String("true") || input == QChar::fromLatin1('1'))
return QVariant(true);
else if(input == QLatin1String("false") || input == QChar::fromLatin1('0'))
return QVariant(false);
else
return QApplicationArgumentParserPrivate::conversionError(typeToName(argument), input);
}
case QVariant::RegExp:
{
const QRegExp exp(input);
if(exp.isValid())
return QVariant(exp);
else
return QApplicationArgumentParserPrivate::conversionError(typeToName(argument), input);
}
case QVariant::Url:
{
const QUrl result(QUrl::fromEncoded(input.toLatin1()));
if(result.isValid())
return QVariant(result);
else
return QApplicationArgumentParserPrivate::conversionError(typeToName(argument), input);
}
default:
{
QVariant result(input);
if(QApplicationArgumentParserPrivate::isBuiltinVariant(type) &&
result.convert(QVariant::Type(type)))
return result;
else
return QApplicationArgumentParserPrivate::conversionError(typeToName(argument), input);
}
}
}
/*!
This function is only of interest when subclassing.
convertToValue() calls this function when requiring a string for referring to \a type,
when generating user messages.
The implementation uses QVariant::typeToName() for most types, but special handles
some types, in order to let the message be better tailored for humans.
\sa convertToValue()
*/
QString QApplicationArgumentParser::typeToName(const QApplicationArgument &argument) const
{
/* Personally I think nameForType() would be a better name but this is consistent
* with QVariant's function of the same name. */
const int type = argument.type();
switch(type)
{
case QVariant::RegExp:
return QApplicationArgumentParserPrivate::tr("regular expression");
case QVariant::Url:
return QLatin1String("URI");
case QVariant::String:
return QLatin1String("string");
default:
{
if(QApplicationArgumentParserPrivate::isBuiltinVariant(type))
return QString::fromLatin1(QVariant::typeToName(QVariant::Type(type)));
else
return QLatin1String(QVariant(type, static_cast<void *>(0)).typeName());
}
}
}
/*!
Returns the default value for \a argument. The default implementation returns
QApplicationArgument::defaultValue(), if \a argument has been added to this parser.
Overriding this function can be useful if creating the default value is resource
consuming, such as opening a file.
*/
QVariant QApplicationArgumentParser::defaultValue(const QApplicationArgument &argument) const
{
return d->declaredArguments.value(argument.name()).defaultValue();
}
/*!
Returns the count of how many times \a argument was used on the command line.
\sa has()
*/
int QApplicationArgumentParser::count(const QApplicationArgument &argument) const
{
Q_ASSERT_X(d->declaredArguments.contains(argument.name()) ||
d->declaredNamelessArguments.contains(argument), Q_FUNC_INFO,
"The argument isn't known to the parser. Has addArgument() been called?");
return d->count(argument);
}
/*!
Returns \c true if \a argument has been
specified one or more times on the command line, otherwise \a false.
\sa count()
*/
bool QApplicationArgumentParser::has(const QApplicationArgument &argument) const
{
Q_ASSERT_X(d->declaredArguments.contains(argument.name()) ||
d->declaredNamelessArguments.contains(argument), Q_FUNC_INFO,
"The argument isn't known to the parser. Has addArgument() been called?");
return d->contains(argument);
}
/*!
// TODO docs
\sa values()
*/
QVariant QApplicationArgumentParser::value(const QApplicationArgument &argument) const
{
Q_ASSERT_X(d->declaredArguments.contains(argument.name()) ||
d->declaredNamelessArguments.contains(argument), Q_FUNC_INFO,
"The argument isn't known to the parser. Has addArgument() been called?");
const int len = d->usedArguments.count();
for(int i = 0; i < len; ++i)
{
if(d->usedArguments.at(i).first == argument)
return d->usedArguments.at(i).second;
}
return defaultValue(argument);
}
/*!
// TODO docs
\sa value()
*/
QVariantList QApplicationArgumentParser::values(const QApplicationArgument &argument) const
{
Q_ASSERT_X(d->declaredArguments.contains(argument.name()) ||
d->declaredNamelessArguments.contains(argument),
Q_FUNC_INFO,
"The argument isn't known to the parser. Has addArgument() been called?");
const int len = d->usedArguments.count();
QVariantList result;
for(int i = 0; i < len; ++i)
{
if(d->usedArguments.at(i).first == argument)
result.append(d->usedArguments.at(i).second);
}
// TODO how do we handle default values?
return result;
}
/*!
After parse() has been called, this function returns a code that can be used to
exit \c main() with. It returns zero upon success or if help was requested, and
otherwise a value signalling failure.
*/
QApplicationArgumentParser::ExitCode QApplicationArgumentParser::exitCode() const
{
return d->exitCode;
}
/*!
This function is only of interest when subclassing.
Makes exitCode() return \a code.
*/
void QApplicationArgumentParser::setExitCode(ExitCode code)
{
d->exitCode = code;
}
/*!
Sets the application description to \a description.
The application description is a sentence or two used for help and version
messages, that briefly describes the application.
The default is the empty string.
*/
void QApplicationArgumentParser::setApplicationDescription(const QString &description)
{
d->applicationDescription = description;
}
/*!
Sets the application version to \a version.
This string, which is arbitrary but typically is "1.0" or so, is used when
generating a version statement.
*/
void QApplicationArgumentParser::setApplicationVersion(const QString &version)
{
d->applicationVersion = version;
}
/*!
Writes out \a message to \c stderr.
*/
void QApplicationArgumentParser::message(const QString &message) const
{
d->errorMessage(message);
}
QT_END_NAMESPACE
|