Licence CC BY-NC-SA

[TP] Logiciel de gestion de bibliothèque

Bienvenue dans le premier TP de la partie III, c'est-à-dire le second TP de ce cours. Comme je vous le disais dans le précédent chapitre, nous allons cette fois créer un logiciel pour gérer votre collection de CD, DVD, VHS, BluRay… Plus précisément, notre programme permettra d'enregistrer un film, un album de musique, un jeu vidéo ou un autre type de donnée, mais aussi de consulter a posteriori la liste des œuvres. Je vous propose d'appeler notre programme Maktaba, ce qui signifie «Bibliothèque» en Swahili (pourquoi pas ? On a bien Ubuntu, Amarok…).

Cela nous permettra de réutiliser les types de données structurés (pour enregistrer un film, il faut indiquer son titre, le type de support, son genre…), les types énumérés (les supports CD, DVD…), les fichiers binaires ou texte (pour enregistrer notre liste d'œuvres ou exporter des informations sur une œuvre), les strings (pour enregistrer les titres des morceaux de musique par exemple) ou encore les packages (notre programme devrait être assez conséquent).

Cette fois encore, je commencerai ce TP en vous fournissant un cahier des charges : que veut-on comme fonctionnalités ? Comme données ? Quelle structure pour nos fichiers et notre code source ? Puis, je vous guiderai dans la conception du programme : nous ne réaliserons pas tout en seule fois, je commencerai par vous demander de réaliser un programme simple avant d'ajouter des fonctionnalités supplémentaires ou de prendre en charge des cas particuliers. Enfin, je vous transmettrai les sources et les spécifications d'une solution possible (bien entendu il n'y a pas qu'une seule solution mais plusieurs, chacune ayant ses avantages et inconvénients). En conclusion, comme pour le premier TP, je vous soumettrai quelques idées d'améliorations possibles de notre programme.

Prêt à démarrer ce nouveau challenge ? Alors au travail ! :soleil:

Cahier des charges

Quelles données pour quels types de données ?

Pour établir les types dont nous pourrions avoir besoin, nous devons lister les données nécessaires à notre programme pour l'enregistrement ou la lecture.

Le contenu d'une œuvre

Nous souhaitons enregistrer des œuvres, mais qu'est-ce qu'une œuvre ? C'est avant tout :

  • un titre : «autant en emporte le vent», «Mario bros 3», «Nevermind»…
  • une catégorie : FILM, JEU (VIDÉO), ALBUM (DE MUSIQUE), AUTRE…
  • un support : CD, DVD, BLURAY, VHS, HDDVD…
  • une note : de zéro à trois étoiles.

Ensuite, selon la catégorie de l'œuvre, d'autres informations peuvent être nécessaires. Pour un film, nous aurons besoin :

  • du nom du réalisateur ;
  • de savoir si le film est en VF.

Pour un jeu vidéo, nous aurons besoin :

  • de la console : Nes, PS1, PC, Nintendo64…;
  • de savoir si vous avez terminé le jeu.

Pour un album de musique, nous aurons besoin :

  • du nom de l'artiste : «Nirvana», «ACDC», «Bob Marley»…
  • D'une liste des morceaux dans l'ordre : «Come as you are», «Something in the way»…

Il serait bon également qu'un type d'oeuvre par défaut existe.

Des types en cascade

Suite à cela, vous avez du comprendre que nous aurons besoin d'un type T_oeuvre qui soit structuré et polymorphe (voire même mutable, ce serait encore mieux et je vous le conseille fortement). Ce type structuré devrait comprendre de nombreuses composantes de types aussi divers que du texte (string mais pas d'unbounded_string, je vous expliquerai plus tard pourquoi), des tableaux, des booléens, des types énumérés (pour les supports ou la catégorie)…

Rien que ça ? T'avais pas plus long ? :(

C'est en effet beaucoup et en même temps bien peu ! Pensez que nous aurions pu enregistrer les durées des films ou des morceaux, si les films sont en VOSTFR, la pochette de l'album par exemple, l'emplacement où est sensé être rangé le CD (sur l'étagère, dans la tour, chez le voisin…) ou encore sa date d'achat ou de parution… Ayez en tête que la plupart des logiciels actuels sont bien plus complexes que cela, notre programme n'est que peu de chose à côté. Toutefois, il vous sera possible de le perfectionner plus tard.

Tout cela laisse supposer que nous devrions créer un package spécifique pour déclarer notre type T_Oeuvre afin de libérer notre code source principal. Enfin, dernière information, nos textes devant être enregistrés dans des fichiers, nous ne pourrons pas utiliser les unbounded_string, mais seulement les string. L'explication technique vous sera révélée à la fin de la partie III, lors du chapitre sur les listes. Mais cela implique donc que vos strings devront être suffisamment longs pour pouvoir accueillir des titres à rallonge comme celui-ci, extrait d'un album de Nirvana: «Frances Farmer will have her revenge on Seattle».

