Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ganesh_JavaSE7_Programming_1z0-804_study_guide.pdf
Скачиваний:
95
Добавлен:
02.02.2015
Размер:
5.88 Mб
Скачать

chapter 9 Java File I/O (NIO.2)

} catch (IOException e) { e.printStackTrace();

}

}

}

Let’s execute this program and see whether it works:

D:\> java FileTreeWalkCopy Test Test2 Files copied successfully!

Well, the program copied the Test directory along with the files contained in the Test directory to the Test2 directory. Essentially, what you are doing is quite simple: in the preVisitDirectory() method, you are copying the directory (which is being visited). To retrieve the new destination path, you are using the relativize() method from the Path class. Similarly, you get a new destination path each time you visit a file, which is used to copy the file to the destination directory. That’s it. You’re done.

Finding a File

Once you understand how to walk through the file tree, it is very straightforward and easy to find a desired file. For instance, if you are looking for a particular file/directory, then you may try to match the file/directory name you are looking for with the visitFile() or preVisitDirectory() method. However, if you are looking for all files matching a particular pattern (for instance, all Java source files or xml files) in a file tree, you can use glob or regex to match the names of files. The PathMatcher interface is useful in this context as it will match a path for you once you specified the desired pattern. The PathMatcher interface is implemented for each file system, and you can get an instance of it from the FileSystem class using the getPathMatcher() method.

Before looking at a detailed example, let’s first understand Glob patterns. Glob is a pattern-specifying mechanism where you can specify file matching patterns as strings. Table 9-4 summarizes the supported patterns by the glob syntax.

Table 9-4.  Patterns Supported by Glob Syntax

 

 

Pattern

Description

*

Matches any string of any length, even zero length.

**

Similar to “*”, but it crosses directory boundaries.

?

Matches to any single character,

[xyz]

Matches to either x , y, or z.

[0–5]

Matches to any character in the range 0 to 5.

[a–z]

Matches to any lowercase letter.

{xyz, abc}

Matches to either xyz or abc.

 

 

271

chapter 9 Java File I/O (NIO.2)

Hence, you can specify syntax such as File*.java to match all Java source files that start with the letters "File" or you can have syntax such as program[0–9].class, which will match files such as program0.class, program1.class, and so on.

Let’s try an example that takes a path (a starting path) and a pattern (to find matching files) and then prints the list of files that match the specified pattern. The program is given in Listing 9-14.

Listing 9-14.  FileTreeWalkFind.java

import java.io.IOException; import java.nio.file.*;

import java.nio.file.attribute.*;

class MyFileFindVisitor extends SimpleFileVisitor<Path> { private PathMatcher matcher;

public MyFileFindVisitor(String pattern){ try {

matcher = FileSystems.getDefault().getPathMatcher(pattern); } catch(IllegalArgumentException iae) {

System.err.println("Invalid pattern; did you forget to prefix \"glob:\"?

(as in  glob:*.java)");

System.exit(−1);

}

}

public FileVisitResult visitFile(Path path, BasicFileAttributes fileAttributes){ find(path);

return FileVisitResult.CONTINUE;

}

private void find(Path path) {

Path name = path.getFileName(); if(matcher.matches(name))

System.out.println("Matching file:" + path.getFileName());

}

public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes fileAttributes){ find(path);

return FileVisitResult.CONTINUE;

}

}

public class FileTreeWalkFind {

public static void main(String[] args) { if(args.length != 2){

System.out.println("usage: FileTreeWalkFind <start-path> <pattern to search>"); System.exit(−1);

}

Path startPath = Paths.get(args[0]); String pattern = args[1];

272

chapter 9 Java File I/O (NIO.2)

try {

Files.walkFileTree(startPath, new MyFileFindVisitor(pattern)); System.out.println("File search completed!");

} catch (IOException e) { e.printStackTrace();

}

}

}

Let’s execute it first and then understand how it works.

d:\> java FileTreeWalkFind ch9-15 glob:File*.java Matching file:FileTreeWalkFind.java

File search completed!

d:\> java FileTreeWalkFind ch9-15 glob:File* Matching file:FileTreeWalkFind.class Matching file:FileTreeWalkFind.class Matching file:FileTreeWalkFind.java

File search completed!

Here’s how it works:

You define your FileVisitor, MyFileFindVisitor, which overrides two methods, visitFile() and preVisitDirectory().

