blob: 1d3947dcdcad9146443d875cc7b9e92656d1308e [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
1029 /** Prefer to use {@link #createSymbolicLink(FileSystem, Path)}. */
1030 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001031 public void createSymbolicLink(Path target) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001032 createSymbolicLink(fileSystem, target);
1033 }
1034
1035 /**
1036 * Creates a symbolic link with the name of the current path, following symbolic links. The
1037 * referent of the created symlink is is the absolute path "target"; it is not possible to create
1038 * relative symbolic links via this method.
1039 *
1040 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1041 * Path.
1042 *
1043 * @throws IOException if the creation of the symbolic link was unsuccessful for any reason
1044 */
1045 public void createSymbolicLink(FileSystem fileSystem, Path target) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001046 checkSameFilesystem(target);
aehligc801c392017-12-19 07:12:25 -08001047 fileSystem.createSymbolicLink(this, target.asFragment());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001048 }
1049
tomlub1e8daf2017-10-27 15:58:21 -04001050 /** Prefer to use {@link #createSymbolicLink(FileSystem, PathFragment)}. */
1051 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001052 public void createSymbolicLink(PathFragment target) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001053 createSymbolicLink(fileSystem, target);
1054 }
1055
1056 /**
1057 * Creates a symbolic link with the name of the current path, following symbolic links. The
1058 * referent of the created symlink is is the path fragment "target", which may be absolute or
1059 * relative.
1060 *
1061 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1062 * Path.
1063 *
1064 * @throws IOException if the creation of the symbolic link was unsuccessful for any reason
1065 */
1066 public void createSymbolicLink(FileSystem fileSystem, PathFragment target) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001067 fileSystem.createSymbolicLink(this, target);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001068 }
Laszlo Csomora2da3112016-09-07 08:06:15 +00001069
tomlub1e8daf2017-10-27 15:58:21 -04001070 /** Prefer to use {@link #readSymbolicLink(FileSystem)}. */
1071 @Deprecated
1072 public PathFragment readSymbolicLink() throws IOException {
1073 return readSymbolicLink(fileSystem);
1074 }
1075
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001076 /**
Laszlo Csomora2da3112016-09-07 08:06:15 +00001077 * Returns the target of the current path, which must be a symbolic link. The link contents are
1078 * returned exactly, and may contain an absolute or relative path. Analogous to readlink(2).
1079 *
tomlu39fab102017-10-19 21:35:06 +02001080 * <p>Note: for {@link FileSystem}s where {@link FileSystem#supportsSymbolicLinksNatively(Path)}
Laszlo Csomora2da3112016-09-07 08:06:15 +00001081 * returns false, this method will throw an {@link UnsupportedOperationException} if the link
1082 * points to a non-existent file.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001083 *
tomlub1e8daf2017-10-27 15:58:21 -04001084 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1085 * Path.
1086 *
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001087 * @return the content (i.e. target) of the symbolic link
Laszlo Csomora2da3112016-09-07 08:06:15 +00001088 * @throws IOException if the current path is not a symbolic link, or the contents of the link
1089 * could not be read for any reason
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001090 */
tomlub1e8daf2017-10-27 15:58:21 -04001091 public PathFragment readSymbolicLink(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001092 return fileSystem.readSymbolicLink(this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001093 }
1094
tomlub1e8daf2017-10-27 15:58:21 -04001095 /** Prefer to use {@link #readSymbolicLinkUnchecked(FileSystem)}. */
1096 @Deprecated
1097 public PathFragment readSymbolicLinkUnchecked() throws IOException {
1098 return readSymbolicLinkUnchecked(fileSystem);
1099 }
1100
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001101 /**
tomlub1e8daf2017-10-27 15:58:21 -04001102 * If the current path is a symbolic link, returns the target of this symbolic link. The semantics
1103 * are intentionally left underspecified otherwise to permit efficient implementations.
1104 *
1105 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1106 * Path.
Nathan Harmata215974e52015-09-16 21:31:49 +00001107 *
1108 * @return the content (i.e. target) of the symbolic link
tomlub1e8daf2017-10-27 15:58:21 -04001109 * @throws IOException if the current path is not a symbolic link, or the contents of the link
1110 * could not be read for any reason
Nathan Harmata215974e52015-09-16 21:31:49 +00001111 */
tomlub1e8daf2017-10-27 15:58:21 -04001112 public PathFragment readSymbolicLinkUnchecked(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001113 return fileSystem.readSymbolicLinkUnchecked(this);
Nathan Harmata215974e52015-09-16 21:31:49 +00001114 }
1115
tomlub1e8daf2017-10-27 15:58:21 -04001116 /** Prefer to use {@link #createHardLink(FileSystem, Path)}. */
1117 @Deprecated
1118 public void createHardLink(Path link) throws IOException {
1119 createHardLink(fileSystem, link);
1120 }
1121
Nathan Harmata215974e52015-09-16 21:31:49 +00001122 /**
Googlere1cd9502016-09-07 14:33:29 +00001123 * Create a hard link for the current path.
1124 *
tomlub1e8daf2017-10-27 15:58:21 -04001125 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1126 * Path.
1127 *
Googlere1cd9502016-09-07 14:33:29 +00001128 * @param link the path of the new link
1129 * @throws IOException if there was an error executing {@link FileSystem#createHardLink}
1130 */
tomlub1e8daf2017-10-27 15:58:21 -04001131 public void createHardLink(FileSystem fileSystem, Path link) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001132 fileSystem.createHardLink(link, this);
Googlere1cd9502016-09-07 14:33:29 +00001133 }
1134
tomlub1e8daf2017-10-27 15:58:21 -04001135 /** Prefer to use {@link #resolveSymbolicLinks(FileSystem)}. */
1136 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001137 public Path resolveSymbolicLinks() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001138 return resolveSymbolicLinks(fileSystem);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001139 }
1140
1141 /**
tomlub1e8daf2017-10-27 15:58:21 -04001142 * Returns the canonical path for this path, by repeatedly replacing symbolic links with their
1143 * referents. Analogous to realpath(3).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001144 *
tomlub1e8daf2017-10-27 15:58:21 -04001145 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1146 * Path.
1147 *
1148 * @return the canonical path for this path
1149 * @throws IOException if any symbolic link could not be resolved, or other error occurred (for
1150 * example, the path does not exist)
1151 */
1152 public Path resolveSymbolicLinks(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001153 return fileSystem.resolveSymbolicLinks(this);
tomlub1e8daf2017-10-27 15:58:21 -04001154 }
1155
1156 /** Prefer to use {@link #renameTo(FileSystem, Path)}. */
1157 @Deprecated
1158 public void renameTo(Path target) throws IOException {
1159 renameTo(fileSystem, target);
1160 }
1161
1162 /**
1163 * Renames the file denoted by the current path to the location "target", not following symbolic
1164 * links.
1165 *
1166 * <p>Files cannot be atomically renamed across devices; copying is required. Use {@link
1167 * FileSystemUtils#copyFile} followed by {@link Path#delete}.
1168 *
1169 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1170 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001171 *
1172 * @throws IOException if the rename failed for any reason
1173 */
tomlub1e8daf2017-10-27 15:58:21 -04001174 public void renameTo(FileSystem fileSystem, Path target) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001175 checkSameFilesystem(target);
aehligc801c392017-12-19 07:12:25 -08001176 fileSystem.renameTo(this, target);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001177 }
1178
tomlub1e8daf2017-10-27 15:58:21 -04001179 /** Prefer to use {@link #getFileSize(FileSystem)}. */
1180 @Deprecated
1181 public long getFileSize() throws IOException, FileNotFoundException {
1182 return getFileSize(fileSystem);
1183 }
1184
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001185 /**
tomlub1e8daf2017-10-27 15:58:21 -04001186 * Returns the size in bytes of the file denoted by the current path, following symbolic links.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001187 *
Nathan Harmatad8b6ff22015-10-20 21:54:34 +00001188 * <p>The size of a directory or special file is undefined and should not be used.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001189 *
tomlub1e8daf2017-10-27 15:58:21 -04001190 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1191 * Path.
1192 *
1193 * @throws FileNotFoundException if the file denoted by the current path does not exist
1194 * @throws IOException if the file's metadata could not be read, or some other error occurred
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001195 */
tomlub1e8daf2017-10-27 15:58:21 -04001196 public long getFileSize(FileSystem fileSystem) throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001197 return fileSystem.getFileSize(this, true);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001198 }
1199
tomlub1e8daf2017-10-27 15:58:21 -04001200 /** Prefer to use {@link #getFileSize(FileSystem, Symlinks)}. */
1201 @Deprecated
1202 public long getFileSize(Symlinks followSymlinks) throws IOException, FileNotFoundException {
1203 return getFileSize(fileSystem, followSymlinks);
1204 }
1205
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001206 /**
1207 * Returns the size in bytes of the file denoted by the current path.
1208 *
tomlub1e8daf2017-10-27 15:58:21 -04001209 * <p>The size of directory or special file is undefined. The size of a symbolic link is the
1210 * length of the name of its referent.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001211 *
tomlub1e8daf2017-10-27 15:58:21 -04001212 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1213 * Path.
1214 *
1215 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
1216 * link is deferenced until a file other than a symbol link is found
1217 * @throws FileNotFoundException if the file denoted by the current path does not exist
1218 * @throws IOException if the file's metadata could not be read, or some other error occurred
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001219 */
tomlub1e8daf2017-10-27 15:58:21 -04001220 public long getFileSize(FileSystem fileSystem, Symlinks followSymlinks)
1221 throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001222 return fileSystem.getFileSize(this, followSymlinks.toBoolean());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001223 }
1224
tomlub1e8daf2017-10-27 15:58:21 -04001225 /** Prefer to use {@link #delete(FileSystem)}. */
1226 @Deprecated
tomluf903eb52017-10-27 12:12:11 -04001227 public boolean delete() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001228 return delete(fileSystem);
tomluf903eb52017-10-27 12:12:11 -04001229 }
1230
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001231 /**
tomluf903eb52017-10-27 12:12:11 -04001232 * Deletes the file denoted by this path, not following symbolic links. Returns normally iff the
1233 * file doesn't exist after the call: true if this call deleted the file, false if the file
1234 * already didn't exist. Throws an exception if the file could not be deleted for any reason.
1235 *
1236 * <p>This is a migration method. The method (and its FileSystem-less counterpart) will be deleted
1237 * once the FileSystem instance is removed from Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001238 *
1239 * @return true iff the file was actually deleted by this call
tomluf903eb52017-10-27 12:12:11 -04001240 * @throws IOException if the deletion failed but the file was present prior to the call
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001241 */
tomluf903eb52017-10-27 12:12:11 -04001242 public boolean delete(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001243 return fileSystem.delete(this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001244 }
1245
tomlub1e8daf2017-10-27 15:58:21 -04001246 /** Prefer to use {@link #getLastModifiedTime(FileSystem)}. */
1247 @Deprecated
1248 public long getLastModifiedTime() throws IOException {
1249 return getLastModifiedTime(fileSystem);
1250 }
1251
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001252 /**
tomlub1e8daf2017-10-27 15:58:21 -04001253 * Returns the last modification time of the file, in milliseconds since the UNIX epoch, of the
1254 * file denoted by the current path, following symbolic links.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001255 *
tomlub1e8daf2017-10-27 15:58:21 -04001256 * <p>Caveat: many filesystems store file times in seconds, so do not rely on the millisecond
1257 * precision.
1258 *
1259 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1260 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001261 *
1262 * @throws IOException if the operation failed for any reason
1263 */
tomlub1e8daf2017-10-27 15:58:21 -04001264 public long getLastModifiedTime(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001265 return fileSystem.getLastModifiedTime(this, true);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001266 }
1267
tomlub1e8daf2017-10-27 15:58:21 -04001268 /** Prefer to use {@link #getLastModifiedTime(FileSystem, Symlinks)}. */
1269 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001270 public long getLastModifiedTime(Symlinks followSymlinks) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001271 return getLastModifiedTime(fileSystem, followSymlinks);
1272 }
1273
1274 /**
1275 * Returns the last modification time of the file, in milliseconds since the UNIX epoch, of the
1276 * file denoted by the current path.
1277 *
1278 * <p>Caveat: many filesystems store file times in seconds, so do not rely on the millisecond
1279 * precision.
1280 *
1281 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1282 * Path.
1283 *
1284 * @param followSymlinks if {@link Symlinks#FOLLOW}, and this path denotes a symbolic link, the
1285 * link is dereferenced until a file other than a symbolic link is found
1286 * @throws IOException if the modification time for the file could not be obtained for any reason
1287 */
1288 public long getLastModifiedTime(FileSystem fileSystem, Symlinks followSymlinks)
1289 throws IOException {
aehligc801c392017-12-19 07:12:25 -08001290 return fileSystem.getLastModifiedTime(this, followSymlinks.toBoolean());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001291 }
1292
tomlub1e8daf2017-10-27 15:58:21 -04001293 /** Prefer to use {@link #setLastModifiedTime(FileSystem, long)}. */
1294 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001295 public void setLastModifiedTime(long newTime) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001296 setLastModifiedTime(fileSystem, newTime);
1297 }
1298
1299 /**
1300 * Sets the modification time of the file denoted by the current path. Follows symbolic links. If
1301 * newTime is -1, the current time according to the kernel is used; this may differ from the JVM's
1302 * clock.
1303 *
1304 * <p>Caveat: many filesystems store file times in seconds, so do not rely on the millisecond
1305 * precision.
1306 *
1307 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1308 * Path.
1309 *
1310 * @param newTime time, in milliseconds since the UNIX epoch, or -1L, meaning use the kernel's
1311 * current time
1312 * @throws IOException if the modification time for the file could not be set for any reason
1313 */
1314 public void setLastModifiedTime(FileSystem fileSystem, long newTime) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001315 fileSystem.setLastModifiedTime(this, newTime);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001316 }
1317
tomlub1e8daf2017-10-27 15:58:21 -04001318 /** Prefer to use {@link #getxattr(FileSystem, String)}. */
1319 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001320 public byte[] getxattr(String name) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001321 return getxattr(fileSystem, name);
1322 }
1323
1324 /**
1325 * Returns value of the given extended attribute name or null if attribute does not exist or file
1326 * system does not support extended attributes. Follows symlinks.
1327 *
1328 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1329 * Path.
1330 */
1331 public byte[] getxattr(FileSystem fileSystem, String name) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001332 return fileSystem.getxattr(this, name);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001333 }
1334
tomlub1e8daf2017-10-27 15:58:21 -04001335 /** Prefer to use {@link #getFastDigest(FileSystem)}. */
1336 @Deprecated
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001337 public byte[] getFastDigest() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001338 return getFastDigest(fileSystem);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001339 }
1340
1341 /**
tomlub1e8daf2017-10-27 15:58:21 -04001342 * Gets a fast digest for the given path, or {@code null} if there isn't one available. The digest
1343 * should be suitable for detecting changes to the file.
1344 *
1345 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1346 * Path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001347 */
tomlub1e8daf2017-10-27 15:58:21 -04001348 public byte[] getFastDigest(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001349 return fileSystem.getFastDigest(this);
tomlub1e8daf2017-10-27 15:58:21 -04001350 }
1351
1352 /** Prefer to use {@link #isValidDigest(FileSystem, byte[])}. */
1353 @Deprecated
1354 public boolean isValidDigest(byte[] digest) {
1355 return isValidDigest(fileSystem, digest);
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001356 }
1357
1358 /**
1359 * Returns whether the given digest is a valid digest for the default system digest function.
tomlub1e8daf2017-10-27 15:58:21 -04001360 *
1361 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1362 * Path.
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001363 */
tomlub1e8daf2017-10-27 15:58:21 -04001364 public boolean isValidDigest(FileSystem fileSystem, byte[] digest) {
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001365 return fileSystem.isValidDigest(digest);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001366 }
1367
tomlub1e8daf2017-10-27 15:58:21 -04001368 /** Prefer to use {@link #getDigest(FileSystem)}. */
1369 @Deprecated
1370 public byte[] getDigest() throws IOException {
1371 return getDigest(fileSystem);
1372 }
1373
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001374 /**
tomlub1e8daf2017-10-27 15:58:21 -04001375 * Returns the digest of the file denoted by the current path, following symbolic links.
1376 *
1377 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1378 * Path.
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001379 *
1380 * @return a new byte array containing the file's digest
1381 * @throws IOException if the digest could not be computed for any reason
1382 */
tomlub1e8daf2017-10-27 15:58:21 -04001383 public byte[] getDigest(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001384 return fileSystem.getDigest(this);
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001385 }
1386
tomlub1e8daf2017-10-27 15:58:21 -04001387 /** Prefer to use {@link #getDigest(FileSystem, HashFunction)}. */
1388 @Deprecated
1389 public byte[] getDigest(HashFunction hashFunction) throws IOException {
1390 return getDigest(fileSystem, hashFunction);
1391 }
1392
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001393 /**
tomlub1e8daf2017-10-27 15:58:21 -04001394 * Returns the digest of the file denoted by the current path and digest function, following
1395 * symbolic links.
1396 *
1397 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1398 * Path.
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001399 *
1400 * @return a new byte array containing the file's digest
1401 * @throws IOException if the digest could not be computed for any reason
1402 */
tomlub1e8daf2017-10-27 15:58:21 -04001403 public byte[] getDigest(FileSystem fileSystem, HashFunction hashFunction) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001404 return fileSystem.getDigest(this, hashFunction);
Ola Rozenfeld39dbc982016-11-17 20:14:56 +00001405 }
1406
tomlub1e8daf2017-10-27 15:58:21 -04001407 /** Prefer to use {@link #getInputStream(FileSystem)}. */
1408 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001409 public InputStream getInputStream() throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001410 return getInputStream(fileSystem);
1411 }
1412
1413 /**
1414 * Opens the file denoted by this path, following symbolic links, for reading, and returns an
1415 * input stream to it.
1416 *
1417 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1418 * Path.
1419 *
1420 * @throws IOException if the file was not found or could not be opened for reading
1421 */
1422 public InputStream getInputStream(FileSystem fileSystem) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001423 return fileSystem.getInputStream(this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001424 }
1425
1426 /**
1427 * Returns a java.io.File representation of this path.
1428 *
1429 * <p>Caveat: the result may be useless if this path's getFileSystem() is not
1430 * the UNIX filesystem.
1431 */
1432 public File getPathFile() {
1433 return new File(getPathString());
1434 }
1435
tomlub1e8daf2017-10-27 15:58:21 -04001436 /** Prefer to use {@link #isWritable(FileSystem)}. */
1437 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001438 public boolean isWritable() throws IOException, FileNotFoundException {
tomlub1e8daf2017-10-27 15:58:21 -04001439 return isWritable(fileSystem);
1440 }
1441
1442 /**
1443 * Returns true if the file denoted by the current path, following symbolic links, is writable for
1444 * the current user.
1445 *
1446 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1447 * Path.
1448 *
1449 * @throws FileNotFoundException if the file does not exist, a dangling symbolic link was
1450 * encountered, or the file's metadata could not be read
1451 */
1452 public boolean isWritable(FileSystem fileSystem) throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001453 return fileSystem.isWritable(this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001454 }
1455
tomlub1e8daf2017-10-27 15:58:21 -04001456 /** Prefer to use {@link #setReadable(FileSystem, boolean)}. */
1457 @Deprecated
1458 public void setReadable(boolean readable) throws IOException, FileNotFoundException {
1459 setReadable(fileSystem, readable);
1460 }
1461
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001462 /**
tomlub1e8daf2017-10-27 15:58:21 -04001463 * Sets the read permissions of the file denoted by the current path, following symbolic links.
1464 * Permissions apply to the current user.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001465 *
tomlub1e8daf2017-10-27 15:58:21 -04001466 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1467 * Path.
1468 *
1469 * @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 +01001470 * @throws FileNotFoundException if the file does not exist
1471 * @throws IOException If the action cannot be taken (ie. permissions)
1472 */
tomlub1e8daf2017-10-27 15:58:21 -04001473 public void setReadable(FileSystem fileSystem, boolean readable)
1474 throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001475 fileSystem.setReadable(this, readable);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001476 }
1477
tomlub1e8daf2017-10-27 15:58:21 -04001478 /** Prefer to use {@link #setWritable(FileSystem, boolean)}. */
1479 @Deprecated
1480 public void setWritable(boolean writable) throws IOException, FileNotFoundException {
1481 setWritable(fileSystem, writable);
1482 }
1483
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001484 /**
tomlub1e8daf2017-10-27 15:58:21 -04001485 * Sets the write permissions of the file denoted by the current path, following symbolic links.
1486 * Permissions apply to the current user.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001487 *
1488 * <p>TODO(bazel-team): (2009) what about owner/group/others?
1489 *
tomlub1e8daf2017-10-27 15:58:21 -04001490 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1491 * Path.
1492 *
1493 * @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 +01001494 * @throws FileNotFoundException if the file does not exist
1495 * @throws IOException If the action cannot be taken (ie. permissions)
1496 */
tomlub1e8daf2017-10-27 15:58:21 -04001497 public void setWritable(FileSystem fileSystem, boolean writable)
1498 throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001499 fileSystem.setWritable(this, writable);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001500 }
1501
tomlub1e8daf2017-10-27 15:58:21 -04001502 /** Prefer to use {@link #isExecutable(FileSystem)}. */
1503 @Deprecated
1504 public boolean isExecutable() throws IOException, FileNotFoundException {
1505 return isExecutable(fileSystem);
1506 }
1507
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001508 /**
tomlub1e8daf2017-10-27 15:58:21 -04001509 * Returns true iff the file specified by the current path, following symbolic links, is
1510 * executable by the current user.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001511 *
tomlub1e8daf2017-10-27 15:58:21 -04001512 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1513 * Path.
1514 *
1515 * @throws FileNotFoundException if the file does not exist or a dangling symbolic link was
1516 * encountered
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001517 * @throws IOException if some other I/O error occurred
1518 */
tomlub1e8daf2017-10-27 15:58:21 -04001519 public boolean isExecutable(FileSystem fileSystem) throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001520 return fileSystem.isExecutable(this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001521 }
1522
tomlub1e8daf2017-10-27 15:58:21 -04001523 /** Prefer to use {@link #isReadable(FileSystem)}. */
1524 @Deprecated
1525 public boolean isReadable() throws IOException, FileNotFoundException {
1526 return isReadable(fileSystem);
1527 }
1528
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001529 /**
tomlub1e8daf2017-10-27 15:58:21 -04001530 * Returns true iff the file specified by the current path, following symbolic links, is readable
1531 * by the current user.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001532 *
tomlub1e8daf2017-10-27 15:58:21 -04001533 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1534 * Path.
1535 *
1536 * @throws FileNotFoundException if the file does not exist or a dangling symbolic link was
1537 * encountered
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001538 * @throws IOException if some other I/O error occurred
1539 */
tomlub1e8daf2017-10-27 15:58:21 -04001540 public boolean isReadable(FileSystem fileSystem) throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001541 return fileSystem.isReadable(this);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001542 }
1543
tomlub1e8daf2017-10-27 15:58:21 -04001544 /** Prefer to use {@link #setExecutable(FileSystem, boolean)}. */
1545 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001546 public void setExecutable(boolean executable) throws IOException, FileNotFoundException {
tomlub1e8daf2017-10-27 15:58:21 -04001547 setExecutable(fileSystem, executable);
1548 }
1549
1550 /**
1551 * Sets the execute permission on the file specified by the current path, following symbolic
1552 * links. Permissions apply to the current user.
1553 *
1554 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1555 * Path.
1556 *
1557 * @throws FileNotFoundException if the file does not exist or a dangling symbolic link was
1558 * encountered
1559 * @throws IOException if the metadata change failed, for example because of permissions
1560 */
1561 public void setExecutable(FileSystem fileSystem, boolean executable)
1562 throws IOException, FileNotFoundException {
aehligc801c392017-12-19 07:12:25 -08001563 fileSystem.setExecutable(this, executable);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001564 }
1565
tomlub1e8daf2017-10-27 15:58:21 -04001566 /** Prefer to use {@link #chmod(FileSystem, int)}. */
1567 @Deprecated
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001568 public void chmod(int mode) throws IOException {
tomlub1e8daf2017-10-27 15:58:21 -04001569 chmod(fileSystem, mode);
1570 }
1571
1572 /**
1573 * Sets the permissions on the file specified by the current path, following symbolic links. If
1574 * permission changes on this path's {@link FileSystem} are slow (e.g. one syscall per change),
1575 * this method should aim to be faster than setting each permission individually. If this path's
1576 * {@link FileSystem} does not support group and others permissions, those bits will be ignored.
1577 *
1578 * <p>This is a migration method. It will be deleted once the file system instance is deleted from
1579 * Path.
1580 *
1581 * @throws FileNotFoundException if the file does not exist or a dangling symbolic link was
1582 * encountered
1583 * @throws IOException if the metadata change failed, for example because of permissions
1584 */
1585 public void chmod(FileSystem fileSystem, int mode) throws IOException {
aehligc801c392017-12-19 07:12:25 -08001586 fileSystem.chmod(this, mode);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001587 }
1588
tomlub1e8daf2017-10-27 15:58:21 -04001589 /** Prefer to use {@link #prefetchPackageAsync(FileSystem, int)}. */
1590 @Deprecated
Googlerc804c662016-12-01 16:53:28 +00001591 public void prefetchPackageAsync(int maxDirs) {
tomlub1e8daf2017-10-27 15:58:21 -04001592 prefetchPackageAsync(fileSystem, maxDirs);
1593 }
1594
1595 public void prefetchPackageAsync(FileSystem fileSystem, int maxDirs) {
aehligc801c392017-12-19 07:12:25 -08001596 fileSystem.prefetchPackageAsync(this, maxDirs);
Googlerc804c662016-12-01 16:53:28 +00001597 }
1598
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001599 /**
1600 * Compare Paths of the same file system using their PathFragments.
1601 *
1602 * <p>Paths from different filesystems will be compared using the identity
1603 * hash code of their respective filesystems.
1604 */
1605 @Override
1606 public int compareTo(Path o) {
1607 // Fast-path.
1608 if (equals(o)) {
1609 return 0;
1610 }
1611
1612 // If they are on different file systems, the file system decides the ordering.
1613 FileSystem otherFs = o.getFileSystem();
1614 if (!fileSystem.equals(otherFs)) {
1615 int thisFileSystemHash = System.identityHashCode(fileSystem);
1616 int otherFileSystemHash = System.identityHashCode(otherFs);
1617 if (thisFileSystemHash < otherFileSystemHash) {
1618 return -1;
1619 } else if (thisFileSystemHash > otherFileSystemHash) {
1620 return 1;
1621 } else {
1622 // TODO(bazel-team): Add a name to every file system to be used here.
1623 return 0;
1624 }
1625 }
1626
1627 // Equal file system, but different paths, because of the canonicalization.
1628 // We expect to often compare Paths that are very similar, for example for files in the same
1629 // directory. This can be done efficiently by going up segment by segment until we get the
1630 // identical path (canonicalization again), and then just compare the immediate child segments.
1631 // Overall this is much faster than creating PathFragment instances, and comparing those, which
1632 // requires us to always go up to the top-level directory and copy all segments into a new
1633 // string array.
1634 // This was previously showing up as a hotspot in a profile of globbing a large directory.
Googlere1cd9502016-09-07 14:33:29 +00001635 Path a = this;
1636 Path b = o;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001637 int maxDepth = Math.min(a.depth, b.depth);
1638 while (a.depth > maxDepth) {
1639 a = a.getParentDirectory();
1640 }
1641 while (b.depth > maxDepth) {
1642 b = b.getParentDirectory();
1643 }
1644 // One is the child of the other.
1645 if (a.equals(b)) {
1646 // If a is the same as this, this.depth must be less than o.depth.
1647 return equals(a) ? -1 : 1;
1648 }
Googlere1cd9502016-09-07 14:33:29 +00001649 Path previousa;
1650 Path previousb;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001651 do {
1652 previousa = a;
1653 previousb = b;
1654 a = a.getParentDirectory();
1655 b = b.getParentDirectory();
Nathan Harmata3c74af02015-10-09 13:16:08 +00001656 } while (!a.equals(b)); // This has to happen eventually.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001657 return previousa.name.compareTo(previousb.name);
1658 }
1659}