t
t
t
t
t t   11) Le système d’E/S de Java
tttt
t
carrea) Préface carreb) Avant-propos carre1) Introduction sur les &laqo; objets » carre2) Tout est &laqo; objet » carre3) Contrôle du flux du programme carre4) Initialization & Cleanup carre5) Cacher l'implémentation carre6) Réutiliser les classes carre7) Polymorphisme carre8) Interfaces & classes internes carre9) Stockage des objets carre10) Error Handling with Exceptions 11) Le système d’E/S de Java carre12) Identification dynamique de type carre13) Création de fenêtres & d'Applets carre14) Les &laqo; Threads » multiples carre15) Informatique distribuée carreA) Passage et retour d'objets carreB) L'Interface Java Natif (JNI) carreC) Conseils pour une programation stylée en Java carreD) Resources
Texte original t Traducteur : Armel Fortun
t
t
///
Ce chapitre contient 10 pages
1 2 3 4 5 6 7 8 9 10
\\\
t t t
t t t
t
t t t
//: c11:ClassScanner.java // Scans all files in directory for classes // and identifiers, to check capitalization. // Assumes properly compiling code listings. // Doesn't do everything right, but is a // useful aid. import java.io.*; import java.util.*; class MultiStringMap extends HashMap { public void add(String key, String value) { if(!containsKey(key)) put(key, new ArrayList()); ((ArrayList)get(key)).add(value); } public ArrayList getArrayList(String key) { if(!containsKey(key)) { System.err.println( "ERROR: can't find key: " + key); System.exit(1); } return (ArrayList)get(key); } public void printValues(PrintStream p) { Iterator k = keySet().iterator(); while(k.hasNext()) { String oneKey = (String)k.next(); ArrayList val = getArrayList(oneKey); for(int i = 0; i < val.size(); i++) p.println((String)val.get(i)); } } } public class ClassScanner { private File path; private String[] fileList; private Properties classes = new Properties(); private MultiStringMap classMap = new MultiStringMap(), identMap = new MultiStringMap(); private StreamTokenizer in; public ClassScanner() throws IOException { path = new File("."); fileList = path.list(new JavaFilter()); for(int i = 0; i < fileList.length; i++) { System.out.println(fileList[i]); try { scanListing(fileList[i]); } catch(FileNotFoundException e) { System.err.println("Could not open " + fileList[i]); } } } void scanListing(String fname) throws IOException { in = new StreamTokenizer( new BufferedReader( new FileReader(fname))); // Doesn't seem to work: // in.slashStarComments(true); // in.slashSlashComments(true); in.ordinaryChar('/'); in.ordinaryChar('.'); in.wordChars('_', '_'); in.eolIsSignificant(true); while(in.nextToken() != StreamTokenizer.TT_EOF) { if(in.ttype == '/') eatComments(); else if(in.ttype == StreamTokenizer.TT_WORD) { if(in.sval.equals("class") || in.sval.equals("interface")) { // Get class name: while(in.nextToken() != StreamTokenizer.TT_EOF && in.ttype != StreamTokenizer.TT_WORD) ; classes.put(in.sval, in.sval); classMap.add(fname, in.sval); } if(in.sval.equals("import") || in.sval.equals("package")) discardLine(); else // It's an identifier or keyword identMap.add(fname, in.sval); } } } void discardLine() throws IOException { while(in.nextToken() != StreamTokenizer.TT_EOF && in.ttype != StreamTokenizer.TT_EOL) ; // Throw away tokens to end of line } // StreamTokenizer's comment removal seemed // to be broken. This extracts them: void eatComments() throws IOException { if(in.nextToken() != StreamTokenizer.TT_EOF) { if(in.ttype == '/') discardLine(); else if(in.ttype != '*') in.pushBack(); else while(true) { if(in.nextToken() == StreamTokenizer.TT_EOF) break; if(in.ttype == '*') if(in.nextToken() != StreamTokenizer.TT_EOF && in.ttype == '/') break; } } } public String[] classNames() { String[] result = new String[classes.size()]; Iterator e = classes.keySet().iterator(); int i = 0; while(e.hasNext()) result[i++] = (String)e.next(); return result; } public void checkClassNames() { Iterator files = classMap.keySet().iterator(); while(files.hasNext()) { String file = (String)files.next(); ArrayList cls = classMap.getArrayList(file); for(int i = 0; i < cls.size(); i++) { String className = (String)cls.get(i); if(Character.isLowerCase( className.charAt(0))) System.out.println( "class capitalization error, file: " + file + ", class: " + className); } } } public void checkIdentNames() { Iterator files = identMap.keySet().iterator(); ArrayList reportSet = new ArrayList(); while(files.hasNext()) { String file = (String)files.next(); ArrayList ids = identMap.getArrayList(file); for(int i = 0; i < ids.size(); i++) { String id = (String)ids.get(i); if(!classes.contains(id)) { // Ignore identifiers of length 3 or // longer that are all uppercase // (probably static final values): if(id.length() >= 3 && id.equals( id.toUpperCase())) continue; // Check to see if first char is upper: if(Character.isUpperCase(id.charAt(0))){ if(reportSet.indexOf(file + id) == -1){ // Not reported yet reportSet.add(file + id); System.out.println( "Ident capitalization error in:" + file + ", ident: " + id); } } } } } } static final String usage = "Usage: \n" + "ClassScanner classnames -a\n" + "\tAdds all the class names in this \n" + "\tdirectory to the repository file \n" + "\tcalled 'classnames'\n" + "ClassScanner classnames\n" + "\tChecks all the java files in this \n" + "\tdirectory for capitalization errors, \n" + "\tusing the repository file 'classnames'"; private static void usage() { System.err.println(usage); System.exit(1); } public static void main(String[] args) throws IOException { if(args.length < 1 || args.length > 2) usage(); ClassScanner c = new ClassScanner(); File old = new File(args[0]); if(old.exists()) { try { // Try to open an existing // properties file: InputStream oldlist = new BufferedInputStream( new FileInputStream(old)); c.classes.load(oldlist); oldlist.close(); } catch(IOException e) { System.err.println("Could not open " + old + " for reading"); System.exit(1); } } if(args.length == 1) { c.checkClassNames(); c.checkIdentNames(); } // Write the class names to a repository: if(args.length == 2) { if(!args[1].equals("-a")) usage(); try { BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream(args[0])); c.classes.store(out, "Classes found by ClassScanner.java"); out.close(); } catch(IOException e) { System.err.println( "Could not write " + args[0]); System.exit(1); } } } } class JavaFilter implements FilenameFilter { public boolean accept(File dir, String name) { // Strip path information: String f = new File(name).getName(); return f.trim().endsWith(".java"); } } ///:~ t
//: c11:ClassScanner.java
// Scanne tous les fichiers dans le répertoire pour les classes
// et les identifiants, pour vérifier la capitalisation.
// Présume des listes de code correctement compilées.
// Ne fait pas tout bien, mais c'est
// une aide utile.
import java.io.*;
import java.util.*;

class MultiStringMap extends HashMap {
  public void add(String key, String value) {
    if(!containsKey(key))
      put(key, new ArrayList());
    ((ArrayList)get(key)).add(value);
  }
  public ArrayList getArrayList(String key) {
    if(!containsKey(key)) {
      System.err.println(
        "ERROR: can't find key: " + key);
      System.exit(1);
    }
    return (ArrayList)get(key);
  }
  public void printValues(PrintStream p) {
    Iterator k = keySet().iterator();
    while(k.hasNext()) {
      String oneKey = (String)k.next();
      ArrayList val = getArrayList(oneKey);
      for(int i = 0; i < val.size(); i++)
        p.println((String)val.get(i));
    }
  }
}

public class ClassScanner {
  private File path;
  private String[] fileList;
  private Properties classes = new Properties();
  private MultiStringMap
    classMap = new MultiStringMap(),
    identMap = new MultiStringMap();
  private StreamTokenizer in;
  public ClassScanner() throws IOException {
    path = new File(".");
    fileList = path.list(new JavaFilter());
    for(int i = 0; i < fileList.length; i++) {
      System.out.println(fileList[i]);
      try {
        scanListing(fileList[i]);
      } catch(FileNotFoundException e) {
        System.err.println("Could not open " +
          fileList[i]);
      }
    }
  }
  void scanListing(String fname)
  throws IOException {
    in = new StreamTokenizer(
        new BufferedReader(
          new FileReader(fname)));
    // Ne semble pas fonctionner :
    // in.slashStarComments(true);
    // in.slashSlashComments(true);
    in.ordinaryChar('/');
    in.ordinaryChar('.');
    in.wordChars('_', '_');
    in.eolIsSignificant(true);
    while(in.nextToken() !=
          StreamTokenizer.TT_EOF) {
      if(in.ttype == '/')
        eatComments();
      else if(in.ttype ==
              StreamTokenizer.TT_WORD) {
        if(in.sval.equals("class") ||
           in.sval.equals("interface")) {
          // Prend le nom de la classe :
             while(in.nextToken() !=
                   StreamTokenizer.TT_EOF
                   && in.ttype !=
                   StreamTokenizer.TT_WORD)
               ;
             classes.put(in.sval, in.sval);
             classMap.add(fname, in.sval);
        }
        if(in.sval.equals("import") ||
           in.sval.equals("package"))
          discardLine();
        else // C'est un identifiant ou un mot-clé
          identMap.add(fname, in.sval);
      }
    }
  }
  void discardLine() throws IOException {
    while(in.nextToken() !=
          StreamTokenizer.TT_EOF
          && in.ttype !=
          StreamTokenizer.TT_EOL)
      ; // Renvoie les tokens en fin de ligne
  }
  // Le déplacement des commentaire de StreamTokenizer semble
  // être cassé. Ceci les extrait :
  void eatComments() throws IOException {
    if(in.nextToken() !=
       StreamTokenizer.TT_EOF) {
      if(in.ttype == '/')
        discardLine();
      else if(in.ttype != '*')
        in.pushBack();
      else
        while(true) {
          if(in.nextToken() ==
            StreamTokenizer.TT_EOF)
            break;
          if(in.ttype == '*')
            if(in.nextToken() !=
              StreamTokenizer.TT_EOF
              && in.ttype == '/')
              break;
        }
    }
  }
  public String[] classNames() {
    String[] result = new String[classes.size()];
    Iterator e = classes.keySet().iterator();
    int i = 0;
    while(e.hasNext())
      result[i++] = (String)e.next();
    return result;
  }
  public void checkClassNames() {
    Iterator files = classMap.keySet().iterator();
    while(files.hasNext()) {
      String file = (String)files.next();
      ArrayList cls = classMap.getArrayList(file);
      for(int i = 0; i < cls.size(); i++) {
        String className = (String)cls.get(i);
        if(Character.isLowerCase(
             className.charAt(0)))
          System.out.println(
            "class capitalization error, file: "
            + file + ", class: "
            + className);
      }
    }
  }
  public void checkIdentNames() {
    Iterator files = identMap.keySet().iterator();
    ArrayList reportSet = new ArrayList();
    while(files.hasNext()) {
      String file = (String)files.next();
      ArrayList ids = identMap.getArrayList(file);
      for(int i = 0; i < ids.size(); i++) {
        String id = (String)ids.get(i);
        if(!classes.contains(id)) {
          // Ignore le identifiants de longeur 3
          // qui sont majuscule sur tout leur longeur
          // (probablement des valeurs finales statiques) :
          if(id.length() >= 3 &&
             id.equals(
               id.toUpperCase()))
            continue;
          // Vérifie pour voir si le premier cractère est majuscule :
          if(Character.isUpperCase(id.charAt(0))){
            if(reportSet.indexOf(file + id)
                == -1){ // Ne rend pas encore compte
              reportSet.add(file + id);
              System.out.println(
                "Ident capitalization error in:"
                + file + ", ident: " + id);
            }
          }
        }
      }
    }
  }
  static final String usage =    "Usage: \n" +
    "ClassScanner classnames -a\n" +
    "\tAdds all the class names in this \n" +
    "\tdirectory to the repository file \n" +
    "\tcalled 'classnames'\n" +
    "ClassScanner classnames\n" +
    "\tChecks all the java files in this \n" +
    "\tdirectory for capitalization errors, \n" +
    "\tusing the repository file 'classnames'";
  private static void usage() {
    System.err.println(usage);
    System.exit(1);
  }
  public static void main(String[] args)
  throws IOException {
    if(args.length < 1 || args.length > 2)
      usage();
    ClassScanner c = new ClassScanner();
    File old = new File(args[0]);
    if(old.exists()) {
      try {
        // Essaye d'ouvir un fichier de
        // propriété existant :
        InputStream oldlist =          new BufferedInputStream(
            new FileInputStream(old));
        c.classes.load(oldlist);
        oldlist.close();
      } catch(IOException e) {
        System.err.println("Could not open "
          + old + " for reading");
        System.exit(1);
      }
    }
    if(args.length == 1) {
      c.checkClassNames();
      c.checkIdentNames();
    }
    // Écrit les noms de classe dans un dépôt :
    if(args.length == 2) {
      if(!args[1].equals("-a"))
        usage();
      try {
        BufferedOutputStream out =          new BufferedOutputStream(
            new FileOutputStream(args[0]));
        c.classes.store(out,
          "Classes found by ClassScanner.java");
        out.close();
      } catch(IOException e) {
        System.err.println(
          "Could not write " + args[0]);
        System.exit(1);
      }
    }
  }
}