Les types de fichiers

Qui dit enregistrement, dit nécessairement fichiers. Aux vues de notre type structuré T_Oeuvre, cela signifie que nous aurions besoin d'un fichier binaire pour jouer le rôle de base de donnée (séquentiel ou à accès direct, c'est à vous de voir. Pour ma part, j'ai choisi les fichiers séquentiels dont la manipulation sera plus simple pour vous). Il serait même bon de séparer les bases de données (une pour les jeux, une pour la musique… ). Ces fichiers porteront par conséquent les noms de «ListeJeu.bdd», "ListeAlbum.bdd", «ListeFilm.bdd» et «ListeAutre.bdd» (bdd = Base De Données).

Quelle architecture pour les fichiers

Notre programme ayant besoin de divers types de fichiers, il serait judicieux de ne pas tout mélanger.

  • Maktaba.exe devra se trouver dans un répertoire Maktaba, libre à vous de placer un raccourci sur le bureau si vous le souhaitez.
  • Les bases de données ListeJeu.bdd et autres devront se trouver dans un sous-répertoire «data».
  • Un sous-répertoire «Manual» permettra l'enregistrement d'un fichier texte.

Cela nous fait donc un répertoire principal et deux sous-répertoires.

Quelles fonctionnalités pour quelles fonctions et procédures ?

Maktaba.exe devra proposer les fonctionnalités suivantes :

  • Saisie d'une nouvelle œuvre par l'utilisateur.
  • Enregistrement d'une nouvelle œuvre par l'utilisateur (ajout dans la base de donnée).
  • Modification par l'utilisateur d'une œuvre existante.
  • Suppression par l'utilisateur d'une œuvre existante.
  • Affichage de la base de donnée ou d'une oeuvre dans la console (sous la forme d'un tableau).
  • Accès aux fonctionnalités par lignes de commande : l'utilisateur devra taper add, modify, delete, print pour accéder à une fonctionnalité. Le programme gèrera bien-sûr d'éventuelles erreurs de frappe commises par l'utilisateur.
  • Possibilité d'accéder à un manuel en console par la commande manual. Ce manuel sera rédigé dans un fichier texte «manual.txt» placé dans le sous-répertoire «manual» évoqué précédemment et décrira les différentes commandes possibles.

Architecture du code source

Argh ! ! ! :waw: Mais jamais je ne parviendrai à faire tout ça ! Je savais bien que je n'aurais pas du me lancer dans cette galère ! :'(

Gardez espoir ! Ce ne sera pas aussi compliqué que cela peut paraître. En revanche, cela risque d'être long (notamment le codage des procédures de saisie et d'affichage), donc il sera nécessaire d'y aller étape par étape et d'adopter une approche par modules (tiens, ça devrait vous rappeler les packages ça). Nous aurons donc à créer les fichiers Ada suivants :

  • Maktaba.adb : la procédure principale qui ne se chargera que de l'interface utilisateur, du cœur du logiciel.
  • Maktaba_Types.ads : pour déclarer nos types et nos constantes.
  • Maktaba_Functions.adb et Maktaba_Functions.ads : pour les différentes fonctionnalités liées à la base de données (lecture, saisie, enregistrement, affichage).

Conception du programme (suivez le guide)

Cette partie n'est pas obligatoire, elle permettra toutefois à ceux qui hésitent à se lancer ou qui ne voient pas comment faire, de trouver une méthode ou des voies pour programmer. Attention, il ne s'agit pas d'une solution toute faite (celle-ci sera fournie à la fin) mais plutôt d'un guide et je vous invite à essayer de réaliser ce TP par vous même, en recourant le moins possible à cette partie.

Création des types

Nous allons commencer par créer deux fichiers : Maktaba.adb et Maktaba_Types.ads. Notre fichier Maktaba.adb ne contiendra pour l'instant pas grand chose :

1
2
3
4
5
6
7
8
WITH Maktaba_Types ;                   USE Maktaba_Types ; 
WITH Ada.Text_IO ;                     USE Ada.Text_IO ; 

PROCEDURE Maktaba IS
   Oeuvre : T_Oeuvre ;
BEGIN
   
END Maktaba ;

