IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java

  Chapitre 11 - Le système d’E/S de Java

pages : 1 2 3 4 5 6 7 8 9 10 

La classe MultiStringMap est un outil qui nous permet d'organiser un groupe de chaînes de caractères sur chaque entrée de clé. Il utilise un HashMap (cette fois avec héritage) avec la clé comme simple chaîne de caractère qui est organisée sur la value de l'ArrayList. La méthode add() vérifiesimplement si il y a déjà une clé dans le HashMap, si ce n'est pas le cas elle en ajoute une à cet endroit. La méthode getArrayList() produit une ArrayList pour une clé particulière, et printValues(), qui est essentiellement utile pour le deboguage, affiche toutes les valeurs ArrayList par ArrayList .

Pour avoir rester simple, les noms de classe des bibliothèques standard de Java sont toutes placées dans un objet Properties (de la bibliothèque standard de Java). Rappelons qu'un objet Properties est un HashMap qui ne garde que des objetsString pour à la fois la clé et les valeurs d'entrée. Cependant, il peut être sauvé vers le disque et restauré depuis le disque par un appel de méthode, donc c'est l'idéal pour le dépôt des noms. Actuellement, on ne désire qu'une liste de noms, et un HashMap ne peut pas accepter une null valeur à la fois pour ces clés ou ces valeurs d'entrée.

Pour les classes et identifiants qui sont découvert pour les fichiers d'un certain répertoire, deux MultiStringMaps sont utilisés : classMap et identMap. Aussi, quand le programme démarre il charge le dépôt de nom de classe standard dans l'objet Properties appelé classes, et quand un nouveau nom de classe est découvert dans le répertoire local il est aussi ajouté à classes en plus de classMap. De cette manière, classMap peut être employé pour se déplacer à travers toutes les classes du répertoire local, et classes peut être utilisé pour voir si le token courant est un nom de classe (qui indique la définition d'un objet ou le début d'une méthode, donc saisit le token suivant — jusqu'au point virgule — et le place dans identMap).

Le constructeur par défaut pour ClassScanner crée une liste de noms de fichier, en utilisant JavaFilter implémentation du FilenameFilter, montré à la fin du fichier. Ensuite il appelle scanListing() pour chaque nom de fichier.

DansscanListing() le fichier de code source est ouvert et transformé en un StreamTokenizer. Dans la documentation, passer true de slashStarComments() à slashSlashComments() est supposé enlever ces commentaires, mais cela semble être quelque peu défectueux, car cela semble ne pas fonctionner. Au lieu de cela, ces lignes sont marquées en tant que commentaires et les commentaires sont extrait par une autre méthode. Pour cela, le « / » doit être capturé comme un caractère ordinaire plutôt que de laisser le StreamTokenizer l'absorber comme une partie de commentaire, et la méthode ordinaryChar() dit au StreamTokenizer de faire cela. C'est aussi vrai pour les points (« . »), vu que nous voulons que l'appel de la méthode soit séparé en deux en identifiants individuels. Pourtant, l'underscore (soulignement), qui est ordinairement traité par StreamTokenizer comme un caractère individuel, doit être laissé comme une partie des identifiants puisqu'il apparaît dans des valeurs static final comme TT_EOF, etc., employé dans tant de nombreux programmes. La méthode wordChars() prend une rangée de caractères que l'on désire ajouter à ceux qui sont laissé dans un token qui à) été analysé comme un mot. Finalement, lorsqu'on fait l'analyse pour une ligne de commentaire ou que l'on met de coté une ligne on veux savoir quand arrive la fin de ligne, c'est pourquoi en appelant eolIsSignificant(true) l'EOL (End Of Line : fin de ligne) apparaîtra plutôt que sera absorbé par le StreamTokenizer.

Le reste du scanListing() lit et réagit aux tokens jusqu'à la fin du fichier, annonçant quand nextToken() renvoie la valeur final static StreamTokenizer.TT_EOF.

Si le token est un « / » il est potentiellement un commentaire, donc eatComments() est appelé pour voir avec lui. La seule autre situation qui nous intéresse est quand il s'agit d'un mot, sur lequel il y a des cas spéciaux.

Si le mot est class ou interface alors le prochain token représente une classe ou une interface, et il est placé dans classes et classMap. Si le mot est import ou package, alors on n'a plus besoin du reste de la ligne. Tout le reste doit être un identifiant (auquel nous sommes intéressé) ou un mot-clé (qui ne nous intéresse pas, mais ils sont tous en minuscule de toutes manières donc cela n'abîmera rien de les placer dedans). Ceux-ci sont ajoutés à identMap.

La méthode discardLine() est un outil simple qui cherche la fin de ligne. Notons que caque fois que l'on trouve un nouveau token, l'on doit effectuer le contrôle de la fin de ligne.

La méthode eatComments() est appelée toutes les fois qu'un slash avant est rencontré dans la boucle d"analyse principale. Cependant, cela ne veut pas forcément dire qu'un commentaire ai été trouvé, donc le prochain token doit être extrait pour voir si c'est un autre slash avant (dans ce cas la ligne est rejetée) ou un astérisque. Mais si c'est n'est pas l'un d'entre eux, cela signifie que le token qui vient d'être sorti est nécessaire dans la boucle d'analyse principale ! Heureusement, la pushBack() méthode nous permet de « pousser en arrière » le token courant dans le flux d'entrée pour que quand la boucle d'analyse principale appelle nextToken() elle prendra celui que l'on viens tout juste de pousser en arrière.

