I. Introduction▲
La plupart des programmes qu'on écrit ont besoin de paramètres en entrée. En java, c'est relativement simple de les gérer, mais c'est toujours un peu rébarbatif. Dans ce tutoriel, nous allons voir que cette tâche peut être finalement assez simple et rapide à l'aide d'une bibliothèque comme Common CLI.
Les codes sources complets des classes importantes sont proposés en annexe.
I-A. Avant de commencer▲
Pour écrire ce tutoriel, j'ai utilisé les éléments suivants :
- JDK 1.8.0_45 ;
- Eclipse 4.6 (Neon) ;
- Apache Common CLI 1.3.1.
II. Découverte du projet d'exemple▲
II-A. Télécharger, installer et importer le projet d'exemple▲
Durée estimée : 1 minute.
Pour commencer, je vous propose de télécharger le fichier Zip article-common-cli-5-min-01.zip, contenant un projet Java-Maven d'exemple.
Compilez le projet d'exemple et importez-le dans votre IDE favori.
Pour suivre ce tutoriel, vous pouvez vous contenter de lire les codes proposés ci-dessous (codes complets en annexe).
II-B. Fibonacci▲
Il fallait un exemple pour illustrer cet article. Je vous propose d'utiliser la suite de Fibonacci que vous ne connaissez sans doute que trop bien. Si ce n'est pas le cas, je vous invite à consulter la page Wikipédia consacrée à la suite de Fibonacci.
Le projet contient plusieurs algorithmes pour calculer les membres de la suite de Fibonacci ainsi que les tests unitaires qui vont de paire. Jetez-y un œil par curiosité, mais ne vous attardez pas trop sur les bogues. Ces classes implémentent l'interface FibonacciCalculator suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
public
interface
FibonacciCalculator {
/**
* Calcule la valeur du membre de la suite de Fibonacci pour le rang n.
*
*
@param
n
* rang positif
*
@return
valeur du membre pour le rand indiqué.
*
@throws
IllegalArgumentException
* si le rang est négatif
*/
long
calculate
(
final
int
n);
II-C. Ce qui est déjà dans l'exemple▲
Le programme d'exemple est une simple classe dotée d'une méthode main(). Les paramètres de configuration (algorithme, rang, timer, verbose) des algorithmes de calcul sont directement codés dans la méthode main() :
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.
public
class
Fibo {
public
static
void
main
(
String[] args) {
// Algo : recursif (r) / memoization (m) / terminal (t) / iteratif (i) / direct (d)
char
algo =
'i'
;
// Rang
int
n =
42
;
// Timer
boolean
timerMode =
false
;
// Verbose
boolean
verboseMode =
true
;
if
(
verboseMode) {
timerMode =
true
;
}
// Calculator
FibonacciCalculator calc =
null
;
String algoName =
null
;
switch
(
algo) {
// recursif (r)
case
'r'
:
calc =
new
RecursifFibonnacciCalculator
(
);
algoName =
"Recursif"
;
break
;
...
}
if
(
verboseMode) {
System.out.println
(
"Calculating Fibonacci for n: "
+
n);
System.out.println
(
"Selected algorithm: "
+
algoName);
}
final
long
time1 =
System.currentTimeMillis
(
);
final
long
result =
calc.calculate
(
n);
final
long
time2 =
System.currentTimeMillis
(
);
if
(
timerMode) {
System.out.println
(
"Duration: "
+
(
time2 -
time1) +
" ms"
);
}
if
(
verboseMode) {
System.out.println
(
"Fibonacci("
+
n +
")="
+
result);
}
System.out.println
(
result);
}
}
Vous excuserez les println de ce bout de code… Ils seront largement suffisants pour ce qu'on a à voir.
Quand on lance l'application, on doit avoir un affichage ressemblant au bloc suivant :
Calculating Fibonacci for
n: 42
Selected algorithm: Iteratif
Duration: 0
ms
Fibonacci
(
42
)=
433494437
433494437
III. À la main▲
Durée estimée : 2 minutes.
On voudrait pouvoir lancer le programme et lui passer des arguments comme dans l'exemple suivant :
FiboManual --algo
=
r --rang
=
42
--timer
=
true --verbose
=
true
Il est facile de coder une première version naïve pour utiliser les valeurs passées en paramètre au lancement du programme. Je vous propose d'utiliser une HashMap toute simple :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
private
static
Map<
String, String>
parseParameters
(
final
String[] args) {
final
Map<
String, String>
parameters =
new
HashMap<>(
);
for
(
final
String arg : args) {
if
(!
arg.startsWith
(
"--"
)) {
System.err.println
(
"Bad parameter: "
+
arg);
System.exit
(
2
);
}
final
String[] tab =
arg.substring
(
2
).split
(
"="
);
parameters.put
(
tab[0
], tab[1
]);
}
return
parameters;
}
Cette méthode a l'avantage de traiter les paramètres sans imposer ni d'ordre ni de présence. Dans une vraie version, on pensera à gérer les exceptions.
Il suffit d'appeler la méthode pour traiter les paramètres :
2.
3.
4.
5.
6.
7.
public
class
FiboManual {
public
static
void
main
(
final
String[] args) {
final
Map<
String, String>
parameters =
parseParameters
(
args);
...
Pour utiliser les valeurs, il suffit de les prendre depuis la map :
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.
final
Map<
String, String>
parameters =
parseParameters
(
args);
// Algo : recursif (r) / memoization (m) / terminal (t) / iteratif (i) / direct (d)
char
algo =
'i'
;
final
String algoFromParamaters =
parameters.get
(
"algo"
);
if
(
algoFromParamaters !=
null
) {
algo =
algoFromParamaters.charAt
(
0
);
}
// Rang
final
String rangFromParamaters =
parameters.get
(
"rang"
);
int
n =
0
;
try
{
n =
Integer.valueOf
(
rangFromParamaters);
}
catch
(
Exception e) {
System.err.println
(
"Bad parameter: algo"
);
System.exit
(
3
);
}
// Timer
boolean
timerMode =
false
;
final
String timerFromParamaters =
parameters.get
(
"timer"
);
if
(
timerFromParamaters !=
null
) {
timerMode =
timerFromParamaters.equalsIgnoreCase
(
"true"
);
}
// Verbose
boolean
verboseMode =
true
;
final
String verboseFromParamaters =
parameters.get
(
"verbose"
);
if
(
verboseFromParamaters !=
null
) {
verboseMode =
verboseFromParamaters.equalsIgnoreCase
(
"true"
);
}
Le code du projet à ce stade est disponible dans le fichier article-common-cli-5-min-02.zip
On voit bien qu'une bonne partie du code va se répéter dans tous les exemples de ce tutoriel. On va donc le factoriser un peu dans une classe abstraite :
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.
public
abstract
class
AbstractFibo {
protected
static
void
work
(
final
int
n, final
FibonacciCalculator calc, final
boolean
timerMode, final
boolean
verboseMode) {
if
(
verboseMode) {
System.out.println
(
"Calculating Fibonacci for n: "
+
n);
System.out.println
(
"Selected algorithm: "
+
calc.getName
(
));
}
final
long
time1 =
System.currentTimeMillis
(
);
final
long
result =
calc.calculate
(
n);
final
long
time2 =
System.currentTimeMillis
(
);
if
(
timerMode) {
System.out.println
(
"Duration: "
+
(
time2 -
time1) +
" ms"
);
}
if
(
verboseMode) {
System.out.println
(
"Fibonacci("
+
n +
")="
+
result);
}
System.out.println
(
result);
}
protected
static
FibonacciCalculator getCalculator
(
final
char
algo) {
switch
(
algo) {
// recursif (r)
case
'r'
:
return
new
RecursifFibonnacciCalculator
(
);
...
}
return
null
;
}
}
Du coup, le code de l'exemple se simplifie comme suit :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
public
class
FiboManual extends
AbstractFibo {
public
static
void
main
(
final
String[] args) {
...
// Calculator
final
FibonacciCalculator calc =
getCalculator
(
algo);
// Work
work
(
n, calc, timerMode, verboseMode);
}
Le code du projet à ce stade est disponible dans le fichier article-common-cli-5-min-03.zip
Ce code n'est pas très difficile à écrire, mais on en voit bien les limites.
IV. Action avec Common CLI▲
La bibliothèque Common CLI va nous permettre d'aller plus loin et plus vite. Pour commencer, plutôt que de passer les arguments sous la forme « key=value », on voudrait utiliser le même format que sous Linux :
FiboCli --algo r --rang 42
--timer --verbose
ou en raccourci :
FiboCli -a r -r 42
-t -v
ou même un mélange :
FiboCli --algo -r --rang 42
-t -v
Vous remarquez que certains paramètres, comme « -v » pour « verbose », n'ont pas de valeur. Quand le paramètre est présent, sa valeur vaut implicitement « true », ce qui est bien pratique et plus simple.
IV-A. Ajout d'Apache Common CLI▲
Durée estimée : 1 minute.
Pour profiter de la bibliothèque « Apache Common CLI », vous devez ajouter des dépendances dans le fichier « pom.xml » :
2.
3.
4.
5.
<dependency>
<groupId>
commons-cli</groupId>
<artifactId>
commons-cli</artifactId>
<version>
1.3.1</version>
</dependency>
IV-B. Configuration▲
Durée estimée : 1 minute.
Pour faire simple, la définition d'un paramètre ressemble au code suivant :
2.
3.
4.
5.
6.
7.
Option algoFileOption =
Option.builder
(
"a"
)
.longOpt
(
"algo"
)
.desc
(
"Bla bla bla"
)
.hasArg
(
true
)
.argName
(
"algo"
)
.required
(
false
)
.build
(
);
Dans notre cas, on va donc écrire quatre blocs, correspondant aux quatre options de lancement :
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.
private
static
Options configParameters
(
) {
final
Option algoFileOption =
Option.builder
(
"a"
)
.longOpt
(
"algo"
) //
.desc
(
"Algorithm: recursif (r) / memoization (m) / terminal (t) / iteratif (i) / direct (d)"
)
.hasArg
(
true
)
.argName
(
"algo"
)
.required
(
false
)
.build
(
);
final
Option rangFileOption =
Option.builder
(
"r"
)
.longOpt
(
"rang"
)
.desc
(
"Rang"
)
.hasArg
(
true
)
.argName
(
"numeric"
)
.required
(
true
)
.build
(
);
final
Option timerFileOption =
Option.builder
(
"t"
)
.longOpt
(
"timer"
)
.desc
(
"Timer"
)
.hasArg
(
false
)
.required
(
false
)
.build
(
);
final
Option verboseFileOption =
Option.builder
(
"v"
)
.longOpt
(
"verbose"
)
.desc
(
"Verbose"
)
.hasArg
(
false
)
.required
(
false
)
.build
(
);
final
Options options =
new
Options
(
);
options.addOption
(
algoFileOption);
options.addOption
(
rangFileOption);
options.addOption
(
timerFileOption);
options.addOption
(
verboseFileOption);
return
options;
}
L'ordre de déclaration des options n'a pas d'importance.
IV-C. Utilisation▲
Durée estimée : 2 minutes.
Une fois qu'on a défini les options, il faut parser les paramètres. Il existe plusieurs parsers, mais celui par défaut fera bien l'affaire dans le cadre de ce tutoriel :
2.
3.
4.
final
Options options =
configParameters
(
);
final
CommandLineParser parser =
new
DefaultParser
(
);
final
CommandLine line =
parser.parse
(
options, args);
Ensuite, on utilisera surtout line.getOptionValue(..) et line.hasOption(..) pour récupérer les valeurs.
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.
public
class
FiboCli extends
AbstractFibo {
public
static
void
main
(
final
String[] args) throws
ParseException {
final
Options options =
configParameters
(
);
final
CommandLineParser parser =
new
DefaultParser
(
);
final
CommandLine line =
parser.parse
(
options, args);
// Algo : recursif (r) / memoization (m) / terminal (t) / iteratif (i) / direct (d)
char
algo =
line.getOptionValue
(
"algo"
, "i"
).charAt
(
0
);
// Rang
final
String rangFromParamaters =
line.getOptionValue
(
"rang"
, ""
);
int
n =
0
;
try
{
n =
Integer.valueOf
(
rangFromParamaters);
}
catch
(
Exception e) {
System.err.println
(
"Bad parameter: algo"
);
System.exit
(
3
);
}
// Timer
boolean
timerMode =
line.hasOption
(
"timer"
);
// Verbose
boolean
verboseMode =
line.hasOption
(
"verbose"
);
if
(
verboseMode) {
timerMode =
true
;
}
IV-D. Option --help▲
Durée estimée : 5 minutes (il y a un piège).
On va aller un peu plus loin en ajoutant l'option de lancement permettant d'afficher l'aide.
2.
3.
4.
final
Option helpFileOption =
Option.builder
(
"h"
)
.longOpt
(
"help"
)
.desc
(
"Affiche le message d'aide"
)
.build
(
);
On ne va pas pouvoir ajouter simplement cette option à la liste d'options ; il y a un piège. En effet, on veut que l'aide s'affiche lorsqu'on utilise l'option « -h » et que le programme s'arrête. Or l'option d'aide est facultative alors que d'autres options, comme le choix du rang, sont obligatoires. Il faut donc mettre en place une petite mécanique.
Pour commencer, il faut définir deux groupes d'options. On mettra l'option d'aide dans le premier :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
private
static
Options configFirstParameters
(
) {
final
Option helpFileOption =
Option.builder
(
"h"
)
.longOpt
(
"help"
)
.desc
(
"Affiche le message d'aide"
)
.build
(
);
final
Options firstOptions =
new
Options
(
);
firstOptions.addOption
(
helpFileOption);
return
firstOptions;
}
Et on pourra recopier les options du premier groupe vers le second :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
private
static
Options configParameters
(
final
Options firstOptions) {
...
final
Options options =
new
Options
(
);
// First Options
for
(
final
Option fo : firstOptions.getOptions
(
)) {
options.addOption
(
fo);
}
// All other options
options.addOption
(
algoFileOption);
options.addOption
(
rangFileOption);
options.addOption
(
timerFileOption);
options.addOption
(
verboseFileOption);
return
options;
}
Au niveau de la méthode main(), il n'y a qu'à enchaîner les deux appels :
2.
3.
4.
5.
6.
7.
public
class
FiboCli extends
AbstractFibo {
public
static
void
main
(
final
String[] args) throws
ParseException {
// Options
final
Options firstOptions =
configFirstParameters
(
);
final
Options options =
configParameters
(
firstOptions);
L'idée, dans un premier temps, est de ne parser que le premier groupe.
2.
3.
4.
5.
6.
7.
// Options
final
Options firstOptions =
configFirstParameters
(
);
final
Options options =
configParameters
(
firstOptions);
// On parse l'aide
final
CommandLineParser parser =
new
DefaultParser
(
);
final
CommandLine firstLine =
parser.parse
(
firstOptions, args, true
);
Le dernier argument de la méthode parse(), ici avec la valeur « true », permet de dire au parser de ne pas s'arrêter si on lui passe des options qu'il ne connaît pas.
Pour afficher l'aide, on doit utilise la méthode printHelp() de HelpFormatter et on quitte simplement :
2.
3.
4.
5.
6.
7.
// Si mode aide
boolean
helpMode =
firstLine.hasOption
(
"help"
);
if
(
helpMode) {
final
HelpFormatter formatter =
new
HelpFormatter
(
);
formatter.printHelp
(
"FiboCLI"
, options, true
);
System.exit
(
0
);
}
Le reste du code ne change pas. Voici à quoi doit ressembler l'aide :
FiboCLI -h
usage: FiboCLI [-a <
algo>
] [-h] -r <
numeric>
[-t] [-v]
-a,--algo <
algo>
Algorithm: recursif (
r) / memoization (
m) /
terminal (
t) / iteratif (
i) / direct (
d)
-h,--help Affiche le message d'aide
-r,--rang <numeric> Rang
-t,--timer Timer
-v,--verbose Verbose
Le code du projet à ce stade est disponible dans le fichier article-common-cli-5-min-04.zip
V. Conclusion▲
Allez, comme d'habitude, on a déjà bien dépassé le contrat des « 5 minutes »". On va s'arrêter là, car c'est largement suffisant pour comprendre comment fonctionne la bibliothèque Apache Common CLI. Vous avez pu constater que c'est relativement simple.
Bien entendu, les fonctionnalités que nous avons découvertes sont relativement simples et CLI sait faire bien plus que cela. Nous avons néanmoins vu celles qui me semblent être les plus importantes et que vous utiliserez à coup sûr dans vos projets. Vous avez toutes les cartes en main. Par exemple, on aurait aussi pu mettre en place un builder, éventuellement générique, pour lire et traiter les paramètres au lieu de tout faire dans le main.
Dans le même goût, on peut aussi s'intéresser à la bibliothèque owner qui permet de gérer les propriétés de configuration depuis un fichier.
Vos retours nous aident à améliorer nos publications. N'hésitez donc pas ? commenter cet article sur le forum : Commentez
VI. Remerciements▲
D'abord j'adresse mes remerciements à l'équipe CLI, chez Apache, pour avoir développé une bibliothèque aussi utile. Je n'oublie pas tous les contributeurs qui participent notamment sur le forum.
Plus spécifiquement en ce qui concerne cet article, je tiens à remercier l'équipe de Developpez.com et plus particulièrement Mickael Baron et Milkoseck.
VII. Annexes▲
VII-A. Liens▲
Apache Common CLI : https://commons.apache.org/proper/commons-cli/
VII-B. Liens personnels▲
Retrouvez ma page et mes autres articles sur Developpez.com à l'adresse:
https://thierry-leriche-dessirier.developpez.com/#page_articles.
Suivez-moi sur Twitter : @thierrylerichehttps://twitter.com/thierryleriche
et sur mon site/blog ICAUDA : http://www.icauda.com.
VII-C. Codes sources complets▲
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.
package
com.icauda.article.commoncli;
import
com.icauda.article.commoncli.fibonacci.DirectFibonacciCalculator;
import
com.icauda.article.commoncli.fibonacci.FibonacciCalculator;
import
com.icauda.article.commoncli.fibonacci.IteratifFibonacciCalculator;
import
com.icauda.article.commoncli.fibonacci.MemoizationFibonnacciCalculator;
import
com.icauda.article.commoncli.fibonacci.RecursifFibonnacciCalculator;
import
com.icauda.article.commoncli.fibonacci.TerminalFibonnacciCalculator;
public
abstract
class
AbstractFibo {
protected
static
void
work
(
final
int
n, final
FibonacciCalculator calc, final
boolean
timerMode, final
boolean
verboseMode) {
if
(
verboseMode) {
System.out.println
(
"Calculating Fibonacci for n: "
+
n);
System.out.println
(
"Selected algorithm: "
+
calc.getName
(
));
}
final
long
time1 =
System.currentTimeMillis
(
);
final
long
result =
calc.calculate
(
n);
final
long
time2 =
System.currentTimeMillis
(
);
if
(
timerMode) {
System.out.println
(
"Duration: "
+
(
time2 -
time1) +
" ms"
);
}
if
(
verboseMode) {
System.out.println
(
"Fibonacci("
+
n +
")="
+
result);
}
System.out.println
(
result);
}
protected
static
FibonacciCalculator getCalculator
(
final
char
algo) {
switch
(
algo) {
// recursif (r)
case
'r'
:
return
new
RecursifFibonnacciCalculator
(
);
// memoization (m)
case
'm'
:
return
new
MemoizationFibonnacciCalculator
(
);
// terminal (t)
case
't'
:
return
new
TerminalFibonnacciCalculator
(
);
// iteratif (i)
case
'i'
:
return
new
IteratifFibonacciCalculator
(
);
// direct (d)
case
'd'
:
return
new
DirectFibonacciCalculator
(
);
default
:
System.err.println
(
"Algo inconnu: "
+
algo);
System.exit
(
1
);
}
return
null
;
}
}
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.
package
com.icauda.article.commoncli;
import
com.icauda.article.commoncli.fibonacci.FibonacciCalculator;
public
class
Fibo extends
AbstractFibo {
public
static
void
main
(
String[] args) {
// Algo : recursif (r) / memoization (m) / terminal (t) / iteratif (i) / direct (d)
char
algo =
'i'
;
// Rang
int
n =
42
;
// Timer
boolean
timerMode =
false
;
// Verbose
boolean
verboseMode =
true
;
if
(
verboseMode) {
timerMode =
true
;
}
// Calculator
final
FibonacciCalculator calc =
getCalculator
(
algo);
// Work
work
(
n, calc, timerMode, verboseMode);
}
}
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.
package
com.icauda.article.commoncli;
import
java.util.HashMap;
import
java.util.Map;
import
com.icauda.article.commoncli.fibonacci.FibonacciCalculator;
/*
* Usage FiboManual --algo=r --rang=42 --timer=true --verbose=true
*/
public
class
FiboManual extends
AbstractFibo {
public
static
void
main
(
final
String[] args) {
final
Map<
String, String>
parameters =
parseParameters
(
args);
// Algo : recursif (r) / memoization (m) / terminal (t) / iteratif (i) / direct (d)
char
algo =
'i'
;
final
String algoFromParamaters =
parameters.get
(
"algo"
);
if
(
algoFromParamaters !=
null
) {
algo =
algoFromParamaters.charAt
(
0
);
}
// Rang
final
String rangFromParamaters =
parameters.get
(
"rang"
);
int
n =
0
;
try
{
n =
Integer.valueOf
(
rangFromParamaters);
}
catch
(
Exception e) {
System.err.println
(
"Bad parameter: algo"
);
System.exit
(
3
);
}
// Timer
boolean
timerMode =
false
;
final
String timerFromParamaters =
parameters.get
(
"timer"
);
if
(
timerFromParamaters !=
null
) {
timerMode =
timerFromParamaters.equalsIgnoreCase
(
"true"
);
}
// Verbose
boolean
verboseMode =
true
;
final
String verboseFromParamaters =
parameters.get
(
"verbose"
);
if
(
verboseFromParamaters !=
null
) {
verboseMode =
verboseFromParamaters.equalsIgnoreCase
(
"true"
);
}
if
(
verboseMode) {
timerMode =
true
;
}
// Calculator
final
FibonacciCalculator calc =
getCalculator
(
algo);
// Work
work
(
n, calc, timerMode, verboseMode);
}
private
static
Map<
String, String>
parseParameters
(
final
String[] args) {
final
Map<
String, String>
parameters =
new
HashMap<>(
);
for
(
final
String arg : args) {
if
(!
arg.startsWith
(
"--"
)) {
System.err.println
(
"Bad parameter: "
+
arg);
System.exit
(
2
);
}
final
String[] tab =
arg.substring
(
2
).split
(
"="
);
parameters.put
(
tab[0
], tab[1
]);
}
return
parameters;
}
}
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.
package
com.icauda.article.commoncli;
import
org.apache.commons.cli.CommandLine;
import
org.apache.commons.cli.CommandLineParser;
import
org.apache.commons.cli.DefaultParser;
import
org.apache.commons.cli.HelpFormatter;
import
org.apache.commons.cli.Option;
import
org.apache.commons.cli.Options;
import
org.apache.commons.cli.ParseException;
import
com.icauda.article.commoncli.fibonacci.FibonacciCalculator;
/*
* Usage FiboCli --algo r --rang 42 --timer --verbose
* -a -r -t -v
*/
public
class
FiboCli extends
AbstractFibo {
public
static
void
main
(
final
String[] args) throws
ParseException {
// Options
final
Options firstOptions =
configFirstParameters
(
);
final
Options options =
configParameters
(
firstOptions);
// On parse l'aide
final
CommandLineParser parser =
new
DefaultParser
(
);
final
CommandLine firstLine =
parser.parse
(
firstOptions, args, true
);
// Si mode aide
boolean
helpMode =
firstLine.hasOption
(
"help"
);
if
(
helpMode) {
final
HelpFormatter formatter =
new
HelpFormatter
(
);
formatter.printHelp
(
"FiboCLI"
, options, true
);
System.exit
(
0
);
}
// On parse la suite
final
CommandLine line =
parser.parse
(
options, args);
// Algo : recursif (r) / memoization (m) / terminal (t) / iteratif (i) / direct (d)
char
algo =
line.getOptionValue
(
"algo"
, "i"
).charAt
(
0
);
// Rang
final
String rangFromParamaters =
line.getOptionValue
(
"rang"
, ""
);
int
n =
0
;
try
{
n =
Integer.valueOf
(
rangFromParamaters);
}
catch
(
Exception e) {
System.err.println
(
"Bad parameter: algo"
);
System.exit
(
3
);
}
// Timer
boolean
timerMode =
line.hasOption
(
"timer"
);
// Verbose
boolean
verboseMode =
line.hasOption
(
"verbose"
);
if
(
verboseMode) {
timerMode =
true
;
}
// Calculator
final
FibonacciCalculator calc =
getCalculator
(
algo);
// Work
work
(
n, calc, timerMode, verboseMode);
}
private
static
Options configFirstParameters
(
) {
final
Option helpFileOption =
Option.builder
(
"h"
) //
.longOpt
(
"help"
) //
.desc
(
"Affiche le message d'aide"
) //
.build
(
);
final
Options firstOptions =
new
Options
(
);
firstOptions.addOption
(
helpFileOption);
return
firstOptions;
}
private
static
Options configParameters
(
final
Options firstOptions) {
final
Option algoFileOption =
Option.builder
(
"a"
) //
.longOpt
(
"algo"
) //
.desc
(
"Algorithm: recursif (r) / memoization (m) / terminal (t) / iteratif (i) / direct (d)"
) //
.hasArg
(
true
) //
.argName
(
"algo"
) //
.required
(
false
) //
.build
(
);
final
Option rangFileOption =
Option.builder
(
"r"
) //
.longOpt
(
"rang"
) //
.desc
(
"Rang"
) //
.hasArg
(
true
) //
.argName
(
"numeric"
) //
.required
(
true
) //
.build
(
);
final
Option timerFileOption =
Option.builder
(
"t"
) //
.longOpt
(
"timer"
) //
.desc
(
"Timer"
) //
.hasArg
(
false
) //
.required
(
false
) //
.build
(
);
final
Option verboseFileOption =
Option.builder
(
"v"
) //
.longOpt
(
"verbose"
) //
.desc
(
"Verbose"
) //
.hasArg
(
false
) //
.required
(
false
) //
.build
(
);
final
Options options =
new
Options
(
);
// First Options
for
(
final
Option fo : firstOptions.getOptions
(
)) {
options.addOption
(
fo);
}
// All other options
options.addOption
(
algoFileOption);
options.addOption
(
rangFileOption);
options.addOption
(
timerFileOption);
options.addOption
(
verboseFileOption);
return
options;
}
}