class JavaFilter implements FilenameFilter {
  public boolean accept(File dir, String name) {
    // Retire les infiormation de chemin :
    String f = new File(name).getName();
    return f.trim().endsWith(".java");
  }
} ///:~
t t t
The class MultiStringMap is a tool that allows you to map a group of strings onto each key entry. It uses a HashMap (this time with inheritance) with the key as the single string that’s mapped onto the ArrayList value. The add( ) method simply checks to see if there’s a key already in the HashMap, and if not it puts one there. The getArrayList( ) method produces an ArrayList for a particular key, and printValues( ), which is primarily useful for debugging, prints out all the values ArrayList by ArrayList.
t 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 .
t t t
To keep life simple, the class names from the standard Java libraries are all put into a Properties object (from the standard Java library). Remember that a Properties object is a HashMap that holds only String objects for both the key and value entries. However, it can be saved to disk and restored from disk in one method call, so it’s ideal for the repository of names. Actually, we need only a list of names, and a HashMap can’t accept null for either its key or its value entry. So the same object will be used for both the key and the value.
t 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.
t t t
For the classes and identifiers that are discovered for the files in a particular directory, two MultiStringMaps are used: classMap and identMap. Also, when the program starts up it loads the standard class name repository into the Properties object called classes, and when a new class name is found in the local directory that is also added to classes as well as to classMap. This way, classMap can be used to step through all the classes in the local directory, and classes can be used to see if the current token is a class name (which indicates a definition of an object or method is beginning, so grab the next tokens—until a semicolon—and put them into identMap).
t 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).
t t t
The default constructor for ClassScanner creates a list of file names, using the JavaFilter implementation of FilenameFilter, shown at the end of the file. Then it calls scanListing( ) for each file name.
t 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.
t t t
Inside scanListing( ) the source code file is opened and turned into a StreamTokenizer. In the documentation, passing true to slashStarComments( ) and slashSlashComments( ) is supposed to strip those comments out, but this seems to be a bit flawed, as it doesn’t quite work. Instead, those lines are commented out and the comments are extracted by another method. To do this, the “/” must be captured as an ordinary character rather than letting the StreamTokenizer absorb it as part of a comment, and the ordinaryChar( ) method tells the StreamTokenizer to do this. This is also true for dots (“.”), since we want to have the method calls pulled apart into individual identifiers. However, the underscore, which is ordinarily treated by StreamTokenizer as an individual character, should be left as part of identifiers since it appears in such static final values as TT_EOF, etc., used in this very program. The wordChars( ) method takes a range of characters you want to add to those that are left inside a token that is being parsed as a word. Finally, when parsing for one-line comments or discarding a line we need to know when an end-of-line occurs, so by calling eolIsSignificant(true) the EOL will show up rather than being absorbed by the StreamTokenizer.
t 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.
t t t
The rest of scanListing( ) reads and reacts to tokens until the end of the file, signified when nextToken( ) returns the final static value StreamTokenizer.TT_EOF.
t 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.
t t t
If the token is a “/” it is potentially a comment, so eatComments( ) is called to deal with it. The only other situation we’re interested in here is if it’s a word, of which there are some special cases.
t 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.
t t t
If the word is class or interface then the next token represents a class or interface name, and it is put into classes and classMap. If the word is import or package, then we don’t want the rest of the line. Anything else must be an identifier (which we’re interested in) or a keyword (which we’re not, but they’re all lowercase anyway so it won’t spoil things to put those in). These are added to identMap.
t 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.
t t t
The discardLine( ) method is a simple tool that looks for the end of a line. Note that any time you get a new token, you must check for the end of the file.
t 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.
t t t
The eatComments( ) method is called whenever a forward slash is encountered in the main parsing loop. However, that doesn’t necessarily mean a comment has been found, so the next token must be extracted to see if it’s another forward slash (in which case the line is discarded) or an asterisk. But if it’s neither of those, it means the token you’ve just pulled out is needed back in the main parsing loop! Fortunately, the pushBack( ) method allows you to “push back” the current token onto the input stream so that when the main parsing loop calls nextToken( ) it will get the one you just pushed back.
t 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.
t t t
For convenience, the classNames( ) method produces an array of all the names in the classes container. This method is not used in the program but is helpful for debugging.
t 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.
t t t
The next two methods are the ones in which the actual checking takes place. In checkClassNames( ), the class names are extracted from the classMap (which, remember, contains only the names in this directory, organized by file name so the file name can be printed along with the errant class name). This is accomplished by pulling each associated ArrayList and stepping through that, looking to see if the first character is lowercase. If so, the appropriate error message is printed.
t 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é.
t t t
In checkIdentNames( ), a similar approach is taken: each identifier name is extracted from identMap. If the name is not in the classes list, it’s assumed to be an identifier or keyword. A special case is checked: if the identifier length is three or more and all the characters are uppercase, this identifier is ignored because it’s probably a static final value such as TT_EOF. Of course, this is not a perfect algorithm, but it assumes that you’ll eventually notice any all-uppercase identifiers that are out of place.
t 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.
t t t
Instead of reporting every identifier that starts with an uppercase character, this method keeps track of which ones have already been reported in an ArrayList called reportSet( ). This treats the ArrayList as a “set” that tells you whether an item is already in the set. The item is produced by concatenating the file name and identifier. If the element isn’t in the set, it’s added and then the report is made.
t 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é.
t t t
The rest of the listing is comprised of main( ), which busies itself by handling the command line arguments and figuring out whether you’re building a repository of class names from the standard Java library or checking the validity of code you’ve written. In both cases it makes a ClassScanner object.
t 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.
t t t
Whether you’re building a repository or using one, you must try to open the existing repository. By making a File object and testing for existence, you can decide whether to open the file and load( ) the Properties list classes inside ClassScanner. (The classes from the repository add to, rather than overwrite, the classes found by the ClassScanner constructor.) If you provide only one command-line argument it means that you want to perform a check of the class names and identifier names, but if you provide two arguments (the second being “-a”) you’re building a class name repository. In this case, an output file is opened and the method Properties.save( ) is used to write the list into a file, along with a string that provides header file information.
t 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.
t t t