Par commodité, la méthode produit un tableau de tous les noms dans le récipient classes. Cette méthode n'est pas utilisée dans le programme mais est utiles pour le déboguage.

Les deux prochaines méthodes sont celles dans lesquelles prend place la réelle vérification. Dans checkClassNames(), les noms des classe sont extraits depuis le classMap (qui, rappelons le, contient seulement les noms dans ce répertoire, organisés par nom de fichier de manière à ce que le nom de fichier puisse être imprimé avec le nom de classe errant). Ceci est réalisé en poussant chaque ArrayList associé et en allant plus loin que ça, en regardant pour voir si le premier caractère est en minuscule. Si c'est le cas, le message approprié est imprimé.

Dans checkIdentNames(), une approche similaire est prise : chaque nom d'identifiant est extrait depuis identMap. Si le nom n'est pas dans la liste classes, il est supposé être un identifiant ou un mot-clé. Un cas spécial est vérifié : si la longueur de l'identifiant est trois et tout les caractères sont en majuscule, cette identifiant est ignoré puisqu'il est probablement une valeur static final comme TT_EOF. Bien sur, ce n'est pas un algorithme parfait, mais il assume que l'on avertira par la suite de tous les identifiants complétement-majuscules qui sont hors sujet.

Au lieu de reporter tout les identifiants qui commencent par un caractère majuscule, cette méthode garde trace de celles qui ont déjà été rapportées dans ArrayList appelées reportSet(). Ceci traite l'ArrayList comme un « jeu » qui vous signale lorsqu'un élément se trouve déjà dans le jeu. L'élément est produit en ajoutant au nom de fichier l'identifiant. Si l'élément n'est pas dans le jeu, il est ajouté puis un le rapport est effectué.

Le reste du listing est composé de main(), qui s'en occupe lui même en manipulant les arguments de ligne de commande et prenant en compte de l'endroit où l'on a construit le dépôt de noms de classe de la bibliothèque standard Java ou en vérifiant la validité du code que l'on a écrit. Dans les deux cas il fait un objet ClassScanner.

Si l'on construit ou utilisons un dépôt, on doit essayer d'ouvrir les dépôts existants. En créant un objet File et en testant son existence, on peut décider que l'on ouvre le fichier et load() la liste de classes Properties dans ClassScanner. (Les classes du dépôt s'ajoutent, ou plutôt recouvrent, les classes trouvées par le constructeur ClassScanner.) Si l'on fournit seulement un seul argument en ligne de commande cela signifie que l'on désire accomplir une vérification des noms de classes et d'identifiants, mais si l'on fournit deux arguments (le second étant un « -a ») on construit un dépôt de noms de classes. Dans ce cas, un fichier de sortie est ouvert et la méthode Properties.save() est utilisée pour écrire la liste dans un fichier, avec une chaîne de caractère fournissant l'information d'en tête du fichier.

Résumé

La bibliothèque Java de flux d'E/S satisfait les exigence de base : on peut faire la lecture et l'écriture avec la console, un fichier, un bloc de mémoire, ou même à travers l'Internet (comme on pourra le voir au Chapitre 15). Avec l'héritage, on peut créer de nouveaux types d'objets d'entrée et de sortie. Et l'on peut même ajouter une simple extension vers les types d'objets qu'un flux pourrait accepter en redéfinissant la méthode toString() qui est automatiquement appelée lorsque l'on passe un objet à une méthode qui attendait un String (« conversion automatique de type » limitée à Java).

Il y a des questions laissées sans réponses par la documentation et la conception de la bibliothèque de flux. Par exemple, il aurait été agréable de pouvoir dire que l'on désire lancer une exception si l'on essaye d'écrire par dessus un fichier en l'ouvrant pour la sortie – certains systèmes de programmation permettant de spécifier que l'on désire ouvrir un fichier de sortie, mais seulement si il n'existe pas déjà. En Java, il apparaît que l'on est supposé utiliser un objet File pour déterminer qu'un fichier existe, puisque si on l'ouvre comme un FileOutputStream ou un FileWriter il sera toujours écrasé.

La bibliothèque de flux d'E/S amène des sentiments mélangés ; elle fait plus de travail et est portable. Mais si vous ne comprenez pas encore le decorator pattern, la conception n'est pas intuitive, il y a donc des frais supplémentaires pour l'apprendre et l'enseigner. Elle est aussi incomplète : il n'y a pas de support pour le genre de format de sortie que presque chaque paquetage d'E/S d'autres langages supportent.

Cependant, dès que vous pourrez comprendre le decorator pattern et commencerez à utiliser la bibliothèque pour des situations demandant sa flexibilité, vous pourrez commencer a bénéficier de cette conception, à tel point que ce que ça vous coûtera en lignes de codes supplémentaires ne vous ennuiera pas beaucoup.

Si vous n'avez pas trouvé ce que vous cherchiez dans ce chapitre (qui a seulement été une introduction, et n'est pas censée être complète), vous pourrez trouver une couverture détaillé dans Java E/S, de Elliotte Rusty Harold (O’Reilly, 1999).

Ce livre a été écrit par Bruce Eckel ( télécharger la version anglaise : Thinking in java )
Ce chapitre a été traduit par Armel Fortun ( groupe de traduction )
télécharger la version francaise (PDF) | Commandez le livre en version anglaise (amazon) | télécharger la version anglaise
pages : 1 2 3 4 5 6 7 8 9 10 
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java