Formes, chenilles, etc .... L'épisode final !
Objectifs : L'objectif de ce TP est de vous faire expérimenter avec la gestion des exceptions en Java. En réalisant les exercices proposés vous serez conduits à écrire du code gérant ainsi que du code provoquant des exceptions.
Dans le TP précédent a été développée une application permettant d'afficher et d'animer dans une même fenêtre toutes sortes d'objets: des formes géométriques, des "visages", des "chenilles".
La séance d'aujourd'hui va consister à étendre cette application afin de pouvoir créer les objets à afficher et éventuellement à animer à partir d'un fichier de description textuel chargé au lancement du programme.
Exercice 1: Utiliser les WorkSpaces de VS Code
Dans ce premier exercice vous allez apprendre à utiliser les WorkSpaces de Visual Studio Code pour pouvoir travailler en même temps sur plusieurs projets reliés entre eux.
Pour en savoir plus sur les WorkSpaces de Visual Studio Code, vous pouvez consulter la page suivante: Multi-root Workspaces
-
Créez un répertoire TP12 dans votre répertoire de travail POOJava
-
Dans ce répertoire téléchargez :
-
sur gitlab, la Release v7.2 du projet Formes-Animées qui contient un projet Java maven correspondant la correction complète du TP Formes Animées.
-
le fichier fichiers-animation.zip qui contient un projet Java maven d'application qui nous servira de point de départ pour la réalisation de ce TP.
Le contenu du répertoire TP12 est alors le suivant
-
-
Dans le répertoire TP12, décompressez puis supprimez les archives téléchargées (fichiers .zip ou .tar.gz).
-
Ouvrez Visual Studio Code avec la commande code, puis ajoutez à votre espace de travail (Workspace) le projet maven formes-animees-v7.2.
-
Une fois le projet chargé, vérifiez que l'application AppliNChenilles peut être compilée et exécutée
-
En procédant comme à l'étape 4, ajoutez à votre espace de travail (Workspace) le projet maven fichiers-animation. Une fois le projet chargé, l'explorateur de VScode doit avoir le contenu suivant :
-
Vous pouvez constater que le code de fichiers-animation ne compile pas. Pouvez-vous expliquer pourquoi ? Sauriez-vous comment remédier à ce problème ?
-
Ce nouveau projet (fichiers-animation) contient du code java qui fait référence au code java des classes et interfaces développées dans le TP précédent et donc présentes dans le projet formes-animees-v7.2. Pour que le projet fichiers-animation puisse être compilé il faut donc indiquer où trouver le code des classes projet formes-animees-v7.2. Plutôt que de recopier (et dupliquer) le code de celles-ci dans ce nouveau projet, il vaut mieux créer une dépendance entre projets. Ces projets étant gérés via l'outil de construction maven, cette dépendance est définie dans le fichier pom.xml du projet fichiers-animation.
La dépendance est définie en indiquant le groupId, l'artifcatId et n° de version du projet formes-animees-v7.2. Le code XML à insérer est le suivant.
<dependencies> <dependency> <groupId>fr.im2ag.m2cci</groupId> <artifactId>Formes-Animees</artifactId> <version>7.2</version> </dependency> </dependencies>
Au moment d'enregister le fichier pom.xml, pensez à demander la synchronisation du classpath de votre application afin que votre code puisse être recompilé correctement.
De plus les deux projets étant dans le même Workspace, VScode ira ainsi automatiquement chercher les classes dans les répertoires du projet formes-animees-v7.2 et toute modification de ce dernier projet sera immédiatement répercutée sur le projet fichiers-animation.
-
Mainteant que le projet fichiers-animation ne comporte plus d'erreurs,
Verifiez que le projet fichiers-animation fonctionne correctement en exécutant le programme TestDessinFileReader situé dans le package fr.im2ag.m2ccci.applis.
Le programme principal (main) situé dans la classe TestDessinFileReader crée un dessin à partir d'une description textuelle de formes contenue dans un fichier. Lorsque vous exécutez cette classe, un nom de fichier vous est demandé sur la console, tappez data/polygones1.txt. Vous devez obtenir un affichage identique à la figure suivante.
Le fichier polgyones1.txt situé dans le répertoire data à la racine du projet fichiers-animation contient les descriptions suivantes (pour le moment les seules supportées par l'application) :
-
Maintenant que tout fonctionne, vous pouvez sauvegarder votre espace de travail afin de pouvoir le réutiliser ultérieurement.
-
Fermer VScode et réouvrez le en lançant la commande code TP12Exceptions.code-workspace & en vous plaçant dans le répertoire TP12
Exercice 2: Attraper des exceptions
Exercice 2.1:
-
Que se passe-t-il lorsque le nom de fichier donné par l'utilisateur au lancement de l'application ne correspond pas à un fichier existant ? Par exemple, observez ce que donne l'exécution du programme avec la data/polygone1.txt comme nom de fichier. A quel endroit du programme l'exécution s'est-elle arrêtée ?
Essayez de trouvez la réponse par vous-même. Vous pouvez ensuite comparer avec la bonne réponse en cliquant sur ce bouton:une exception de type FileNotFoundException est levée, ce qui interrompt l'exécution normale du programme.
-
Modifiez le programme pour que, en cas d'erreur sur le nom du fichier, l'utilisateur puisse proposer un nouveau nom.
Essayez de trouvez une solution par vous-même.Vous pouvez ensuite comparer avec une solution en cliquant sur ce bouton:Pour cela il faut attraper l'exception et recommencer la saisie à l'aide d'une boucle do while. Cela entraine donc les modifications suivantes dans le programme principal:
... // chargement des données à partir d'un fichier Scanner sc = new Scanner(System.in); boolean chargementOK = false; do { try { System.out.println("Entrez le chemin relatif pour accéder au fichier de données : "); String cheminRelatif = sc.nextLine(); String cheminAbsolu = System.getProperty("user.dir") + "/" + cheminRelatif; DessinFileReader.chargerDessinables(cheminAbsolu, d); // le fichier a été correctement chargé chargementOK = true; // en cas de problème une exception est levée et // cette affectation n'est pas effectuée, on // passe directement dans la clause catch } catch (FileNotFoundException e) { System.out.println(e.getMessage()); } } while (! chargementOK); ...
Le format général des fichiers de descriptions d'objets est le suivant:
Toute ligne débutant par un # est un commentaire qui est ignoré lorsque le fichier est lu.
-
Les lignes blanches ne sont pas significatives et sont également ignorées à la lecture.
-
Une ligne de description permet de définir les caractéristique de l'objet à créer et à afficher:
la ligne débute un code qui définit le type d'objet décrit, ici la lettre 'F' indique qu'il s'agit d'une forme géométrique
cette lettre est suivie d'un code (une chaîne de caractères sans espaces) indiquant le type de la forme,
-
ce code est suivi des paramètres permettant de spécifier la forme,
les différents éléments sont séparés par un ou plusieurs espaces qui ne sont pas significatifs.
Par exemple pour un polygone regulier on aura :
Exercice 2.2:
-
Que se passe-t-il lorsque l'utilisateur propose de charger le fichier polygones2.txt situé dans le répertoire data ?
Essayez de trouvez la réponse par vous-même. Vous pouvez ensuite comparer avec la bonne réponse en cliquant sur ce bouton:
Une exception de type NombreArgumentsIncorrect est levée par la méthode lirePolygoneRegulier. Cette exception n'est pas traitée par le code (pas de clause catch l'attrapant), elle remonte donc jusqu'au programme principal, l'exécution est interrompue et la pile des appels est affichée sur la console.
Cette exception est une exception contrôlée définie dans l'application d'animation des formes. C'est une sous classe de la classe DessinFileReaderException qui regroupe plusieurs type d'exceptions:
- UnkownObjectException la ligne de description d'un objet lue dans le fichier texte débute par un code qui ne correspond pas à un type objet reconnu par le "reader"
- UnkownFormeException si dans le fichier texte la ligne de description lue dans le fichier texte contient un code de forme qui n'est pas reconnu par le "reader"
- NombreArgumentsIncorrect si pour une forme ou un animateur il manque ou il y trop d'arguments dans la ligne de description lue dans le fichier texte.
-
Sans modifiez le fichier polygones2.txt, corrigez le programme pour que, en cas d'erreur sur une description d'objet, l'exécution du programme ne soit pas arrêtée mais que la ligne soit simplement ignorée.
Vérifiez que votre correction est bonne en rechargeant le fichier polygones2.txt. Vous ne devez alors obtenir l'affichage que des formes dont la description est correcte, à savoir, comme dans l'exercice 1, un pentagone rouge, un octogone vert et un hexagone bleu (voir figure 2).
Essayez de trouvez une solution par vous-même. Cependant, si vous n'avez pas d'idées sur la manière de procéder et si vous souhaitez être guidé vers la solution, cliquez sur le bouton ci-dessous
Si l'on observe le parcours de l'exception
NombreArgumentsIncorrect
, celle-ci est lancée (levée) dans la méthodelirePolyRegulier
qui la déclare dans sa signature (clause throws).private static PolygoneRegulier lirePolyRegulier(String[] params) throws NombreArgumentsIncorrect { if (params.length != 7) { throw new NombreArgumentsIncorrect("POLYR", params.length, 7); } ...
lirePolyRegulier
est invoquée par la méthodelireForme
qui non seulement ne traite pas l'exceptionNombreArgumentsIncorrect
mais en plus peut lancer une exceptionUnknownFormeException
au cas où le type de forme passé en paramètre est inconnu.private static IForme lireForme(String typeForme, String[] paramsForme) throws UnknownFormeException, NombreArgumentsIncorrect { switch (typeForme) { case "POLYR": return lirePolyRegulier(paramsForme); default: throw new UnknownFormeException(typeForme); } }
lireForme
est elle même invoquée par la méthodechargerDessinables
qui elle n'ont plus ne traite pas ces exceptions et les transmet au programme appelant (dans le cas de notre application le programme principal,main
de la classeTPExceptions
).public static void chargerDessinables(String fileName, Dessin dessin) throws FileNotFoundException, IOException, UnknownObjectException, UnknownFormeException, NombreArgumentsIncorrect
La figure 3 illustre cette remontée.
La question qui se pose est donc la suivante:
A quel niveau l'exception
NombreArgumentsIncorrect
doit-elle être attrapée et traitée, afin de ne pas interrompre la lecture d'un fichier de description lorsqu'elle est levée ?Il est clair qu'attraper l'exception au niveau du programme principal comme nous l'avons fait pour l'exception
FileNotFoundException
interviendrait trop tard. La lecture du fichier de description ne pourrait être poursuivie.Attraper l'exception au niveau de
lireForme
interviendrait trop tôt. En effet du coup l'exécution dechargerDessinable
pourrait alors se poursuivre normalement, alors que la forme n'a pu être créée, ce qui n'a pas de sens (voir figure 4).L'exception doit donc être attrapée au niveau de la méthode
chargerDessinables
. La question est maintenant : dans quel bloc ?Il est clair que l'exception doit être attrapée dans le bloc de la boucle
while
puisque l'objectif est justement de ne pas interrompre celle-ci. La figure 5 nous montre les principales options qu'il nous reste.Examinons chacune de ces possibilités
- (3) mettre le
try ... catch
dans le case : sachant que ce type d'exception (NombreArgumentsIncorrect
) peut aussi intervenir dans les appels de fonction effectués dans les autrescase
, faire le traitement à ce niveau là impliquerait de faire la même chose dans chacun descase
. Nous rejetons donc cette solution. - (2) englober tout le
switch
par le bloctry ... catch
: si cela permettrait d'avoir un seul bloctry ... catch
pour attraper toutes les exceptions pouvant intervenir dans les différentscase
et fonctionnerait dans le cas présent cette solution a néanmoins un inconvenient. Le point de reprise après le bloccatch
n'est pas directement l'instruction de passage à la ligne suivante. Si jamais, plus tard, le code est modifié et que de nouvelles instructions sont ajoutées après le catch avant l'instruction de lecture de la ligne suivante il y a un risque potentiel d'erreur. - (1) englober tout le contenu de la boucle
while
, excepté l'instruction de passage à la ligne suivante, par le bloctry ... catch
: c'est cette solution que nous adopterons. En effet, contrairement à la précédente, elle indique sans ambiguité où est le point de reprise normale de l'exécution.
Le traitement de l'exception consiste à simplement écrire sur un message signalant que la ligne a été ignorée en indiquant la raison. On obtient donc le code suivant pour la méthode
chargerDessinables
:public static void chargerDessinables(String fileName, Dessin dessin) throws FileNotFoundException, IOException, UnknownObjectException, UnknownFormeException { try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) { // try avec ressources, qui permet de fermer automatiquement le reader String ligne; // chaîne contenant la ligne courante. int noLigne = 0; // numéro de la ligne en cours d'analyse. IForme forme; // la dernière forme créée IFormeAnimator animator = null; // le dernier animateur lu ligne = reader.readLine(); while (ligne != null) { noLigne++; System.out.println(ligne); try { if (!"".equals(ligne) && !ligne.startsWith("#")) { // ligne non vide et non commentaire // recupération dans un tableau de chaînes des différents élements de la ligne String[] tokens = ligne.toUpperCase().split("\\s+"); switch (tokens[0]) { case "F": forme = lireForme(tokens[1], Arrays.copyOfRange(tokens, 2, tokens.length)); System.out.println("--> forme créée"); if (animator != null) { dessin.ajouterObjet(new FormeAnimee(forme, animator)); animator = null; } else { dessin.ajouterObjet(forme); } break; case "A": System.out.println("Animateur"); animator = lireAnimator(tokens); break; case "C": System.out.println("Chenille"); dessin.ajouterObjet(lireChenille(tokens)); break; default: throw new UnknownObjectException(tokens[0]); } // fin du switch } // fin du if (! ligne.equals("")) } // fin du bloc try catch (NombreArgumentsIncorrect ex) { System.out.println("Ligne no " + noLigne + " ignorée"); System.out.println(ex.getMessage()); } // passage à la ligne suivante ligne = reader.readLine(); } // fin du while } // fin du try avec resources }
Les exceptions de type
NombreArgumentsIncorrect
étant traitées au niveau de la méthodechargerDessinables
, elles ne sont plus transmises au programme appelant et sont donc retirées dae la clausethrows
de la signature de la méthode. - (3) mettre le
Exercice 2.3: Même exercice que le précédent, mais avec le fichier data/polygones3.txt.
Le fichier de données contient deux erreurs qui soulèvent respectivement des
exceptions de type UnknownFormeException
et UnknownObjectException
.
Le traitement de ces exceptions est similaire à celui des exceptions NombreArgumentsIncorrect
.
Elles sont déclenchées dans le même bloc try. Le traitement par un bloc catch consiste à simplement écrire sur
un message signalant que la ligne a été ignorée en indiquant la raison (fournie par le message de l'exception).
Il y a plusieurs manières de programmer cela.
-
En écrivant un bloc
catch
pour chaque type d'exception.try { ... } catch (NombreArgumentsIncorrect ex) { System.out.println("ligne ignorée : " + ex.getMessage()); } catch (UnknownFormeException ex) { System.out.println("ligne ignorée : " + ex.getMessage()); } catch (UnknownObjectException ex) { System.out.println("ligne ignorée : " + ex.getMessage()); }
-
On voit bien que le traitement est le même pour chaque type d'exception. On peut simplifier le code sachant que
NombreArgumentsIncorrect
,UnknownFormeException
etUnknownObjectException
sont sous classes deDessinFileReaderException
. Elles peuvent donc toutes être traitées par une seule clausecatch
. Ce qui donne donc le code suivant :try { ... } catch (DessinFileReaderException ex) { System.out.println("ligne ignorée : " + ex.getMessage()); }
Ces exceptions étant traitées dans la méthode chargerDessinables
il faut
les retirer de la clause throws dans sa signature qui devient
public static void chargerDessinables(String fileName, Dessin dessin) throws FileNotFoundException, IOException
Exercice 2.4: Même exercice que le précédent, mais avec le fichier data/polygones4.txt.
la lecture du fichier data/polygones4.txt provoque la levée d'une
exception de type NumberFormatException
qui peut être levée lorsqu'un argument associé
à une valeur numérique ne correspond pas au type attendu (par exemple 8.0 au lieu de 8
pour un entier, ou 12a4 ...).
remarque: cette exception étant de type RuntimeException
il n'y a pas obligation de la déclarer dans la clause throws
des méthodes pouvant la
lever. Ce qui ne nous empêche pas de la traiter ici.
Le traitement de ces exceptions est similaire à celui des exceptions DessinFileReaderException
considérées dans
les exercices précédents. Il consiste à simplement écrire sur
un message signalant que la ligne a été ignorée en indiquant la raison et est localisée au même endroit dans la boucle
while de la méthode chargerDessinables
. Il y a plusieurs manières de programmer cela :
-
En écrivant un bloc
catch
spécifique.try { ... } catch (DessinFileReaderException ex) { System.out.println("ligne ignorée : " + ex.getMessage()); } catch (NumberFormatException ex) { System.out.println("ligne ignorée : " + ex.getMessage()); }
-
Depuis, la version 7 de Java, il est même possible de regrouper plusieurs types d'exceptions dans une même clause catch. Le code précédent peut donc être encore simplifié.
try { ... } catch (DessinFileReaderException | NumberFormatException ex) { System.out.println("ligne ignorée : " + ex.getMessage()); }
Exercice 3: Prise en charge de nouveaux types de formes : les étoiles et disques
Modifiez l'application pour pouvoir prendre en compte d'autres types de formes que les polygones réguliers : les étoiles et les disques.
-
Définissez un format textuel pour ces deux type de formes.
-
Ajoutez au fichier polygones1.txt une description pour une étoile de couleur orange et un disque de couleur jaune.
-
Modifiez l'application pour qu'elle puisse les afficher et vérifiez que vous obtenez bien l'affichage escompté
Essayez de trouvez une solution par vous-même. Cependant, si vous n'avez pas d'idées sur la manière de procéder et si vous souhaitez avoir quelques indications, cliquez sur ce bouton:
Il s'agit ici de simplement produire un code relativement proche de celui de lirePolyRegulier
et de modifier en conséquence la méthode lireForme
en ajoutant les différents nouveaux
cas de formes.
La description d'une étoile ou d'un disque sont très similaires :
Seul diffère le type de forme avec par exemple le ETOILE
pour les étoiles et
le code DISQUE
pour les disques. Aussi pouvons nous écrire
une seule méthode pour lire ces deux types de formes :
/**
* Construit une forme décrite à partir des paramètres lus dans un fichier
* de descriptions, ces paramètres étant la description d'un cercle et d'une
* couleur. La forme peut être un disque ou une étoile.
*
* @param noLigne numéro de la ligne dans le fichier
* @param params les paramètre décrivant la forme (x, y, r, r, v, b)
* @return la forme (Etoile ou Disque)
* @throws NombreArgumentsIncorrect si le nombre de paramètres de
* description ne correspond pas au nombre
* attendu.
*/
private static FormeCirculaire lireFormeCirculaire(int noLigne, String typeForme, String[] params)
throws NombreArgumentsIncorrect, UnknownFormeException {
if (params.length != 6) {
throw new NombreArgumentsIncorrect(noLigne, typeForme, params.length, 6);
}
int x = Integer.parseInt(params[0]);
int y = Integer.parseInt(params[1]);
int r = Integer.parseInt(params[2]);
Color c = new Color(Integer.parseInt(params[3]), Integer.parseInt(params[4]),
Integer.parseInt(params[5]));
switch (typeForme.toUpperCase()) {
case "ETOILE":
return new Etoile(x, y, r, 1.0f, c, c);
case "DISQUE":
return new Disque(x, y, r, 1.0f, c, c);
default:
throw new UnknownFormeException(noLigne, typeForme);
}
}
Le code de lireForme
est alors modifié comme suit:
/**
* @param noLigne numéro de la ligne dans le fichier
* @param typeForme le type de la forme
* @param paramsForme le tableau des paramètres de la forme
*
* @return la référence d'un objet forme correspondant à la description
*
* @throws UnknownFormeException si le type de forme n'est pas reconnu.
*
* @throws NombreArgumentsIncorrect si le nombre de paramètres de
* description n'est pas le nombre attendu.
*/
private static IForme lireForme(int noLigne, String typeForme, String[] paramsForme)
throws UnknownFormeException, NombreArgumentsIncorrect {
switch (typeForme) {
case "POLYR":
return lirePolyRegulier(noLigne, paramsForme);
case "ETOILE":
case "DISQUE":
return lireFormeCirculaire(noLigne, typeForme, paramsForme);
default:
throw new UnknownFormeException(noLigne, typeForme);
}
}
Exercice 4: Animer les formes
On souhaite rajouter dans les fichiers texte, la possibilité de décrire des animateurs pour les formes. Comme pour une forme, un Animateur sera décrit par une ligne dans le fichier texte. L'animateur s'applique à la forme qui le suit dans le fichier.
Une ligne de description d'un Animateur a la structure suivante:
la ligne débute par la lettre
'A'
,cette lettre est suivie d'un code (une chaîne de caractères sans espaces) indiquant le type de l'animateur,
-
ce code est suivi des paramètres permettant de spécifier l'animateur,
les différents éléments sont séparés par un ou plusieurs espaces qui ne sont pas significatifs.
Par exemple pour un animateur de cap on aura :
Exercice 4.1: Modifiez le fichier de données et l'application de manière à ce que l'étoile jaune ait un mouvement aléatoire (animateur de cap) et le pentagone rouge ait un mouvement circulaire.
Essayez de trouvez une solution par vous-même. Cependant, si vous n'avez pas d'idées sur la manière de procéder et si vous souhaitez avoir quelques indications, cliquez sur ce bouton:
La aussi le code est très similaire. On peut néanmoins introduire un nouveau type d'exception
UnkownAnimatorException
pour signaler un type d'animateur non reconnu. On peut à cette occasion restructurer un peu nos
type d'exceptions (voir figure 7) en plaçant les types UnkownObjectException
, UnkownFormeException
et
UnkownAnimatorException
sous un nouveau type plus génrique UnkownTypeException
La classe d'exception UnkownTypeException
est définie comme suit:
public class UnknownTypeException extends DessinFileReaderException {
public UnknownTypeException(int noLigne, String codeType) {
super(noLigne,codeType + " est un type non connu");
}
}
La classe d'exception UnkownAnimatorException
est définie comme suit:
public class UnknownAnimatorException extends UnknownTypeException {
public UnknownAnimatorException(int noLigne, String animatorType) {
super(noLigne, animatorType + " est un type d'animateur non connu");
}
}
et les classes d'exception UnkownObjectException
et UnkownFormeException
sont modifiées en changeant leur super classe
public class UnkownObjectException extends UnknownTypeException
public class UnkownFormeException extends UnknownTypeException
Dans le code de chargerDessinables
le bloc
catch(DessinFileReaderException | NumberFormatException ex)
n'a pas besoin d'être modifié,
il attrapera ce nouveau type d'exception qui est un sous type de DessinFileReaderException:
noLigne++;
try {
...
} catch (DessinFileReaderException | NumberFormatException ex) {
System.out.println("Ligne ignorée");
System.out.println(ex.getMessage());
}
// passage à la ligne suivante
ligne = reader.readLine();
Le code pour lire la description d'un animateur de type CapAnimator
/**
* construit une animateur de type CapAnimator à partir des paramètres
* définis dans un fichier de description textuel
*
* @param noLigne numéro de la ligne dans le fichier
* @param paramsAnimator les paramètre décrivant l'animateur
* @param d le dessin dans lequel l'animateur travaille
*
* @return l'animateur
* @throws NombreArgumentsIncorrect si le nomnre de paramètres de
* description ne correspond pas au nombre
* attendu.
*/
private static IAnimateur lireCapAnimator(int noLigne, String[] paramsAnimator, Dessin d)
throws NombreArgumentsIncorrect {
if (paramsAnimator.length != 2) {
throw new NombreArgumentsIncorrect(noLigne, "CAP", paramsAnimator.length, 2);
}
return new AnimateurCap(d, Integer.parseInt(paramsAnimator[0]), Double.parseDouble(paramsAnimator[1]));
}
Le code de la méthode lireAnimator
/**
* @param noLigne numéro de la ligne dans le fichier
* @param typeAnimator le type de l'animateur
* @param paramsAnimator le tableau des paramètres de l'animateur
*
* @return la référence d'un objet animateur correspondant à la description
*
* @throws UnknownAnimatorException si le type d'animateur n'est pas
* reconnu.
*
* @throws NombreArgumentsIncorrect si le nombre de paramètres de
* description n'est pas le nombre attendu.
*/
private static IAnimateur lireAnimator(int noLigne, String typeAnimator, String[] paramsAnimator, Dessin d)
throws UnknownAnimatorException, NombreArgumentsIncorrect {
switch (typeAnimator.toUpperCase()) {
case "CAP":
return lireCapAnimator(noLigne, paramsAnimator, d);
case "REB":
return lireRebondAnimator(noLigne, paramsAnimator, d);
case "CIRC":
return lireAnimateurCercle(noLigne, paramsAnimator);
default:
throw new UnknownAnimatorException(noLigne, typeAnimator);
}
}
et il ne faudra pas oublier de modifier dans la méthode chargerDessinables
le case correspondant aux animateurs
pour ajuster les paramètres de l'appel à la méthode lireAnimator
case "A":
System.out.println("Animateur");
animator = lireAnimator(noLigne, tokens[1], Arrays.copyOfRange(tokens, 2, tokens.length), dessin);
break;
Exercice 4.2: Rajoutez le support pour les Chenilles et puis ensuite, si vous avez le temps, enrichissez la description des formes pour prendre en compte la couleur et l'épaisseur du trait.
Essayez de trouvez une solution par vous-même. Cependant, si vous n'avez pas d'idées sur la manière de procéder et si vous souhaitez avoir quelques indications, cliquez sur ce bouton:
il n'y a aucune difficulté particulière, le seul point est de définir la description de la chenille dans le fichier texte. On peut par exemple choisir le format suivant :
Le code pour créer une chenille est alors:
/**
* construit une chenille à partir des paramètres définis dans un fichier de
* description textuel
*
* @param noLigne numéro de la ligne dans le fichier
* @param typeChenille le type de chenille à créer
* @param params les paramètres décrivant la chenille
* @param animator l'animateur de la chenille, si l'animateur est null la
* chenille aura un AnimateurCap par défaut.
* @param d le dessin ou se trouve la chenille
*
* @return la chenille
*
* @throws UnknowChenilleException si le type de chenille n'est pas reconnu
*
* @throws NombreArgumentsIncorrect si le nombre de paramètres de
* description ne correspond pas au nombre
* attendu.
*
* @throws IOException si la l'image pour une chenille image n'a pu
* ête lue.
*
*/
private static Chenille lireChenille(int noLigne, String typeChenille, String[] params, IAnimateur animator,
Dessin d)
throws NombreArgumentsIncorrect, UnknownChenilleException, IOException {
switch (typeChenille.toUpperCase()) {
case "COUL":
return lireChenilleCouleur(noLigne, params, animator, d);
case "IMG":
return lireChenilleImage(noLigne, params, animator, d);
default:
throw new UnknownChenilleException(noLigne, typeChenille);
}
}
/**
* construit une chenille couleur à partir des paramètres définis dans un
* fichier de
* description textuel
*
* @param noLigne numéro de la ligne dans le fichier
* @param params les paramètres décrivant la chenille
* @param animator l'animateur de la chenille, si l'animateur est null la
* chenille aura un AnimateurCap par défaut.
* @param d le dessin ou se trouve la chenille
*
* @return la chenille
* @throws NombreArgumentsIncorrect si le nomnre de paramètres de
* description ne correspond pas au nombre
* attendu.
*/
private static Chenille lireChenilleCouleur(int noLigne, String[] params, IAnimateur animator, Dessin d)
throws NombreArgumentsIncorrect, UnknownChenilleException, IOException {
if (params.length != 5) {
throw new NombreArgumentsIncorrect(noLigne, "CHENILLE COULEUR", params.length, 5);
}
int r = Integer.parseInt(params[0]); // rayon
int nbA = Integer.parseInt(params[1]); // nombre d'anneaux
Color coul = new Color(Integer.parseInt(params[2]), Integer.parseInt(params[3]),
Integer.parseInt(params[4]));
if (animator == null) {
return new ChenilleCouleur(d, r, nbA, coul);
} else {
return new ChenilleCouleur(d, r, nbA, coul, animator);
}
}
/**
* construit une chenille image à partir des paramètres définis dans un
* fichier de description textuel
*
* @param noLigne numéro de la ligne dans le fichier
* @param params les paramètres décrivant la chenille
* @param animator l'animateur de la chenille, si l'animateur est null la
* chenille aura un AnimateurCap par défaut.
* @param d le dessin ou se trouve la chenille
*
* @return la chenille
* @throws NombreArgumentsIncorrect si le nombre de paramètres de description ne
* correspond pas au nombre attendu.
*/
private static Chenille lireChenilleImage(int noLigne, String[] params, IAnimateur animator, Dessin d)
throws NombreArgumentsIncorrect, UnknownChenilleException, IOException {
if (params.length != 3) {
throw new NombreArgumentsIncorrect(noLigne, "CHENILLE IMAGE", params.length, 2);
}
int rayon = Integer.parseInt(params[0]); // rayon des anneaux
int nbA = Integer.parseInt(params[1]); // nombre d'anneaux
if (animator == null) {
return new ChenilleImage(d, nbA, rayon, ImageIO.read(new File(params[2])));
} else {
return new ChenilleImage(d, nbA, rayon, ImageIO.read(new File(params[2])), animator);
}
}
L'application est maintenant terminée, son exécution avec le fichier de description data/animation.txt
ci dessous:
# un pentagone rouge fixe F POLYR 100 100 40 5 255 0 0 # un octogone vert animé d'un mouvement avec rebonds A REB 5 -5 F POLYR 200 190 40 8 0 255 0 # un octogone animé d'un mouvement circulaire A CIRC 100 190 100 0 5 F POLYR 200 190 35 8 0 255 255 # un etoile jaune animée se déplaçant aléatoirement A CAP 120 7 F ETOILE 230 290 35 255 255 0 #un disque magenta animé se déplaçant aléatoirement A CAP 190 4 F DISQUE 410 300 20 255 0 255 # un cercle bleu F CERCLE 300 270 30 0 0 255 # une chenille noire à 10 anneaux de rayon 10 C COUL 10 10 0 0 0 #une chenille verte à 20 anneaux de rayon 8 C COUL 8 20 0 255 0 #une chenille rouge à 14 anneaux de rayon 7 qui tourne en rond A CIRC 120 240 240 0 -5 C COUL 7 14 255 0 0 #une chenille image (DarthVador) 14 anneaux de rayon 20 qui tourne en rond A CIRC 100 400 300 0 5 C IMG 20 14 images/darthVador.png
doit produire l'affichage suivant: