History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/nio/file/FileTreeWalker.java (1 of 18352 files). Show all

Unified · Split

java/pkg/java/nio/file/FileTreeWalker.java+248−0
@@ -0,0 +1,248 @@
1/*
2 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.nio.file;
27
28import java.nio.file.attribute.*;
29import java.io.IOException;
30import java.util.*;
31import sun.nio.fs.BasicFileAttributesHolder;
32
33/**
34 * Simple file tree walker that works in a similar manner to nftw(3C).
35 *
36 * @see Files#walkFileTree
37 */
38
39class FileTreeWalker {
40 private final boolean followLinks;
41 private final LinkOption[] linkOptions;
42 private final FileVisitor<? super Path> visitor;
43 private final int maxDepth;
44
45 FileTreeWalker(Set<FileVisitOption> options,
46 FileVisitor<? super Path> visitor,
47 int maxDepth)
48 {
49 boolean fl = false;
50 for (FileVisitOption option: options) {
51 // will throw NPE if options contains null
52 switch (option) {
53 case FOLLOW_LINKS : fl = true; break;
54 default:
55 throw new AssertionError("Should not get here");
56 }
57 }
58 this.followLinks = fl;
59 this.linkOptions = (fl) ? new LinkOption[0] :
60 new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
61 this.visitor = visitor;
62 this.maxDepth = maxDepth;
63 }
64
65 /**
66 * Walk file tree starting at the given file
67 */
68 void walk(Path start) throws IOException {
69 FileVisitResult result = walk(start,
70 0,
71 new ArrayList<AncestorDirectory>());
72 Objects.requireNonNull(result, "FileVisitor returned null");
73 }
74
75 /**
76 * @param file
77 * the directory to visit
78 * @param depth
79 * depth remaining
80 * @param ancestors
81 * use when cycle detection is enabled
82 */
83 private FileVisitResult walk(Path file,
84 int depth,
85 List<AncestorDirectory> ancestors)
86 throws IOException
87 {
88 // if attributes are cached then use them if possible
89 BasicFileAttributes attrs = null;
90 if ((depth > 0) &&
91 (file instanceof BasicFileAttributesHolder) &&
92 (System.getSecurityManager() == null))
93 {
94 BasicFileAttributes cached = ((BasicFileAttributesHolder)file).get();
95 if (!followLinks || !cached.isSymbolicLink())
96 attrs = cached;
97 }
98 IOException exc = null;
99
100 // attempt to get attributes of file. If fails and we are following
101 // links then a link target might not exist so get attributes of link
102 if (attrs == null) {
103 try {
104 try {
105 attrs = Files.readAttributes(file, BasicFileAttributes.class, linkOptions);
106 } catch (IOException x1) {
107 if (followLinks) {
108 try {
109 attrs = Files.readAttributes(file,
110 BasicFileAttributes.class,
111 LinkOption.NOFOLLOW_LINKS);
112 } catch (IOException x2) {
113 exc = x2;
114 }
115 } else {
116 exc = x1;
117 }
118 }
119 } catch (SecurityException x) {
120 // If access to starting file is denied then SecurityException
121 // is thrown, otherwise the file is ignored.
122 if (depth == 0)
123 throw x;
124 return FileVisitResult.CONTINUE;
125 }
126 }
127
128 // unable to get attributes of file
129 if (exc != null) {
130 return visitor.visitFileFailed(file, exc);
131 }
132
133 // at maximum depth or file is not a directory
134 if (depth >= maxDepth || !attrs.isDirectory()) {
135 return visitor.visitFile(file, attrs);
136 }
137
138 // check for cycles when following links
139 if (followLinks) {
140 Object key = attrs.fileKey();
141
142 // if this directory and ancestor has a file key then we compare
143 // them; otherwise we use less efficient isSameFile test.
144 for (AncestorDirectory ancestor: ancestors) {
145 Object ancestorKey = ancestor.fileKey();
146 if (key != null && ancestorKey != null) {
147 if (key.equals(ancestorKey)) {
148 // cycle detected
149 return visitor.visitFileFailed(file,
150 new FileSystemLoopException(file.toString()));
151 }
152 } else {
153 boolean isSameFile = false;
154 try {
155 isSameFile = Files.isSameFile(file, ancestor.file());
156 } catch (IOException x) {
157 // ignore
158 } catch (SecurityException x) {
159 // ignore
160 }
161 if (isSameFile) {
162 // cycle detected
163 return visitor.visitFileFailed(file,
164 new FileSystemLoopException(file.toString()));
165 }
166 }
167 }
168
169 ancestors.add(new AncestorDirectory(file, key));
170 }
171
172 // visit directory
173 try {
174 DirectoryStream<Path> stream = null;
175 FileVisitResult result;
176
177 // open the directory
178 try {
179 stream = Files.newDirectoryStream(file);
180 } catch (IOException x) {
181 return visitor.visitFileFailed(file, x);
182 } catch (SecurityException x) {
183 // ignore, as per spec
184 return FileVisitResult.CONTINUE;
185 }
186
187 // the exception notified to the postVisitDirectory method
188 IOException ioe = null;
189
190 // invoke preVisitDirectory and then visit each entry
191 try {
192 result = visitor.preVisitDirectory(file, attrs);
193 if (result != FileVisitResult.CONTINUE) {
194 return result;
195 }
196
197 try {
198 for (Path entry: stream) {
199 result = walk(entry, depth+1, ancestors);
200
201 // returning null will cause NPE to be thrown
202 if (result == null || result == FileVisitResult.TERMINATE)
203 return result;
204
205 // skip remaining siblings in this directory
206 if (result == FileVisitResult.SKIP_SIBLINGS)
207 break;
208 }
209 } catch (DirectoryIteratorException e) {
210 // IOException will be notified to postVisitDirectory
211 ioe = e.getCause();
212 }
213 } finally {
214 try {
215 stream.close();
216 } catch (IOException e) {
217 // IOException will be notified to postVisitDirectory
218 if (ioe == null)
219 ioe = e;
220 }
221 }
222
223 // invoke postVisitDirectory last
224 return visitor.postVisitDirectory(file, ioe);
225
226 } finally {
227 // remove key from trail if doing cycle detection
228 if (followLinks) {
229 ancestors.remove(ancestors.size()-1);
230 }
231 }
232 }
233
234 private static class AncestorDirectory {
235 private final Path dir;
236 private final Object key;
237 AncestorDirectory(Path dir, Object key) {
238 this.dir = dir;
239 this.key = key;
240 }
241 Path file() {
242 return dir;
243 }
244 Object fileKey() {
245 return key;
246 }
247 }
248}