|
1 /* |
|
2 * Copyright 2007-2009 Sun Microsystems, Inc. All Rights Reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Sun designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Sun in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 * have any questions. |
|
24 */ |
|
25 |
|
26 package java.nio.file; |
|
27 |
|
28 import java.nio.file.spi.FileTypeDetector; |
|
29 import java.io.IOException; |
|
30 import java.util.*; |
|
31 import java.security.AccessController; |
|
32 import java.security.PrivilegedAction; |
|
33 |
|
34 /** |
|
35 * Utility methods for files and directories. |
|
36 * |
|
37 * @since 1.7 |
|
38 */ |
|
39 |
|
40 public final class Files { |
|
41 private Files() { } |
|
42 |
|
43 // lazy loading of default and installed file type detectors |
|
44 private static class DefaultFileTypeDetectorHolder { |
|
45 static final FileTypeDetector defaultFileTypeDetector = |
|
46 sun.nio.fs.DefaultFileTypeDetector.create(); |
|
47 static final List<FileTypeDetector> installeDetectors = |
|
48 loadInstalledDetectors(); |
|
49 |
|
50 // loads all installed file type detectors |
|
51 private static List<FileTypeDetector> loadInstalledDetectors() { |
|
52 return AccessController |
|
53 .doPrivileged(new PrivilegedAction<List<FileTypeDetector>>() { |
|
54 @Override public List<FileTypeDetector> run() { |
|
55 List<FileTypeDetector> list = new ArrayList<FileTypeDetector>(); |
|
56 ServiceLoader<FileTypeDetector> loader = ServiceLoader |
|
57 .load(FileTypeDetector.class, ClassLoader.getSystemClassLoader()); |
|
58 for (FileTypeDetector detector: loader) { |
|
59 list.add(detector); |
|
60 } |
|
61 return list; |
|
62 }}); |
|
63 } |
|
64 } |
|
65 |
|
66 /** |
|
67 * Probes the content type of a file. |
|
68 * |
|
69 * <p> This method uses the installed {@link FileTypeDetector} implementations |
|
70 * to probe the given file to determine its content type. Each file type |
|
71 * detector's {@link FileTypeDetector#probeContentType probeContentType} is |
|
72 * invoked, in turn, to probe the file type. If the file is recognized then |
|
73 * the content type is returned. If the file is not recognized by any of the |
|
74 * installed file type detectors then a system-default file type detector is |
|
75 * invoked to guess the content type. |
|
76 * |
|
77 * <p> A given invocation of the Java virtual machine maintains a system-wide |
|
78 * list of file type detectors. Installed file type detectors are loaded |
|
79 * using the service-provider loading facility defined by the {@link ServiceLoader} |
|
80 * class. Installed file type detectors are loaded using the system class |
|
81 * loader. If the system class loader cannot be found then the extension class |
|
82 * loader is used; If the extension class loader cannot be found then the |
|
83 * bootstrap class loader is used. File type detectors are typically installed |
|
84 * by placing them in a JAR file on the application class path or in the |
|
85 * extension directory, the JAR file contains a provider-configuration file |
|
86 * named {@code java.nio.file.spi.FileTypeDetector} in the resource directory |
|
87 * {@code META-INF/services}, and the file lists one or more fully-qualified |
|
88 * names of concrete subclass of {@code FileTypeDetector } that have a zero |
|
89 * argument constructor. If the process of locating or instantiating the |
|
90 * installed file type detectors fails then an unspecified error is thrown. |
|
91 * The ordering that installed providers are located is implementation |
|
92 * specific. |
|
93 * |
|
94 * <p> The return value of this method is the string form of the value of a |
|
95 * Multipurpose Internet Mail Extension (MIME) content type as |
|
96 * defined by <a href="http://www.ietf.org/rfc/rfc2045.txt"><i>RFC 2045: |
|
97 * Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet |
|
98 * Message Bodies</i></a>. The string is guaranteed to be parsable according |
|
99 * to the grammar in the RFC. |
|
100 * |
|
101 * @param file |
|
102 * The file reference |
|
103 * |
|
104 * @return The content type of the file, or {@code null} if the content |
|
105 * type cannot be determined |
|
106 * |
|
107 * @throws IOException |
|
108 * If an I/O error occurs |
|
109 * @throws SecurityException |
|
110 * If a security manager is installed and it denies an unspecified |
|
111 * permission required by a file type detector implementation. |
|
112 * |
|
113 * @see DirectoryStreamFilters#newContentTypeFilter |
|
114 */ |
|
115 public static String probeContentType(FileRef file) |
|
116 throws IOException |
|
117 { |
|
118 // try installed file type detectors |
|
119 for (FileTypeDetector detector: DefaultFileTypeDetectorHolder.installeDetectors) { |
|
120 String result = detector.probeContentType(file); |
|
121 if (result != null) |
|
122 return result; |
|
123 } |
|
124 |
|
125 // fallback to default |
|
126 return DefaultFileTypeDetectorHolder.defaultFileTypeDetector |
|
127 .probeContentType(file); |
|
128 } |
|
129 |
|
130 /** |
|
131 * Invokes a {@link FileAction} for each entry in a directory accepted |
|
132 * by a given {@link java.nio.file.DirectoryStream.Filter filter}. |
|
133 * |
|
134 * <p> This method opens the given directory and invokes the file action's |
|
135 * {@link FileAction#invoke invoke} method for each entry accepted by the |
|
136 * filter. When iteration is completed then the directory is closed. If the |
|
137 * {@link DirectoryStream#close close} method throws an {@code IOException} |
|
138 * then it is silently ignored. |
|
139 * |
|
140 * <p> If the {@code FileAction}'s {@code invoke} method terminates due |
|
141 * to an uncaught {@link IOException}, {@code Error} or {@code RuntimeException} |
|
142 * then the exception is propagated by this method after closing the |
|
143 * directory. |
|
144 * |
|
145 * @param dir |
|
146 * The directory |
|
147 * @param filter |
|
148 * The filter |
|
149 * @param action |
|
150 * The {@code FileAction} to invoke for each accepted entry |
|
151 * |
|
152 * @throws NotDirectoryException |
|
153 * If the {@code dir} parameter is not a directory <i>(optional |
|
154 * specific exception)</i> |
|
155 * @throws IOException |
|
156 * If an I/O error occurs or the {@code invoke} method terminates |
|
157 * due to an uncaught {@code IOException} |
|
158 * @throws SecurityException |
|
159 * In the case of the default provider, the {@link |
|
160 * SecurityManager#checkRead(String) checkRead} method is invoked |
|
161 * to check read access to the directory. |
|
162 */ |
|
163 public static void withDirectory(Path dir, |
|
164 DirectoryStream.Filter<? super Path> filter, |
|
165 FileAction<? super Path> action) |
|
166 throws IOException |
|
167 { |
|
168 // explicit null check required in case directory is empty |
|
169 if (action == null) |
|
170 throw new NullPointerException(); |
|
171 |
|
172 DirectoryStream<Path> stream = dir.newDirectoryStream(filter); |
|
173 try { |
|
174 // set to true when invoking the action so as to distinguish a |
|
175 // CME thrown by the iteration from a CME thrown by the invoke |
|
176 boolean inAction = false; |
|
177 try { |
|
178 for (Path entry: stream) { |
|
179 inAction = true; |
|
180 action.invoke(entry); |
|
181 inAction = false; |
|
182 } |
|
183 } catch (ConcurrentModificationException cme) { |
|
184 if (!inAction) { |
|
185 Throwable cause = cme.getCause(); |
|
186 if (cause instanceof IOException) |
|
187 throw (IOException)cause; |
|
188 } |
|
189 throw cme; |
|
190 } |
|
191 } finally { |
|
192 try { |
|
193 stream.close(); |
|
194 } catch (IOException x) { } |
|
195 } |
|
196 } |
|
197 |
|
198 /** |
|
199 * Invokes a {@link FileAction} for each entry in a directory with a |
|
200 * file name that matches a given pattern. |
|
201 * |
|
202 * <p> This method opens the given directory and invokes the file action's |
|
203 * {@link FileAction#invoke invoke} method for each entry that matches the |
|
204 * given pattern. When iteration is completed then the directory is closed. |
|
205 * If the {@link DirectoryStream#close close} method throws an {@code |
|
206 * IOException} then it is silently ignored. |
|
207 * |
|
208 * <p> If the {@code FileAction}'s {@code invoke} method terminates due |
|
209 * to an uncaught {@link IOException}, {@code Error} or {@code RuntimeException} |
|
210 * then the exception is propagated by this method after closing the |
|
211 * directory. |
|
212 * |
|
213 * <p> The globbing pattern language supported by this method is as |
|
214 * specified by the {@link FileSystem#getPathMatcher getPathMatcher} method. |
|
215 * |
|
216 * @param dir |
|
217 * The directory |
|
218 * @param glob |
|
219 * The globbing pattern |
|
220 * @param action |
|
221 * The {@code FileAction} to invoke for each entry |
|
222 * |
|
223 * @throws NotDirectoryException |
|
224 * If the {@code dir} parameter is not a directory <i>(optional |
|
225 * specific exception)</i> |
|
226 * @throws IOException |
|
227 * If an I/O error occurs or the {@code invoke} method terminates |
|
228 * due to an uncaught {@code IOException} |
|
229 * @throws SecurityException |
|
230 * In the case of the default provider, the {@link |
|
231 * SecurityManager#checkRead(String) checkRead} method is invoked |
|
232 * to check read access to the directory. |
|
233 */ |
|
234 public static void withDirectory(Path dir, |
|
235 String glob, |
|
236 FileAction<? super Path> action) |
|
237 throws IOException |
|
238 { |
|
239 if (glob == null) |
|
240 throw new NullPointerException("'glob' is null"); |
|
241 final PathMatcher matcher = dir.getFileSystem().getPathMatcher("glob:" + glob); |
|
242 DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() { |
|
243 @Override |
|
244 public boolean accept(Path entry) { |
|
245 return matcher.matches(entry.getName()); |
|
246 } |
|
247 }; |
|
248 withDirectory(dir, filter, action); |
|
249 } |
|
250 |
|
251 /** |
|
252 * Invokes a {@link FileAction} for all entries in a directory. |
|
253 * |
|
254 * <p> This method works as if invoking it were equivalent to evaluating the |
|
255 * expression: |
|
256 * <blockquote><pre> |
|
257 * withDirectory(dir, "*", action) |
|
258 * </pre></blockquote> |
|
259 * |
|
260 * @param dir |
|
261 * The directory |
|
262 * @param action |
|
263 * The {@code FileAction} to invoke for each entry |
|
264 * |
|
265 * @throws NotDirectoryException |
|
266 * If the {@code dir} parameter is not a directory <i>(optional |
|
267 * specific exception)</i> |
|
268 * @throws IOException |
|
269 * If an I/O error occurs or the {@code invoke} method terminates |
|
270 * due to an uncaught {@code IOException} |
|
271 * @throws SecurityException |
|
272 * In the case of the default provider, the {@link |
|
273 * SecurityManager#checkRead(String) checkRead} method is invoked |
|
274 * to check read access to the directory. |
|
275 */ |
|
276 public static void withDirectory(Path dir, FileAction<? super Path> action) |
|
277 throws IOException |
|
278 { |
|
279 withDirectory(dir, "*", action); |
|
280 } |
|
281 |
|
282 /** |
|
283 * Walks a file tree. |
|
284 * |
|
285 * <p> This method walks a file tree rooted at a given starting file. The |
|
286 * file tree traversal is <em>depth-first</em> with the given {@link |
|
287 * FileVisitor} invoked for each file encountered. File tree traversal |
|
288 * completes when all accessible files in the tree have been visited, a |
|
289 * visitor returns a result of {@link FileVisitResult#TERMINATE TERMINATE}, |
|
290 * or the visitor terminates due to an uncaught {@code Error} or {@code |
|
291 * RuntimeException}. |
|
292 * |
|
293 * <p> For each file encountered this method attempts to gets its {@link |
|
294 * java.nio.file.attribute.BasicFileAttributes}. If the file is not a |
|
295 * directory then the {@link FileVisitor#visitFile visitFile} method is |
|
296 * invoked with the file attributes. If the file attributes cannot be read, |
|
297 * due to an I/O exception, then the {@link FileVisitor#visitFileFailed |
|
298 * visitFileFailed} method is invoked with the I/O exception. |
|
299 * |
|
300 * <p> Where the file is a directory, this method attempts to open it by |
|
301 * invoking its {@link Path#newDirectoryStream newDirectoryStream} method. |
|
302 * Where the directory could not be opened, due to an {@code IOException}, |
|
303 * then the {@link FileVisitor#preVisitDirectoryFailed preVisitDirectoryFailed} |
|
304 * method is invoked with the I/O exception, after which, the file tree walk |
|
305 * continues, by default, at the next <em>sibling</em> of the directory. |
|
306 * |
|
307 * <p> Where the directory is opened successfully, then the entries in the |
|
308 * directory, and their <em>descendants</em> are visited. When all entries |
|
309 * have been visited, or an I/O error occurs during iteration of the |
|
310 * directory, then the directory is closed and the visitor's {@link |
|
311 * FileVisitor#postVisitDirectory postVisitDirectory} method is invoked. |
|
312 * The file tree walk then continues, by default, at the next <em>sibling</em> |
|
313 * of the directory. |
|
314 * |
|
315 * <p> By default, symbolic links are not automatically followed by this |
|
316 * method. If the {@code options} parameter contains the {@link |
|
317 * FileVisitOption#FOLLOW_LINKS FOLLOW_LINKS} option then symbolic links are |
|
318 * followed. When following links, and the attributes of the target cannot |
|
319 * be read, then this method attempts to get the {@code BasicFileAttributes} |
|
320 * of the link. If they can be read then the {@code visitFile} method is |
|
321 * invoked with the attributes of the link (otherwise the {@code visitFileFailed} |
|
322 * method is invoked as specified above). |
|
323 * |
|
324 * <p> If the {@code options} parameter contains the {@link |
|
325 * FileVisitOption#DETECT_CYCLES DETECT_CYCLES} or {@link |
|
326 * FileVisitOption#FOLLOW_LINKS FOLLOW_LINKS} options then this method keeps |
|
327 * track of directories visited so that cycles can be detected. A cycle |
|
328 * arises when there is an entry in a directory that is an ancestor of the |
|
329 * directory. Cycle detection is done by recording the {@link |
|
330 * java.nio.file.attribute.BasicFileAttributes#fileKey file-key} of directories, |
|
331 * or if file keys are not available, by invoking the {@link FileRef#isSameFile |
|
332 * isSameFile} method to test if a directory is the same file as an |
|
333 * ancestor. When a cycle is detected the {@link FileVisitor#visitFile |
|
334 * visitFile} is invoked with the attributes of the directory. The {@link |
|
335 * java.nio.file.attribute.BasicFileAttributes#isDirectory isDirectory} |
|
336 * method may be used to test if the file is a directory and that a cycle is |
|
337 * detected. The {@code preVisitDirectory} and {@code postVisitDirectory} |
|
338 * methods are not invoked. |
|
339 * |
|
340 * <p> The {@code maxDepth} parameter is the maximum number of levels of |
|
341 * directories to visit. A value of {@code 0} means that only the starting |
|
342 * file is visited, unless denied by the security manager. A value of |
|
343 * {@link Integer#MAX_VALUE MAX_VALUE} may be used to indicate that all |
|
344 * levels should be visited. |
|
345 * |
|
346 * <p> If a visitor returns a result of {@code null} then {@code |
|
347 * NullPointerException} is thrown. |
|
348 * |
|
349 * <p> When a security manager is installed and it denies access to a file |
|
350 * (or directory), then it is ignored and the visitor is not invoked for |
|
351 * that file (or directory). |
|
352 * |
|
353 * @param start |
|
354 * The starting file |
|
355 * @param options |
|
356 * Options to configure the traversal |
|
357 * @param maxDepth |
|
358 * The maximum number of directory levels to visit |
|
359 * @param visitor |
|
360 * The file visitor to invoke for each file |
|
361 * |
|
362 * @throws IllegalArgumentException |
|
363 * If the {@code maxDepth} parameter is negative |
|
364 * @throws SecurityException |
|
365 * If the security manager denies access to the starting file. |
|
366 * In the case of the default provider, the {@link |
|
367 * SecurityManager#checkRead(String) checkRead} method is invoked |
|
368 * to check read access to the directory. |
|
369 */ |
|
370 public static void walkFileTree(Path start, |
|
371 Set<FileVisitOption> options, |
|
372 int maxDepth, |
|
373 FileVisitor<? super Path> visitor) |
|
374 { |
|
375 if (maxDepth < 0) |
|
376 throw new IllegalArgumentException("'maxDepth' is negative"); |
|
377 new FileTreeWalker(options, visitor).walk(start, maxDepth); |
|
378 } |
|
379 |
|
380 /** |
|
381 * Walks a file tree. |
|
382 * |
|
383 * <p> This method works as if invoking it were equivalent to evaluating the |
|
384 * expression: |
|
385 * <blockquote><pre> |
|
386 * walkFileTree(start, EnumSet.noneOf(FileVisitOption.class), Integer.MAX_VALUE, visitor) |
|
387 * </pre></blockquote> |
|
388 * |
|
389 * @param start |
|
390 * The starting file |
|
391 * @param visitor |
|
392 * The file visitor to invoke for each file |
|
393 * |
|
394 * @throws SecurityException |
|
395 * If the security manager denies access to the starting file. |
|
396 * In the case of the default provider, the {@link |
|
397 * SecurityManager#checkRead(String) checkRead} method is invoked |
|
398 * to check read access to the directory. |
|
399 */ |
|
400 public static void walkFileTree(Path start, FileVisitor<? super Path> visitor) { |
|
401 walkFileTree(start, |
|
402 EnumSet.noneOf(FileVisitOption.class), |
|
403 Integer.MAX_VALUE, |
|
404 visitor); |
|
405 } |
|
406 } |