blob: 6fe6118b9ae789844ba5762c7e8027a1d5704fdc [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/**
tomlu67c84b12017-11-06 19:49:16 +010042 * Instances of this class represent pathnames, forming a tree structure to implement sharing of
43 * 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 * <p>On Unix filesystems this method merely calls through to {@code
83 * FileSystem.getCachedChildPathInternal(parent, child)}, but on Windows this can be used to
84 * handle the translatation of absolute Unix paths to absolute Windows paths, e.g. "/c" to "C:/"
85 * or "/usr" to "C:/tools/msys64/usr".
Laszlo Csomorca99bb72016-10-25 13:15:55 +000086 */
nharmata39e659e2017-04-04 14:42:23 +000087 Path getCachedChildPathInternal(Path path, String childName);
Laszlo Csomorca99bb72016-10-25 13:15:55 +000088 }
89
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010090 private static FileSystem fileSystemForSerialization;
91
92 /**
93 * We need to specify used FileSystem. In this case we can save memory during the serialization.
94 */
95 public static void setFileSystemForSerialization(FileSystem fileSystem) {
96 fileSystemForSerialization = fileSystem;
97 }
98
99 /**
100 * Returns FileSystem that we are using.
101 */
102 public static FileSystem getFileSystemForSerialization() {
103 return fileSystemForSerialization;
104 }
105
106 // These are basically final, but can't be marked as such in order to support serialization.
107 private FileSystem fileSystem;
108 private String name;
109 private Path parent;
110 private int depth;
111 private int hashCode;
112
113 private static final ReferenceQueue<Path> REFERENCE_QUEUE = new ReferenceQueue<>();
114
115 private static class PathWeakReferenceForCleanup extends WeakReference<Path> {
116 final Path parent;
117 final String baseName;
118
119 PathWeakReferenceForCleanup(Path referent, ReferenceQueue<Path> referenceQueue) {
120 super(referent, referenceQueue);
121 parent = referent.getParentDirectory();
122 baseName = referent.getBaseName();
123 }
124 }
125
126 private static final Thread PATH_CHILD_CACHE_CLEANUP_THREAD = new Thread("Path cache cleanup") {
127 @Override
128 public void run() {
129 while (true) {
130 try {
131 PathWeakReferenceForCleanup ref = (PathWeakReferenceForCleanup) REFERENCE_QUEUE.remove();
132 Path parent = ref.parent;
133 synchronized (parent) {
134 // It's possible that since this reference was enqueued for deletion, the Path was
135 // recreated with a new entry in the map. We definitely shouldn't delete that entry, so
136 // check to make sure they're the same.
137 Reference<Path> currentRef = ref.parent.children.get(ref.baseName);
138 if (currentRef == ref) {
139 ref.parent.children.remove(ref.baseName);
140 }
141 }
142 } catch (InterruptedException e) {
143 // Ignored.
144 }
145 }
146 }
147 };
148
149 static {
150 PATH_CHILD_CACHE_CLEANUP_THREAD.setDaemon(true);
151 PATH_CHILD_CACHE_CLEANUP_THREAD.start();
152 }
153
154 /**
155 * A mapping from a child file name to the {@link Path} representing it.
156 *
157 * <p>File names must be a single path segment. The strings must be
158 * canonical. We use IdentityHashMap instead of HashMap for reasons of space
159 * efficiency: instances are smaller by a single word. Also, since all path
160 * segments are interned, the universe of Paths holds a minimal number of
161 * references to strings. (It's doubtful that there's any time gain from use
162 * of an IdentityHashMap, since the time saved by avoiding string equality
163 * tests during hash lookups is probably equal to the time spent eagerly
164 * interning strings, unless the collision rate is high.)
165 *
166 * <p>The Paths are stored as weak references to ensure that a live
167 * Path for a directory does not hold a strong reference to all of its
168 * descendants, which would prevent collection of paths we never intend to
169 * use again. Stale references in the map must be treated as absent.
170 *
171 * <p>A Path may be recycled once there is no Path that refers to it or
172 * to one of its descendants. This means that any data stored in the
173 * Path instance by one of its subclasses must be recoverable: it's ok to
174 * store data in Paths as an optimization, but there must be another
175 * source for that data in case the Path is recycled.
176 *
177 * <p>We intentionally avoid using the existing library classes for reasons of
178 * space efficiency: while ConcurrentHashMap would reduce our locking
179 * overhead, and ReferenceMap would simplify the code a little, both of those
180 * classes have much higher per-instance overheads than IdentityHashMap.
181 *
182 * <p>The Path object must be synchronized while children is being
183 * accessed.
184 */
Nathan Harmataa65fcf92015-08-20 21:14:44 +0000185 private volatile IdentityHashMap<String, Reference<Path>> children;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100186
187 /**
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000188 * Create a path instance.
189 *
190 * <p>Should only be called by {@link PathFactory#createChildPath(Path, String)}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100191 *
192 * @param name the name of this path; it must be canonicalized with {@link
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000193 * StringCanonicalizer#intern}
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100194 * @param parent this path's parent
195 */
196 protected Path(FileSystem fileSystem, String name, Path parent) {
197 this.fileSystem = fileSystem;
198 this.name = name;
199 this.parent = parent;
200 this.depth = parent == null ? 0 : parent.depth + 1;
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000201
202 // No need to include the drive letter in the hash code, because it's derived from the parent
203 // and/or the name.
Yun Peng352f7e72016-05-09 11:08:25 +0000204 if (fileSystem == null || fileSystem.isFilePathCaseSensitive()) {
205 this.hashCode = Objects.hash(parent, name);
206 } else {
207 this.hashCode = Objects.hash(parent, name.toLowerCase());
208 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100209 }
210
211 /**
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000212 * Create the root path.
213 *
214 * <p>Should only be called by {@link PathFactory#createRootPath(FileSystem)}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100215 */
216 protected Path(FileSystem fileSystem) {
217 this(fileSystem, StringCanonicalizer.intern("/"), null);
218 }
219
220 private void writeObject(ObjectOutputStream out) throws IOException {
janakr150858b2017-07-25 00:04:53 +0200221 Preconditions.checkState(
222 fileSystem == fileSystemForSerialization, "%s %s", fileSystem, fileSystemForSerialization);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100223 out.writeUTF(getPathString());
224 }
225
226 private void readObject(ObjectInputStream in) throws IOException {
227 fileSystem = fileSystemForSerialization;
228 String p = in.readUTF();
nharmatab4060b62017-04-04 17:11:39 +0000229 PathFragment pf = PathFragment.create(p);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100230 PathFragment parentDir = pf.getParentDirectory();
231 if (parentDir == null) {
232 this.name = "/";
233 this.parent = null;
234 this.depth = 0;
235 } else {
236 this.name = pf.getBaseName();
237 this.parent = fileSystem.getPath(parentDir);
238 this.depth = this.parent.depth + 1;
239 }
240 this.hashCode = Objects.hash(parent, name);
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000241 reinitializeAfterDeserialization();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100242 }
243
244 /**
245 * Returns the filesystem instance to which this path belongs.
246 */
247 public FileSystem getFileSystem() {
248 return fileSystem;
249 }
250
251 public boolean isRootDirectory() {
252 return parent == null;
253 }
254
255 protected Path createChildPath(String childName) {
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000256 return fileSystem.getPathFactory().createChildPath(this, childName);
257 }
258
259 /**
260 * Reinitializes this object after deserialization.
261 *
262 * <p>Derived classes should use this hook to initialize additional state.
263 */
264 protected void reinitializeAfterDeserialization() {}
265
266 /**
267 * Returns true if {@code ancestorPath} may be an ancestor of {@code path}.
268 *
269 * <p>The return value may be a false positive, but it cannot be a false negative. This means that
270 * a true return value doesn't mean the ancestor candidate is really an ancestor, however a false
271 * return value means it's guaranteed that {@code ancestorCandidate} is not an ancestor of this
272 * path.
273 *
274 * <p>Subclasses may override this method with filesystem-specific logic, e.g. a Windows
275 * filesystem may return false if the ancestor path is on a different drive than this one, because
276 * it is then guaranteed that the ancestor candidate cannot be an ancestor of this path.
277 *
278 * @param ancestorCandidate the path that may or may not be an ancestor of this one
279 */
280 protected boolean isMaybeRelativeTo(Path ancestorCandidate) {
281 return true;
282 }
283
284 /**
285 * Returns true if this directory is top-level, i.e. it is its own parent.
286 *
287 * <p>When canonicalizing paths the ".." segment of a top-level directory always resolves to the
288 * directory itself.
289 *
290 * <p>On Unix, a top-level directory would be just the filesystem root ("/), on Windows it would
291 * be the filesystem root and the volume roots.
292 */
293 protected boolean isTopLevelDirectory() {
294 return isRootDirectory();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100295 }
296
297 /**
298 * Returns the child path named name, or creates such a path (and caches it)
299 * if it doesn't already exist.
300 */
301 private Path getCachedChildPath(String childName) {
nharmata39e659e2017-04-04 14:42:23 +0000302 return fileSystem.getPathFactory().getCachedChildPathInternal(this, childName);
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000303 }
304
nharmata39e659e2017-04-04 14:42:23 +0000305 /**
306 * Internal method only intended to be called by {@link PathFactory#getCachedChildPathInternal}.
307 */
308 public static Path getCachedChildPathInternal(Path parent, String childName, boolean cacheable) {
Nathan Harmataa65fcf92015-08-20 21:14:44 +0000309 // We get a canonical instance since 'children' is an IdentityHashMap.
nharmata39e659e2017-04-04 14:42:23 +0000310 childName = StringCanonicalizer.intern(childName);
311 if (!cacheable) {
Laszlo Csomor8679e4c2017-01-03 15:21:08 +0000312 // Non-cacheable children won't show up in `children` so applyToChildren won't run for these.
313 return parent.createChildPath(childName);
314 }
Googler0e59bb62017-02-04 08:31:20 +0000315
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000316 synchronized (parent) {
Googler0e59bb62017-02-04 08:31:20 +0000317 if (parent.children == null) {
318 // 66% of Paths have size == 1, 80% <= 2
319 parent.children = new IdentityHashMap<>(1);
320 }
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000321 Reference<Path> childRef = parent.children.get(childName);
Nathan Harmatafee390d2015-09-02 22:46:53 +0000322 Path child;
323 if (childRef == null || (child = childRef.get()) == null) {
Laszlo Csomor8679e4c2017-01-03 15:21:08 +0000324 child = parent.createChildPath(childName);
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000325 parent.children.put(childName, new PathWeakReferenceForCleanup(child, REFERENCE_QUEUE));
Nathan Harmataa65fcf92015-08-20 21:14:44 +0000326 }
Nathan Harmatafee390d2015-09-02 22:46:53 +0000327 return child;
Nathan Harmataa65fcf92015-08-20 21:14:44 +0000328 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100329 }
330
331 /**
332 * Applies the specified function to each {@link Path} that is an existing direct
333 * descendant of this one. The Predicate is evaluated only for its
334 * side-effects.
335 *
336 * <p>This function exists to hide the "children" field, whose complex
337 * synchronization and identity requirements are too unsafe to be exposed to
338 * subclasses. For example, the "children" field must be synchronized for
339 * the duration of any iteration over it; it may be null; and references
340 * within it may be stale, and must be ignored.
341 */
342 protected synchronized void applyToChildren(Predicate<Path> function) {
343 if (children != null) {
344 for (Reference<Path> childRef : children.values()) {
345 Path child = childRef.get();
346 if (child != null) {
347 function.apply(child);
348 }
349 }
350 }
351 }
352
353 /**
354 * Returns whether this path is recursively "under" {@code prefix} - that is,
355 * whether {@code path} is a prefix of this path.
356 *
357 * <p>This method returns {@code true} when called with this path itself. This
358 * method acts independently of the existence of files or folders.
359 *
360 * @param prefix a path which may or may not be a prefix of this path
361 */
362 public boolean startsWith(Path prefix) {
363 Path n = this;
364 for (int i = 0, len = depth - prefix.depth; i < len; i++) {
365 n = n.getParentDirectory();
366 }
367 return prefix.equals(n);
368 }
369
370 /**
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000371 * Computes a string representation of this path, and writes it to the given string builder. Only
372 * called locally with a new instance.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100373 */
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000374 protected void buildPathString(StringBuilder result) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100375 if (isRootDirectory()) {
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000376 result.append(PathFragment.ROOT_DIR);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100377 } else {
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000378 parent.buildPathString(result);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100379 if (!parent.isRootDirectory()) {
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000380 result.append(PathFragment.SEPARATOR_CHAR);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100381 }
382 result.append(name);
383 }
384 }
385
386 /**
buchgr5ef576f2017-09-14 14:36:44 +0200387 * Returns the path encoded as an {@link URI}.
388 *
389 * <p>This concrete implementation returns URIs with "file" as the scheme.
390 * For Example:
391 * - On Unix the path "/tmp/foo bar.txt" will be encoded as
392 * "file:///tmp/foo%20bar.txt".
393 * - On Windows the path "C:\Temp\Foo Bar.txt" will be encoded as
394 * "file:///C:/Temp/Foo%20Bar.txt"
395 *
396 * <p>Implementors extending this class for special filesystems will likely need to override
397 * this method.
398 *
399 * @throws URISyntaxException if the URI cannot be constructed.
400 */
401 public URI toURI() {
402 String ps = getPathString();
403 if (!ps.startsWith("/")) {
404 // On Windows URI's need to start with a '/'. i.e. C:\Foo\Bar would be file:///C:/Foo/Bar
405 ps = "/" + ps;
406 }
407 try {
408 return new URI("file",
409 // Needs to be "" instead of null, so that toString() will append "//" after the scheme.
410 // We need this for backwards compatibility reasons as some consumers of the BEP are
411 // broken.
412 "",
413 ps, null, null);
414 } catch (URISyntaxException e) {
415 throw new IllegalStateException(e);
416 }
417 }
418
419 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100420 * Returns the path as a string.
421 */
422 public String getPathString() {
423 // Profile driven optimization:
424 // Preallocate a size determined by the depth, so that
425 // we do not have to expand the capacity of the StringBuilder
426 StringBuilder builder = new StringBuilder(depth * 20);
427 buildPathString(builder);
428 return builder.toString();
429 }
430
tomlu67c84b12017-11-06 19:49:16 +0100431 @Override
432 public void repr(SkylarkPrinter printer) {
433 printer.append(getPathString());
434 }
435
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100436 /**
437 * Returns the path as a string.
438 */
439 @Override
440 public String toString() {
441 return getPathString();
442 }
443
444 @Override
445 public int hashCode() {
446 return hashCode;
447 }
448
449 @Override
450 public boolean equals(Object other) {
451 if (this == other) {
452 return true;
453 }
454 if (!(other instanceof Path)) {
455 return false;
456 }
Yun Peng352f7e72016-05-09 11:08:25 +0000457
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100458 Path otherPath = (Path) other;
Googlerbaeba8b22016-04-20 19:20:37 +0000459 if (hashCode != otherPath.hashCode) {
460 return false;
461 }
Yun Peng352f7e72016-05-09 11:08:25 +0000462
463 if (!fileSystem.equals(otherPath.fileSystem)) {
464 return false;
465 }
466
467 if (fileSystem.isFilePathCaseSensitive()) {
468 return name.equals(otherPath.name)
469 && Objects.equals(parent, otherPath.parent);
470 } else {
471 return name.toLowerCase().equals(otherPath.name.toLowerCase())
472 && Objects.equals(parent, otherPath.parent);
473 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100474 }
475
476 /**
477 * Returns a string of debugging information associated with this path.
478 * The results are unspecified and MUST NOT be interpreted programmatically.
479 */
480 protected String toDebugString() {
481 return "";
482 }
483
484 /**
485 * Returns a path representing the parent directory of this path,
486 * or null iff this Path represents the root of the filesystem.
487 *
488 * <p>Note: This method normalises ".." and "." path segments by string
489 * processing, not by directory lookups.
490 */
491 public Path getParentDirectory() {
492 return parent;
493 }
494
tomlub1e8daf2017-10-27 15:58:21 -0400495 /** Prefer to use {@link #exists(FileSystem)}. */
496 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100497 public boolean exists() {
tomlub1e8daf2017-10-27 15:58:21 -0400498 return exists(fileSystem);
499 }
500
501 /**
502 * Returns true iff this path denotes an existing file of any kind. Follows symbolic links.
503 *
504 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
505 * Path.
506 */
507 public boolean exists(FileSystem fileSystem) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100508 return fileSystem.exists(this, true);
509 }
510
tomlub1e8daf2017-10-27 15:58:21 -0400511 /** Prefer to use {@link #exists(FileSystem, Symlinks)}. */
512 @Deprecated
513 public boolean exists(Symlinks followSymlinks) {
514 return exists(fileSystem, followSymlinks);
515 }
516
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100517 /**
518 * Returns true iff this path denotes an existing file of any kind.
519 *
tomlub1e8daf2017-10-27 15:58:21 -0400520 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
521 * Path.
522 *
523 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
524 * link is dereferenced until a file other than a symbolic link is found
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100525 */
tomlub1e8daf2017-10-27 15:58:21 -0400526 public boolean exists(FileSystem fileSystem, Symlinks followSymlinks) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100527 return fileSystem.exists(this, followSymlinks.toBoolean());
528 }
529
tomlub1e8daf2017-10-27 15:58:21 -0400530 /** Prefer to use {@link #getDirectoryEntries(FileSystem)}. */
531 @Deprecated
532 public Collection<Path> getDirectoryEntries() throws IOException, FileNotFoundException {
533 return getDirectoryEntries(fileSystem);
534 }
535
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100536 /**
tomlub1e8daf2017-10-27 15:58:21 -0400537 * Returns a new, immutable collection containing the names of all entities within the directory
538 * denoted by the current path. Follows symbolic links.
539 *
540 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
541 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100542 *
543 * @throws FileNotFoundException If the directory is not found
544 * @throws IOException If the path does not denote a directory
545 */
tomlub1e8daf2017-10-27 15:58:21 -0400546 public Collection<Path> getDirectoryEntries(FileSystem fileSystem)
547 throws IOException, FileNotFoundException {
tomlu0a82e702017-10-23 18:16:44 +0200548 Collection<String> entries = fileSystem.getDirectoryEntries(this);
549 Collection<Path> result = new ArrayList<>(entries.size());
550 for (String entry : entries) {
551 result.add(getChild(entry));
552 }
553 return result;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100554 }
555
tomlub1e8daf2017-10-27 15:58:21 -0400556 /** Prefer to use {@link #readdir(FileSystem, Symlinks)}. */
557 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100558 public Collection<Dirent> readdir(Symlinks followSymlinks) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -0400559 return readdir(fileSystem, followSymlinks);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100560 }
561
562 /**
tomlub1e8daf2017-10-27 15:58:21 -0400563 * Returns a collection of the names and types of all entries within the directory denoted by the
564 * current path. Follows symbolic links if {@code followSymlinks} is true. Note that the order of
565 * the returned entries is not guaranteed.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100566 *
tomlub1e8daf2017-10-27 15:58:21 -0400567 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
568 * Path.
569 *
570 * @param followSymlinks whether to follow symlinks or not
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100571 * @throws FileNotFoundException If the directory is not found
572 * @throws IOException If the path does not denote a directory
573 */
tomlub1e8daf2017-10-27 15:58:21 -0400574 public Collection<Dirent> readdir(FileSystem fileSystem, Symlinks followSymlinks)
575 throws IOException {
576 return fileSystem.readdir(this, followSymlinks.toBoolean());
577 }
578
579 /** Prefer to use {@link #stat(FileSystem)}. */
580 @Deprecated
581 public FileStatus stat() throws IOException {
582 return stat(fileSystem);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100583 }
584
585 /**
586 * Returns the status of a file, following symbolic links.
587 *
tomlub1e8daf2017-10-27 15:58:21 -0400588 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
589 * Path.
590 *
591 * @throws IOException if there was an error obtaining the file status. Note, some implementations
592 * may defer the I/O, and hence the throwing of the exception, until the accessor methods of
593 * {@code FileStatus} are called.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100594 */
tomlub1e8daf2017-10-27 15:58:21 -0400595 public FileStatus stat(FileSystem fileSystem) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100596 return fileSystem.stat(this, true);
597 }
598
tomlub1e8daf2017-10-27 15:58:21 -0400599 /** Prefer to use {@link #statNullable(FileSystem)}. */
600 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100601 public FileStatus statNullable() {
tomlub1e8daf2017-10-27 15:58:21 -0400602 return statNullable(fileSystem);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100603 }
604
605 /**
606 * Like stat(), but returns null on file-nonexistence instead of throwing.
tomlub1e8daf2017-10-27 15:58:21 -0400607 *
608 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
609 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100610 */
tomlub1e8daf2017-10-27 15:58:21 -0400611 public FileStatus statNullable(FileSystem fileSystem) {
612 return statNullable(fileSystem, Symlinks.FOLLOW);
613 }
614
615 /** Prefer to use {@link #statNullable(FileSystem, Symlinks)}. */
616 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100617 public FileStatus statNullable(Symlinks symlinks) {
tomlub1e8daf2017-10-27 15:58:21 -0400618 return statNullable(fileSystem, symlinks);
619 }
620
621 /**
622 * Like stat(), but returns null on file-nonexistence instead of throwing.
623 *
624 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
625 * Path.
626 */
627 public FileStatus statNullable(FileSystem fileSystem, Symlinks symlinks) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100628 return fileSystem.statNullable(this, symlinks.toBoolean());
629 }
630
tomlub1e8daf2017-10-27 15:58:21 -0400631 /** Prefer to use {@link #stat(FileSystem, Symlinks)}. */
632 @Deprecated
633 public FileStatus stat(Symlinks followSymlinks) throws IOException {
634 return stat(fileSystem, followSymlinks);
635 }
636
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100637 /**
638 * Returns the status of a file, optionally following symbolic links.
639 *
tomlub1e8daf2017-10-27 15:58:21 -0400640 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
641 * Path.
642 *
643 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
644 * link is dereferenced until a file other than a symbolic link is found
645 * @throws IOException if there was an error obtaining the file status. Note, some implementations
646 * may defer the I/O, and hence the throwing of the exception, until the accessor methods of
647 * {@code FileStatus} are called
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100648 */
tomlub1e8daf2017-10-27 15:58:21 -0400649 public FileStatus stat(FileSystem fileSystem, Symlinks followSymlinks) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100650 return fileSystem.stat(this, followSymlinks.toBoolean());
651 }
652
tomlub1e8daf2017-10-27 15:58:21 -0400653 /** Prefer to use {@link #statIfFound(FileSystem)}. */
654 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100655 public FileStatus statIfFound() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -0400656 return statIfFound(fileSystem);
657 }
658
659 /**
660 * Like {@link #stat}, but may return null if the file is not found (corresponding to {@code
661 * ENOENT} and {@code ENOTDIR} in Unix's stat(2) function) instead of throwing. Follows symbolic
662 * links.
663 *
664 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
665 * Path.
666 */
667 public FileStatus statIfFound(FileSystem fileSystem) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100668 return fileSystem.statIfFound(this, true);
669 }
670
tomlub1e8daf2017-10-27 15:58:21 -0400671 /** Prefer to use {@link #statIfFound(FileSystem, Symlinks)}. */
672 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100673 public FileStatus statIfFound(Symlinks followSymlinks) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -0400674 return statIfFound(fileSystem, followSymlinks);
675 }
676
677 /**
678 * Like {@link #stat}, but may return null if the file is not found (corresponding to {@code
679 * ENOENT} and {@code ENOTDIR} in Unix's stat(2) function) instead of throwing.
680 *
681 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
682 * Path.
683 *
684 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
685 * link is dereferenced until a file other than a symbolic link is found
686 */
687 public FileStatus statIfFound(FileSystem fileSystem, Symlinks followSymlinks) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100688 return fileSystem.statIfFound(this, followSymlinks.toBoolean());
689 }
690
tomlub1e8daf2017-10-27 15:58:21 -0400691 /** Prefer to use {@link #isDirectory()} (FileSystem)}. */
692 @Deprecated
693 public boolean isDirectory() {
694 return isDirectory(fileSystem);
695 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100696
697 /**
tomlub1e8daf2017-10-27 15:58:21 -0400698 * Returns true iff this path denotes an existing directory. Follows symbolic links.
699 *
700 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
701 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100702 */
tomlub1e8daf2017-10-27 15:58:21 -0400703 public boolean isDirectory(FileSystem fileSystem) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100704 return fileSystem.isDirectory(this, true);
705 }
706
tomlub1e8daf2017-10-27 15:58:21 -0400707 /** Prefer to use {@link #isDirectory(FileSystem, Symlinks)}. */
708 @Deprecated
709 public boolean isDirectory(Symlinks followSymlinks) {
710 return isDirectory(fileSystem, followSymlinks);
711 }
712
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100713 /**
714 * Returns true iff this path denotes an existing directory.
715 *
tomlub1e8daf2017-10-27 15:58:21 -0400716 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
717 * Path.
718 *
719 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
720 * link is dereferenced until a file other than a symbolic link is found
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100721 */
tomlub1e8daf2017-10-27 15:58:21 -0400722 public boolean isDirectory(FileSystem fileSystem, Symlinks followSymlinks) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100723 return fileSystem.isDirectory(this, followSymlinks.toBoolean());
724 }
725
tomlub1e8daf2017-10-27 15:58:21 -0400726 /** Prefer to use {@link #isFile(FileSystem)}. */
727 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100728 public boolean isFile() {
tomlub1e8daf2017-10-27 15:58:21 -0400729 return isFile(fileSystem);
730 }
731
732 /**
733 * Returns true iff this path denotes an existing regular or special file. Follows symbolic links.
734 *
735 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
736 * Path.
737 *
738 * <p>For our purposes, "file" includes special files (socket, fifo, block or char devices) too;
739 * it excludes symbolic links and directories.
740 */
741 public boolean isFile(FileSystem fileSystem) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100742 return fileSystem.isFile(this, true);
743 }
744
tomlub1e8daf2017-10-27 15:58:21 -0400745 /** Prefer to use {@link #isFile(FileSystem, Symlinks)}. */
746 @Deprecated
747 public boolean isFile(Symlinks followSymlinks) {
748 return isFile(fileSystem, followSymlinks);
749 }
750
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100751 /**
752 * Returns true iff this path denotes an existing regular or special file.
753 *
tomlub1e8daf2017-10-27 15:58:21 -0400754 * <p>For our purposes, a "file" includes special files (socket, fifo, block or char devices) too;
755 * it excludes symbolic links and directories.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100756 *
tomlub1e8daf2017-10-27 15:58:21 -0400757 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
758 * Path.
759 *
760 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
761 * link is dereferenced until a file other than a symbolic link is found.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100762 */
tomlub1e8daf2017-10-27 15:58:21 -0400763 public boolean isFile(FileSystem fileSystem, Symlinks followSymlinks) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100764 return fileSystem.isFile(this, followSymlinks.toBoolean());
765 }
766
tomlub1e8daf2017-10-27 15:58:21 -0400767 /** Prefer to use {@link #isSpecialFile(FileSystem)}. */
768 @Deprecated
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000769 public boolean isSpecialFile() {
tomlub1e8daf2017-10-27 15:58:21 -0400770 return isSpecialFile(fileSystem);
771 }
772
773 /**
774 * Returns true iff this path denotes an existing special file (e.g. fifo). Follows symbolic
775 * links.
776 *
777 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
778 * Path.
779 */
780 public boolean isSpecialFile(FileSystem fileSystem) {
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000781 return fileSystem.isSpecialFile(this, true);
782 }
783
tomlub1e8daf2017-10-27 15:58:21 -0400784 /** Prefer to use {@link #isSpecialFile(FileSystem, Symlinks)}. */
785 @Deprecated
786 public boolean isSpecialFile(Symlinks followSymlinks) {
787 return isSpecialFile(fileSystem, followSymlinks);
788 }
789
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000790 /**
791 * Returns true iff this path denotes an existing special file (e.g. fifo).
792 *
tomlub1e8daf2017-10-27 15:58:21 -0400793 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
794 * Path.
795 *
796 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
797 * link is dereferenced until a path other than a symbolic link is found.
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000798 */
tomlub1e8daf2017-10-27 15:58:21 -0400799 public boolean isSpecialFile(FileSystem fileSystem, Symlinks followSymlinks) {
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000800 return fileSystem.isSpecialFile(this, followSymlinks.toBoolean());
801 }
802
tomlub1e8daf2017-10-27 15:58:21 -0400803 /** Prefer to use {@link #isSymbolicLink(FileSystem)}. */
804 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100805 public boolean isSymbolicLink() {
tomlub1e8daf2017-10-27 15:58:21 -0400806 return isSymbolicLink(fileSystem);
807 }
808
809 /**
810 * Returns true iff this path denotes an existing symbolic link. Does not follow symbolic links.
811 *
812 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
813 * Path.
814 */
815 public boolean isSymbolicLink(FileSystem fileSystem) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100816 return fileSystem.isSymbolicLink(this);
817 }
818
819 /**
820 * Returns the last segment of this path, or "/" for the root directory.
821 */
822 public String getBaseName() {
823 return name;
824 }
825
826 /**
827 * Interprets the name of a path segment relative to the current path and
828 * returns the result.
829 *
830 * <p>This is a purely syntactic operation, i.e. it does no I/O, it does not
831 * validate the existence of any path, nor resolve symbolic links. If 'prefix'
832 * is not canonical, then a 'name' of '..' will be interpreted incorrectly.
833 *
834 * @precondition segment contains no slashes.
835 */
836 private Path getCanonicalPath(String segment) {
Ulf Adams07dba942015-03-05 14:47:37 +0000837 if (segment.equals(".") || segment.isEmpty()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100838 return this; // that's a noop
839 } else if (segment.equals("..")) {
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000840 // top-level directory's parent is root, when canonicalising:
841 return isTopLevelDirectory() ? this : parent;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100842 } else {
843 return getCachedChildPath(segment);
844 }
845 }
846
847 /**
848 * Returns the path formed by appending the single non-special segment
849 * "baseName" to this path.
850 *
851 * <p>You should almost always use {@link #getRelative} instead, which has
852 * the same performance characteristics if the given name is a valid base
853 * name, and which also works for '.', '..', and strings containing '/'.
854 *
855 * @throws IllegalArgumentException if {@code baseName} is not a valid base
856 * name according to {@link FileSystemUtils#checkBaseName}
857 */
858 public Path getChild(String baseName) {
859 FileSystemUtils.checkBaseName(baseName);
860 return getCachedChildPath(baseName);
861 }
862
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000863 protected Path getRootForRelativePathComputation(PathFragment suffix) {
864 return suffix.isAbsolute() ? fileSystem.getRootDirectory() : this;
865 }
866
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100867 /**
868 * Returns the path formed by appending the relative or absolute path fragment
869 * {@code suffix} to this path.
870 *
871 * <p>If suffix is absolute, the current path will be ignored; otherwise, they
872 * will be combined. Up-level references ("..") cause the preceding path
873 * segment to be elided; this interpretation is only correct if the base path
874 * is canonical.
875 */
876 public Path getRelative(PathFragment suffix) {
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000877 Path result = getRootForRelativePathComputation(suffix);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100878 for (String segment : suffix.segments()) {
879 result = result.getCanonicalPath(segment);
880 }
881 return result;
882 }
883
884 /**
885 * Returns the path formed by appending the relative or absolute string
886 * {@code path} to this path.
887 *
888 * <p>If the given path string is absolute, the current path will be ignored;
889 * otherwise, they will be combined. Up-level references ("..") cause the
890 * preceding path segment to be elided.
891 *
892 * <p>This is a purely syntactic operation, i.e. it does no I/O, it does not
893 * validate the existence of any path, nor resolve symbolic links.
894 */
895 public Path getRelative(String path) {
896 // Fast path for valid base names.
897 if ((path.length() == 0) || (path.equals("."))) {
898 return this;
899 } else if (path.equals("..")) {
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000900 return isTopLevelDirectory() ? this : parent;
nharmataaac13242017-04-24 17:41:23 +0200901 } else if (PathFragment.containsSeparator(path)) {
nharmatab4060b62017-04-04 17:11:39 +0000902 return getRelative(PathFragment.create(path));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100903 } else {
904 return getCachedChildPath(path);
905 }
906 }
907
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000908 protected final String[] getSegments() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100909 String[] resultSegments = new String[depth];
910 Path currentPath = this;
911 for (int pos = depth - 1; pos >= 0; pos--) {
912 resultSegments[pos] = currentPath.getBaseName();
913 currentPath = currentPath.getParentDirectory();
914 }
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000915 return resultSegments;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100916 }
917
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000918 /** Returns an absolute PathFragment representing this path. */
919 public PathFragment asFragment() {
nharmataaac13242017-04-24 17:41:23 +0200920 return PathFragment.createAlreadyInterned('\0', true, getSegments());
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000921 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100922
923 /**
924 * Returns a relative path fragment to this path, relative to {@code
925 * ancestorDirectory}. {@code ancestorDirectory} must be on the same
926 * filesystem as this path. (Currently, both this path and "ancestorDirectory"
927 * must be absolute, though this restriction could be loosened.)
928 * <p>
929 * <code>x.relativeTo(z) == y</code> implies
930 * <code>z.getRelative(y.getPathString()) == x</code>.
931 * <p>
932 * For example, <code>"/foo/bar/wiz".relativeTo("/foo")</code> returns
933 * <code>"bar/wiz"</code>.
934 *
935 * @throws IllegalArgumentException if this path is not beneath {@code
936 * ancestorDirectory} or if they are not part of the same filesystem
937 */
938 public PathFragment relativeTo(Path ancestorPath) {
939 checkSameFilesystem(ancestorPath);
940
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000941 if (isMaybeRelativeTo(ancestorPath)) {
942 // Fast path: when otherPath is the ancestor of this path
943 int resultSegmentCount = depth - ancestorPath.depth;
944 if (resultSegmentCount >= 0) {
945 String[] resultSegments = new String[resultSegmentCount];
946 Path currentPath = this;
947 for (int pos = resultSegmentCount - 1; pos >= 0; pos--) {
948 resultSegments[pos] = currentPath.getBaseName();
949 currentPath = currentPath.getParentDirectory();
950 }
951 if (ancestorPath.equals(currentPath)) {
nharmataaac13242017-04-24 17:41:23 +0200952 return PathFragment.createAlreadyInterned('\0', false, resultSegments);
Laszlo Csomorca99bb72016-10-25 13:15:55 +0000953 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100954 }
955 }
956
957 throw new IllegalArgumentException("Path " + this + " is not beneath " + ancestorPath);
958 }
959
960 /**
961 * Checks that "this" and "that" are paths on the same filesystem.
962 */
963 protected void checkSameFilesystem(Path that) {
964 if (this.fileSystem != that.fileSystem) {
965 throw new IllegalArgumentException("Files are on different filesystems: "
966 + this + ", " + that);
967 }
968 }
969
tomlub1e8daf2017-10-27 15:58:21 -0400970 /** Prefer to use {@link #getOutputStream(FileSystem)}. */
971 @Deprecated
972 public OutputStream getOutputStream() throws IOException, FileNotFoundException {
973 return getOutputStream(fileSystem);
974 }
975
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100976 /**
tomlub1e8daf2017-10-27 15:58:21 -0400977 * Returns an output stream to the file denoted by the current path, creating it and truncating it
978 * if necessary. The stream is opened for writing.
979 *
980 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
981 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100982 *
983 * @throws FileNotFoundException If the file cannot be found or created.
984 * @throws IOException If a different error occurs.
985 */
tomlub1e8daf2017-10-27 15:58:21 -0400986 public OutputStream getOutputStream(FileSystem fileSystem)
987 throws IOException, FileNotFoundException {
988 return getOutputStream(fileSystem, false);
989 }
990
991 /** Prefer to use {@link #getOutputStream(FileSystem, boolean)}. */
992 @Deprecated
993 public OutputStream getOutputStream(boolean append) throws IOException, FileNotFoundException {
994 return getOutputStream(fileSystem, append);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100995 }
996
997 /**
tomlub1e8daf2017-10-27 15:58:21 -0400998 * Returns an output stream to the file denoted by the current path, creating it and truncating it
999 * if necessary. The stream is opened for writing.
1000 *
1001 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1002 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001003 *
1004 * @param append whether to open the file in append mode.
1005 * @throws FileNotFoundException If the file cannot be found or created.
1006 * @throws IOException If a different error occurs.
1007 */
tomlub1e8daf2017-10-27 15:58:21 -04001008 public OutputStream getOutputStream(FileSystem fileSystem, boolean append)
1009 throws IOException, FileNotFoundException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001010 return fileSystem.getOutputStream(this, append);
1011 }
1012
tomlub1e8daf2017-10-27 15:58:21 -04001013 /** Prefer to use {@link #createDirectory(FileSystem)}. */
1014 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001015 public boolean createDirectory() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001016 return createDirectory(fileSystem);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001017 }
1018
1019 /**
tomlub1e8daf2017-10-27 15:58:21 -04001020 * Creates a directory with the name of the current path, not following symbolic links. Returns
1021 * normally iff the directory exists after the call: true if the directory was created by this
1022 * call, false if the directory was already in existence. Throws an exception if the directory
1023 * could not be created for any reason.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001024 *
tomlub1e8daf2017-10-27 15:58:21 -04001025 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1026 * Path.
1027 *
1028 * @throws IOException if the directory creation failed for any reason
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001029 */
tomlub1e8daf2017-10-27 15:58:21 -04001030 public boolean createDirectory(FileSystem fileSystem) throws IOException {
1031 return fileSystem.createDirectory(this);
1032 }
1033
1034 /** Prefer to use {@link #createSymbolicLink(FileSystem, Path)}. */
1035 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001036 public void createSymbolicLink(Path target) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001037 createSymbolicLink(fileSystem, target);
1038 }
1039
1040 /**
1041 * Creates a symbolic link with the name of the current path, following symbolic links. The
1042 * referent of the created symlink is is the absolute path "target"; it is not possible to create
1043 * relative symbolic links via this method.
1044 *
1045 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1046 * Path.
1047 *
1048 * @throws IOException if the creation of the symbolic link was unsuccessful for any reason
1049 */
1050 public void createSymbolicLink(FileSystem fileSystem, Path target) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001051 checkSameFilesystem(target);
1052 fileSystem.createSymbolicLink(this, target.asFragment());
1053 }
1054
tomlub1e8daf2017-10-27 15:58:21 -04001055 /** Prefer to use {@link #createSymbolicLink(FileSystem, PathFragment)}. */
1056 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001057 public void createSymbolicLink(PathFragment target) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001058 createSymbolicLink(fileSystem, target);
1059 }
1060
1061 /**
1062 * Creates a symbolic link with the name of the current path, following symbolic links. The
1063 * referent of the created symlink is is the path fragment "target", which may be absolute or
1064 * relative.
1065 *
1066 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1067 * Path.
1068 *
1069 * @throws IOException if the creation of the symbolic link was unsuccessful for any reason
1070 */
1071 public void createSymbolicLink(FileSystem fileSystem, PathFragment target) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001072 fileSystem.createSymbolicLink(this, target);
1073 }
Laszlo Csomora2da3112016-09-07 08:06:15 +00001074
tomlub1e8daf2017-10-27 15:58:21 -04001075 /** Prefer to use {@link #readSymbolicLink(FileSystem)}. */
1076 @Deprecated
1077 public PathFragment readSymbolicLink() throws IOException {
1078 return readSymbolicLink(fileSystem);
1079 }
1080
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001081 /**
Laszlo Csomora2da3112016-09-07 08:06:15 +00001082 * Returns the target of the current path, which must be a symbolic link. The link contents are
1083 * returned exactly, and may contain an absolute or relative path. Analogous to readlink(2).
1084 *
tomlu39fab102017-10-19 21:35:06 +02001085 * <p>Note: for {@link FileSystem}s where {@link FileSystem#supportsSymbolicLinksNatively(Path)}
Laszlo Csomora2da3112016-09-07 08:06:15 +00001086 * returns false, this method will throw an {@link UnsupportedOperationException} if the link
1087 * points to a non-existent file.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001088 *
tomlub1e8daf2017-10-27 15:58:21 -04001089 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1090 * Path.
1091 *
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001092 * @return the content (i.e. target) of the symbolic link
Laszlo Csomora2da3112016-09-07 08:06:15 +00001093 * @throws IOException if the current path is not a symbolic link, or the contents of the link
1094 * could not be read for any reason
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001095 */
tomlub1e8daf2017-10-27 15:58:21 -04001096 public PathFragment readSymbolicLink(FileSystem fileSystem) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001097 return fileSystem.readSymbolicLink(this);
1098 }
1099
tomlub1e8daf2017-10-27 15:58:21 -04001100 /** Prefer to use {@link #readSymbolicLinkUnchecked(FileSystem)}. */
1101 @Deprecated
1102 public PathFragment readSymbolicLinkUnchecked() throws IOException {
1103 return readSymbolicLinkUnchecked(fileSystem);
1104 }
1105
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001106 /**
tomlub1e8daf2017-10-27 15:58:21 -04001107 * If the current path is a symbolic link, returns the target of this symbolic link. The semantics
1108 * are intentionally left underspecified otherwise to permit efficient implementations.
1109 *
1110 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1111 * Path.
Nathan Harmata215974e52015-09-16 21:31:49 +00001112 *
1113 * @return the content (i.e. target) of the symbolic link
tomlub1e8daf2017-10-27 15:58:21 -04001114 * @throws IOException if the current path is not a symbolic link, or the contents of the link
1115 * could not be read for any reason
Nathan Harmata215974e52015-09-16 21:31:49 +00001116 */
tomlub1e8daf2017-10-27 15:58:21 -04001117 public PathFragment readSymbolicLinkUnchecked(FileSystem fileSystem) throws IOException {
Nathan Harmata215974e52015-09-16 21:31:49 +00001118 return fileSystem.readSymbolicLinkUnchecked(this);
1119 }
1120
tomlub1e8daf2017-10-27 15:58:21 -04001121 /** Prefer to use {@link #createHardLink(FileSystem, Path)}. */
1122 @Deprecated
1123 public void createHardLink(Path link) throws IOException {
1124 createHardLink(fileSystem, link);
1125 }
1126
Nathan Harmata215974e52015-09-16 21:31:49 +00001127 /**
Googlere1cd9502016-09-07 14:33:29 +00001128 * Create a hard link for the current path.
1129 *
tomlub1e8daf2017-10-27 15:58:21 -04001130 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1131 * Path.
1132 *
Googlere1cd9502016-09-07 14:33:29 +00001133 * @param link the path of the new link
1134 * @throws IOException if there was an error executing {@link FileSystem#createHardLink}
1135 */
tomlub1e8daf2017-10-27 15:58:21 -04001136 public void createHardLink(FileSystem fileSystem, Path link) throws IOException {
Googlere1cd9502016-09-07 14:33:29 +00001137 fileSystem.createHardLink(link, this);
1138 }
1139
tomlub1e8daf2017-10-27 15:58:21 -04001140 /** Prefer to use {@link #resolveSymbolicLinks(FileSystem)}. */
1141 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001142 public Path resolveSymbolicLinks() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001143 return resolveSymbolicLinks(fileSystem);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001144 }
1145
1146 /**
tomlub1e8daf2017-10-27 15:58:21 -04001147 * Returns the canonical path for this path, by repeatedly replacing symbolic links with their
1148 * referents. Analogous to realpath(3).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001149 *
tomlub1e8daf2017-10-27 15:58:21 -04001150 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1151 * Path.
1152 *
1153 * @return the canonical path for this path
1154 * @throws IOException if any symbolic link could not be resolved, or other error occurred (for
1155 * example, the path does not exist)
1156 */
1157 public Path resolveSymbolicLinks(FileSystem fileSystem) throws IOException {
1158 return fileSystem.resolveSymbolicLinks(this);
1159 }
1160
1161 /** Prefer to use {@link #renameTo(FileSystem, Path)}. */
1162 @Deprecated
1163 public void renameTo(Path target) throws IOException {
1164 renameTo(fileSystem, target);
1165 }
1166
1167 /**
1168 * Renames the file denoted by the current path to the location "target", not following symbolic
1169 * links.
1170 *
1171 * <p>Files cannot be atomically renamed across devices; copying is required. Use {@link
1172 * FileSystemUtils#copyFile} followed by {@link Path#delete}.
1173 *
1174 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1175 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001176 *
1177 * @throws IOException if the rename failed for any reason
1178 */
tomlub1e8daf2017-10-27 15:58:21 -04001179 public void renameTo(FileSystem fileSystem, Path target) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001180 checkSameFilesystem(target);
1181 fileSystem.renameTo(this, target);
1182 }
1183
tomlub1e8daf2017-10-27 15:58:21 -04001184 /** Prefer to use {@link #getFileSize(FileSystem)}. */
1185 @Deprecated
1186 public long getFileSize() throws IOException, FileNotFoundException {
1187 return getFileSize(fileSystem);
1188 }
1189
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001190 /**
tomlub1e8daf2017-10-27 15:58:21 -04001191 * Returns the size in bytes of the file denoted by the current path, following symbolic links.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001192 *
Nathan Harmatad8b6ff22015-10-20 21:54:34 +00001193 * <p>The size of a directory or special file is undefined and should not be used.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001194 *
tomlub1e8daf2017-10-27 15:58:21 -04001195 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1196 * Path.
1197 *
1198 * @throws FileNotFoundException if the file denoted by the current path does not exist
1199 * @throws IOException if the file's metadata could not be read, or some other error occurred
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001200 */
tomlub1e8daf2017-10-27 15:58:21 -04001201 public long getFileSize(FileSystem fileSystem) throws IOException, FileNotFoundException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001202 return fileSystem.getFileSize(this, true);
1203 }
1204
tomlub1e8daf2017-10-27 15:58:21 -04001205 /** Prefer to use {@link #getFileSize(FileSystem, Symlinks)}. */
1206 @Deprecated
1207 public long getFileSize(Symlinks followSymlinks) throws IOException, FileNotFoundException {
1208 return getFileSize(fileSystem, followSymlinks);
1209 }
1210
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001211 /**
1212 * Returns the size in bytes of the file denoted by the current path.
1213 *
tomlub1e8daf2017-10-27 15:58:21 -04001214 * <p>The size of directory or special file is undefined. The size of a symbolic link is the
1215 * length of the name of its referent.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001216 *
tomlub1e8daf2017-10-27 15:58:21 -04001217 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1218 * Path.
1219 *
1220 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
1221 * link is deferenced until a file other than a symbol link is found
1222 * @throws FileNotFoundException if the file denoted by the current path does not exist
1223 * @throws IOException if the file's metadata could not be read, or some other error occurred
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001224 */
tomlub1e8daf2017-10-27 15:58:21 -04001225 public long getFileSize(FileSystem fileSystem, Symlinks followSymlinks)
1226 throws IOException, FileNotFoundException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001227 return fileSystem.getFileSize(this, followSymlinks.toBoolean());
1228 }
1229
tomlub1e8daf2017-10-27 15:58:21 -04001230 /** Prefer to use {@link #delete(FileSystem)}. */
1231 @Deprecated
tomluf903eb52017-10-27 12:12:11 -04001232 public boolean delete() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001233 return delete(fileSystem);
tomluf903eb52017-10-27 12:12:11 -04001234 }
1235
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001236 /**
tomluf903eb52017-10-27 12:12:11 -04001237 * Deletes the file denoted by this path, not following symbolic links. Returns normally iff the
1238 * file doesn't exist after the call: true if this call deleted the file, false if the file
1239 * already didn't exist. Throws an exception if the file could not be deleted for any reason.
1240 *
1241 * <p>This is a migration method. The method (and its FileSystem-less counterpart) will be deleted
1242 * once the FileSystem instance is removed from Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001243 *
1244 * @return true iff the file was actually deleted by this call
tomluf903eb52017-10-27 12:12:11 -04001245 * @throws IOException if the deletion failed but the file was present prior to the call
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001246 */
tomluf903eb52017-10-27 12:12:11 -04001247 public boolean delete(FileSystem fileSystem) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001248 return fileSystem.delete(this);
1249 }
1250
tomlub1e8daf2017-10-27 15:58:21 -04001251 /** Prefer to use {@link #getLastModifiedTime(FileSystem)}. */
1252 @Deprecated
1253 public long getLastModifiedTime() throws IOException {
1254 return getLastModifiedTime(fileSystem);
1255 }
1256
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001257 /**
tomlub1e8daf2017-10-27 15:58:21 -04001258 * Returns the last modification time of the file, in milliseconds since the UNIX epoch, of the
1259 * file denoted by the current path, following symbolic links.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001260 *
tomlub1e8daf2017-10-27 15:58:21 -04001261 * <p>Caveat: many filesystems store file times in seconds, so do not rely on the millisecond
1262 * precision.
1263 *
1264 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1265 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001266 *
1267 * @throws IOException if the operation failed for any reason
1268 */
tomlub1e8daf2017-10-27 15:58:21 -04001269 public long getLastModifiedTime(FileSystem fileSystem) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001270 return fileSystem.getLastModifiedTime(this, true);
1271 }
1272
tomlub1e8daf2017-10-27 15:58:21 -04001273 /** Prefer to use {@link #getLastModifiedTime(FileSystem, Symlinks)}. */
1274 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001275 public long getLastModifiedTime(Symlinks followSymlinks) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001276 return getLastModifiedTime(fileSystem, followSymlinks);
1277 }
1278
1279 /**
1280 * Returns the last modification time of the file, in milliseconds since the UNIX epoch, of the
1281 * file denoted by the current path.
1282 *
1283 * <p>Caveat: many filesystems store file times in seconds, so do not rely on the millisecond
1284 * precision.
1285 *
1286 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1287 * Path.
1288 *
1289 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
1290 * link is dereferenced until a file other than a symbolic link is found
1291 * @throws IOException if the modification time for the file could not be obtained for any reason
1292 */
1293 public long getLastModifiedTime(FileSystem fileSystem, Symlinks followSymlinks)
1294 throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001295 return fileSystem.getLastModifiedTime(this, followSymlinks.toBoolean());
1296 }
1297
tomlub1e8daf2017-10-27 15:58:21 -04001298 /** Prefer to use {@link #setLastModifiedTime(FileSystem, long)}. */
1299 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001300 public void setLastModifiedTime(long newTime) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001301 setLastModifiedTime(fileSystem, newTime);
1302 }
1303
1304 /**
1305 * Sets the modification time of the file denoted by the current path. Follows symbolic links. If
1306 * newTime is -1, the current time according to the kernel is used; this may differ from the JVM's
1307 * clock.
1308 *
1309 * <p>Caveat: many filesystems store file times in seconds, so do not rely on the millisecond
1310 * precision.
1311 *
1312 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1313 * Path.
1314 *
1315 * @param newTime time, in milliseconds since the UNIX epoch, or -1L, meaning use the kernel's
1316 * current time
1317 * @throws IOException if the modification time for the file could not be set for any reason
1318 */
1319 public void setLastModifiedTime(FileSystem fileSystem, long newTime) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001320 fileSystem.setLastModifiedTime(this, newTime);
1321 }
1322
tomlub1e8daf2017-10-27 15:58:21 -04001323 /** Prefer to use {@link #getxattr(FileSystem, String)}. */
1324 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001325 public byte[] getxattr(String name) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001326 return getxattr(fileSystem, name);
1327 }
1328
1329 /**
1330 * Returns value of the given extended attribute name or null if attribute does not exist or file
1331 * system does not support extended attributes. Follows symlinks.
1332 *
1333 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1334 * Path.
1335 */
1336 public byte[] getxattr(FileSystem fileSystem, String name) throws IOException {
Nathan Harmatae1f187a2015-09-16 20:03:08 +00001337 return fileSystem.getxattr(this, name);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001338 }
1339
tomlub1e8daf2017-10-27 15:58:21 -04001340 /** Prefer to use {@link #getFastDigest(FileSystem)}. */
1341 @Deprecated
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001342 public byte[] getFastDigest() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001343 return getFastDigest(fileSystem);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001344 }
1345
1346 /**
tomlub1e8daf2017-10-27 15:58:21 -04001347 * Gets a fast digest for the given path, or {@code null} if there isn't one available. The digest
1348 * should be suitable for detecting changes to the file.
1349 *
1350 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1351 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001352 */
tomlub1e8daf2017-10-27 15:58:21 -04001353 public byte[] getFastDigest(FileSystem fileSystem) throws IOException {
1354 return fileSystem.getFastDigest(this);
1355 }
1356
1357 /** Prefer to use {@link #isValidDigest(FileSystem, byte[])}. */
1358 @Deprecated
1359 public boolean isValidDigest(byte[] digest) {
1360 return isValidDigest(fileSystem, digest);
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001361 }
1362
1363 /**
1364 * Returns whether the given digest is a valid digest for the default system digest function.
tomlub1e8daf2017-10-27 15:58:21 -04001365 *
1366 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1367 * Path.
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001368 */
tomlub1e8daf2017-10-27 15:58:21 -04001369 public boolean isValidDigest(FileSystem fileSystem, byte[] digest) {
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001370 return fileSystem.isValidDigest(digest);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001371 }
1372
tomlub1e8daf2017-10-27 15:58:21 -04001373 /** Prefer to use {@link #getDigest(FileSystem)}. */
1374 @Deprecated
1375 public byte[] getDigest() throws IOException {
1376 return getDigest(fileSystem);
1377 }
1378
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001379 /**
tomlub1e8daf2017-10-27 15:58:21 -04001380 * Returns the digest of the file denoted by the current path, following symbolic links.
1381 *
1382 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1383 * Path.
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001384 *
1385 * @return a new byte array containing the file's digest
1386 * @throws IOException if the digest could not be computed for any reason
1387 */
tomlub1e8daf2017-10-27 15:58:21 -04001388 public byte[] getDigest(FileSystem fileSystem) throws IOException {
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001389 return fileSystem.getDigest(this);
1390 }
1391
tomlub1e8daf2017-10-27 15:58:21 -04001392 /** Prefer to use {@link #getDigest(FileSystem, HashFunction)}. */
1393 @Deprecated
1394 public byte[] getDigest(HashFunction hashFunction) throws IOException {
1395 return getDigest(fileSystem, hashFunction);
1396 }
1397
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001398 /**
tomlub1e8daf2017-10-27 15:58:21 -04001399 * Returns the digest of the file denoted by the current path and digest function, following
1400 * symbolic links.
1401 *
1402 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1403 * Path.
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001404 *
1405 * @return a new byte array containing the file's digest
1406 * @throws IOException if the digest could not be computed for any reason
1407 */
tomlub1e8daf2017-10-27 15:58:21 -04001408 public byte[] getDigest(FileSystem fileSystem, HashFunction hashFunction) throws IOException {
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001409 return fileSystem.getDigest(this, hashFunction);
1410 }
1411
tomlub1e8daf2017-10-27 15:58:21 -04001412 /** Prefer to use {@link #getInputStream(FileSystem)}. */
1413 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001414 public InputStream getInputStream() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001415 return getInputStream(fileSystem);
1416 }
1417
1418 /**
1419 * Opens the file denoted by this path, following symbolic links, for reading, and returns an
1420 * input stream to it.
1421 *
1422 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1423 * Path.
1424 *
1425 * @throws IOException if the file was not found or could not be opened for reading
1426 */
1427 public InputStream getInputStream(FileSystem fileSystem) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001428 return fileSystem.getInputStream(this);
1429 }
1430
1431 /**
1432 * Returns a java.io.File representation of this path.
1433 *
1434 * <p>Caveat: the result may be useless if this path's getFileSystem() is not
1435 * the UNIX filesystem.
1436 */
1437 public File getPathFile() {
1438 return new File(getPathString());
1439 }
1440
tomlub1e8daf2017-10-27 15:58:21 -04001441 /** Prefer to use {@link #isWritable(FileSystem)}. */
1442 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001443 public boolean isWritable() throws IOException, FileNotFoundException {
tomlub1e8daf2017-10-27 15:58:21 -04001444 return isWritable(fileSystem);
1445 }
1446
1447 /**
1448 * Returns true if the file denoted by the current path, following symbolic links, is writable for
1449 * the current user.
1450 *
1451 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1452 * Path.
1453 *
1454 * @throws FileNotFoundException if the file does not exist, a dangling symbolic link was
1455 * encountered, or the file's metadata could not be read
1456 */
1457 public boolean isWritable(FileSystem fileSystem) throws IOException, FileNotFoundException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001458 return fileSystem.isWritable(this);
1459 }
1460
tomlub1e8daf2017-10-27 15:58:21 -04001461 /** Prefer to use {@link #setReadable(FileSystem, boolean)}. */
1462 @Deprecated
1463 public void setReadable(boolean readable) throws IOException, FileNotFoundException {
1464 setReadable(fileSystem, readable);
1465 }
1466
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001467 /**
tomlub1e8daf2017-10-27 15:58:21 -04001468 * Sets the read permissions of the file denoted by the current path, following symbolic links.
1469 * Permissions apply to the current user.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001470 *
tomlub1e8daf2017-10-27 15:58:21 -04001471 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1472 * Path.
1473 *
1474 * @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 +01001475 * @throws FileNotFoundException if the file does not exist
1476 * @throws IOException If the action cannot be taken (ie. permissions)
1477 */
tomlub1e8daf2017-10-27 15:58:21 -04001478 public void setReadable(FileSystem fileSystem, boolean readable)
1479 throws IOException, FileNotFoundException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001480 fileSystem.setReadable(this, readable);
1481 }
1482
tomlub1e8daf2017-10-27 15:58:21 -04001483 /** Prefer to use {@link #setWritable(FileSystem, boolean)}. */
1484 @Deprecated
1485 public void setWritable(boolean writable) throws IOException, FileNotFoundException {
1486 setWritable(fileSystem, writable);
1487 }
1488
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001489 /**
tomlub1e8daf2017-10-27 15:58:21 -04001490 * Sets the write permissions of the file denoted by the current path, following symbolic links.
1491 * Permissions apply to the current user.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001492 *
1493 * <p>TODO(bazel-team): (2009) what about owner/group/others?
1494 *
tomlub1e8daf2017-10-27 15:58:21 -04001495 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1496 * Path.
1497 *
1498 * @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 +01001499 * @throws FileNotFoundException if the file does not exist
1500 * @throws IOException If the action cannot be taken (ie. permissions)
1501 */
tomlub1e8daf2017-10-27 15:58:21 -04001502 public void setWritable(FileSystem fileSystem, boolean writable)
1503 throws IOException, FileNotFoundException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001504 fileSystem.setWritable(this, writable);
1505 }
1506
tomlub1e8daf2017-10-27 15:58:21 -04001507 /** Prefer to use {@link #isExecutable(FileSystem)}. */
1508 @Deprecated
1509 public boolean isExecutable() throws IOException, FileNotFoundException {
1510 return isExecutable(fileSystem);
1511 }
1512
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001513 /**
tomlub1e8daf2017-10-27 15:58:21 -04001514 * Returns true iff the file specified by the current path, following symbolic links, is
1515 * executable by the current user.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001516 *
tomlub1e8daf2017-10-27 15:58:21 -04001517 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1518 * Path.
1519 *
1520 * @throws FileNotFoundException if the file does not exist or a dangling symbolic link was
1521 * encountered
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001522 * @throws IOException if some other I/O error occurred
1523 */
tomlub1e8daf2017-10-27 15:58:21 -04001524 public boolean isExecutable(FileSystem fileSystem) throws IOException, FileNotFoundException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001525 return fileSystem.isExecutable(this);
1526 }
1527
tomlub1e8daf2017-10-27 15:58:21 -04001528 /** Prefer to use {@link #isReadable(FileSystem)}. */
1529 @Deprecated
1530 public boolean isReadable() throws IOException, FileNotFoundException {
1531 return isReadable(fileSystem);
1532 }
1533
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001534 /**
tomlub1e8daf2017-10-27 15:58:21 -04001535 * Returns true iff the file specified by the current path, following symbolic links, is readable
1536 * by the current user.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001537 *
tomlub1e8daf2017-10-27 15:58:21 -04001538 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1539 * Path.
1540 *
1541 * @throws FileNotFoundException if the file does not exist or a dangling symbolic link was
1542 * encountered
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001543 * @throws IOException if some other I/O error occurred
1544 */
tomlub1e8daf2017-10-27 15:58:21 -04001545 public boolean isReadable(FileSystem fileSystem) throws IOException, FileNotFoundException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001546 return fileSystem.isReadable(this);
1547 }
1548
tomlub1e8daf2017-10-27 15:58:21 -04001549 /** Prefer to use {@link #setExecutable(FileSystem, boolean)}. */
1550 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001551 public void setExecutable(boolean executable) throws IOException, FileNotFoundException {
tomlub1e8daf2017-10-27 15:58:21 -04001552 setExecutable(fileSystem, executable);
1553 }
1554
1555 /**
1556 * Sets the execute permission on the file specified by the current path, following symbolic
1557 * links. Permissions apply to the current user.
1558 *
1559 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1560 * Path.
1561 *
1562 * @throws FileNotFoundException if the file does not exist or a dangling symbolic link was
1563 * encountered
1564 * @throws IOException if the metadata change failed, for example because of permissions
1565 */
1566 public void setExecutable(FileSystem fileSystem, boolean executable)
1567 throws IOException, FileNotFoundException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001568 fileSystem.setExecutable(this, executable);
1569 }
1570
tomlub1e8daf2017-10-27 15:58:21 -04001571 /** Prefer to use {@link #chmod(FileSystem, int)}. */
1572 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001573 public void chmod(int mode) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001574 chmod(fileSystem, mode);
1575 }
1576
1577 /**
1578 * Sets the permissions on the file specified by the current path, following symbolic links. If
1579 * permission changes on this path's {@link FileSystem} are slow (e.g. one syscall per change),
1580 * this method should aim to be faster than setting each permission individually. If this path's
1581 * {@link FileSystem} does not support group and others permissions, those bits will be ignored.
1582 *
1583 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1584 * Path.
1585 *
1586 * @throws FileNotFoundException if the file does not exist or a dangling symbolic link was
1587 * encountered
1588 * @throws IOException if the metadata change failed, for example because of permissions
1589 */
1590 public void chmod(FileSystem fileSystem, int mode) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001591 fileSystem.chmod(this, mode);
1592 }
1593
tomlub1e8daf2017-10-27 15:58:21 -04001594 /** Prefer to use {@link #prefetchPackageAsync(FileSystem, int)}. */
1595 @Deprecated
Googlerc804c662016-12-01 16:53:28 +00001596 public void prefetchPackageAsync(int maxDirs) {
tomlub1e8daf2017-10-27 15:58:21 -04001597 prefetchPackageAsync(fileSystem, maxDirs);
1598 }
1599
1600 public void prefetchPackageAsync(FileSystem fileSystem, int maxDirs) {
Googlerc804c662016-12-01 16:53:28 +00001601 fileSystem.prefetchPackageAsync(this, maxDirs);
1602 }
1603
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001604 /**
1605 * Compare Paths of the same file system using their PathFragments.
1606 *
1607 * <p>Paths from different filesystems will be compared using the identity
1608 * hash code of their respective filesystems.
1609 */
1610 @Override
1611 public int compareTo(Path o) {
1612 // Fast-path.
1613 if (equals(o)) {
1614 return 0;
1615 }
1616
1617 // If they are on different file systems, the file system decides the ordering.
1618 FileSystem otherFs = o.getFileSystem();
1619 if (!fileSystem.equals(otherFs)) {
1620 int thisFileSystemHash = System.identityHashCode(fileSystem);
1621 int otherFileSystemHash = System.identityHashCode(otherFs);
1622 if (thisFileSystemHash < otherFileSystemHash) {
1623 return -1;
1624 } else if (thisFileSystemHash > otherFileSystemHash) {
1625 return 1;
1626 } else {
1627 // TODO(bazel-team): Add a name to every file system to be used here.
1628 return 0;
1629 }
1630 }
1631
1632 // Equal file system, but different paths, because of the canonicalization.
1633 // We expect to often compare Paths that are very similar, for example for files in the same
1634 // directory. This can be done efficiently by going up segment by segment until we get the
1635 // identical path (canonicalization again), and then just compare the immediate child segments.
1636 // Overall this is much faster than creating PathFragment instances, and comparing those, which
1637 // requires us to always go up to the top-level directory and copy all segments into a new
1638 // string array.
1639 // This was previously showing up as a hotspot in a profile of globbing a large directory.
Googlere1cd9502016-09-07 14:33:29 +00001640 Path a = this;
1641 Path b = o;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001642 int maxDepth = Math.min(a.depth, b.depth);
1643 while (a.depth > maxDepth) {
1644 a = a.getParentDirectory();
1645 }
1646 while (b.depth > maxDepth) {
1647 b = b.getParentDirectory();
1648 }
1649 // One is the child of the other.
1650 if (a.equals(b)) {
1651 // If a is the same as this, this.depth must be less than o.depth.
1652 return equals(a) ? -1 : 1;
1653 }
Googlere1cd9502016-09-07 14:33:29 +00001654 Path previousa;
1655 Path previousb;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001656 do {
1657 previousa = a;
1658 previousb = b;
1659 a = a.getParentDirectory();
1660 b = b.getParentDirectory();
Nathan Harmata3c74af02015-10-09 13:16:08 +00001661 } while (!a.equals(b)); // This has to happen eventually.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001662 return previousa.name.compareTo(previousb.name);
1663 }
1664}