blob: 06a884b4a952ceeaf573e08ce01f906553af97bd [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.vfs;
15
tomlua155b532017-11-08 20:12:47 +010016import com.google.common.base.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010017import com.google.common.base.Predicate;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010018import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
tomlu67c84b12017-11-06 19:49:16 +010019import com.google.devtools.build.lib.skylarkinterface.SkylarkPrintable;
20import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010021import com.google.devtools.build.lib.util.StringCanonicalizer;
Ola Rozenfeld39dbc982016-11-17 20:14:56 +000022import com.google.devtools.build.lib.vfs.FileSystem.HashFunction;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010023import java.io.File;
24import java.io.FileNotFoundException;
25import java.io.IOException;
26import java.io.InputStream;
27import java.io.ObjectInputStream;
28import java.io.ObjectOutputStream;
29import java.io.OutputStream;
30import java.io.Serializable;
31import java.lang.ref.Reference;
32import java.lang.ref.ReferenceQueue;
33import java.lang.ref.WeakReference;
buchgr5ef576f2017-09-14 14:36:44 +020034import java.net.URI;
35import java.net.URISyntaxException;
tomlu0a82e702017-10-23 18:16:44 +020036import java.util.ArrayList;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010037import java.util.Collection;
38import java.util.IdentityHashMap;
39import java.util.Objects;
40
41/**
aehligc801c392017-12-19 07:12:25 -080042 * Instances of this class represent pathnames, forming a tree structure to implement sharing of
tomlu67c84b12017-11-06 19:49:16 +010043 * common prefixes (parent directory names). A node in these trees is something like foo, bar, ..,
44 * ., or /. If the instance is not a root path, it will have a parent path. A path can also have
45 * children, which are indexed by name in a map.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010046 *
tomlu67c84b12017-11-06 19:49:16 +010047 * <p>There is some limited support for Windows-style paths. Most importantly, drive identifiers in
48 * front of a path (c:/abc) are supported. However, Windows-style backslash separators
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010049 * (C:\\foo\\bar) and drive-relative paths ("C:foo") are explicitly not supported, same with
50 * advanced features like \\\\network\\paths and \\\\?\\unc\\paths.
51 *
52 * <p>{@link FileSystem} implementations maintain pointers into this graph.
53 */
54@ThreadSafe
tomlu67c84b12017-11-06 19:49:16 +010055public class Path implements Comparable<Path>, Serializable, SkylarkPrintable {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010056
Laszlo Csomorca99bb72016-10-25 13:15:55 +000057 /** Filesystem-specific factory for {@link Path} objects. */
58 public static interface PathFactory {
Laszlo Csomorca99bb72016-10-25 13:15:55 +000059 /**
60 * Creates the root of all paths used by a filesystem.
61 *
62 * <p>All other paths are instantiated via {@link Path#createChildPath(String)} which calls
63 * {@link #createChildPath(Path, String)}.
64 *
65 * <p>Beware: this is called during the FileSystem constructor which may occur before subclasses
66 * are completely initialized.
67 */
68 Path createRootPath(FileSystem filesystem);
69
70 /**
71 * Create a child path of the given parent.
72 *
73 * <p>All {@link Path} objects are instantiated via this method, with the sole exception of the
74 * filesystem root, which is created by {@link #createRootPath(FileSystem)}.
75 */
76 Path createChildPath(Path parent, String childName);
77
78 /**
nharmata39e659e2017-04-04 14:42:23 +000079 * Makes the proper invocation of {@link FileSystem#getCachedChildPathInternal}, doing
80 * filesystem-specific logic if necessary.
Laszlo Csomorca99bb72016-10-25 13:15:55 +000081 */
nharmata39e659e2017-04-04 14:42:23 +000082 Path getCachedChildPathInternal(Path path, String childName);
Laszlo Csomorca99bb72016-10-25 13:15:55 +000083 }
84
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010085 private static FileSystem fileSystemForSerialization;
86
87 /**
88 * We need to specify used FileSystem. In this case we can save memory during the serialization.
89 */
90 public static void setFileSystemForSerialization(FileSystem fileSystem) {
91 fileSystemForSerialization = fileSystem;
92 }
93
94 /**
95 * Returns FileSystem that we are using.
96 */
97 public static FileSystem getFileSystemForSerialization() {
98 return fileSystemForSerialization;
99 }
100
101 // These are basically final, but can't be marked as such in order to support serialization.
102 private FileSystem fileSystem;
103 private String name;
104 private Path parent;
105 private int depth;
106 private int hashCode;
107
108 private static final ReferenceQueue<Path> REFERENCE_QUEUE = new ReferenceQueue<>();
109
110 private static class PathWeakReferenceForCleanup extends WeakReference<Path> {
111 final Path parent;
112 final String baseName;
113
114 PathWeakReferenceForCleanup(Path referent, ReferenceQueue<Path> referenceQueue) {
115 super(referent, referenceQueue);
116 parent = referent.getParentDirectory();
117 baseName = referent.getBaseName();
118 }
119 }
120
121 private static final Thread PATH_CHILD_CACHE_CLEANUP_THREAD = new Thread("Path cache cleanup") {
122 @Override
123 public void run() {
124 while (true) {
125 try {
126 PathWeakReferenceForCleanup ref = (PathWeakReferenceForCleanup) REFERENCE_QUEUE.remove();
127 Path parent = ref.parent;
128 synchronized (parent) {
129 // It's possible that since this reference was enqueued for deletion, the Path was
130 // recreated with a new entry in the map. We definitely shouldn't delete that entry, so
131 // check to make sure they're the same.
132 Reference<Path> currentRef = ref.parent.children.get(ref.baseName);
133 if (currentRef == ref) {
134 ref.parent.children.remove(ref.baseName);
135 }
136 }
137 } catch (InterruptedException e) {
138 // Ignored.
139 }
140 }
141 }
142 };
143
144 static {
145 PATH_CHILD_CACHE_CLEANUP_THREAD.setDaemon(true);
146 PATH_CHILD_CACHE_CLEANUP_THREAD.start();
147 }
148
149 /**
150 * A mapping from a child file name to the {@link Path} representing it.
151 *
152 * <p>File names must be a single path segment. The strings must be
153 * canonical. We use IdentityHashMap instead of HashMap for reasons of space
154 * efficiency: instances are smaller by a single word. Also, since all path
155 * segments are interned, the universe of Paths holds a minimal number of
156 * references to strings. (It's doubtful that there's any time gain from use
157 * of an IdentityHashMap, since the time saved by avoiding string equality
158 * tests during hash lookups is probably equal to the time spent eagerly
159 * interning strings, unless the collision rate is high.)
160 *
161 * <p>The Paths are stored as weak references to ensure that a live
162 * Path for a directory does not hold a strong reference to all of its
163 * descendants, which would prevent collection of paths we never intend to
164 * use again. Stale references in the map must be treated as absent.
165 *
166 * <p>A Path may be recycled once there is no Path that refers to it or
167 * to one of its descendants. This means that any data stored in the
168 * Path instance by one of its subclasses must be recoverable: it's ok to
169 * store data in Paths as an optimization, but there must be another
170 * source for that data in case the Path is recycled.
171 *
172 * <p>We intentionally avoid using the existing library classes for reasons of
173 * space efficiency: while ConcurrentHashMap would reduce our locking
174 * overhead, and ReferenceMap would simplify the code a little, both of those
175 * classes have much higher per-instance overheads than IdentityHashMap.
176 *
177 * <p>The Path object must be synchronized while children is being
178 * accessed.
179 */
Nathan Harmataa65fcf92015-08-20 21:14:44 +0000180 private volatile IdentityHashMap<String, Reference<Path>> children;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100181
182 /**
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000183 * Create a path instance.
184 *
185 * <p>Should only be called by {@link PathFactory#createChildPath(Path, String)}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100186 *
187 * @param name the name of this path; it must be canonicalized with {@link
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000188 * StringCanonicalizer#intern}
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100189 * @param parent this path's parent
190 */
191 protected Path(FileSystem fileSystem, String name, Path parent) {
192 this.fileSystem = fileSystem;
193 this.name = name;
194 this.parent = parent;
195 this.depth = parent == null ? 0 : parent.depth + 1;
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000196
197 // No need to include the drive letter in the hash code, because it's derived from the parent
198 // and/or the name.
Yun Peng352f7e72016-05-09 11:08:25 +0000199 if (fileSystem == null || fileSystem.isFilePathCaseSensitive()) {
200 this.hashCode = Objects.hash(parent, name);
201 } else {
202 this.hashCode = Objects.hash(parent, name.toLowerCase());
203 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100204 }
205
206 /**
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000207 * Create the root path.
208 *
209 * <p>Should only be called by {@link PathFactory#createRootPath(FileSystem)}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100210 */
211 protected Path(FileSystem fileSystem) {
212 this(fileSystem, StringCanonicalizer.intern("/"), null);
213 }
214
215 private void writeObject(ObjectOutputStream out) throws IOException {
janakr150858b2017-07-25 00:04:53 +0200216 Preconditions.checkState(
217 fileSystem == fileSystemForSerialization, "%s %s", fileSystem, fileSystemForSerialization);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100218 out.writeUTF(getPathString());
219 }
220
221 private void readObject(ObjectInputStream in) throws IOException {
222 fileSystem = fileSystemForSerialization;
223 String p = in.readUTF();
nharmatab4060b62017-04-04 17:11:39 +0000224 PathFragment pf = PathFragment.create(p);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100225 PathFragment parentDir = pf.getParentDirectory();
226 if (parentDir == null) {
227 this.name = "/";
228 this.parent = null;
229 this.depth = 0;
230 } else {
231 this.name = pf.getBaseName();
232 this.parent = fileSystem.getPath(parentDir);
233 this.depth = this.parent.depth + 1;
234 }
235 this.hashCode = Objects.hash(parent, name);
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000236 reinitializeAfterDeserialization();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100237 }
238
239 /**
240 * Returns the filesystem instance to which this path belongs.
241 */
242 public FileSystem getFileSystem() {
243 return fileSystem;
244 }
245
246 public boolean isRootDirectory() {
247 return parent == null;
248 }
249
250 protected Path createChildPath(String childName) {
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000251 return fileSystem.getPathFactory().createChildPath(this, childName);
252 }
253
254 /**
255 * Reinitializes this object after deserialization.
256 *
257 * <p>Derived classes should use this hook to initialize additional state.
258 */
259 protected void reinitializeAfterDeserialization() {}
260
261 /**
262 * Returns true if {@code ancestorPath} may be an ancestor of {@code path}.
263 *
264 * <p>The return value may be a false positive, but it cannot be a false negative. This means that
265 * a true return value doesn't mean the ancestor candidate is really an ancestor, however a false
266 * return value means it's guaranteed that {@code ancestorCandidate} is not an ancestor of this
267 * path.
268 *
269 * <p>Subclasses may override this method with filesystem-specific logic, e.g. a Windows
270 * filesystem may return false if the ancestor path is on a different drive than this one, because
271 * it is then guaranteed that the ancestor candidate cannot be an ancestor of this path.
272 *
273 * @param ancestorCandidate the path that may or may not be an ancestor of this one
274 */
275 protected boolean isMaybeRelativeTo(Path ancestorCandidate) {
276 return true;
277 }
278
279 /**
280 * Returns true if this directory is top-level, i.e. it is its own parent.
281 *
282 * <p>When canonicalizing paths the ".." segment of a top-level directory always resolves to the
283 * directory itself.
284 *
285 * <p>On Unix, a top-level directory would be just the filesystem root ("/), on Windows it would
286 * be the filesystem root and the volume roots.
287 */
288 protected boolean isTopLevelDirectory() {
289 return isRootDirectory();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100290 }
291
292 /**
293 * Returns the child path named name, or creates such a path (and caches it)
294 * if it doesn't already exist.
295 */
296 private Path getCachedChildPath(String childName) {
nharmata39e659e2017-04-04 14:42:23 +0000297 return fileSystem.getPathFactory().getCachedChildPathInternal(this, childName);
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000298 }
299
nharmata39e659e2017-04-04 14:42:23 +0000300 /**
301 * Internal method only intended to be called by {@link PathFactory#getCachedChildPathInternal}.
302 */
303 public static Path getCachedChildPathInternal(Path parent, String childName, boolean cacheable) {
Nathan Harmataa65fcf92015-08-20 21:14:44 +0000304 // We get a canonical instance since 'children' is an IdentityHashMap.
nharmata39e659e2017-04-04 14:42:23 +0000305 childName = StringCanonicalizer.intern(childName);
306 if (!cacheable) {
Laszlo Csomor8679e4c2017-01-03 15:21:08 +0000307 // Non-cacheable children won't show up in `children` so applyToChildren won't run for these.
308 return parent.createChildPath(childName);
309 }
Googler0e59bb62017-02-04 08:31:20 +0000310
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000311 synchronized (parent) {
Googler0e59bb62017-02-04 08:31:20 +0000312 if (parent.children == null) {
313 // 66% of Paths have size == 1, 80% <= 2
314 parent.children = new IdentityHashMap<>(1);
315 }
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000316 Reference<Path> childRef = parent.children.get(childName);
Nathan Harmatafee390d2015-09-02 22:46:53 +0000317 Path child;
318 if (childRef == null || (child = childRef.get()) == null) {
Laszlo Csomor8679e4c2017-01-03 15:21:08 +0000319 child = parent.createChildPath(childName);
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000320 parent.children.put(childName, new PathWeakReferenceForCleanup(child, REFERENCE_QUEUE));
Nathan Harmataa65fcf92015-08-20 21:14:44 +0000321 }
Nathan Harmatafee390d2015-09-02 22:46:53 +0000322 return child;
Nathan Harmataa65fcf92015-08-20 21:14:44 +0000323 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100324 }
325
326 /**
327 * Applies the specified function to each {@link Path} that is an existing direct
328 * descendant of this one. The Predicate is evaluated only for its
329 * side-effects.
330 *
331 * <p>This function exists to hide the "children" field, whose complex
332 * synchronization and identity requirements are too unsafe to be exposed to
333 * subclasses. For example, the "children" field must be synchronized for
334 * the duration of any iteration over it; it may be null; and references
335 * within it may be stale, and must be ignored.
336 */
337 protected synchronized void applyToChildren(Predicate<Path> function) {
338 if (children != null) {
339 for (Reference<Path> childRef : children.values()) {
340 Path child = childRef.get();
341 if (child != null) {
342 function.apply(child);
343 }
344 }
345 }
346 }
347
348 /**
349 * Returns whether this path is recursively "under" {@code prefix} - that is,
350 * whether {@code path} is a prefix of this path.
351 *
352 * <p>This method returns {@code true} when called with this path itself. This
353 * method acts independently of the existence of files or folders.
354 *
355 * @param prefix a path which may or may not be a prefix of this path
356 */
357 public boolean startsWith(Path prefix) {
358 Path n = this;
359 for (int i = 0, len = depth - prefix.depth; i < len; i++) {
360 n = n.getParentDirectory();
361 }
362 return prefix.equals(n);
363 }
364
365 /**
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000366 * Computes a string representation of this path, and writes it to the given string builder. Only
367 * called locally with a new instance.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100368 */
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000369 protected void buildPathString(StringBuilder result) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100370 if (isRootDirectory()) {
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000371 result.append(PathFragment.ROOT_DIR);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100372 } else {
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000373 parent.buildPathString(result);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100374 if (!parent.isRootDirectory()) {
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000375 result.append(PathFragment.SEPARATOR_CHAR);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100376 }
377 result.append(name);
378 }
379 }
380
381 /**
buchgr5ef576f2017-09-14 14:36:44 +0200382 * Returns the path encoded as an {@link URI}.
383 *
384 * <p>This concrete implementation returns URIs with "file" as the scheme.
385 * For Example:
386 * - On Unix the path "/tmp/foo bar.txt" will be encoded as
387 * "file:///tmp/foo%20bar.txt".
388 * - On Windows the path "C:\Temp\Foo Bar.txt" will be encoded as
389 * "file:///C:/Temp/Foo%20Bar.txt"
390 *
391 * <p>Implementors extending this class for special filesystems will likely need to override
392 * this method.
393 *
394 * @throws URISyntaxException if the URI cannot be constructed.
395 */
396 public URI toURI() {
397 String ps = getPathString();
398 if (!ps.startsWith("/")) {
399 // On Windows URI's need to start with a '/'. i.e. C:\Foo\Bar would be file:///C:/Foo/Bar
400 ps = "/" + ps;
401 }
402 try {
403 return new URI("file",
404 // Needs to be "" instead of null, so that toString() will append "//" after the scheme.
405 // We need this for backwards compatibility reasons as some consumers of the BEP are
406 // broken.
407 "",
408 ps, null, null);
409 } catch (URISyntaxException e) {
410 throw new IllegalStateException(e);
411 }
412 }
413
414 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100415 * Returns the path as a string.
416 */
417 public String getPathString() {
418 // Profile driven optimization:
419 // Preallocate a size determined by the depth, so that
420 // we do not have to expand the capacity of the StringBuilder
421 StringBuilder builder = new StringBuilder(depth * 20);
422 buildPathString(builder);
423 return builder.toString();
424 }
425
tomlu67c84b12017-11-06 19:49:16 +0100426 @Override
427 public void repr(SkylarkPrinter printer) {
428 printer.append(getPathString());
429 }
430
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100431 /**
432 * Returns the path as a string.
433 */
434 @Override
435 public String toString() {
436 return getPathString();
437 }
438
439 @Override
440 public int hashCode() {
441 return hashCode;
442 }
443
444 @Override
445 public boolean equals(Object other) {
446 if (this == other) {
447 return true;
448 }
449 if (!(other instanceof Path)) {
450 return false;
451 }
Yun Peng352f7e72016-05-09 11:08:25 +0000452
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100453 Path otherPath = (Path) other;
Googlerbaeba8b22016-04-20 19:20:37 +0000454 if (hashCode != otherPath.hashCode) {
455 return false;
456 }
Yun Peng352f7e72016-05-09 11:08:25 +0000457
458 if (!fileSystem.equals(otherPath.fileSystem)) {
459 return false;
460 }
461
462 if (fileSystem.isFilePathCaseSensitive()) {
463 return name.equals(otherPath.name)
464 && Objects.equals(parent, otherPath.parent);
465 } else {
466 return name.toLowerCase().equals(otherPath.name.toLowerCase())
467 && Objects.equals(parent, otherPath.parent);
468 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100469 }
470
471 /**
472 * Returns a string of debugging information associated with this path.
473 * The results are unspecified and MUST NOT be interpreted programmatically.
474 */
475 protected String toDebugString() {
476 return "";
477 }
478
479 /**
480 * Returns a path representing the parent directory of this path,
481 * or null iff this Path represents the root of the filesystem.
482 *
483 * <p>Note: This method normalises ".." and "." path segments by string
484 * processing, not by directory lookups.
485 */
486 public Path getParentDirectory() {
487 return parent;
488 }
489
tomlub1e8daf2017-10-27 15:58:21 -0400490 /** Prefer to use {@link #exists(FileSystem)}. */
491 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100492 public boolean exists() {
tomlub1e8daf2017-10-27 15:58:21 -0400493 return exists(fileSystem);
494 }
495
496 /**
497 * Returns true iff this path denotes an existing file of any kind. Follows symbolic links.
498 *
499 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
500 * Path.
501 */
502 public boolean exists(FileSystem fileSystem) {
aehligc801c392017-12-19 07:12:25 -0800503 return fileSystem.exists(this, true);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100504 }
505
tomlub1e8daf2017-10-27 15:58:21 -0400506 /** Prefer to use {@link #exists(FileSystem, Symlinks)}. */
507 @Deprecated
508 public boolean exists(Symlinks followSymlinks) {
509 return exists(fileSystem, followSymlinks);
510 }
511
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100512 /**
513 * Returns true iff this path denotes an existing file of any kind.
514 *
tomlub1e8daf2017-10-27 15:58:21 -0400515 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
516 * Path.
517 *
518 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
519 * link is dereferenced until a file other than a symbolic link is found
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100520 */
tomlub1e8daf2017-10-27 15:58:21 -0400521 public boolean exists(FileSystem fileSystem, Symlinks followSymlinks) {
aehligc801c392017-12-19 07:12:25 -0800522 return fileSystem.exists(this, followSymlinks.toBoolean());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100523 }
524
tomlub1e8daf2017-10-27 15:58:21 -0400525 /** Prefer to use {@link #getDirectoryEntries(FileSystem)}. */
526 @Deprecated
527 public Collection<Path> getDirectoryEntries() throws IOException, FileNotFoundException {
528 return getDirectoryEntries(fileSystem);
529 }
530
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100531 /**
tomlub1e8daf2017-10-27 15:58:21 -0400532 * Returns a new, immutable collection containing the names of all entities within the directory
533 * denoted by the current path. Follows symbolic links.
534 *
535 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
536 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100537 *
538 * @throws FileNotFoundException If the directory is not found
539 * @throws IOException If the path does not denote a directory
540 */
tomlub1e8daf2017-10-27 15:58:21 -0400541 public Collection<Path> getDirectoryEntries(FileSystem fileSystem)
542 throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -0800543 Collection<String> entries = fileSystem.getDirectoryEntries(this);
tomlu0a82e702017-10-23 18:16:44 +0200544 Collection<Path> result = new ArrayList<>(entries.size());
545 for (String entry : entries) {
546 result.add(getChild(entry));
547 }
548 return result;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100549 }
550
tomlub1e8daf2017-10-27 15:58:21 -0400551 /** Prefer to use {@link #readdir(FileSystem, Symlinks)}. */
552 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100553 public Collection<Dirent> readdir(Symlinks followSymlinks) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -0400554 return readdir(fileSystem, followSymlinks);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100555 }
556
557 /**
tomlub1e8daf2017-10-27 15:58:21 -0400558 * Returns a collection of the names and types of all entries within the directory denoted by the
559 * current path. Follows symbolic links if {@code followSymlinks} is true. Note that the order of
560 * the returned entries is not guaranteed.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100561 *
tomlub1e8daf2017-10-27 15:58:21 -0400562 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
563 * Path.
564 *
565 * @param followSymlinks whether to follow symlinks or not
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100566 * @throws FileNotFoundException If the directory is not found
567 * @throws IOException If the path does not denote a directory
568 */
tomlub1e8daf2017-10-27 15:58:21 -0400569 public Collection<Dirent> readdir(FileSystem fileSystem, Symlinks followSymlinks)
570 throws IOException {
aehligc801c392017-12-19 07:12:25 -0800571 return fileSystem.readdir(this, followSymlinks.toBoolean());
tomlub1e8daf2017-10-27 15:58:21 -0400572 }
573
574 /** Prefer to use {@link #stat(FileSystem)}. */
575 @Deprecated
576 public FileStatus stat() throws IOException {
577 return stat(fileSystem);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100578 }
579
580 /**
581 * Returns the status of a file, following symbolic links.
582 *
tomlub1e8daf2017-10-27 15:58:21 -0400583 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
584 * Path.
585 *
586 * @throws IOException if there was an error obtaining the file status. Note, some implementations
587 * may defer the I/O, and hence the throwing of the exception, until the accessor methods of
588 * {@code FileStatus} are called.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100589 */
tomlub1e8daf2017-10-27 15:58:21 -0400590 public FileStatus stat(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -0800591 return fileSystem.stat(this, true);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100592 }
593
tomlub1e8daf2017-10-27 15:58:21 -0400594 /** Prefer to use {@link #statNullable(FileSystem)}. */
595 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100596 public FileStatus statNullable() {
tomlub1e8daf2017-10-27 15:58:21 -0400597 return statNullable(fileSystem);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100598 }
599
600 /**
601 * Like stat(), but returns null on file-nonexistence instead of throwing.
tomlub1e8daf2017-10-27 15:58:21 -0400602 *
603 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
604 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100605 */
tomlub1e8daf2017-10-27 15:58:21 -0400606 public FileStatus statNullable(FileSystem fileSystem) {
607 return statNullable(fileSystem, Symlinks.FOLLOW);
608 }
609
610 /** Prefer to use {@link #statNullable(FileSystem, Symlinks)}. */
611 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100612 public FileStatus statNullable(Symlinks symlinks) {
tomlub1e8daf2017-10-27 15:58:21 -0400613 return statNullable(fileSystem, symlinks);
614 }
615
616 /**
617 * Like stat(), but returns null on file-nonexistence instead of throwing.
618 *
619 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
620 * Path.
621 */
622 public FileStatus statNullable(FileSystem fileSystem, Symlinks symlinks) {
aehligc801c392017-12-19 07:12:25 -0800623 return fileSystem.statNullable(this, symlinks.toBoolean());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100624 }
625
tomlub1e8daf2017-10-27 15:58:21 -0400626 /** Prefer to use {@link #stat(FileSystem, Symlinks)}. */
627 @Deprecated
628 public FileStatus stat(Symlinks followSymlinks) throws IOException {
629 return stat(fileSystem, followSymlinks);
630 }
631
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100632 /**
633 * Returns the status of a file, optionally following symbolic links.
634 *
tomlub1e8daf2017-10-27 15:58:21 -0400635 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
636 * Path.
637 *
638 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
639 * link is dereferenced until a file other than a symbolic link is found
640 * @throws IOException if there was an error obtaining the file status. Note, some implementations
641 * may defer the I/O, and hence the throwing of the exception, until the accessor methods of
642 * {@code FileStatus} are called
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100643 */
tomlub1e8daf2017-10-27 15:58:21 -0400644 public FileStatus stat(FileSystem fileSystem, Symlinks followSymlinks) throws IOException {
aehligc801c392017-12-19 07:12:25 -0800645 return fileSystem.stat(this, followSymlinks.toBoolean());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100646 }
647
tomlub1e8daf2017-10-27 15:58:21 -0400648 /** Prefer to use {@link #statIfFound(FileSystem)}. */
649 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100650 public FileStatus statIfFound() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -0400651 return statIfFound(fileSystem);
652 }
653
654 /**
655 * Like {@link #stat}, but may return null if the file is not found (corresponding to {@code
656 * ENOENT} and {@code ENOTDIR} in Unix's stat(2) function) instead of throwing. Follows symbolic
657 * links.
658 *
659 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
660 * Path.
661 */
662 public FileStatus statIfFound(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -0800663 return fileSystem.statIfFound(this, true);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100664 }
665
tomlub1e8daf2017-10-27 15:58:21 -0400666 /** Prefer to use {@link #statIfFound(FileSystem, Symlinks)}. */
667 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100668 public FileStatus statIfFound(Symlinks followSymlinks) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -0400669 return statIfFound(fileSystem, followSymlinks);
670 }
671
672 /**
673 * Like {@link #stat}, but may return null if the file is not found (corresponding to {@code
674 * ENOENT} and {@code ENOTDIR} in Unix's stat(2) function) instead of throwing.
675 *
676 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
677 * Path.
678 *
679 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
680 * link is dereferenced until a file other than a symbolic link is found
681 */
682 public FileStatus statIfFound(FileSystem fileSystem, Symlinks followSymlinks) throws IOException {
aehligc801c392017-12-19 07:12:25 -0800683 return fileSystem.statIfFound(this, followSymlinks.toBoolean());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100684 }
685
tomlub1e8daf2017-10-27 15:58:21 -0400686 /** Prefer to use {@link #isDirectory()} (FileSystem)}. */
687 @Deprecated
688 public boolean isDirectory() {
689 return isDirectory(fileSystem);
690 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100691
692 /**
tomlub1e8daf2017-10-27 15:58:21 -0400693 * Returns true iff this path denotes an existing directory. Follows symbolic links.
694 *
695 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
696 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100697 */
tomlub1e8daf2017-10-27 15:58:21 -0400698 public boolean isDirectory(FileSystem fileSystem) {
aehligc801c392017-12-19 07:12:25 -0800699 return fileSystem.isDirectory(this, true);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100700 }
701
tomlub1e8daf2017-10-27 15:58:21 -0400702 /** Prefer to use {@link #isDirectory(FileSystem, Symlinks)}. */
703 @Deprecated
704 public boolean isDirectory(Symlinks followSymlinks) {
705 return isDirectory(fileSystem, followSymlinks);
706 }
707
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100708 /**
709 * Returns true iff this path denotes an existing directory.
710 *
tomlub1e8daf2017-10-27 15:58:21 -0400711 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
712 * Path.
713 *
714 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
715 * link is dereferenced until a file other than a symbolic link is found
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100716 */
tomlub1e8daf2017-10-27 15:58:21 -0400717 public boolean isDirectory(FileSystem fileSystem, Symlinks followSymlinks) {
aehligc801c392017-12-19 07:12:25 -0800718 return fileSystem.isDirectory(this, followSymlinks.toBoolean());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100719 }
720
tomlub1e8daf2017-10-27 15:58:21 -0400721 /** Prefer to use {@link #isFile(FileSystem)}. */
722 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100723 public boolean isFile() {
tomlub1e8daf2017-10-27 15:58:21 -0400724 return isFile(fileSystem);
725 }
726
727 /**
728 * Returns true iff this path denotes an existing regular or special file. Follows symbolic links.
729 *
730 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
731 * Path.
732 *
733 * <p>For our purposes, "file" includes special files (socket, fifo, block or char devices) too;
734 * it excludes symbolic links and directories.
735 */
736 public boolean isFile(FileSystem fileSystem) {
aehligc801c392017-12-19 07:12:25 -0800737 return fileSystem.isFile(this, true);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100738 }
739
tomlub1e8daf2017-10-27 15:58:21 -0400740 /** Prefer to use {@link #isFile(FileSystem, Symlinks)}. */
741 @Deprecated
742 public boolean isFile(Symlinks followSymlinks) {
743 return isFile(fileSystem, followSymlinks);
744 }
745
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100746 /**
747 * Returns true iff this path denotes an existing regular or special file.
748 *
tomlub1e8daf2017-10-27 15:58:21 -0400749 * <p>For our purposes, a "file" includes special files (socket, fifo, block or char devices) too;
750 * it excludes symbolic links and directories.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100751 *
tomlub1e8daf2017-10-27 15:58:21 -0400752 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
753 * Path.
754 *
755 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
756 * link is dereferenced until a file other than a symbolic link is found.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100757 */
tomlub1e8daf2017-10-27 15:58:21 -0400758 public boolean isFile(FileSystem fileSystem, Symlinks followSymlinks) {
aehligc801c392017-12-19 07:12:25 -0800759 return fileSystem.isFile(this, followSymlinks.toBoolean());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100760 }
761
tomlub1e8daf2017-10-27 15:58:21 -0400762 /** Prefer to use {@link #isSpecialFile(FileSystem)}. */
763 @Deprecated
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000764 public boolean isSpecialFile() {
tomlub1e8daf2017-10-27 15:58:21 -0400765 return isSpecialFile(fileSystem);
766 }
767
768 /**
769 * Returns true iff this path denotes an existing special file (e.g. fifo). Follows symbolic
770 * links.
771 *
772 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
773 * Path.
774 */
775 public boolean isSpecialFile(FileSystem fileSystem) {
aehligc801c392017-12-19 07:12:25 -0800776 return fileSystem.isSpecialFile(this, true);
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000777 }
778
tomlub1e8daf2017-10-27 15:58:21 -0400779 /** Prefer to use {@link #isSpecialFile(FileSystem, Symlinks)}. */
780 @Deprecated
781 public boolean isSpecialFile(Symlinks followSymlinks) {
782 return isSpecialFile(fileSystem, followSymlinks);
783 }
784
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000785 /**
786 * Returns true iff this path denotes an existing special file (e.g. fifo).
787 *
tomlub1e8daf2017-10-27 15:58:21 -0400788 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
789 * Path.
790 *
791 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
792 * link is dereferenced until a path other than a symbolic link is found.
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000793 */
tomlub1e8daf2017-10-27 15:58:21 -0400794 public boolean isSpecialFile(FileSystem fileSystem, Symlinks followSymlinks) {
aehligc801c392017-12-19 07:12:25 -0800795 return fileSystem.isSpecialFile(this, followSymlinks.toBoolean());
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000796 }
797
tomlub1e8daf2017-10-27 15:58:21 -0400798 /** Prefer to use {@link #isSymbolicLink(FileSystem)}. */
799 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100800 public boolean isSymbolicLink() {
tomlub1e8daf2017-10-27 15:58:21 -0400801 return isSymbolicLink(fileSystem);
802 }
803
804 /**
805 * Returns true iff this path denotes an existing symbolic link. Does not follow symbolic links.
806 *
807 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
808 * Path.
809 */
810 public boolean isSymbolicLink(FileSystem fileSystem) {
aehligc801c392017-12-19 07:12:25 -0800811 return fileSystem.isSymbolicLink(this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100812 }
813
814 /**
815 * Returns the last segment of this path, or "/" for the root directory.
816 */
817 public String getBaseName() {
818 return name;
819 }
820
821 /**
822 * Interprets the name of a path segment relative to the current path and
823 * returns the result.
824 *
825 * <p>This is a purely syntactic operation, i.e. it does no I/O, it does not
826 * validate the existence of any path, nor resolve symbolic links. If 'prefix'
827 * is not canonical, then a 'name' of '..' will be interpreted incorrectly.
828 *
829 * @precondition segment contains no slashes.
830 */
831 private Path getCanonicalPath(String segment) {
Ulf Adams07dba942015-03-05 14:47:37 +0000832 if (segment.equals(".") || segment.isEmpty()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100833 return this; // that's a noop
834 } else if (segment.equals("..")) {
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000835 // top-level directory's parent is root, when canonicalising:
836 return isTopLevelDirectory() ? this : parent;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100837 } else {
838 return getCachedChildPath(segment);
839 }
840 }
841
842 /**
843 * Returns the path formed by appending the single non-special segment
844 * "baseName" to this path.
845 *
846 * <p>You should almost always use {@link #getRelative} instead, which has
847 * the same performance characteristics if the given name is a valid base
848 * name, and which also works for '.', '..', and strings containing '/'.
849 *
850 * @throws IllegalArgumentException if {@code baseName} is not a valid base
851 * name according to {@link FileSystemUtils#checkBaseName}
852 */
853 public Path getChild(String baseName) {
854 FileSystemUtils.checkBaseName(baseName);
855 return getCachedChildPath(baseName);
856 }
857
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000858 protected Path getRootForRelativePathComputation(PathFragment suffix) {
859 return suffix.isAbsolute() ? fileSystem.getRootDirectory() : this;
860 }
861
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100862 /**
863 * Returns the path formed by appending the relative or absolute path fragment
864 * {@code suffix} to this path.
865 *
866 * <p>If suffix is absolute, the current path will be ignored; otherwise, they
867 * will be combined. Up-level references ("..") cause the preceding path
868 * segment to be elided; this interpretation is only correct if the base path
869 * is canonical.
870 */
871 public Path getRelative(PathFragment suffix) {
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000872 Path result = getRootForRelativePathComputation(suffix);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100873 for (String segment : suffix.segments()) {
874 result = result.getCanonicalPath(segment);
875 }
876 return result;
877 }
878
879 /**
880 * Returns the path formed by appending the relative or absolute string
881 * {@code path} to this path.
882 *
883 * <p>If the given path string is absolute, the current path will be ignored;
884 * otherwise, they will be combined. Up-level references ("..") cause the
885 * preceding path segment to be elided.
886 *
887 * <p>This is a purely syntactic operation, i.e. it does no I/O, it does not
888 * validate the existence of any path, nor resolve symbolic links.
889 */
890 public Path getRelative(String path) {
891 // Fast path for valid base names.
892 if ((path.length() == 0) || (path.equals("."))) {
893 return this;
894 } else if (path.equals("..")) {
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000895 return isTopLevelDirectory() ? this : parent;
nharmataaac13242017-04-24 17:41:23 +0200896 } else if (PathFragment.containsSeparator(path)) {
nharmatab4060b62017-04-04 17:11:39 +0000897 return getRelative(PathFragment.create(path));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100898 } else {
899 return getCachedChildPath(path);
900 }
901 }
902
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000903 protected final String[] getSegments() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100904 String[] resultSegments = new String[depth];
905 Path currentPath = this;
906 for (int pos = depth - 1; pos >= 0; pos--) {
907 resultSegments[pos] = currentPath.getBaseName();
908 currentPath = currentPath.getParentDirectory();
909 }
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000910 return resultSegments;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100911 }
912
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000913 /** Returns an absolute PathFragment representing this path. */
914 public PathFragment asFragment() {
nharmataaac13242017-04-24 17:41:23 +0200915 return PathFragment.createAlreadyInterned('\0', true, getSegments());
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000916 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100917
918 /**
919 * Returns a relative path fragment to this path, relative to {@code
920 * ancestorDirectory}. {@code ancestorDirectory} must be on the same
921 * filesystem as this path. (Currently, both this path and "ancestorDirectory"
922 * must be absolute, though this restriction could be loosened.)
923 * <p>
924 * <code>x.relativeTo(z) == y</code> implies
925 * <code>z.getRelative(y.getPathString()) == x</code>.
926 * <p>
927 * For example, <code>"/foo/bar/wiz".relativeTo("/foo")</code> returns
928 * <code>"bar/wiz"</code>.
929 *
930 * @throws IllegalArgumentException if this path is not beneath {@code
931 * ancestorDirectory} or if they are not part of the same filesystem
932 */
933 public PathFragment relativeTo(Path ancestorPath) {
934 checkSameFilesystem(ancestorPath);
935
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000936 if (isMaybeRelativeTo(ancestorPath)) {
937 // Fast path: when otherPath is the ancestor of this path
938 int resultSegmentCount = depth - ancestorPath.depth;
939 if (resultSegmentCount >= 0) {
940 String[] resultSegments = new String[resultSegmentCount];
941 Path currentPath = this;
942 for (int pos = resultSegmentCount - 1; pos >= 0; pos--) {
943 resultSegments[pos] = currentPath.getBaseName();
944 currentPath = currentPath.getParentDirectory();
945 }
946 if (ancestorPath.equals(currentPath)) {
nharmataaac13242017-04-24 17:41:23 +0200947 return PathFragment.createAlreadyInterned('\0', false, resultSegments);
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000948 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100949 }
950 }
951
952 throw new IllegalArgumentException("Path " + this + " is not beneath " + ancestorPath);
953 }
954
955 /**
956 * Checks that "this" and "that" are paths on the same filesystem.
957 */
958 protected void checkSameFilesystem(Path that) {
959 if (this.fileSystem != that.fileSystem) {
960 throw new IllegalArgumentException("Files are on different filesystems: "
961 + this + ", " + that);
962 }
963 }
964
tomlub1e8daf2017-10-27 15:58:21 -0400965 /** Prefer to use {@link #getOutputStream(FileSystem)}. */
966 @Deprecated
967 public OutputStream getOutputStream() throws IOException, FileNotFoundException {
968 return getOutputStream(fileSystem);
969 }
970
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100971 /**
tomlub1e8daf2017-10-27 15:58:21 -0400972 * Returns an output stream to the file denoted by the current path, creating it and truncating it
973 * if necessary. The stream is opened for writing.
974 *
975 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
976 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100977 *
978 * @throws FileNotFoundException If the file cannot be found or created.
979 * @throws IOException If a different error occurs.
980 */
tomlub1e8daf2017-10-27 15:58:21 -0400981 public OutputStream getOutputStream(FileSystem fileSystem)
982 throws IOException, FileNotFoundException {
983 return getOutputStream(fileSystem, false);
984 }
985
986 /** Prefer to use {@link #getOutputStream(FileSystem, boolean)}. */
987 @Deprecated
988 public OutputStream getOutputStream(boolean append) throws IOException, FileNotFoundException {
989 return getOutputStream(fileSystem, append);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100990 }
991
992 /**
tomlub1e8daf2017-10-27 15:58:21 -0400993 * Returns an output stream to the file denoted by the current path, creating it and truncating it
994 * if necessary. The stream is opened for writing.
995 *
996 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
997 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100998 *
999 * @param append whether to open the file in append mode.
1000 * @throws FileNotFoundException If the file cannot be found or created.
1001 * @throws IOException If a different error occurs.
1002 */
tomlub1e8daf2017-10-27 15:58:21 -04001003 public OutputStream getOutputStream(FileSystem fileSystem, boolean append)
1004 throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001005 return fileSystem.getOutputStream(this, append);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001006 }
1007
tomlub1e8daf2017-10-27 15:58:21 -04001008 /** Prefer to use {@link #createDirectory(FileSystem)}. */
1009 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001010 public boolean createDirectory() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001011 return createDirectory(fileSystem);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001012 }
1013
1014 /**
tomlub1e8daf2017-10-27 15:58:21 -04001015 * Creates a directory with the name of the current path, not following symbolic links. Returns
1016 * normally iff the directory exists after the call: true if the directory was created by this
1017 * call, false if the directory was already in existence. Throws an exception if the directory
1018 * could not be created for any reason.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001019 *
tomlub1e8daf2017-10-27 15:58:21 -04001020 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1021 * Path.
1022 *
1023 * @throws IOException if the directory creation failed for any reason
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001024 */
tomlub1e8daf2017-10-27 15:58:21 -04001025 public boolean createDirectory(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001026 return fileSystem.createDirectory(this);
tomlub1e8daf2017-10-27 15:58:21 -04001027 }
1028
tomludecca2b2017-12-21 08:59:51 -08001029 /**
1030 * Ensures that the directory with the name of the current path and all its ancestor directories
1031 * exist.
1032 *
1033 * <p>Does not return whether the directory already existed or was created by some other
1034 * concurrent call to this method.
1035 *
1036 * @throws IOException if the directory creation failed for any reason
1037 */
1038 public void createDirectoryAndParents() throws IOException {
1039 fileSystem.createDirectoryAndParents(this);
1040 }
1041
tomlub1e8daf2017-10-27 15:58:21 -04001042 /** Prefer to use {@link #createSymbolicLink(FileSystem, Path)}. */
1043 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001044 public void createSymbolicLink(Path target) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001045 createSymbolicLink(fileSystem, target);
1046 }
1047
1048 /**
1049 * Creates a symbolic link with the name of the current path, following symbolic links. The
1050 * referent of the created symlink is is the absolute path "target"; it is not possible to create
1051 * relative symbolic links via this method.
1052 *
1053 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1054 * Path.
1055 *
1056 * @throws IOException if the creation of the symbolic link was unsuccessful for any reason
1057 */
1058 public void createSymbolicLink(FileSystem fileSystem, Path target) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001059 checkSameFilesystem(target);
aehligc801c392017-12-19 07:12:25 -08001060 fileSystem.createSymbolicLink(this, target.asFragment());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001061 }
1062
tomlub1e8daf2017-10-27 15:58:21 -04001063 /** Prefer to use {@link #createSymbolicLink(FileSystem, PathFragment)}. */
1064 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001065 public void createSymbolicLink(PathFragment target) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001066 createSymbolicLink(fileSystem, target);
1067 }
1068
1069 /**
1070 * Creates a symbolic link with the name of the current path, following symbolic links. The
1071 * referent of the created symlink is is the path fragment "target", which may be absolute or
1072 * relative.
1073 *
1074 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1075 * Path.
1076 *
1077 * @throws IOException if the creation of the symbolic link was unsuccessful for any reason
1078 */
1079 public void createSymbolicLink(FileSystem fileSystem, PathFragment target) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001080 fileSystem.createSymbolicLink(this, target);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001081 }
Laszlo Csomora2da3112016-09-07 08:06:15 +00001082
tomlub1e8daf2017-10-27 15:58:21 -04001083 /** Prefer to use {@link #readSymbolicLink(FileSystem)}. */
1084 @Deprecated
1085 public PathFragment readSymbolicLink() throws IOException {
1086 return readSymbolicLink(fileSystem);
1087 }
1088
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001089 /**
Laszlo Csomora2da3112016-09-07 08:06:15 +00001090 * Returns the target of the current path, which must be a symbolic link. The link contents are
1091 * returned exactly, and may contain an absolute or relative path. Analogous to readlink(2).
1092 *
tomlu39fab102017-10-19 21:35:06 +02001093 * <p>Note: for {@link FileSystem}s where {@link FileSystem#supportsSymbolicLinksNatively(Path)}
Laszlo Csomora2da3112016-09-07 08:06:15 +00001094 * returns false, this method will throw an {@link UnsupportedOperationException} if the link
1095 * points to a non-existent file.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001096 *
tomlub1e8daf2017-10-27 15:58:21 -04001097 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1098 * Path.
1099 *
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001100 * @return the content (i.e. target) of the symbolic link
Laszlo Csomora2da3112016-09-07 08:06:15 +00001101 * @throws IOException if the current path is not a symbolic link, or the contents of the link
1102 * could not be read for any reason
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001103 */
tomlub1e8daf2017-10-27 15:58:21 -04001104 public PathFragment readSymbolicLink(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001105 return fileSystem.readSymbolicLink(this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001106 }
1107
tomlub1e8daf2017-10-27 15:58:21 -04001108 /** Prefer to use {@link #readSymbolicLinkUnchecked(FileSystem)}. */
1109 @Deprecated
1110 public PathFragment readSymbolicLinkUnchecked() throws IOException {
1111 return readSymbolicLinkUnchecked(fileSystem);
1112 }
1113
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001114 /**
tomlub1e8daf2017-10-27 15:58:21 -04001115 * If the current path is a symbolic link, returns the target of this symbolic link. The semantics
1116 * are intentionally left underspecified otherwise to permit efficient implementations.
1117 *
1118 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1119 * Path.
Nathan Harmata215974e52015-09-16 21:31:49 +00001120 *
1121 * @return the content (i.e. target) of the symbolic link
tomlub1e8daf2017-10-27 15:58:21 -04001122 * @throws IOException if the current path is not a symbolic link, or the contents of the link
1123 * could not be read for any reason
Nathan Harmata215974e52015-09-16 21:31:49 +00001124 */
tomlub1e8daf2017-10-27 15:58:21 -04001125 public PathFragment readSymbolicLinkUnchecked(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001126 return fileSystem.readSymbolicLinkUnchecked(this);
Nathan Harmata215974e52015-09-16 21:31:49 +00001127 }
1128
tomlub1e8daf2017-10-27 15:58:21 -04001129 /** Prefer to use {@link #createHardLink(FileSystem, Path)}. */
1130 @Deprecated
1131 public void createHardLink(Path link) throws IOException {
1132 createHardLink(fileSystem, link);
1133 }
1134
Nathan Harmata215974e52015-09-16 21:31:49 +00001135 /**
Googlere1cd9502016-09-07 14:33:29 +00001136 * Create a hard link for the current path.
1137 *
tomlub1e8daf2017-10-27 15:58:21 -04001138 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1139 * Path.
1140 *
Googlere1cd9502016-09-07 14:33:29 +00001141 * @param link the path of the new link
1142 * @throws IOException if there was an error executing {@link FileSystem#createHardLink}
1143 */
tomlub1e8daf2017-10-27 15:58:21 -04001144 public void createHardLink(FileSystem fileSystem, Path link) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001145 fileSystem.createHardLink(link, this);
Googlere1cd9502016-09-07 14:33:29 +00001146 }
1147
tomlub1e8daf2017-10-27 15:58:21 -04001148 /** Prefer to use {@link #resolveSymbolicLinks(FileSystem)}. */
1149 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001150 public Path resolveSymbolicLinks() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001151 return resolveSymbolicLinks(fileSystem);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001152 }
1153
1154 /**
tomlub1e8daf2017-10-27 15:58:21 -04001155 * Returns the canonical path for this path, by repeatedly replacing symbolic links with their
1156 * referents. Analogous to realpath(3).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001157 *
tomlub1e8daf2017-10-27 15:58:21 -04001158 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1159 * Path.
1160 *
1161 * @return the canonical path for this path
1162 * @throws IOException if any symbolic link could not be resolved, or other error occurred (for
1163 * example, the path does not exist)
1164 */
1165 public Path resolveSymbolicLinks(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001166 return fileSystem.resolveSymbolicLinks(this);
tomlub1e8daf2017-10-27 15:58:21 -04001167 }
1168
1169 /** Prefer to use {@link #renameTo(FileSystem, Path)}. */
1170 @Deprecated
1171 public void renameTo(Path target) throws IOException {
1172 renameTo(fileSystem, target);
1173 }
1174
1175 /**
1176 * Renames the file denoted by the current path to the location "target", not following symbolic
1177 * links.
1178 *
1179 * <p>Files cannot be atomically renamed across devices; copying is required. Use {@link
1180 * FileSystemUtils#copyFile} followed by {@link Path#delete}.
1181 *
1182 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1183 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001184 *
1185 * @throws IOException if the rename failed for any reason
1186 */
tomlub1e8daf2017-10-27 15:58:21 -04001187 public void renameTo(FileSystem fileSystem, Path target) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001188 checkSameFilesystem(target);
aehligc801c392017-12-19 07:12:25 -08001189 fileSystem.renameTo(this, target);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001190 }
1191
tomlub1e8daf2017-10-27 15:58:21 -04001192 /** Prefer to use {@link #getFileSize(FileSystem)}. */
1193 @Deprecated
1194 public long getFileSize() throws IOException, FileNotFoundException {
1195 return getFileSize(fileSystem);
1196 }
1197
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001198 /**
tomlub1e8daf2017-10-27 15:58:21 -04001199 * Returns the size in bytes of the file denoted by the current path, following symbolic links.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001200 *
Nathan Harmatad8b6ff22015-10-20 21:54:34 +00001201 * <p>The size of a directory or special file is undefined and should not be used.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001202 *
tomlub1e8daf2017-10-27 15:58:21 -04001203 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1204 * Path.
1205 *
1206 * @throws FileNotFoundException if the file denoted by the current path does not exist
1207 * @throws IOException if the file's metadata could not be read, or some other error occurred
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001208 */
tomlub1e8daf2017-10-27 15:58:21 -04001209 public long getFileSize(FileSystem fileSystem) throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001210 return fileSystem.getFileSize(this, true);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001211 }
1212
tomlub1e8daf2017-10-27 15:58:21 -04001213 /** Prefer to use {@link #getFileSize(FileSystem, Symlinks)}. */
1214 @Deprecated
1215 public long getFileSize(Symlinks followSymlinks) throws IOException, FileNotFoundException {
1216 return getFileSize(fileSystem, followSymlinks);
1217 }
1218
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001219 /**
1220 * Returns the size in bytes of the file denoted by the current path.
1221 *
tomlub1e8daf2017-10-27 15:58:21 -04001222 * <p>The size of directory or special file is undefined. The size of a symbolic link is the
1223 * length of the name of its referent.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001224 *
tomlub1e8daf2017-10-27 15:58:21 -04001225 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1226 * Path.
1227 *
1228 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
1229 * link is deferenced until a file other than a symbol link is found
1230 * @throws FileNotFoundException if the file denoted by the current path does not exist
1231 * @throws IOException if the file's metadata could not be read, or some other error occurred
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001232 */
tomlub1e8daf2017-10-27 15:58:21 -04001233 public long getFileSize(FileSystem fileSystem, Symlinks followSymlinks)
1234 throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001235 return fileSystem.getFileSize(this, followSymlinks.toBoolean());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001236 }
1237
tomlub1e8daf2017-10-27 15:58:21 -04001238 /** Prefer to use {@link #delete(FileSystem)}. */
1239 @Deprecated
tomluf903eb52017-10-27 12:12:11 -04001240 public boolean delete() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001241 return delete(fileSystem);
tomluf903eb52017-10-27 12:12:11 -04001242 }
1243
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001244 /**
tomluf903eb52017-10-27 12:12:11 -04001245 * Deletes the file denoted by this path, not following symbolic links. Returns normally iff the
1246 * file doesn't exist after the call: true if this call deleted the file, false if the file
1247 * already didn't exist. Throws an exception if the file could not be deleted for any reason.
1248 *
1249 * <p>This is a migration method. The method (and its FileSystem-less counterpart) will be deleted
1250 * once the FileSystem instance is removed from Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001251 *
1252 * @return true iff the file was actually deleted by this call
tomluf903eb52017-10-27 12:12:11 -04001253 * @throws IOException if the deletion failed but the file was present prior to the call
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001254 */
tomluf903eb52017-10-27 12:12:11 -04001255 public boolean delete(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001256 return fileSystem.delete(this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001257 }
1258
tomlub1e8daf2017-10-27 15:58:21 -04001259 /** Prefer to use {@link #getLastModifiedTime(FileSystem)}. */
1260 @Deprecated
1261 public long getLastModifiedTime() throws IOException {
1262 return getLastModifiedTime(fileSystem);
1263 }
1264
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001265 /**
tomlub1e8daf2017-10-27 15:58:21 -04001266 * Returns the last modification time of the file, in milliseconds since the UNIX epoch, of the
1267 * file denoted by the current path, following symbolic links.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001268 *
tomlub1e8daf2017-10-27 15:58:21 -04001269 * <p>Caveat: many filesystems store file times in seconds, so do not rely on the millisecond
1270 * precision.
1271 *
1272 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1273 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001274 *
1275 * @throws IOException if the operation failed for any reason
1276 */
tomlub1e8daf2017-10-27 15:58:21 -04001277 public long getLastModifiedTime(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001278 return fileSystem.getLastModifiedTime(this, true);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001279 }
1280
tomlub1e8daf2017-10-27 15:58:21 -04001281 /** Prefer to use {@link #getLastModifiedTime(FileSystem, Symlinks)}. */
1282 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001283 public long getLastModifiedTime(Symlinks followSymlinks) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001284 return getLastModifiedTime(fileSystem, followSymlinks);
1285 }
1286
1287 /**
1288 * Returns the last modification time of the file, in milliseconds since the UNIX epoch, of the
1289 * file denoted by the current path.
1290 *
1291 * <p>Caveat: many filesystems store file times in seconds, so do not rely on the millisecond
1292 * precision.
1293 *
1294 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1295 * Path.
1296 *
1297 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
1298 * link is dereferenced until a file other than a symbolic link is found
1299 * @throws IOException if the modification time for the file could not be obtained for any reason
1300 */
1301 public long getLastModifiedTime(FileSystem fileSystem, Symlinks followSymlinks)
1302 throws IOException {
aehligc801c392017-12-19 07:12:25 -08001303 return fileSystem.getLastModifiedTime(this, followSymlinks.toBoolean());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001304 }
1305
tomlub1e8daf2017-10-27 15:58:21 -04001306 /** Prefer to use {@link #setLastModifiedTime(FileSystem, long)}. */
1307 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001308 public void setLastModifiedTime(long newTime) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001309 setLastModifiedTime(fileSystem, newTime);
1310 }
1311
1312 /**
1313 * Sets the modification time of the file denoted by the current path. Follows symbolic links. If
1314 * newTime is -1, the current time according to the kernel is used; this may differ from the JVM's
1315 * clock.
1316 *
1317 * <p>Caveat: many filesystems store file times in seconds, so do not rely on the millisecond
1318 * precision.
1319 *
1320 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1321 * Path.
1322 *
1323 * @param newTime time, in milliseconds since the UNIX epoch, or -1L, meaning use the kernel's
1324 * current time
1325 * @throws IOException if the modification time for the file could not be set for any reason
1326 */
1327 public void setLastModifiedTime(FileSystem fileSystem, long newTime) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001328 fileSystem.setLastModifiedTime(this, newTime);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001329 }
1330
tomlub1e8daf2017-10-27 15:58:21 -04001331 /** Prefer to use {@link #getxattr(FileSystem, String)}. */
1332 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001333 public byte[] getxattr(String name) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001334 return getxattr(fileSystem, name);
1335 }
1336
1337 /**
1338 * Returns value of the given extended attribute name or null if attribute does not exist or file
1339 * system does not support extended attributes. Follows symlinks.
1340 *
1341 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1342 * Path.
1343 */
1344 public byte[] getxattr(FileSystem fileSystem, String name) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001345 return fileSystem.getxattr(this, name);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001346 }
1347
tomlub1e8daf2017-10-27 15:58:21 -04001348 /** Prefer to use {@link #getFastDigest(FileSystem)}. */
1349 @Deprecated
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001350 public byte[] getFastDigest() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001351 return getFastDigest(fileSystem);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001352 }
1353
1354 /**
tomlub1e8daf2017-10-27 15:58:21 -04001355 * Gets a fast digest for the given path, or {@code null} if there isn't one available. The digest
1356 * should be suitable for detecting changes to the file.
1357 *
1358 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1359 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001360 */
tomlub1e8daf2017-10-27 15:58:21 -04001361 public byte[] getFastDigest(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001362 return fileSystem.getFastDigest(this);
tomlub1e8daf2017-10-27 15:58:21 -04001363 }
1364
1365 /** Prefer to use {@link #isValidDigest(FileSystem, byte[])}. */
1366 @Deprecated
1367 public boolean isValidDigest(byte[] digest) {
1368 return isValidDigest(fileSystem, digest);
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001369 }
1370
1371 /**
1372 * Returns whether the given digest is a valid digest for the default system digest function.
tomlub1e8daf2017-10-27 15:58:21 -04001373 *
1374 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1375 * Path.
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001376 */
tomlub1e8daf2017-10-27 15:58:21 -04001377 public boolean isValidDigest(FileSystem fileSystem, byte[] digest) {
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001378 return fileSystem.isValidDigest(digest);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001379 }
1380
tomlub1e8daf2017-10-27 15:58:21 -04001381 /** Prefer to use {@link #getDigest(FileSystem)}. */
1382 @Deprecated
1383 public byte[] getDigest() throws IOException {
1384 return getDigest(fileSystem);
1385 }
1386
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001387 /**
tomlub1e8daf2017-10-27 15:58:21 -04001388 * Returns the digest of the file denoted by the current path, following symbolic links.
1389 *
1390 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1391 * Path.
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001392 *
1393 * @return a new byte array containing the file's digest
1394 * @throws IOException if the digest could not be computed for any reason
1395 */
tomlub1e8daf2017-10-27 15:58:21 -04001396 public byte[] getDigest(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001397 return fileSystem.getDigest(this);
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001398 }
1399
tomlub1e8daf2017-10-27 15:58:21 -04001400 /** Prefer to use {@link #getDigest(FileSystem, HashFunction)}. */
1401 @Deprecated
1402 public byte[] getDigest(HashFunction hashFunction) throws IOException {
1403 return getDigest(fileSystem, hashFunction);
1404 }
1405
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001406 /**
tomlub1e8daf2017-10-27 15:58:21 -04001407 * Returns the digest of the file denoted by the current path and digest function, following
1408 * symbolic links.
1409 *
1410 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1411 * Path.
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001412 *
1413 * @return a new byte array containing the file's digest
1414 * @throws IOException if the digest could not be computed for any reason
1415 */
tomlub1e8daf2017-10-27 15:58:21 -04001416 public byte[] getDigest(FileSystem fileSystem, HashFunction hashFunction) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001417 return fileSystem.getDigest(this, hashFunction);
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001418 }
1419
tomlub1e8daf2017-10-27 15:58:21 -04001420 /** Prefer to use {@link #getInputStream(FileSystem)}. */
1421 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001422 public InputStream getInputStream() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001423 return getInputStream(fileSystem);
1424 }
1425
1426 /**
1427 * Opens the file denoted by this path, following symbolic links, for reading, and returns an
1428 * input stream to it.
1429 *
1430 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1431 * Path.
1432 *
1433 * @throws IOException if the file was not found or could not be opened for reading
1434 */
1435 public InputStream getInputStream(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001436 return fileSystem.getInputStream(this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001437 }
1438
1439 /**
1440 * Returns a java.io.File representation of this path.
1441 *
1442 * <p>Caveat: the result may be useless if this path's getFileSystem() is not
1443 * the UNIX filesystem.
1444 */
1445 public File getPathFile() {
1446 return new File(getPathString());
1447 }
1448
tomlub1e8daf2017-10-27 15:58:21 -04001449 /** Prefer to use {@link #isWritable(FileSystem)}. */
1450 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001451 public boolean isWritable() throws IOException, FileNotFoundException {
tomlub1e8daf2017-10-27 15:58:21 -04001452 return isWritable(fileSystem);
1453 }
1454
1455 /**
1456 * Returns true if the file denoted by the current path, following symbolic links, is writable for
1457 * the current user.
1458 *
1459 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1460 * Path.
1461 *
1462 * @throws FileNotFoundException if the file does not exist, a dangling symbolic link was
1463 * encountered, or the file's metadata could not be read
1464 */
1465 public boolean isWritable(FileSystem fileSystem) throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001466 return fileSystem.isWritable(this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001467 }
1468
tomlub1e8daf2017-10-27 15:58:21 -04001469 /** Prefer to use {@link #setReadable(FileSystem, boolean)}. */
1470 @Deprecated
1471 public void setReadable(boolean readable) throws IOException, FileNotFoundException {
1472 setReadable(fileSystem, readable);
1473 }
1474
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001475 /**
tomlub1e8daf2017-10-27 15:58:21 -04001476 * Sets the read permissions of the file denoted by the current path, following symbolic links.
1477 * Permissions apply to the current user.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001478 *
tomlub1e8daf2017-10-27 15:58:21 -04001479 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1480 * Path.
1481 *
1482 * @param readable if true, the file is set to readable; otherwise the file is made non-readable
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001483 * @throws FileNotFoundException if the file does not exist
1484 * @throws IOException If the action cannot be taken (ie. permissions)
1485 */
tomlub1e8daf2017-10-27 15:58:21 -04001486 public void setReadable(FileSystem fileSystem, boolean readable)
1487 throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001488 fileSystem.setReadable(this, readable);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001489 }
1490
tomlub1e8daf2017-10-27 15:58:21 -04001491 /** Prefer to use {@link #setWritable(FileSystem, boolean)}. */
1492 @Deprecated
1493 public void setWritable(boolean writable) throws IOException, FileNotFoundException {
1494 setWritable(fileSystem, writable);
1495 }
1496
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001497 /**
tomlub1e8daf2017-10-27 15:58:21 -04001498 * Sets the write permissions of the file denoted by the current path, following symbolic links.
1499 * Permissions apply to the current user.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001500 *
1501 * <p>TODO(bazel-team): (2009) what about owner/group/others?
1502 *
tomlub1e8daf2017-10-27 15:58:21 -04001503 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1504 * Path.
1505 *
1506 * @param writable if true, the file is set to writable; otherwise the file is made non-writable
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001507 * @throws FileNotFoundException if the file does not exist
1508 * @throws IOException If the action cannot be taken (ie. permissions)
1509 */
tomlub1e8daf2017-10-27 15:58:21 -04001510 public void setWritable(FileSystem fileSystem, boolean writable)
1511 throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001512 fileSystem.setWritable(this, writable);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001513 }
1514
tomlub1e8daf2017-10-27 15:58:21 -04001515 /** Prefer to use {@link #isExecutable(FileSystem)}. */
1516 @Deprecated
1517 public boolean isExecutable() throws IOException, FileNotFoundException {
1518 return isExecutable(fileSystem);
1519 }
1520
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001521 /**
tomlub1e8daf2017-10-27 15:58:21 -04001522 * Returns true iff the file specified by the current path, following symbolic links, is
1523 * executable by the current user.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001524 *
tomlub1e8daf2017-10-27 15:58:21 -04001525 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1526 * Path.
1527 *
1528 * @throws FileNotFoundException if the file does not exist or a dangling symbolic link was
1529 * encountered
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001530 * @throws IOException if some other I/O error occurred
1531 */
tomlub1e8daf2017-10-27 15:58:21 -04001532 public boolean isExecutable(FileSystem fileSystem) throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001533 return fileSystem.isExecutable(this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001534 }
1535
tomlub1e8daf2017-10-27 15:58:21 -04001536 /** Prefer to use {@link #isReadable(FileSystem)}. */
1537 @Deprecated
1538 public boolean isReadable() throws IOException, FileNotFoundException {
1539 return isReadable(fileSystem);
1540 }
1541
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001542 /**
tomlub1e8daf2017-10-27 15:58:21 -04001543 * Returns true iff the file specified by the current path, following symbolic links, is readable
1544 * by the current user.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001545 *
tomlub1e8daf2017-10-27 15:58:21 -04001546 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1547 * Path.
1548 *
1549 * @throws FileNotFoundException if the file does not exist or a dangling symbolic link was
1550 * encountered
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001551 * @throws IOException if some other I/O error occurred
1552 */
tomlub1e8daf2017-10-27 15:58:21 -04001553 public boolean isReadable(FileSystem fileSystem) throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001554 return fileSystem.isReadable(this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001555 }
1556
tomlub1e8daf2017-10-27 15:58:21 -04001557 /** Prefer to use {@link #setExecutable(FileSystem, boolean)}. */
1558 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001559 public void setExecutable(boolean executable) throws IOException, FileNotFoundException {
tomlub1e8daf2017-10-27 15:58:21 -04001560 setExecutable(fileSystem, executable);
1561 }
1562
1563 /**
1564 * Sets the execute permission on the file specified by the current path, following symbolic
1565 * links. Permissions apply to the current user.
1566 *
1567 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1568 * Path.
1569 *
1570 * @throws FileNotFoundException if the file does not exist or a dangling symbolic link was
1571 * encountered
1572 * @throws IOException if the metadata change failed, for example because of permissions
1573 */
1574 public void setExecutable(FileSystem fileSystem, boolean executable)
1575 throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001576 fileSystem.setExecutable(this, executable);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001577 }
1578
tomlub1e8daf2017-10-27 15:58:21 -04001579 /** Prefer to use {@link #chmod(FileSystem, int)}. */
1580 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001581 public void chmod(int mode) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001582 chmod(fileSystem, mode);
1583 }
1584
1585 /**
1586 * Sets the permissions on the file specified by the current path, following symbolic links. If
1587 * permission changes on this path's {@link FileSystem} are slow (e.g. one syscall per change),
1588 * this method should aim to be faster than setting each permission individually. If this path's
1589 * {@link FileSystem} does not support group and others permissions, those bits will be ignored.
1590 *
1591 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1592 * Path.
1593 *
1594 * @throws FileNotFoundException if the file does not exist or a dangling symbolic link was
1595 * encountered
1596 * @throws IOException if the metadata change failed, for example because of permissions
1597 */
1598 public void chmod(FileSystem fileSystem, int mode) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001599 fileSystem.chmod(this, mode);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001600 }
1601
tomlub1e8daf2017-10-27 15:58:21 -04001602 /** Prefer to use {@link #prefetchPackageAsync(FileSystem, int)}. */
1603 @Deprecated
Googlerc804c662016-12-01 16:53:28 +00001604 public void prefetchPackageAsync(int maxDirs) {
tomlub1e8daf2017-10-27 15:58:21 -04001605 prefetchPackageAsync(fileSystem, maxDirs);
1606 }
1607
1608 public void prefetchPackageAsync(FileSystem fileSystem, int maxDirs) {
aehligc801c392017-12-19 07:12:25 -08001609 fileSystem.prefetchPackageAsync(this, maxDirs);
Googlerc804c662016-12-01 16:53:28 +00001610 }
1611
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001612 /**
1613 * Compare Paths of the same file system using their PathFragments.
1614 *
1615 * <p>Paths from different filesystems will be compared using the identity
1616 * hash code of their respective filesystems.
1617 */
1618 @Override
1619 public int compareTo(Path o) {
1620 // Fast-path.
1621 if (equals(o)) {
1622 return 0;
1623 }
1624
1625 // If they are on different file systems, the file system decides the ordering.
1626 FileSystem otherFs = o.getFileSystem();
1627 if (!fileSystem.equals(otherFs)) {
1628 int thisFileSystemHash = System.identityHashCode(fileSystem);
1629 int otherFileSystemHash = System.identityHashCode(otherFs);
1630 if (thisFileSystemHash < otherFileSystemHash) {
1631 return -1;
1632 } else if (thisFileSystemHash > otherFileSystemHash) {
1633 return 1;
1634 } else {
1635 // TODO(bazel-team): Add a name to every file system to be used here.
1636 return 0;
1637 }
1638 }
1639
1640 // Equal file system, but different paths, because of the canonicalization.
1641 // We expect to often compare Paths that are very similar, for example for files in the same
1642 // directory. This can be done efficiently by going up segment by segment until we get the
1643 // identical path (canonicalization again), and then just compare the immediate child segments.
1644 // Overall this is much faster than creating PathFragment instances, and comparing those, which
1645 // requires us to always go up to the top-level directory and copy all segments into a new
1646 // string array.
1647 // This was previously showing up as a hotspot in a profile of globbing a large directory.
Googlere1cd9502016-09-07 14:33:29 +00001648 Path a = this;
1649 Path b = o;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001650 int maxDepth = Math.min(a.depth, b.depth);
1651 while (a.depth > maxDepth) {
1652 a = a.getParentDirectory();
1653 }
1654 while (b.depth > maxDepth) {
1655 b = b.getParentDirectory();
1656 }
1657 // One is the child of the other.
1658 if (a.equals(b)) {
1659 // If a is the same as this, this.depth must be less than o.depth.
1660 return equals(a) ? -1 : 1;
1661 }
Googlere1cd9502016-09-07 14:33:29 +00001662 Path previousa;
1663 Path previousb;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001664 do {
1665 previousa = a;
1666 previousb = b;
1667 a = a.getParentDirectory();
1668 b = b.getParentDirectory();
Nathan Harmata3c74af02015-10-09 13:16:08 +00001669 } while (!a.equals(b)); // This has to happen eventually.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001670 return previousa.name.compareTo(previousb.name);
1671 }
1672}