Summary

t

Résumé

t t t
The Java I/O stream library does satisfy the basic requirements: you can perform reading and writing with the console, a file, a block of memory, or even across the Internet (as you will see in Chapter 15). With inheritance, you can create new types of input and output objects. And you can even add a simple extensibility to the kinds of objects a stream will accept by redefining the toString( ) method that’s automatically called when you pass an object to a method that’s expecting a String (Java’s limited “automatic type conversion”).
t 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).
t t t
There are questions left unanswered by the documentation and design of the I/O stream library. For example, it would have been nice if you could say that you want an exception thrown if you try to overwrite a file when opening it for output—some programming systems allow you to specify that you want to open an output file, but only if it doesn’t already exist. In Java, it appears that you are supposed to use a File object to determine whether a file exists, because if you open it as a FileOutputStream or FileWriter it will always get overwritten.
t 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é.
t t t
The I/O stream library brings up mixed feelings; it does much of the job and it’s portable. But if you don’t already understand the decorator pattern, the design is nonintuitive, so there’s extra overhead in learning and teaching it. It’s also incomplete: there’s no support for the kind of output formatting that almost every other language’s I/O package supports.
t 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.
t t t
However, once you do understand the decorator pattern and begin using the library in situations that require its flexibility, you can begin to benefit from this design, at which point its cost in extra lines of code may not bother you as much.
t 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.
t t t
If you do not find what you’re looking for in this chapter (which has only been an introduction, and is not meant to be comprehensive), you can find in-depth coverage in Java I/O, by Elliotte Rusty Harold (O’Reilly, 1999).
t 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).
t t t
t t t
t t
\\\
///
t t t
t
     
Sommaire Le site de Bruce Eckel