TP Java Avancé 24/03/2004 -- Ing2 SIGL1 -- Exercice ====================================================== I. Modalités de rendu --------------------- - Date/heure : mardi 30/03/2004 à 14:00 - Répertoire : ~/rendu/java/sigl1/serialization - Fichiers : CriminalList.java* - Droits : 700 répertoires, 600 fichiers (*) Gardez la clause package (org.insia.interpol) mais ne me rendez que le fichier, sans le placer dans un sous-répertoire correspondant. Juste à même le répertoire de rendu. Evidemment, ne TRAVAILLEZ PAS dans votre répertoire de RENDU. ------------------------------------------------------------------------------ Principe ------------------------------------------------------------------------------ Vous allez devoir créer un système de recensement de grands criminels. A chaque criminel est associée une *fiche* (son casier judiciaire si vous préférez) qui indique son nom complet, son identifiant, sa date de naissance, ses divers alias ainsi que la liste de ses crimes. Certaines de ces informations sont modifiables (la liste de crimes...), d'autres non (son nom...). Un répertoire est un ensemble de fiches, sauvegardable dans un fichier (on suppose qu'on ne s'en servira que de cette manière). Nous vous demandons de réaliser 2 classes : - CriminalList : un répertoire de fiches. - Criminal : une fiche. Note pour la suite : à chaque occurence de "Date", il faut comprendre "java.util.Date". ------------------------------------------------------------------------------ Paquet ------------------------------------------------------------------------------ Le paquet de vos classes est : org.insia.interpol. ------------------------------------------------------------------------------ Criminal ------------------------------------------------------------------------------ La classe Criminal représente une fiche de criminel. C'est une classe public static imbriquée ("static member class") dans la classe CriminalList. Voici le constructeur et la liste des méthodes : - public Criminal(final int criminalId, final String fullName, final Date birthDate) Son unique constructeur qui prend : l'identifiant du criminel, son nom complet et sa date de naissance. - public void addAlias(final String alias) Ajoute un alias au criminel. - public void addCrime(final int crimeId, final String description) Ajoute un crime au criminel ssi crimeId est un entier strictement positif, sinon ne fait rien. Vous devez vérifier que l'ID n'existe pas déjà pour ce criminel, sinon vous levez une IllegalArgumentException avec un message "Crime ID # already exists." (sans les chevrons, bien entendu...). - public List getAliases() Retourne la liste d'aliases du criminel en lecture seule. Pas de tri, l'ordre est celui dans lequel on les a ajoutés. Par ailleurs, n appels successifs doivent toujours obtenir le même objet. Voyez les méthodes de java.util.Collections. - public Date getBirthDate() Retourne la date de naissance du criminel. - public Map getCrimes() Retourne la liste de crimes du criminel (la Map est en permanence triée par ID. A vous de voir quelle implémentation choisir). Là aussi cette Map est en lecture seule, et la même Map est retournée à chaque appel. - public String getFullName() Retourne le nom complet du criminel. - public int getId() Retourne l'identifiant du criminel. - public void removeAlias(final String alias) Enlève cet alias au criminel. - public void removeCrime(final int crimeId) Enlève ce crime au criminel. - public String toString() La description de la fiche du criminel au format suivant : Criminal # Full Name: Birth Date: Aliases: , , . Crimes: # - # - La date de naissance est au format "2004.03.22". Si aucun alias n'existe pour ce criminel, la ligne n'apparaît pas. Si aucun crime n'existe pour ce criminel, "No known crime. D'oh." est retournée sur une seule et même ligne. Aucun autre membre ne doit être visible de l'extérieur. A vous d'utiliser les mécanismes adéquats. Vous aurez besoin d'implémenter ces 2 méthodes private pour réaliser la suite de ce que nous vous demandons : - private void readObject(final ObjectInputStream ois) throws ClassNotFoundException, IOException - private void writeObject(final ObjectOutputStream oos) throws IOException Votre classe doit pouvoir se sérialiser et se désérialiser au travers du mécanisme standard. ATTENTION toutefois ! Vous ne devez pas laisser vos structures de données internes (listes, Maps...) se (dé)sérialiser toutes seules (trop encombrant). Stockez/relisez manuellement leur contenu en passant par des itérateurs (à l'écriture) et des ajouts (à la lecture). Les seules méthodes d'écriture sérialisée autorisées sont : writeInt, writeObject, writeUTF. La symétrique vaut pour la désérialisation. ------------------------------------------------------------------------------ CriminalList ------------------------------------------------------------------------------ La classe CriminalList correspond à un répertoire de fiches. Elle dispose d'un constructeur unique, no-arg public. Voici sa liste de méthodes public : - public void addCriminal(final Criminal criminal) Ajoute une fiche de criminel au répertoire si son identifiant n'est pas encore enregistré (le criminalId). S'il est déjà présent ou si "criminal" est null la méthode lève une IllegalArgumentException avec un message de votre choix, au contenu idoine (et bien entendu en anglais). - public Criminal getCriminal(final int criminalId) Retourne la fiche du criminel associée au "criminalId", ou null si la fiche n'existe pas. - public String getCriminalList() Retourne une String représentant l'ensemble du répertoire. Si le répertoire est vide elle retourne "--- Empty Database ---". Si le répertoire contient au moins une fiche elle retourne : - "--- Database Listing ---" sur une première ligne - les descriptions pour chaque fiche, triées par criminalId, avec une ligne vide *entre* chaque. - "--- End Of Listing ---" sur la dernière ligne. - public Criminal removeCriminal(final int criminalId) Enlève du répertoire la fiche associée au "criminalId" si elle existe, sinon ne fait rien. Vous aurez besoin d'implémenter ces 2 méthodes private pour réaliser la suite de ce que nous vous demandons : - private void readObject(final ObjectInputStream ois) throws ClassNotFoundException, IOException - private void writeObject(final ObjectOutputStream oos) throws IOException Pour finir on vous demande d'implémenter le mécanisme suivant : - nous souhaitons qu'à chaque demande de sérialisation de la classe CriminalList, soit sérialisé un objet de type Date correspondant à l'heure courante. De plus, on affiche sur la sortie standard cette date au format suivant : Saved on 2004.03.22 at 23:14:03 - de la même manière, à chaqe demande de désérialisation de la classe CriminalList, on récupère la date de dernière sauvegarde (celle qui a été stockée dans le fichier, pas celle de dernière modification du fichier) et on l'affiche : Was saved on 2004.03.22 at 23:14:03 Les seules méthodes d'écriture sérialisée autorisées sont : writeInt, writeObject, writeUTF. La symétrique vaut pour la désérialisation. Enfin, réalisez un main qui s'occupe uniquement de lire un CriminalList auparavant sérialisé. Une fois lu, le main fais un System.out.println(...) sur un appel à la méthode getCriminalList(). Vous devez gérer les erreurs suivantes : Retour d'erreur -> Message affiché sur le flux standard d'erreur. 1 -> Usage: java org.insia.interpol.CriminalList 2 -> File is not a regular file or does not exist. 3 -> Cannot read . (Remplacez correctement dans 2 et 3). Ainsi qu'un message d'erreur "The class format is invalid." si vous rencontrez un problème de versionId ou de champ non sérialisé. ------------------------------------------------------------------------------ Ressources ------------------------------------------------------------------------------ http://profs.insia.org/~tdd/sigl1/java http://java.sun.com/developer/technicalArticles/Programming/serialization/ http://java.sun.com/products/jdk/serialization/faq/ http://java.sun.com/j2se/1.4.2/docs/guide/serialization/examples/index.html -- FIN DE SUPPORT TP --