jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtPath.java
changeset 37365 9cc4eb4d7491
parent 36511 9d0388c6b336
child 41121 91734a3ed04b
equal deleted inserted replaced
37364:80be215c8c51 37365:9cc4eb4d7491
     1 /*
     1 /*
     2  * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    22  * or visit www.oracle.com if you need additional information or have any
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 package jdk.internal.jrtfs;
    25 package jdk.internal.jrtfs;
    26 
    26 
       
    27 import java.io.File;
       
    28 import java.io.IOException;
       
    29 import java.io.InputStream;
       
    30 import java.io.OutputStream;
       
    31 import java.net.URI;
       
    32 import java.net.URISyntaxException;
       
    33 import java.nio.channels.FileChannel;
       
    34 import java.nio.channels.SeekableByteChannel;
       
    35 import java.nio.file.*;
       
    36 import java.nio.file.DirectoryStream.Filter;;
       
    37 import java.nio.file.attribute.BasicFileAttributes;
       
    38 import java.nio.file.attribute.BasicFileAttributeView;
       
    39 import java.nio.file.attribute.FileAttribute;
       
    40 import java.nio.file.attribute.FileTime;
       
    41 import java.util.Iterator;
       
    42 import java.util.Map;
       
    43 import java.util.NoSuchElementException;
       
    44 import java.util.Objects;
       
    45 import java.util.Set;
       
    46 import static java.nio.file.StandardOpenOption.*;
       
    47 import static java.nio.file.StandardCopyOption.*;
       
    48 
    27 /**
    49 /**
    28  * jrt Path implementation for jrt on .jimage files.
    50  * Base class for Path implementation of jrt file systems.
    29  *
    51  *
    30  * @implNote This class needs to maintain JDK 8 source compatibility.
    52  * @implNote This class needs to maintain JDK 8 source compatibility.
    31  *
    53  *
    32  * It is used internally in the JDK to implement jimage/jrtfs access,
    54  * It is used internally in the JDK to implement jimage/jrtfs access,
    33  * but also compiled and delivered as part of the jrtfs.jar to support access
    55  * but also compiled and delivered as part of the jrtfs.jar to support access
    34  * to the jimage file provided by the shipped JDK by tools running on JDK 8.
    56  * to the jimage file provided by the shipped JDK by tools running on JDK 8.
    35  */
    57  */
    36 final class JrtPath extends AbstractJrtPath {
    58 final class JrtPath implements Path {
    37 
    59 
    38     JrtPath(AbstractJrtFileSystem jrtfs, byte[] path) {
    60     final JrtFileSystem jrtfs;
    39         this(jrtfs, path, false);
    61     private final String path;
    40     }
    62     private volatile int[] offsets;
    41 
    63 
    42     JrtPath(AbstractJrtFileSystem jrtfs, byte[] path, boolean normalized) {
    64     JrtPath(JrtFileSystem jrtfs, String path) {
    43         super(jrtfs, path, normalized);
    65         this.jrtfs = jrtfs;
    44     }
    66         this.path = normalize(path);
    45 
    67         this.resolved = null;
    46     @Override
    68     }
    47     protected JrtPath newJrtPath(byte[] path) {
    69 
    48         return new JrtPath(jrtfs, path);
    70     JrtPath(JrtFileSystem jrtfs, String path, boolean normalized) {
    49     }
    71         this.jrtfs = jrtfs;
    50 
    72         this.path = normalized ? path : normalize(path);
    51     @Override
    73         this.resolved = null;
    52     protected JrtPath newJrtPath(byte[] path, boolean normalized) {
    74     }
    53         return new JrtPath(jrtfs, path, normalized);
    75 
       
    76     final String getName() {
       
    77         return path;
       
    78     }
       
    79 
       
    80     @Override
       
    81     public final JrtPath getRoot() {
       
    82         if (this.isAbsolute()) {
       
    83             return jrtfs.getRootPath();
       
    84         } else {
       
    85             return null;
       
    86         }
       
    87     }
       
    88 
       
    89     @Override
       
    90     public final JrtPath getFileName() {
       
    91         if (path.length() == 0)
       
    92             return this;
       
    93         if (path.length() == 1 && path.charAt(0) == '/')
       
    94             return null;
       
    95         int off = path.lastIndexOf('/');
       
    96         if (off == -1)
       
    97             return this;
       
    98         return new JrtPath(jrtfs, path.substring(off + 1), true);
       
    99     }
       
   100 
       
   101     @Override
       
   102     public final JrtPath getParent() {
       
   103         initOffsets();
       
   104         int count = offsets.length;
       
   105         if (count == 0) {     // no elements so no parent
       
   106             return null;
       
   107         }
       
   108         int off = offsets[count - 1] - 1;
       
   109         if (off <= 0) {       // parent is root only (may be null)
       
   110             return getRoot();
       
   111         }
       
   112         return new JrtPath(jrtfs, path.substring(0, off));
       
   113     }
       
   114 
       
   115     @Override
       
   116     public final int getNameCount() {
       
   117         initOffsets();
       
   118         return offsets.length;
       
   119     }
       
   120 
       
   121     @Override
       
   122     public final JrtPath getName(int index) {
       
   123         initOffsets();
       
   124         if (index < 0 || index >= offsets.length) {
       
   125             throw new IllegalArgumentException();
       
   126         }
       
   127         int begin = offsets[index];
       
   128         int end;
       
   129         if (index == (offsets.length - 1)) {
       
   130             end = path.length();
       
   131         } else {
       
   132             end = offsets[index + 1];
       
   133         }
       
   134         return new JrtPath(jrtfs, path.substring(begin, end));
       
   135     }
       
   136 
       
   137     @Override
       
   138     public final JrtPath subpath(int beginIndex, int endIndex) {
       
   139         initOffsets();
       
   140         if (beginIndex < 0 || endIndex > offsets.length ||
       
   141             beginIndex >= endIndex) {
       
   142             throw new IllegalArgumentException();
       
   143         }
       
   144         // starting/ending offsets
       
   145         int begin = offsets[beginIndex];
       
   146         int end;
       
   147         if (endIndex == offsets.length) {
       
   148             end = path.length();
       
   149         } else {
       
   150             end = offsets[endIndex];
       
   151         }
       
   152         return new JrtPath(jrtfs, path.substring(begin, end));
       
   153     }
       
   154 
       
   155     @Override
       
   156     public final JrtPath toRealPath(LinkOption... options) throws IOException {
       
   157         return jrtfs.toRealPath(this, options);
       
   158     }
       
   159 
       
   160     @Override
       
   161     public final JrtPath toAbsolutePath() {
       
   162         if (isAbsolute())
       
   163             return this;
       
   164         return new JrtPath(jrtfs, "/" + path, true);
       
   165     }
       
   166 
       
   167     @Override
       
   168     public final URI toUri() {
       
   169         try {
       
   170             return new URI("jrt", toAbsolutePath().path, null);
       
   171         } catch (URISyntaxException ex) {
       
   172             throw new AssertionError(ex);
       
   173         }
       
   174     }
       
   175 
       
   176     private boolean equalsNameAt(JrtPath other, int index) {
       
   177         int mbegin = offsets[index];
       
   178         int mlen;
       
   179         if (index == (offsets.length - 1)) {
       
   180             mlen = path.length() - mbegin;
       
   181         } else {
       
   182             mlen = offsets[index + 1] - mbegin - 1;
       
   183         }
       
   184         int obegin = other.offsets[index];
       
   185         int olen;
       
   186         if (index == (other.offsets.length - 1)) {
       
   187             olen = other.path.length() - obegin;
       
   188         } else {
       
   189             olen = other.offsets[index + 1] - obegin - 1;
       
   190         }
       
   191         if (mlen != olen) {
       
   192             return false;
       
   193         }
       
   194         int n = 0;
       
   195         while (n < mlen) {
       
   196             if (path.charAt(mbegin + n) != other.path.charAt(obegin + n)) {
       
   197                 return false;
       
   198             }
       
   199             n++;
       
   200         }
       
   201         return true;
       
   202     }
       
   203 
       
   204     @Override
       
   205     public final JrtPath relativize(Path other) {
       
   206         final JrtPath o = checkPath(other);
       
   207         if (o.equals(this)) {
       
   208             return new JrtPath(jrtfs, "", true);
       
   209         }
       
   210         if (path.length() == 0) {
       
   211             return o;
       
   212         }
       
   213         if (jrtfs != o.jrtfs || isAbsolute() != o.isAbsolute()) {
       
   214             throw new IllegalArgumentException();
       
   215         }
       
   216         final String tp = this.path;
       
   217         final String op = o.path;
       
   218         if (op.startsWith(tp)) {    // fast path
       
   219             int off = tp.length();
       
   220             if (op.charAt(off - 1) == '/')
       
   221                 return new JrtPath(jrtfs, op.substring(off), true);
       
   222             if (op.charAt(off) == '/')
       
   223                 return new JrtPath(jrtfs, op.substring(off + 1), true);
       
   224         }
       
   225         int mc = this.getNameCount();
       
   226         int oc = o.getNameCount();
       
   227         int n = Math.min(mc, oc);
       
   228         int i = 0;
       
   229         while (i < n) {
       
   230             if (!equalsNameAt(o, i)) {
       
   231                 break;
       
   232             }
       
   233             i++;
       
   234         }
       
   235         int dotdots = mc - i;
       
   236         int len = dotdots * 3 - 1;
       
   237         if (i < oc) {
       
   238             len += (o.path.length() - o.offsets[i] + 1);
       
   239         }
       
   240         StringBuilder sb  = new StringBuilder(len);
       
   241         while (dotdots > 0) {
       
   242             sb.append("..");
       
   243             if (sb.length() < len) {  // no tailing slash at the end
       
   244                 sb.append('/');
       
   245             }
       
   246             dotdots--;
       
   247         }
       
   248         if (i < oc) {
       
   249             sb.append(o.path, o.offsets[i], o.path.length());
       
   250         }
       
   251         return new JrtPath(jrtfs, sb.toString(), true);
       
   252     }
       
   253 
       
   254     @Override
       
   255     public JrtFileSystem getFileSystem() {
       
   256         return jrtfs;
       
   257     }
       
   258 
       
   259     @Override
       
   260     public final boolean isAbsolute() {
       
   261         return path.length() > 0 && path.charAt(0) == '/';
       
   262     }
       
   263 
       
   264     @Override
       
   265     public final JrtPath resolve(Path other) {
       
   266         final JrtPath o = checkPath(other);
       
   267         if (this.path.length() == 0 || o.isAbsolute()) {
       
   268             return o;
       
   269         }
       
   270         if (o.path.length() == 0) {
       
   271             return this;
       
   272         }
       
   273         StringBuilder sb = new StringBuilder(path.length() + o.path.length());
       
   274         sb.append(path);
       
   275         if (path.charAt(path.length() - 1) != '/')
       
   276             sb.append('/');
       
   277         sb.append(o.path);
       
   278         return new JrtPath(jrtfs, sb.toString(), true);
       
   279     }
       
   280 
       
   281     @Override
       
   282     public final Path resolveSibling(Path other) {
       
   283         Objects.requireNonNull(other, "other");
       
   284         Path parent = getParent();
       
   285         return (parent == null) ? other : parent.resolve(other);
       
   286     }
       
   287 
       
   288     @Override
       
   289     public final boolean startsWith(Path other) {
       
   290         if (!(Objects.requireNonNull(other) instanceof JrtPath))
       
   291             return false;
       
   292         final JrtPath o = (JrtPath)other;
       
   293         final String tp = this.path;
       
   294         final String op = o.path;
       
   295         if (isAbsolute() != o.isAbsolute() || !tp.startsWith(op)) {
       
   296             return false;
       
   297         }
       
   298         int off = op.length();
       
   299         if (off == 0) {
       
   300             return tp.length() == 0;
       
   301         }
       
   302         // check match is on name boundary
       
   303         return tp.length() == off || tp.charAt(off) == '/' ||
       
   304                off == 0 || op.charAt(off - 1) == '/';
       
   305     }
       
   306 
       
   307     @Override
       
   308     public final boolean endsWith(Path other) {
       
   309         if (!(Objects.requireNonNull(other) instanceof JrtPath))
       
   310             return false;
       
   311         final JrtPath o = (JrtPath)other;
       
   312         final JrtPath t = this;
       
   313         int olast = o.path.length() - 1;
       
   314         if (olast > 0 && o.path.charAt(olast) == '/') {
       
   315             olast--;
       
   316         }
       
   317         int last = t.path.length() - 1;
       
   318         if (last > 0 && t.path.charAt(last) == '/') {
       
   319             last--;
       
   320         }
       
   321         if (olast == -1) {  // o.path.length == 0
       
   322             return last == -1;
       
   323         }
       
   324         if ((o.isAbsolute() && (!t.isAbsolute() || olast != last))
       
   325             || last < olast) {
       
   326             return false;
       
   327         }
       
   328         for (; olast >= 0; olast--, last--) {
       
   329             if (o.path.charAt(olast) != t.path.charAt(last)) {
       
   330                 return false;
       
   331             }
       
   332         }
       
   333         return o.path.charAt(olast + 1) == '/' ||
       
   334                last == -1 || t.path.charAt(last) == '/';
       
   335     }
       
   336 
       
   337     @Override
       
   338     public final JrtPath resolve(String other) {
       
   339         return resolve(getFileSystem().getPath(other));
       
   340     }
       
   341 
       
   342     @Override
       
   343     public final Path resolveSibling(String other) {
       
   344         return resolveSibling(getFileSystem().getPath(other));
       
   345     }
       
   346 
       
   347     @Override
       
   348     public final boolean startsWith(String other) {
       
   349         return startsWith(getFileSystem().getPath(other));
       
   350     }
       
   351 
       
   352     @Override
       
   353     public final boolean endsWith(String other) {
       
   354         return endsWith(getFileSystem().getPath(other));
       
   355     }
       
   356 
       
   357     @Override
       
   358     public final JrtPath normalize() {
       
   359         String res = getResolved();
       
   360         if (res == path) {  // no change
       
   361             return this;
       
   362         }
       
   363         return new JrtPath(jrtfs, res, true);
       
   364     }
       
   365 
       
   366     private JrtPath checkPath(Path path) {
       
   367         Objects.requireNonNull(path);
       
   368         if (!(path instanceof JrtPath))
       
   369             throw new ProviderMismatchException();
       
   370         return (JrtPath) path;
       
   371     }
       
   372 
       
   373     // create offset list if not already created
       
   374     private void initOffsets() {
       
   375         if (this.offsets == null) {
       
   376             int len = path.length();
       
   377             // count names
       
   378             int count = 0;
       
   379             int off = 0;
       
   380             while (off < len) {
       
   381                 char c = path.charAt(off++);
       
   382                 if (c != '/') {
       
   383                     count++;
       
   384                     off = path.indexOf('/', off);
       
   385                     if (off == -1)
       
   386                         break;
       
   387                 }
       
   388             }
       
   389             // populate offsets
       
   390             int[] offsets = new int[count];
       
   391             count = 0;
       
   392             off = 0;
       
   393             while (off < len) {
       
   394                 char c = path.charAt(off);
       
   395                 if (c == '/') {
       
   396                     off++;
       
   397                 } else {
       
   398                     offsets[count++] = off++;
       
   399                     off = path.indexOf('/', off);
       
   400                     if (off == -1)
       
   401                         break;
       
   402                 }
       
   403             }
       
   404             this.offsets = offsets;
       
   405         }
       
   406     }
       
   407 
       
   408     private volatile String resolved;
       
   409 
       
   410     final String getResolvedPath() {
       
   411         String r = resolved;
       
   412         if (r == null) {
       
   413             if (isAbsolute()) {
       
   414                 r = getResolved();
       
   415             } else {
       
   416                 r = toAbsolutePath().getResolvedPath();
       
   417             }
       
   418             resolved = r;
       
   419         }
       
   420         return r;
       
   421     }
       
   422 
       
   423     // removes redundant slashs, replace "\" to separator "/"
       
   424     // and check for invalid characters
       
   425     private static String normalize(String path) {
       
   426         int len = path.length();
       
   427         if (len == 0) {
       
   428             return path;
       
   429         }
       
   430         char prevC = 0;
       
   431         for (int i = 0; i < len; i++) {
       
   432             char c = path.charAt(i);
       
   433             if (c == '\\' || c == '\u0000') {
       
   434                 return normalize(path, i);
       
   435             }
       
   436             if (c == '/' && prevC == '/') {
       
   437                 return normalize(path, i - 1);
       
   438             }
       
   439             prevC = c;
       
   440         }
       
   441         if (prevC == '/' && len > 1) {
       
   442             return path.substring(0, len - 1);
       
   443         }
       
   444         return path;
       
   445     }
       
   446 
       
   447     private static String normalize(String path, int off) {
       
   448         int len = path.length();
       
   449         StringBuilder to = new StringBuilder(len);
       
   450         to.append(path, 0, off);
       
   451         char prevC = 0;
       
   452         while (off < len) {
       
   453             char c = path.charAt(off++);
       
   454             if (c == '\\') {
       
   455                 c = '/';
       
   456             }
       
   457             if (c == '/' && prevC == '/') {
       
   458                 continue;
       
   459             }
       
   460             if (c == '\u0000') {
       
   461                 throw new InvalidPathException(path,
       
   462                         "Path: nul character not allowed");
       
   463             }
       
   464             to.append(c);
       
   465             prevC = c;
       
   466         }
       
   467         len = to.length();
       
   468         if (len > 1 && to.charAt(len - 1) == '/') {
       
   469             to.deleteCharAt(len - 1);
       
   470         }
       
   471         return to.toString();
       
   472     }
       
   473 
       
   474     // Remove DotSlash(./) and resolve DotDot (..) components
       
   475     private String getResolved() {
       
   476         if (path.length() == 0) {
       
   477             return path;
       
   478         }
       
   479         if (path.indexOf('.') == -1) {
       
   480             return path;
       
   481         }
       
   482         int length = path.length();
       
   483         char[] to = new char[length];
       
   484         int nc = getNameCount();
       
   485         int[] lastM = new int[nc];
       
   486         int lastMOff = -1;
       
   487         int m = 0;
       
   488         for (int i = 0; i < nc; i++) {
       
   489             int n = offsets[i];
       
   490             int len = (i == offsets.length - 1) ? length - n
       
   491                                                 : offsets[i + 1] - n - 1;
       
   492             if (len == 1 && path.charAt(n) == '.') {
       
   493                 if (m == 0 && path.charAt(0) == '/')   // absolute path
       
   494                     to[m++] = '/';
       
   495                 continue;
       
   496             }
       
   497             if (len == 2 && path.charAt(n) == '.' && path.charAt(n + 1) == '.') {
       
   498                 if (lastMOff >= 0) {
       
   499                     m = lastM[lastMOff--];    // retreat
       
   500                     continue;
       
   501                 }
       
   502                 if (path.charAt(0) == '/') {  // "/../xyz" skip
       
   503                     if (m == 0)
       
   504                         to[m++] = '/';
       
   505                 } else {                      // "../xyz" -> "../xyz"
       
   506                     if (m != 0 && to[m-1] != '/')
       
   507                         to[m++] = '/';
       
   508                     while (len-- > 0)
       
   509                         to[m++] = path.charAt(n++);
       
   510                 }
       
   511                 continue;
       
   512             }
       
   513             if (m == 0 && path.charAt(0) == '/' ||   // absolute path
       
   514                 m != 0 && to[m-1] != '/') {   // not the first name
       
   515                 to[m++] = '/';
       
   516             }
       
   517             lastM[++lastMOff] = m;
       
   518             while (len-- > 0)
       
   519                 to[m++] = path.charAt(n++);
       
   520         }
       
   521         if (m > 1 && to[m - 1] == '/')
       
   522             m--;
       
   523         return (m == to.length) ? new String(to) : new String(to, 0, m);
       
   524     }
       
   525 
       
   526     @Override
       
   527     public final String toString() {
       
   528         return path;
       
   529     }
       
   530 
       
   531     @Override
       
   532     public final int hashCode() {
       
   533         return path.hashCode();
       
   534     }
       
   535 
       
   536     @Override
       
   537     public final boolean equals(Object obj) {
       
   538         return obj instanceof JrtPath &&
       
   539                this.path.equals(((JrtPath) obj).path);
       
   540     }
       
   541 
       
   542     @Override
       
   543     public final int compareTo(Path other) {
       
   544         final JrtPath o = checkPath(other);
       
   545         return path.compareTo(o.path);
       
   546     }
       
   547 
       
   548     @Override
       
   549     public final WatchKey register(
       
   550             WatchService watcher,
       
   551             WatchEvent.Kind<?>[] events,
       
   552             WatchEvent.Modifier... modifiers) {
       
   553         Objects.requireNonNull(watcher, "watcher");
       
   554         Objects.requireNonNull(events, "events");
       
   555         Objects.requireNonNull(modifiers, "modifiers");
       
   556         throw new UnsupportedOperationException();
       
   557     }
       
   558 
       
   559     @Override
       
   560     public final WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events) {
       
   561         return register(watcher, events, new WatchEvent.Modifier[0]);
       
   562     }
       
   563 
       
   564     @Override
       
   565     public final File toFile() {
       
   566         throw new UnsupportedOperationException();
       
   567     }
       
   568 
       
   569     @Override
       
   570     public final Iterator<Path> iterator() {
       
   571         return new Iterator<Path>() {
       
   572             private int i = 0;
       
   573 
       
   574             @Override
       
   575             public boolean hasNext() {
       
   576                 return (i < getNameCount());
       
   577             }
       
   578 
       
   579             @Override
       
   580             public Path next() {
       
   581                 if (i < getNameCount()) {
       
   582                     Path result = getName(i);
       
   583                     i++;
       
   584                     return result;
       
   585                 } else {
       
   586                     throw new NoSuchElementException();
       
   587                 }
       
   588             }
       
   589 
       
   590             @Override
       
   591             public void remove() {
       
   592                 throw new ReadOnlyFileSystemException();
       
   593             }
       
   594         };
       
   595     }
       
   596 
       
   597     // Helpers for JrtFileSystemProvider and JrtFileSystem
       
   598 
       
   599     final JrtPath readSymbolicLink() throws IOException {
       
   600         if (!jrtfs.isLink(this)) {
       
   601             throw new IOException("not a symbolic link");
       
   602         }
       
   603         return jrtfs.resolveLink(this);
       
   604     }
       
   605 
       
   606     final boolean isHidden() {
       
   607         return false;
       
   608     }
       
   609 
       
   610     final void createDirectory(FileAttribute<?>... attrs)
       
   611             throws IOException {
       
   612         jrtfs.createDirectory(this, attrs);
       
   613     }
       
   614 
       
   615     final InputStream newInputStream(OpenOption... options) throws IOException {
       
   616         if (options.length > 0) {
       
   617             for (OpenOption opt : options) {
       
   618                 if (opt != READ) {
       
   619                     throw new UnsupportedOperationException("'" + opt + "' not allowed");
       
   620                 }
       
   621             }
       
   622         }
       
   623         return jrtfs.newInputStream(this);
       
   624     }
       
   625 
       
   626     final DirectoryStream<Path> newDirectoryStream(Filter<? super Path> filter)
       
   627             throws IOException {
       
   628         return new JrtDirectoryStream(this, filter);
       
   629     }
       
   630 
       
   631     final void delete() throws IOException {
       
   632         jrtfs.deleteFile(this, true);
       
   633     }
       
   634 
       
   635     final void deleteIfExists() throws IOException {
       
   636         jrtfs.deleteFile(this, false);
       
   637     }
       
   638 
       
   639     final JrtFileAttributes getAttributes(LinkOption... options) throws IOException {
       
   640         JrtFileAttributes zfas = jrtfs.getFileAttributes(this, options);
       
   641         if (zfas == null) {
       
   642             throw new NoSuchFileException(toString());
       
   643         }
       
   644         return zfas;
       
   645     }
       
   646 
       
   647     final void setAttribute(String attribute, Object value, LinkOption... options)
       
   648             throws IOException {
       
   649         JrtFileAttributeView.setAttribute(this, attribute, value);
       
   650     }
       
   651 
       
   652     final Map<String, Object> readAttributes(String attributes, LinkOption... options)
       
   653             throws IOException {
       
   654         return JrtFileAttributeView.readAttributes(this, attributes, options);
       
   655     }
       
   656 
       
   657     final void setTimes(FileTime mtime, FileTime atime, FileTime ctime)
       
   658             throws IOException {
       
   659         jrtfs.setTimes(this, mtime, atime, ctime);
       
   660     }
       
   661 
       
   662     final FileStore getFileStore() throws IOException {
       
   663         // each JrtFileSystem only has one root (as requested for now)
       
   664         if (exists()) {
       
   665             return jrtfs.getFileStore(this);
       
   666         }
       
   667         throw new NoSuchFileException(path);
       
   668     }
       
   669 
       
   670     final boolean isSameFile(Path other) throws IOException {
       
   671         if (this == other || this.equals(other)) {
       
   672             return true;
       
   673         }
       
   674         if (other == null || this.getFileSystem() != other.getFileSystem()) {
       
   675             return false;
       
   676         }
       
   677         this.checkAccess();
       
   678         JrtPath o = (JrtPath) other;
       
   679         o.checkAccess();
       
   680         return this.getResolvedPath().equals(o.getResolvedPath()) ||
       
   681                jrtfs.isSameFile(this, o);
       
   682     }
       
   683 
       
   684     final SeekableByteChannel newByteChannel(Set<? extends OpenOption> options,
       
   685                                              FileAttribute<?>... attrs)
       
   686             throws IOException
       
   687     {
       
   688         return jrtfs.newByteChannel(this, options, attrs);
       
   689     }
       
   690 
       
   691     final FileChannel newFileChannel(Set<? extends OpenOption> options,
       
   692             FileAttribute<?>... attrs)
       
   693             throws IOException {
       
   694         return jrtfs.newFileChannel(this, options, attrs);
       
   695     }
       
   696 
       
   697     final void checkAccess(AccessMode... modes) throws IOException {
       
   698         if (modes.length == 0) {    // check if the path exists
       
   699             jrtfs.checkNode(this);  // no need to follow link. the "link" node
       
   700                                     // is built from real node under "/module"
       
   701         } else {
       
   702             boolean w = false;
       
   703             for (AccessMode mode : modes) {
       
   704                 switch (mode) {
       
   705                     case READ:
       
   706                         break;
       
   707                     case WRITE:
       
   708                         w = true;
       
   709                         break;
       
   710                     case EXECUTE:
       
   711                         throw new AccessDeniedException(toString());
       
   712                     default:
       
   713                         throw new UnsupportedOperationException();
       
   714                 }
       
   715             }
       
   716             jrtfs.checkNode(this);
       
   717             if (w && jrtfs.isReadOnly()) {
       
   718                 throw new AccessDeniedException(toString());
       
   719             }
       
   720         }
       
   721     }
       
   722 
       
   723     final boolean exists() {
       
   724         try {
       
   725             return jrtfs.exists(this);
       
   726         } catch (IOException x) {}
       
   727         return false;
       
   728     }
       
   729 
       
   730     final OutputStream newOutputStream(OpenOption... options) throws IOException {
       
   731         if (options.length == 0) {
       
   732             return jrtfs.newOutputStream(this, CREATE_NEW, WRITE);
       
   733         }
       
   734         return jrtfs.newOutputStream(this, options);
       
   735     }
       
   736 
       
   737     final void move(JrtPath target, CopyOption... options) throws IOException {
       
   738         if (this.jrtfs == target.jrtfs) {
       
   739             jrtfs.copyFile(true, this, target, options);
       
   740         } else {
       
   741             copyToTarget(target, options);
       
   742             delete();
       
   743         }
       
   744     }
       
   745 
       
   746     final void copy(JrtPath target, CopyOption... options) throws IOException {
       
   747         if (this.jrtfs == target.jrtfs) {
       
   748             jrtfs.copyFile(false, this, target, options);
       
   749         } else {
       
   750             copyToTarget(target, options);
       
   751         }
       
   752     }
       
   753 
       
   754     private void copyToTarget(JrtPath target, CopyOption... options)
       
   755             throws IOException {
       
   756         boolean replaceExisting = false;
       
   757         boolean copyAttrs = false;
       
   758         for (CopyOption opt : options) {
       
   759             if (opt == REPLACE_EXISTING) {
       
   760                 replaceExisting = true;
       
   761             } else if (opt == COPY_ATTRIBUTES) {
       
   762                 copyAttrs = true;
       
   763             }
       
   764         }
       
   765         // attributes of source file
       
   766         BasicFileAttributes jrtfas = getAttributes();
       
   767         // check if target exists
       
   768         boolean exists;
       
   769         if (replaceExisting) {
       
   770             try {
       
   771                 target.deleteIfExists();
       
   772                 exists = false;
       
   773             } catch (DirectoryNotEmptyException x) {
       
   774                 exists = true;
       
   775             }
       
   776         } else {
       
   777             exists = target.exists();
       
   778         }
       
   779         if (exists) {
       
   780             throw new FileAlreadyExistsException(target.toString());
       
   781         }
       
   782         if (jrtfas.isDirectory()) {
       
   783             // create directory or file
       
   784             target.createDirectory();
       
   785         } else {
       
   786             try (InputStream is = jrtfs.newInputStream(this);
       
   787                  OutputStream os = target.newOutputStream()) {
       
   788                 byte[] buf = new byte[8192];
       
   789                 int n;
       
   790                 while ((n = is.read(buf)) != -1) {
       
   791                     os.write(buf, 0, n);
       
   792                 }
       
   793             }
       
   794         }
       
   795         if (copyAttrs) {
       
   796             BasicFileAttributeView view =
       
   797                 Files.getFileAttributeView(target, BasicFileAttributeView.class);
       
   798             try {
       
   799                 view.setTimes(jrtfas.lastModifiedTime(),
       
   800                               jrtfas.lastAccessTime(),
       
   801                               jrtfas.creationTime());
       
   802             } catch (IOException x) {
       
   803                 try {
       
   804                     target.delete();  // rollback?
       
   805                 } catch (IOException ignore) {}
       
   806                 throw x;
       
   807             }
       
   808         }
    54     }
   809     }
    55 }
   810 }