On souhaite écrite un petit programme qui découpe les arguments de la ligne de commande, en séparant les arguments classiques ("foo.txt", "bar.png"), des options qui généralement commencent par "-" ou "--". En fait, c'est un peu plus compliqué, un argument peut commencer par "-" ou "--" si jamais il n'est pas listé dans la liste des options.
Pour l'exercice, on considérera qu'il existe les options "-v", "--verbose", "-a" et "--all", les deux premières correspondent à la valeur d'enum OptionInfo.VERBOSE et les deux dernières à la valeur d'enum OptionInfo.ALL. Avec l'enum OptionInfo défini comme ceci
package fr.uge.cmdline1; public enum OptionInfo { ALL, VERBOSE }
Mais avant d'utiliser les OptionInfo, nous allons commencer par un code plus simple. On souhaite écrire une classe Argument avec un champ text de telle sorte à ce que le code suivant fonctionne et affiche les bonnes valeurs
public static void main(String[] args) { var argument1 = new Argument("foo.txt"); var argument2 = new Argument("bar.png"); System.out.println(argument1); // Argument{ text:'foo.txt' } System.out.println(argument2); // Argument{ text:'bar.png' } ... }Écrire la classe Argument avec les préconditions habituelles.
On souhaite écrire une méthode parseCmdLine dans la classe CmdLine1 qui renvoie pour chaque chaine de caractères l'argument correspondant, le tout dans une liste.
... List<Argument> arguments1 = CmdLine1.parseCmdLine("foo.txt", "bar.png"); System.out.println(arguments1); // [Argument{ text:'foo.txt' }, Argument{ text:'bar.png' }] ...Doit-on renvoyer une liste mutable ou non mutable ?
On souhaite maintenant écrire une classe Option qui va représenter les options de la ligne de commande avec un texte (text) et la valeur de l'enum OptionInfo correspondant (nommé info) tel que le code suivant fonctionne correctement. Comme c'est un TP sur l'héritage, on vous demande d'utiliser l'héritage.
... var option1 = new Option("--verbose", OptionInfo.VERBOSE); var option2 = new Option("-v", OptionInfo.VERBOSE); System.out.println(option1); // Option{ text: '--verbose', info: VERBOSE } System.out.println(option2); // Option{ text: '-v', info: VERBOSE } ...Écrire le code de la classe Option sachant que l'on veut que l'affichage soit exactement celui demandé et que vous ne devez pas utiliser de getter sous peine de mort lente à coup de petite cuillère.
On veut maintenant modifier la méthode parseCmdLine pour reconnaitre les arguments et les options. Pour cela, nous allons introduire, dans la classe CmdLine1, une méthode intermédiaire asOptionInfo qui prend en paramètre un argument sous forme de chaine de caractère et renvoie soit la bonne valeur de l'enum OptionInfo soit null si la chaine de caractère ne correspond pas à une des options "-v", "--verbose", "-a" ou "--all".
... var arguments2 = CmdLine1.parseCmdLine("-v", "bar.png"); System.out.println(arguments2); // [Option{ text: '-v', info: VERBOSE }, Argument{ text:'bar.png' }] ...Modifier le code de la méthode parseCmdLine.
En fait, au lieu de renvoyer null, on voudrait que asOptionInfo utilise un Optional. En terme d'utilisation de l'API d'Optional, au lieu d'utiiser isEmpty/isPresent, vous pouvez utiliser map et orElseGet pour chainer les opérations, comme ceci:
Optional<OptionInfo> optionInfoOpt = CmdLine1.asOptionInfo(arg); Argument argument = optionInfoOpt.map(...).orElseGet(...);Modifier le code de parseCmdLine en conséquence.
On souhaite valider que la ligne de commande ne contient pas deux fois les mêmes arguments/les mêmes options, pour cela, on va écrire une méthode checkCmdLine qui prend en paramètre une liste d'arguments et lève une exception si un des arguments est dupliqué.
var arguments3 = CmdLine1.parseCmdLine("-v", "bar.png", "--verbose"); CmdLine1.checkCmdLine(arguments3); // java.lang.IllegalArgumentException: duplicate argument Option{ text: '--verbose', info: VERBOSE }Pour détecter, s'il y a de la duplication d'objets en Java, l'astuce est d'utiliser un Set, et sa méthode add, celle-ci renvoie false si essaye un objet déjà présent.
En fait, les méthodes equals que vous avez écrites sont fausses, car non reflexive, c-a-d, a.equals(b) et b.equals(a) devrait renvoyer la même valeur.
var argument3 = new Argument("-v"); var option3 = new Option("-v", OptionInfo.VERBOSE); System.out.println(argument3.equals(argument3)); // true System.out.println(argument3.equals(option3)); // false System.out.println(option3.equals(option3)); // true System.out.println(option3.equals(argument3)); // falseQu'affiche votre implantation si on exécute le code ci-dessus ? Pourquoi ? Comment doit-on corriger votre implantation ? Faites les modifications qui s'imposent.
En fait, avoir des arguments identiques sur la ligne de commande n'est pas un vrai problème, avoir des options identiques est le vrai problème, on se propose de modifier checkCmdLine pour tester uniquement si les options sont les mêmes.
Pour cela, on doit savoir si un Argument est une Option ou pas. Il existe deux façons d'implanter ce test, avec une méthode isOption dans Argument et Option en utilisant le polymorphisme ou en utilisant le pattern matching.
Quelle technique allons-nous utiliser ici ? Pourquoi ?
var arguments4 = CmdLine1.parseCmdLine("-v", "bar.png", "bar.png"); CmdLine1.checkCmdLine(arguments4); // ok !
En fait, utiliser l'héritage est souvent compliqué, comme nous l'avons vu dans l'exercice précédent
On se propose de réimplanter l'exercice précédent en utilisant une interface et des records. On utilisera Argument comme nom pour l'interface et Plain pour les arguments pas spéciaux et Option pour les arguments qui sont des options. Enfin pour l'implantation de checkCmdLine, on utilisera le pattern matching.
Créer un package fr.uge.cmdline2 ainsi qu'une classe CmdLine2 avec le main ci dessous (Note: pour dupliquer un package vous pouvez faire un Control+C puis un Control+V dans la vue en projet).
Faire en sorte que le main ci-dessous fonctionne.
Note: attention à ce que deux options soient identiques si elles ont le même OptionInfo !
public static void main(String[] args) { // 1 var argument1 = new Plain("foo.txt"); var argument2 = new Plain("bar.png"); System.out.println(argument1); // Argument{ text:'foo.txt' } System.out.println(argument2); // Argument{ text:'bar.png' } // 2 var arguments1 = CmdLine2.parseCmdLine("foo.txt", "bar.png"); System.out.println(arguments1); // [Argument{ text:'foo.txt' }, Argument{ text:'bar.png' }] // 3 var option1 = new Option("--verbose", OptionInfo.VERBOSE); var option2 = new Option("-v", OptionInfo.VERBOSE); System.out.println(option1); // Option{ text: '--verbose', info: VERBOSE } System.out.println(option2); // Option{ text: '-v', info: VERBOSE } // 4 & 5 var arguments2 = CmdLine2.parseCmdLine("-v", "bar.png"); System.out.println(arguments2); // [Option{ text: '-v', info: VERBOSE }, Argument{ text:'bar.png' }] // 6 var arguments3 = CmdLine2.parseCmdLine("-v", "bar.png", "--verbose"); // CmdLine2.checkCmdLine(arguments3); // java.lang.IllegalArgumentException: duplicate argument Option{ text: '--verbose', info: VERBOSE } // 7 var argument3 = new Plain("-v"); var option3 = new Option("-v", OptionInfo.VERBOSE); System.out.println(argument3.equals(argument3)); // true System.out.println(argument3.equals(option3)); // false System.out.println(option3.equals(option3)); // true System.out.println(option3.equals(argument3)); // false // 8, 9 & 10 var arguments4 = CmdLine2.parseCmdLine("-v", "bar.png", "bar.png"); checkCmdLine(arguments4); // ok ! }