In the constructor of your visitor class, you retrieve a PathMatcher instance using a

FileSystem instance.

The overridden methods call a method of find(); this method creates a Path object from the file name of the passed Path object. This is necessary since you want matcher to match only the file name, not the whole path.

You start walking through the file tree using the method walkFileTree(); you specify an instance of MyFileFindVisitor as the FileVisitor.

If the current visiting file/directory matches the pattern, you print the file name. The process of matching the specified pattern is carried out by a PathMatcher instance.

Watching a Directory for Changes

Let’s assume that you have implemented a simple IDE to work on Java programs. You have loaded a Java source file in it and you are working on it. What happens if some other program changes the source file you are working on? You might want to ask the user whether he wants to reload the source file. In fact, many IDEs and other programs shows a message to the user and ask permission from the user to reload the files (see Figure 9-2). However, the key point is: how do you get notified that the file you are working on was modified by some other program?

273

chapter 9 Java File I/O (NIO.2)

Figure 9-2.  File change notification shown in Eclipse IDE

Java 7 offers a directory watch service that can achieve exactly the same result. You can register a directory using this service to change event notification, and whenever any change happens in the directory (such as new file

creation, file deletion, and file modification) you will get an event notification about the change. The watch service is a convenient, scalable, and an easy way to keep track of the changes in a directory.

Let’s look at a program in Listing 9-15 first and then see how the watch service API works. Assume that you want to monitor the src directory of your current project. You are interested in file modification events, such that any change in any file of the directory results in an event notification to your program.

Listing 9-15.  KeepAnEye.java

import java.io.IOException; import java.nio.file.*;

public class KeepAnEye {

public static void main(String[] args) { Path path = Paths.get("..\\src"); WatchService watchService = null; try {

watchService = path.getFileSystem().newWatchService(); path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);

} catch (IOException e1) { e1.printStackTrace();

}

//infinite loop for(;;){

WatchKey key = null; try {

key = watchService.take(); } catch (InterruptedException e) {

e.printStackTrace();

}

// iterate for each event for(WatchEvent<?> event:key.pollEvents()){

switch(event.kind().name()){ case "OVERFLOW":

System.out.println("We lost some events"); break;

case "ENTRY_MODIFY":

274

chapter 9 Java File I/O (NIO.2)

System.out.println("File " + event.context() + " is changed!"); break;

}

}

//resetting the key is important to receive subsequent notifications key.reset();

}

}

}

Execute this program and meanwhile try to change the source file as well as the class file in the src directory. You may get results like this:

d:\workspace\ch9-16\src>java KeepAnEye File KeepAnEye.java is changed!

File KeepAnEye.java is changed! File KeepAnEye.java is changed! File KeepAnEye.class is changed! File KeepAnEye.class is changed!

Well, that’s great—it’s working as intended. Now, let’s understand the program step by step:

The first thing you need to do is to get an instance of WatchService. You can get a watch service instance using the FileSystem class. Here, you are getting a FileSystem instance using a path instance, and then you are requesting an instance of watch service from the FileSystem. You may also get an instance of the FileSystem from FileSystems

(FileSystems.getDefault()).

Once you have an instance of the watch service, the next step is to register the directory to the watch service. The Path object provides two methods for registration: the first register() method takes variable arguments (first an instance of watch service and subsequently the kind of watch event in which you are interested). The second register() method takes one additional parameter—the watch event modifier. Here, you are using the first register() method.

You want to receive an event notification only when a file is modified; thus you specify ENTRY_MODIFY (belonging to StandardWatchEventKinds). Other kinds watch events include ENTRY_CREATE, ENTRY_DELETE, and OVERFLOW. The first three kinds are self-explanatory; OVERFLOW specifies that a few event notifications are discarded or missed. These event kinds can be specified based on the requirements.

Once the registration is done, you are ready to receive event notifications. You can implement an infinite loop in which you wait for the suitable event to happen.

In the loop, you need to wait for the event to happen. Here, you can ask the watch service to notify this program when an event occurs. You can do this using three methods:

The poll() method returns a queued key if available; otherwise it returns immediately.

The poll(long, TimeUnit) method returns a queued key if available; otherwise it waits for the specified time (the long value) and for the specified time unit. The method returns after the specified time limit is elapsed.

275

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]