Comme vous pouvez le constater il n'y a quasiment rien. Nous allons nous concentrer sur le fichier Maktaba_Types.ads et les différents types : nous devons créer un type structuré et polymorphe (et même mutable) T_Oeuvre. Ce type devra contenir différentes composantes :

  • titre de l’œuvre, realisateur, console et artiste seront des strings (avec les contraintes que cela implique en terme de taille), mais surtout pas des unbounded_string (cela poserait problème pour l'enregistrement). Il serait bon que les strings soient initialisés.
  • Categorie et support seront des types énumérés.
  • VF et Termine seront des boolean (vrai ou faux).
  • Note sera un sous-type natural de 0 à 3 ou Integer (voire modulaire).
  • Morceaux sera un tableau de 30 strings (ou plus).

À vous donc de créer ce type T_Oeuvre ainsi que les types énumérés T_Categorie et T_Support et le sous-type T_Note. Pensez toutefois que, pour être polymorphe, T_Oeuvre doit dépendre de la catégorie de l'oeuvre et que pour être mutable, cette catégorie doit être initialisée :

1
2
3
4
5
6
7
8
type T_Oeuvre(categorie : T_Categorie := #Une_Valeur#)is
   record
   ...
   case categorie is
      when FILM => ...
      ...
   end case ; 
end record ;

Affichage d'une œuvre

Il est temps de créer nos fichiers Maktaba_Functions.adb et Maktaba_Functions.ads ! Nous aurons besoin à l'avenir d'afficher l'intégralité de notre base de données, mais avant d'afficher 300 œuvres, nous devrions créer une fonction ou procédure qui en affiche une et une seule. Cela nous permettra d'avoir dors et déjà un programme Maktaba.exe opérationnel qui saisirait une œuvre (arbitrairement pour l'instant) puis l'afficherait.

1
procedure Affichage(oeuvre : T_Oeuvre) ;

Pensez à faire un affichage compact et clair : il y aura à terme des dizaines d'oeuvres ! Pensez également à terminer l'affichage par un ou plusieurs new_line pour éviter les soucis d'affichage plus tard.

Saisie d'une oeuvre

La première chose à faire par la suite sera de créer une procédure ou une fonction de saisie d'une œuvre. Le sous-programme de saisie ne sera pas compliqué à mettre en oeuvre mais sera long à rédiger car il devra prévoir toutes les composantes. Mais avant de vous lancer dans la saisie d'une œuvre, je vous conseille d'implémenter une fonction de saisie de string. Il existe bien get_line ou get, mais si vous souhaitez saisir un string de taille 20, il ne faut pas que l'utilisateur saisisse un texte de 35 ou 3 caractères. Or l'utilisateur final n'aura généralement aucune connaissance de ces contraintes, donc je conseille de commencer par là :

1
function get_text(taille : natural) return string ;

Autre indication pour simplifier votre code, il serait bon que votre fonction de saisie d’œuvre (appelons-la Saisie_Oeuvre) ne s’occupe pas de la saisie de la catégorie. Ce travail sera effectué par une fonction tierce (Saisie_Categorie) qui fournira à la première la catégorie à saisir. Cela simplifiera grandement votre travail et votre réflexion. De manière générale, une grande partie de vos fonctions et procédures devraient avoir la catégorie de l'œuvre en paramètre.

1
2
Function Saisie_Categorie return T_Categorie ; 
Function Saisie_Oeuvre(Cat : T_Categorie) return T_Oeuvre ;

Les saisies de strings se feront avec notre fonction get_text. En revanche, les saisies d'entiers devront gérer les cas où l'utilisateur entrerait une note supérieure à 3 :

1
2
3
TANT QUE choix>3
 | Saisir(choix)
FIN DE BOUCLE

De même, pour saisir un booléen ou un type structuré, vous pourrez proposer à l'utilisateur un choix similaire à celui-ci :

1
2
3
4
5
Votre film est enregistré sur : 
   1. un CD
   2. un DVD
   3. une VHS
Votre film est-il en VF ? (O : oui / N : Non) _

Donc prévoyez les cas où l'utilisateur répondrait de travers pour limiter les plantages.

Gestion des fichiers

La plupart des opérations suivantes se feront uniquement sur les fichiers : sauvegarde dans la base de données (bdd), affichage d'une bdd, modification d'un élément d'une bdd, suppression d'un élément d'une bdd… Nous devrons créer un package pour manipuler des fichiers binaires. Comme dit précédemment, je vous invite à utiliser les fichiers séquentiels plutôt qu'à accès direct pour éviter de rajouter de la difficulté à la difficulté (bien sûr vous êtes libres de votre choix, le type de fichier binaire ne fait pas partie du cahier des charges).

Sauvegarde et affichage avec la BDD

L'implémentation de ces deux fonctionnalités ne devrait pas poser de problème. Il vous suffira d'ouvrir un fichier, de le fermer, en pensant entre temps à soit ajouter un élément (Append_File), soit parcourir le fichier pour le lire (In_file).

Mais si vous avez essayé d'implémenter ces fonctionnalités, vous avez du vous rendre compte qu'elles exigent toutes les deux de commencer par traiter une question toute bête : «Quel fichier dois-je ouvrir ? ». Et cette question, vous devrez vous la reposer à chaque fois. Il y a donc plusieurs façons de faire : soit on la joue gros bourrin et alors «Vive le copier-coller !», soit on est un peu plus futé et on rédige une procédure qui se charge d'ouvrir le bon fichier selon la catégorie fournie en paramètre. Ce paramètre peut être lui-même fourni par la fonction Saisie_categorie évoquée précédemment.

1
2
3
procedure Ouvrir(cat : T_Categorie) ; 
procedure Affichage_BDD(cat : T_Categorie) ; 
procedure Sauvegarde(Oeuvre : T_Oeuvre) ;

Modification et suppression d'un élément de la BDD

Le plus simple pour effectuer cette opération est de ne manipuler que des fichiers : pas la peine de se casser la tête à tenter de supprimer un élément du fichier. Voici une méthode pour supprimer l'élément numéro N d'un fichier F :

1
2
3
4
5
6
7
8
9
Ouvrir le fichier F (Nom : truc)
Ouvrir le fichier G (Nom : truc2)
Copier les (N-1) premiers éléments de F dans G
sauter le N-ème élément de F
Copier le reste des éléments de F dans G
Supprimer F
Recreer F (Nom : Truc) comme une copie de G
Fermer F
Fermer G

Un raisonnement similaire peut être effectué pour la modification de la BDD.

Affichage du manuel

Là, j'espère bien que vous n'avez pas besoin de moi ! :colere:

Les commandes

Jusque là, notre fichier Maktaba.adb ne contient rien de bien sérieux, il ne nous sert qu'à tester nos procédures et fonctions. Mais puisque nous avons fini, nous allons pouvoir le rédiger correctement. L'idée ici est simple : si l'utilisateur tape un mot particulier, le programme réalise une opération particulière. Nous allons donc réutiliser notre fonction get_text pour la saisie des commandes. Le corps de la procédure principale sera simple : une boucle infinie qui se contente de demander de saisir du texte et qui, si le texte correspond à une commande connue, lance quelques sous-programmes déjà rédigés. L'un de ces strings entraînera bien entendu la sortie de la boucle (commande : quit ou exit par exemple). En cas d'erreur de l'utilisateur, le programme affichera toutefois une phrase du genre «Si vous ne comprenez rien, vous n'avez qu'à taper Manual pour lire ce #*§%\$€ de Manuel (RTFM)» (en plus aimable bien sûr :-° ) de façon à ne pas laisser l'utilisateur sans indications.

Solutions possibles

Comme promis, voici une solution possible à comparer avec votre travail.

Maktaba.adb :

 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
------------------------------------------------------------------------------
--                                                                          --
--                               PROJET MAKTABA                             --
--                                                                          --
--                              MAKTABA.ADS                                 --
--                                                                          --
--                                                                          --
-- AUTEUR : KAJI9                                                           --
-- DATE : 13/11/2011                                                        --
--                                                                          --
-- Contient la procédure principale du logiciel MAKTBA ainsi que les        --
-- procédures d'affichage et de saisie.                                     --
--                                                                          --
------------------------------------------------------------------------------


--Retrouvez le tuto à l'adresse suivante http://www.siteduzero.com/tutoriel-3-558031-1-second-tp-un-logiciel-de-gestion-de-bibliotheque.html


with Maktaba_Types ;                        use Maktaba_Types ;
with Maktaba_Functions ;                    use Maktaba_Functions ;
with Ada.Characters.Handling ;              use Ada.Characters.Handling ;
with Ada.Text_IO ;                          use Ada.Text_IO ;

procedure Maktaba is
   oeuvre  : T_Oeuvre ;
   reponse : string(1..6) ;
begin
   loop
      Put("> ") ; reponse := Get_Text(6) ; 
      reponse := To_Upper(Reponse) ; 
      if reponse = "QUIT  " or reponse = "EXIT  "
         then exit ;
      elsif reponse = "NEW   "
         then oeuvre := saisie(get_categorie) ;
              sauvegarde(oeuvre) ;
      elsif reponse = "MANUAL"
         then Affichage_Manuel ;
      elsif reponse = "INIT  " or reponse = "ERASE " 
         then creation(get_categorie) ;
      elsif reponse = "PRINT "
         then affichage_bdd(get_categorie) ;
      elsif reponse = "EDIT  "
         then Edit_bdd(get_categorie) ;
         else put_line("Commande inconnue. Pour plus d'informations, tapez l'instruction MANUAL. ") ; 
      end if ; 
   end loop ;
end Maktaba ;

Maktaba_Types.ads :

 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
------------------------------------------------------------------------------
--                                                                          --
--                               PROJET MAKTABA                             --
--                                                                          --
--                              MAKTABA_TYPES.ADS                           --
--                                                                          --
--                                                                          --
-- AUTEUR : KAJI9                                                           --
-- DATE : 13/11/2011                                                        --
--                                                                          --
-- Contient les différents types nécessaires au fonctionnement du logiciel  --
-- MAKTABA.                                                                 --
--                                                                          --
------------------------------------------------------------------------------


--Retrouvez le tuto à l'adresse suivante http://www.siteduzero.com/tutoriel-3-558031-1-second-tp-un-logiciel-de-gestion-de-bibliotheque.html




package Maktaba_Types is

   type T_Categorie is (FILM,JEU,ALBUM,AUTRE) ;
   type T_Support is (CD, DVD, BLURAY, VHS, HDDVD) ;
   type T_Morceaux is array(1..30) of string(1..50) ;

   type T_Oeuvre(categorie : T_Categorie := AUTRE) is
   record
      titre : string(1..50) := (others => ' ') ;
      support : T_Support := CD ;
      note : natural range 0..3 ;

      case categorie is
         when FILM  => realisateur : string(1..20) := (others => ' ') ;
                       VF : boolean := false ;
         when JEU   => console : string(1..20) := (others => ' ') ;
                       termine : boolean := false ;
         when ALBUM => artiste  : string(1..20) := (others => ' ') ;
                       morceaux : T_Morceaux := (others =>(others => ' ')) ;
         when AUTRE => null ;
      end case ;
   end record ;

   type T_Film is new T_Oeuvre(FILM) ;
   type T_Jeu is new T_Oeuvre(JEU) ;
   type T_Album is new T_Oeuvre(ALBUM) ;
   type T_Autre is new T_Oeuvre(AUTRE) ;

end ;

Maktaba_Functions.ads :

 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
------------------------------------------------------------------------------
--                                                                          --
--                               PROJET MAKTABA                             --
--                                                                          --
--                              MAKTABA_FUNCTIONS.ADS                       --
--                                                                          --
--                                                                          --
-- AUTEUR : KAJI9                                                           --
-- DATE : 13/11/2011                                                        --
--                                                                          --
-- Contient les fonctions de saisie, d'affichage, de sauvegarde… du      --
-- logiciel MAKTABA.                                                        --
--                                                                          --
------------------------------------------------------------------------------


--Retrouvez le tuto à l'adresse suivante http://www.siteduzero.com/tutoriel-3-558031-1-second-tp-un-logiciel-de-gestion-de-bibliotheque.html

with Maktaba_Types ;                        use Maktaba_Types ;
WITH Ada.Sequential_IO ;


package Maktaba_Functions is

   Package P_Fichier  is new Ada.Sequential_IO(T_Oeuvre) ;
   use P_Fichier ;
   subtype T_Fichier is P_Fichier.File_type ;

   procedure sauvegarde(Oeuvre : in T_Oeuvre) ;

   procedure sauvegarde(oeuvre : in T_Oeuvre ; rang : natural) ;

   procedure creation(cat : T_categorie) ;

   procedure Ouvrir(F: in out T_Fichier ; mode : P_Fichier.File_Mode := P_Fichier.In_file ; cat : T_Categorie);

   procedure supprimer(cat : T_Categorie ; rang : natural) ;

      --   SAISIES

   function get_text(size : integer) return string ;

   function get_categorie return T_Categorie ;

   function saisie(cat : T_Categorie) return T_Oeuvre ;

      --   MANIPULATION DE LA BDD

   procedure affichage_oeuvre(Oeuvre : in T_Oeuvre) ;

   procedure affichage_bdd(cat : T_categorie) ;

   procedure Edit_bdd(cat : T_Categorie) ;

      --   Manuel

   procedure affichage_manuel ;

end Maktaba_Functions ;

Maktaba_Functions.adb :

  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
------------------------------------------------------------------------------
--                                                                          --
--                               PROJET MAKTABA                             --
--                                                                          --
--                              MAKTABA_FUNCTIONS.ADB                       --
--                                                                          --
--                                                                          --
-- AUTEUR : KAJI9                                                           --
-- DATE : 13/11/2011                                                        --
--                                                                          --
-- Contient les fonctions de saisie, d'affichage, de sauvegarde ... du      --
-- logiciel MAKTABA.                                                        --
--                                                                          --
------------------------------------------------------------------------------


--Retrouvez le tuto à l'adresse suivante http://www.siteduzero.com/tutoriel-3-558031-1-second-tp-un-logiciel-de-gestion-de-bibliotheque.html



with Ada.Text_IO ;                          use Ada.Text_IO ;
with Ada.Integer_Text_IO ;                  use Ada.Integer_Text_IO ;
with Ada.Characters.Handling ;              use Ada.Characters.Handling ;
with ada.Strings.Unbounded;                 Use ada.Strings.Unbounded ;


package body Maktaba_Functions is


               -----------------------------------------------
               --            GESTION DES FICHIERS           --
               -----------------------------------------------


procedure sauvegarde(Oeuvre : in T_Oeuvre) is
   F : T_Fichier ;
begin
   ouvrir(F,Append_File,oeuvre.categorie) ;
   write(F,Oeuvre) ;
   close(F) ;
end sauvegarde ;


procedure sauvegarde(oeuvre : in T_Oeuvre ; rang : natural) is
   F,G : T_Fichier ;
   tmp : T_Oeuvre ;
begin
      --   Ouverture de F en In et de G en Append
   ouvrir(F,In_File,oeuvre.categorie) ;
   create(G,Append_File,Name(F) & "2") ;
      --   copie de F+oeuvre dans G
   for i in 1..rang-1 loop
      read(F,tmp) ;
      write(G,tmp) ;
   end loop ;
   write(G,oeuvre) ;
   read(F,tmp) ; --lecture de l'élément à supprimer
   while not end_of_file(F) loop
      read(F,tmp) ;
      write(G,tmp) ;
   end loop ;
      --   Suppression de F / recréation de F
      --   fermeture de G / réouverture de G
   delete(F) ;
   creation(oeuvre.categorie) ;
   ouvrir(F,Append_File,oeuvre.categorie) ;
   close(G) ;
   open(G,In_File,Name(F) & "2") ;
      --   copie de G dans F
   while not end_of_file(G) loop
      read(G,tmp) ;
      write(F,tmp) ;
   end loop ;
      --Fermeture de F / Suppression de G
   close(F) ;
   delete(G) ;
end sauvegarde ;


procedure Creation(cat : T_categorie) is
   F : T_Fichier ;
begin
   case cat is
      when FILM => create(F,out_file,"./data/ListeFilm.bdd") ;
                   close(F) ;
      when JEU => create(F,out_file,"./data/ListeJeu.bdd") ;
                  close(F) ;
      when ALBUM => create(F,out_file,"./data/ListeAlbum.bdd") ;
                    close(F) ;
      when AUTRE => create(F,out_file,"./data/ListeAutre.bdd") ;
                    close(F) ;
   end case ;
end creation ;

procedure Ouvrir(F: in out T_Fichier ;
                 mode : P_Fichier.File_Mode := P_Fichier.In_file ;
                 cat : T_Categorie) is
begin
   case cat is
      when FILM  => open(F,mode,"./data/ListeFilm.bdd") ;
      when JEU   => open(F,mode,"./data/ListeJeu.bdd") ;
      when ALBUM => open(F,mode,"./data/ListeAlbum.bdd") ;
      when AUTRE => open(F,mode,"./data/ListeAutre.bdd") ;
   end case ;
end Ouvrir;

procedure supprimer(cat : T_Categorie ; rang : natural) is
   F,G : T_Fichier ;
   tmp : T_Oeuvre ;
begin
      --   Ouverture de F en In et de G en Append
   ouvrir(F,In_File,cat) ;
   create(G,Append_File,Name(F) & "2") ;
      --   copie de F-1 oeuvre dans G
   for i in 1..rang-1 loop
      read(F,tmp) ;
      write(G,tmp) ;
   end loop ;
   read(F,tmp) ;
   while not end_of_file(F) loop
      read(F,tmp) ;
      write(G,tmp) ;
   end loop ;
      --   Suppression de F / recréation de F
      --   fermeture de G / réouverture de G
   delete(F) ;
   creation(cat) ;
   ouvrir(F,Append_File,cat) ;
   close(G) ;
   open(G,In_File,Name(F) & "2") ;
      --   copie de G dans F
   while not end_of_file(G) loop
      read(G,tmp) ;
      write(F,tmp) ;
   end loop ;
      --Fermeture de F / Suppression de G
   close(F) ;
   delete(G) ;
end supprimer ;


               ----------------------------------
               --            SAISIE            --
               ----------------------------------

   function get_text(size : integer) return string is
      U : Unbounded_String := Null_Unbounded_String ;
      T : string(1..size) := (others => ' ') ;
   begin
      U := to_unbounded_string(get_line) ;
      if length(U) > size
         then T := to_string(U)(1..size) ;
         else T(1..length(U)) := to_string(U) ;
              for i in length(U)+1..size loop
                 T(i) := ' ' ; 
              end loop ; 
      end if ;
      return T ;
   end get_text ;


   function get_categorie return T_Categorie is
      choix_cat : character ;
   begin
      Put_line("Choisissez la categorie desiree : ") ;
      Put_line("F-Film              M-Album de Musique") ;
      Put_line("J-Jeu Video         Autre ?") ;
      Get_Immediate(choix_cat) ; choix_cat := to_upper(choix_cat) ;
      case choix_cat is
         when 'F'    => return FILM ;
         when 'M'    => return ALBUM ;
         when 'J'    => return JEU;
         when others => return AUTRE ;
      end case ;
   end get_categorie ;

   function saisie(cat : T_Categorie) return T_Oeuvre is
      oeuvre : T_Oeuvre(cat) ;
      choix : character ;
      note : integer ;
   begin
            --        SAISIE DES PARAMETRES COMMUNS
      Put_line("Quel est le titre de l'oeuvre ? ") ;
      oeuvre.titre := get_text(50) ;
      Put_line("Quelle note donneriez-vous ? (Entre 0 et 3) ") ;
      loop
         get(note) ; skip_line ;
         if note in 0..3
            then oeuvre.note := note ;
                 exit ;
            else Put_line("ERREUR ! La note doit être comprise entre 0 et 3 !") ;
         end if ;
      end loop ;

      Put_line("Sur quel type de support l'oeuvre est-elle enregistree ?") ;
      Put_line("1-VHS      2-CD       3-DVD") ;
      Put_Line("4-HDDVD    5-BLURAY") ;
      loop
         get_immediate(choix) ; choix := to_upper(choix) ;
         case choix is
            when '1'    => oeuvre.support := VHS ; exit ;
            when '2'    => oeuvre.support := CD ; exit ;
            when '3'    => oeuvre.support := DVD ; exit ;
            when '4'    => oeuvre.support := HDDVD ; exit ;
            when '5'    => oeuvre.support := BLURAY ; exit ;
            when others => Put_line("Veuillez reconfirmer votre choix.") ;
         end case ;
      end loop ;
                  --       SAISIE DES PARAMETRES SPECIFIQUES
      case cat is
         when FILM => Put_line("Quel est le realisateur ? ") ;
                     oeuvre.realisateur:= get_text(20) ;
                     Put_line("Le film est-il en VF ? (O : Oui / N : Non)") ;
                     loop
                        get_immediate(choix) ; choix := to_upper(choix) ;
                        if choix = 'O'
                           then oeuvre.vf := true ; exit ;
                        elsif choix = 'N'
                           then oeuvre.vf := false ; exit ;
                           else Put_line("Veuillez appuyer sur O pour Oui ou sur N pour Non") ;
                        end if ;
                     end loop ;
                     return oeuvre ;
         when ALBUM => Put_line("Quel est l'artiste ? ") ;
                     oeuvre.artiste := get_text(20) ;
                     for i in oeuvre.morceaux'range loop
                        Put_line("Voulez-vous ajouter un morceau ? (O : Oui / N : Non)") ;
                        get_immediate(choix) ; choix := to_upper(choix) ;
                        if choix = 'O'
                           then Put_line("Quel est le titre du morceau ? ") ;
                                oeuvre.morceaux(i) := get_text(50) ;
                           else exit ;
                        end if ;
                     end loop ;
                     return oeuvre ;
         when JEU => Put_line("Quelle est la console ? ") ;
                     oeuvre.console := get_text(20) ;
                     Put_line("Avez-vous fini le jeu ? (O : Oui / N : Non)") ;
                     loop
                        get_immediate(choix) ; choix := to_upper(choix) ;
                        if choix = 'O'
                           then oeuvre.termine := true ; exit ;
                        elsif choix = 'N'
                           then oeuvre.termine := false ; exit ;
                           else Put_line("Veuillez appuyer sur O pour Oui ou sur N pour Non") ;
                        end if ;
                     end loop ;
                     return oeuvre ;
         when AUTRE => return oeuvre ;
      end case ;
   end Saisie ;

               -----------------------------------------------
               --            AFFICHAGE D'UNE BDD            --
               -----------------------------------------------

procedure affichage_oeuvre(oeuvre : T_oeuvre) is                     --Affiche une seule oeuvre
   null_string : constant string(1..50) := (others => ' ') ;
begin
   put(" >>>Titre       : ") ; put(Oeuvre.titre) ; new_line ;
   put(" >>>Support     : ") ; put(T_Support'image(Oeuvre.support)) ; new_line ;
   put(" >>>Note        : ") ; put(Oeuvre.note,1) ; new_line ;
   case oeuvre.categorie is
      when FILM => put(" >>>Realisateur : ") ; put(Oeuvre.realisateur) ; new_line ;
                   put(" >>>VF          : ") ;
                   if Oeuvre.vf
                      then put("oui") ;
                      else put("non") ;
                   end if ; new_line(2) ;
      when JEU => put(" >>>Console     : ") ; put(Oeuvre.console) ; new_line ;
                  put(" >>>Termine     : ") ;
                  if Oeuvre.termine
                     then put("oui") ;
                     else put("non") ;
                  end if ; new_line(2) ;
      when ALBUM => put(" >>>Artiste     : ") ; put(Oeuvre.artiste) ; new_line ;
                    put_line(" >>>Morceaux    : ") ;
                    for i in Oeuvre.morceaux'range loop
                       exit when Oeuvre.morceaux(i) = null_string ;
                       put("     ") ; put(i,2) ; put(" : ") ; Put(oeuvre.morceaux(I)); New_Line;
                    end loop ;
                    new_line(2) ;
      when AUTRE => new_line ;
   end case ;
end affichage_oeuvre ;


procedure affichage_bdd(cat : T_categorie) is                        --Affiche toute une base de données selon la catégorie demandée
   n : natural := 1 ;
   suite : character ;
   Oeuvre : T_Oeuvre(cat) ;
   F : T_Fichier ;
begin
   ouvrir(F,In_File,cat) ;

   while not end_of_file(F) loop
      if n = 0
         then put("Cliquez pour continuer") ;
         get_immediate(suite) ; new_line ;
      end if ;
      read(F,Oeuvre) ;
      Affichage_oeuvre(oeuvre) ;
      n := (n + 1) mod 5 ;
   end loop ;
   close(F) ;
end Affichage_bdd ;

               ------------------------------------------------------
               --            EDITION DE LA BASE DE DONNEES         --
               ------------------------------------------------------

procedure Edit_bdd(cat : T_Categorie) is                                                   --Edite une base de données pour modification ou suppression
   choix : character ;
   n : natural := 1 ;
   Oeuvre : T_Oeuvre(cat) ;
   F : T_Fichier ;
begin
   ouvrir(F,In_File,cat) ;

   while not end_of_file(F) loop
      read(F,Oeuvre) ;
      Affichage_oeuvre(oeuvre) ;
      put_line("Que voulez-vous faire : Supprimer(S), Modifier(M), Quitter(Q) ou Continuer ?") ;
      Get_Immediate(choix) ; choix := to_upper(choix) ;
      if choix = 'M'
         then close(F) ;
              oeuvre := saisie(cat) ;
              sauvegarde(oeuvre,n) ;
              exit ;
      elsif choix = 'S'
         then close(F) ;
              Supprimer(cat,n) ;
              exit ;
      elsif choix = 'Q'
         then close(F) ;
              exit ;
      end if ;
      n := n + 1 ;
   end loop ;

   if Is_Open(F)
      then close(F) ;
   end if ;
end Edit_bdd ;


               ------------------------------------------------------
               --            EDITION DE LA BASE DE DONNEES         --
               ------------------------------------------------------

procedure affichage_manuel is
   F : Ada.text_IO.File_Type ;
begin
   open(F,In_File,"./manual/manual.txt") ;
   while not end_of_file(F) loop
      put_line(get_line(F)) ;
   end loop ;
   close(F) ;
end affichage_manuel ;

end Maktaba_Functions ;

Manual.txt :

1
2
3
4
5
6
7
Commandes : 
     Quitter                : Quit / Exit
     Nouveau                : New
     Manuel                 : Manual
     Réinitialiser une base : Init / Erase
     Afficher une base      : Print
     Modifier une base      : Edit

Pistes d'amélioration :

  • Fournir une interface graphique plutôt que cette vilaine console. Pour l'instant, vous ne pouvez pas encore le faire, mais cela viendra.
  • Proposer de gérer d'autres types de supports : disque durs, clés USB…
  • Proposer l'enregistrement de nouvelles informations : le genre de l’œuvre (film d'horreur ou d'aventure, album rock ou rap…), une description, les acteurs du film, les membres du groupe de musique, l'éditeur du jeu vidéo, la durée des morceaux…
  • Proposer l'exportation de vos bases de données sous formes de fichiers textes consultables plus aisément ou l'importation d'une œuvre à partir d'un fichier texte.
  • Permettre de compléter les instructions en ligne de commande par des options : modify -f modifierait un film par exemple.
  • Implémenter un service de recherche d’œuvre par titre, par auteur ou même par mots-clés !

Encore une fois, ce ne sont pas les idées qui manquent pour développer un tel logiciel. J'espère toutefois que ce TP vous aura permis de faire un point sur les notions abordées depuis le début de la partie III car nous allons maintenant nous atteler à un type de données plus complexe : les pointeurs. Et, de la même manière que nous avons parlé, reparlé et rereparlé des tableaux, nous allons parler, reparler et rereparler des pointeurs dans les prochains chapitres.